* 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 (20):
mtd: nand: qcom: fix read failure without complete bootchain
mtd: nand: qcom: support for NAND controller properties
mtd: nand: qcom: add bam property for QPIC NAND controller
mtd: nand: qcom: add and initialize QPIC DMA resources
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 transfer 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 | 866 ++++++++++++++++++---
2 files changed, 814 insertions(+), 115 deletions(-)
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
The current driver only supports EBI2 NAND controller which uses
ADM DMA. The latest QCOM SoC uses QPIC NAND controller with BAM
DMA. NAND registers and programming sequence are same for EBI2
and QPIC NAND so the same driver can support QPIC NAND also by
adding the BAM DMA support. This patch adds the is_bam in NAND
property which will be checked for determining the DMA engine type.
Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/mtd/nand/qcom_nandc.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 79f7de8..2d44eeb 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -328,9 +328,11 @@ struct qcom_nand_host {
* This data type corresponds to the NAND controller properties which varies
* among different NAND controllers.
* @ecc_modes - ecc mode for NAND
+ * @is_bam - whether NAND controller is using BAM
*/
struct qcom_nandc_props {
u32 ecc_modes;
+ bool is_bam;
};
static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
@@ -2251,6 +2253,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,
};
/*
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
1. QPIC NAND controller uses 3 BAM channels: command, data tx
and data rx while EBI2 NAND controller uses only single ADM
channel.
2. CRCI is only required for ADM DMA and it's not required for
BAM DMA.
Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/mtd/nand/qcom_nandc.c | 86 ++++++++++++++++++++++++++++++++++---------
1 file changed, 68 insertions(+), 18 deletions(-)
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 2d44eeb..59b764a 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -255,9 +255,22 @@ struct qcom_nand_controller {
struct clk *core_clk;
struct clk *aon_clk;
- struct dma_chan *chan;
- unsigned int cmd_crci;
- unsigned int data_crci;
+ union {
+ /* will be used only by QPIC for BAM DMA */
+ struct {
+ struct dma_chan *tx_chan;
+ struct dma_chan *rx_chan;
+ struct dma_chan *cmd_chan;
+ };
+
+ /* will be used only by EBI2 for ADM DMA */
+ struct {
+ struct dma_chan *chan;
+ unsigned int cmd_crci;
+ unsigned int data_crci;
+ };
+ };
+
struct list_head desc_list;
u8 *data_buffer;
@@ -1989,10 +2002,31 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
if (!nandc->reg_read_buf)
return -ENOMEM;
- nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx");
- if (!nandc->chan) {
- dev_err(nandc->dev, "failed to request slave channel\n");
- return -ENODEV;
+ if (nandc->props->is_bam) {
+ nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx");
+ if (!nandc->tx_chan) {
+ dev_err(nandc->dev, "failed to request tx channel\n");
+ return -ENODEV;
+ }
+
+ nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx");
+ if (!nandc->rx_chan) {
+ dev_err(nandc->dev, "failed to request rx channel\n");
+ return -ENODEV;
+ }
+
+ nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd");
+ if (!nandc->cmd_chan) {
+ dev_err(nandc->dev, "failed to request cmd channel\n");
+ return -ENODEV;
+ }
+ } else {
+ nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx");
+ if (!nandc->chan) {
+ dev_err(nandc->dev,
+ "failed to request slave channel\n");
+ return -ENODEV;
+ }
}
INIT_LIST_HEAD(&nandc->desc_list);
@@ -2005,7 +2039,19 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
{
- dma_release_channel(nandc->chan);
+ if (nandc->props->is_bam) {
+ if (nandc->tx_chan)
+ dma_release_channel(nandc->tx_chan);
+
+ if (nandc->rx_chan)
+ dma_release_channel(nandc->rx_chan);
+
+ if (nandc->cmd_chan)
+ dma_release_channel(nandc->cmd_chan);
+ } else {
+ if (nandc->chan)
+ dma_release_channel(nandc->chan);
+ }
}
/* one time setup of a few nand controller registers */
@@ -2146,16 +2192,20 @@ static int qcom_nandc_parse_dt(struct platform_device *pdev)
struct device_node *np = nandc->dev->of_node;
int ret;
- ret = of_property_read_u32(np, "qcom,cmd-crci", &nandc->cmd_crci);
- if (ret) {
- dev_err(nandc->dev, "command CRCI unspecified\n");
- return ret;
- }
+ if (!nandc->props->is_bam) {
+ ret = of_property_read_u32(np, "qcom,cmd-crci",
+ &nandc->cmd_crci);
+ if (ret) {
+ dev_err(nandc->dev, "command CRCI unspecified\n");
+ return ret;
+ }
- ret = of_property_read_u32(np, "qcom,data-crci", &nandc->data_crci);
- if (ret) {
- dev_err(nandc->dev, "data CRCI unspecified\n");
- return ret;
+ ret = of_property_read_u32(np, "qcom,data-crci",
+ &nandc->data_crci);
+ if (ret) {
+ dev_err(nandc->dev, "data CRCI unspecified\n");
+ return ret;
+ }
}
return 0;
@@ -2205,7 +2255,7 @@ static int qcom_nandc_probe(struct platform_device *pdev)
ret = qcom_nandc_alloc(nandc);
if (ret)
- return ret;
+ goto err_core_clk;
ret = clk_prepare_enable(nandc->core_clk);
if (ret)
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Currently driver data is being assigned directly with ECC modes.
Now, the plan is to add more NAND controller versions which will
have different properties. This patch reorganizes the current driver
data assignment by creating NAND controller properties structure
which will contain all properties specific to NAND controller.
Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/mtd/nand/qcom_nandc.c | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index e5cb8f1..79f7de8 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -240,7 +240,7 @@ struct nandc_regs {
* writes. contains the register values to be
* written to controller
* @cmd1/vld: some fixed controller register values
- * @ecc_modes: supported ECC modes by the current controller,
+ * @props: properties of current NAND controller,
* initialized via DT match data
*/
struct qcom_nand_controller {
@@ -271,7 +271,7 @@ struct qcom_nand_controller {
struct nandc_regs *regs;
u32 cmd1, vld;
- u32 ecc_modes;
+ const struct qcom_nandc_props *props;
};
/*
@@ -324,6 +324,15 @@ struct qcom_nand_host {
u32 clrreadstatus;
};
+/*
+ * This data type corresponds to the NAND controller properties which varies
+ * among different NAND controllers.
+ * @ecc_modes - ecc mode for NAND
+ */
+struct qcom_nandc_props {
+ u32 ecc_modes;
+};
+
static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
{
return container_of(chip, struct qcom_nand_host, chip);
@@ -1824,7 +1833,7 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
* uses lesser bytes for ECC. If RS is used, the ECC bytes is
* always 10 bytes
*/
- if (nandc->ecc_modes & ECC_BCH_4BIT) {
+ if (nandc->props->ecc_modes & ECC_BCH_4BIT) {
/* BCH */
host->bch_enabled = true;
ecc_mode = 0;
@@ -2171,7 +2180,7 @@ static int qcom_nandc_probe(struct platform_device *pdev)
return -ENODEV;
}
- nandc->ecc_modes = (unsigned long)dev_data;
+ nandc->props = dev_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nandc->base = devm_ioremap_resource(dev, res);
@@ -2240,15 +2249,18 @@ static int qcom_nandc_remove(struct platform_device *pdev)
return 0;
}
-#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT)
+static const struct qcom_nandc_props ipq806x_nandc_props = {
+ .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
+};
/*
* data will hold a struct pointer containing more differences once we support
* more controller variants
*/
static const struct of_device_id qcom_nandc_of_match[] = {
- { .compatible = "qcom,ipq806x-nand",
- .data = (void *)EBI2_NANDC_ECC_MODES,
+ {
+ .compatible = "qcom,ipq806x-nand",
+ .data = &ipq806x_nandc_props,
},
{}
};
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
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.
Signed-off-by: Abhishek Sahu <[email protected]>
---
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
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]>
---
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
The BAM has multiple flags to control the transfer. This patch
adds flags parameter in register and data transfer 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.
Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/mtd/nand/qcom_nandc.c | 122 ++++++++++++++++++++++++------------------
1 file changed, 70 insertions(+), 52 deletions(-)
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index f52a692..d9c8a6b 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -180,6 +180,14 @@
#define QPIC_PER_CW_CMD_SGL 32
#define QPIC_PER_CW_DATA_SGL 8
+/* Flags used for BAM DMA desc preparation*/
+/* Don't set the EOT in current tx sgl */
+#define NAND_BAM_NO_EOT BIT(0)
+/* Set the NWD flag in current sgl */
+#define NAND_BAM_NWD BIT(1)
+/* Finish writing in the current sgl and start writing in another sgl */
+#define NAND_BAM_NEXT_SGL BIT(2)
+
/*
* This data type corresponds to the BAM transaction which will be used for all
* NAND transfers.
@@ -731,7 +739,7 @@ static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
* @num_regs: number of registers to read
*/
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;
@@ -755,7 +763,7 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
* @num_regs: number of registers to write
*/
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 +775,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;
@@ -788,7 +799,7 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
* @size: DMA transaction size in bytes
*/
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);
}
@@ -802,7 +813,7 @@ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
* @size: DMA transaction size in bytes
*/
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 +824,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 +835,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 +859,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 +871,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 +924,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 +933,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 +959,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 +986,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 +1003,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 +1402,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 +1418,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 +1460,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 +1529,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 +1608,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 +1622,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 +1677,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 +1746,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 +1831,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
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.
Signed-off-by: Abhishek Sahu <[email protected]>
---
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 b452cfb..3d9fd7f 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -200,6 +200,11 @@
#define NAND_BAM_NWD BIT(1)
/* Finish writing in the current sgl and start writing in another 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
@@ -278,6 +283,8 @@ struct nandc_regs {
__le32 read_location2;
__le32 read_location3;
+ __le32 erased_cw_detect_cfg_clr;
+ __le32 erased_cw_detect_cfg_set;
};
/*
@@ -805,6 +812,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 = ®s->erased_cw_detect_cfg_set;
+ else
+ vaddr = ®s->erased_cw_detect_cfg_clr;
+ }
+
if (first == NAND_EXEC_CMD)
flags |= NAND_BAM_NWD;
@@ -857,6 +871,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);
}
/*
@@ -2258,6 +2275,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
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.
Signed-off-by: Abhishek Sahu <[email protected]>
---
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 3d9fd7f..ae873d3 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
@@ -1035,7 +1038,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);
@@ -2408,12 +2412,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
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.
Signed-off-by: Abhishek Sahu <[email protected]>
---
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 ae873d3..85fbe00 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -470,6 +470,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);
@@ -701,6 +722,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)
@@ -848,6 +904,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);
}
@@ -862,6 +921,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);
}
@@ -1149,6 +1211,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);
}
/*
@@ -1553,6 +1619,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");
@@ -1578,6 +1645,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);
@@ -1649,6 +1718,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);
@@ -1672,6 +1742,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;
@@ -1737,6 +1808,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;
@@ -1813,11 +1885,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);
@@ -1870,6 +1944,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;
@@ -1900,6 +1975,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
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.
Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/mtd/nand/qcom_nandc.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 85fbe00..c0c140b 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -193,6 +193,9 @@
((size) << READ_LOCATION_SIZE) | \
((is_last) << READ_LOCATION_LAST))
+/* Returns the actual register address for NAND_FLASH_DEV_* */
+#define nandc_dev_addr(nandc, reg) ((nandc)->props->flash_dev_offset + (reg))
+
#define QPIC_PER_CW_CMD_SGL 32
#define QPIC_PER_CW_DATA_SGL 8
@@ -426,10 +429,12 @@ struct qcom_nand_host {
* among different NAND controllers.
* @ecc_modes - ecc mode for NAND
* @is_bam - whether NAND controller is using BAM
+ * @flash_dev_offset - NAND_FLASH_DEV_* registers start offset
*/
struct qcom_nandc_props {
u32 ecc_modes;
bool is_bam;
+ u32 flash_dev_offset;
};
/* Frees the BAM transaction memory */
@@ -844,6 +849,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 = nandc_dev_addr(nandc, first);
+
size = num_regs * sizeof(u32);
vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
nandc->reg_read_pos += num_regs;
@@ -881,11 +889,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 = nandc_dev_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 = nandc_dev_addr(nandc, NAND_DEV_CMD_VLD);
size = num_regs * sizeof(u32);
@@ -2492,7 +2500,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, nandc_dev_addr(nandc, NAND_DEV_CMD_VLD),
+ NAND_DEV_CMD_VLD_VAL);
/* enable ADM or BAM DMA */
if (nandc->props->is_bam) {
@@ -2503,7 +2512,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, nandc_dev_addr(nandc, NAND_DEV_CMD1));
nandc->vld = NAND_DEV_CMD_VLD_VAL;
return 0;
@@ -2752,6 +2761,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,
+ .flash_dev_offset = 0x0,
};
/*
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
All the QPIC register read/write through BAM DMA requires
command descriptor which contains the array of command elements.
Signed-off-by: Abhishek Sahu <[email protected]>
---
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 c0c140b..d17c466 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
@@ -196,6 +197,7 @@
/* Returns the actual register address for NAND_FLASH_DEV_* */
#define nandc_dev_addr(nandc, reg) ((nandc)->props->flash_dev_offset + (reg))
+#define QPIC_PER_CW_CMD_ELEMENTS 32
#define QPIC_PER_CW_CMD_SGL 32
#define QPIC_PER_CW_DATA_SGL 8
@@ -215,8 +217,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.
@@ -225,8 +232,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;
@@ -456,7 +466,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);
@@ -466,6 +477,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;
@@ -483,6 +498,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
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
Signed-off-by: Abhishek Sahu <[email protected]>
---
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 d17c466..f2cb696 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -194,6 +194,14 @@
((size) << READ_LOCATION_SIZE) | \
((is_last) << READ_LOCATION_LAST))
+/* 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))
+
/* Returns the actual register address for NAND_FLASH_DEV_* */
#define nandc_dev_addr(nandc, reg) ((nandc)->props->flash_dev_offset + (reg))
@@ -311,7 +319,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
*
@@ -344,6 +353,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;
@@ -745,6 +755,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.
*/
@@ -861,19 +931,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 = nandc_dev_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);
}
/*
@@ -889,13 +962,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 = ®s->erased_cw_detect_cfg_set;
@@ -912,10 +981,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 = nandc_dev_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);
}
/*
@@ -1177,7 +1251,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;
}
@@ -2713,6 +2788,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
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]>
---
.../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
Qualcom IPQ8074 SoC uses QPIC NAND controller version 1.5.0
which uses BAM DMA Engine.
Signed-off-by: Abhishek Sahu <[email protected]>
---
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
Add the compatible string for IPQ4019 QPIC NAND controller
version 1.4.0 which uses BAM DMA.
Signed-off-by: Abhishek Sahu <[email protected]>
---
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 f2cb696..03e671e 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -2857,6 +2857,12 @@ static int qcom_nandc_remove(struct platform_device *pdev)
.flash_dev_offset = 0x0,
};
+static const struct qcom_nandc_props ipq4019_nandc_props = {
+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+ .is_bam = true,
+ .flash_dev_offset = 0x0,
+};
+
/*
* data will hold a struct pointer containing more differences once we support
* more controller variants
@@ -2866,6 +2872,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
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.
Signed-off-by: Abhishek Sahu <[email protected]>
---
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 03e671e..1484fb4 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -2863,6 +2863,12 @@ static int qcom_nandc_remove(struct platform_device *pdev)
.flash_dev_offset = 0x0,
};
+static const struct qcom_nandc_props ipq8074_nandc_props = {
+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+ .is_bam = true,
+ .flash_dev_offset = 0x7000,
+};
+
/*
* data will hold a struct pointer containing more differences once we support
* more controller variants
@@ -2876,6 +2882,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
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]>
---
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
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.
Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/mtd/nand/qcom_nandc.c | 64 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index d9c8a6b..b452cfb 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_readl(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
@@ -260,6 +273,11 @@ struct nandc_regs {
__le32 orig_vld;
__le32 ecc_buf_cfg;
+ __le32 read_location0;
+ __le32 read_location1;
+ __le32 read_location2;
+ __le32 read_location3;
+
};
/*
@@ -516,6 +534,14 @@ static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
return ®s->orig_vld;
case NAND_EBI2_ECC_BUF_CFG:
return ®s->ecc_buf_cfg;
+ case NAND_READ_LOCATION_0:
+ return ®s->read_location0;
+ case NAND_READ_LOCATION_1:
+ return ®s->read_location1;
+ case NAND_READ_LOCATION_2:
+ return ®s->read_location2;
+ case NAND_READ_LOCATION_3:
+ return ®s->read_location3;
default:
return NULL;
}
@@ -590,6 +616,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_readl(nandc, 0, 0, host->use_ecc ?
+ host->cw_data : host->cw_size, 1);
}
/*
@@ -835,6 +865,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);
@@ -923,6 +957,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_readl(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);
@@ -1398,6 +1433,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_readl(nandc, 0, 0, data_size, 0);
+ nandc_set_readl(nandc, 1, data_size,
+ oob_size, 1);
+ } else if (data_buf) {
+ nandc_set_readl(nandc, 0, 0, data_size, 1);
+ } else {
+ nandc_set_readl(nandc, 0, data_size,
+ oob_size, 1);
+ }
+ }
+
config_nand_cw_read(nandc);
if (data_buf)
@@ -1457,6 +1505,7 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)
set_address(host, host->cw_size * (ecc->steps - 1), page);
update_rw_regs(host, 1, true);
+ nandc_set_readl(nandc, 0, 0, size, 1);
config_nand_single_cw_page_read(nandc);
@@ -1502,6 +1551,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;
@@ -1527,6 +1577,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_readl(nandc, 0, read_loc, data_size1, 0);
+ read_loc += data_size1;
+
+ nandc_set_readl(nandc, 1, read_loc, oob_size1, 0);
+ read_loc += oob_size1;
+
+ nandc_set_readl(nandc, 2, read_loc, data_size2, 0);
+ read_loc += data_size2;
+
+ nandc_set_readl(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
- 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.
Signed-off-by: Abhishek Sahu <[email protected]>
---
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
The NAND page read fails without complete boot chain since
NAND_DEV_CMD_VLD value is not proper. The default power on reset
value for this register is
0xe - ERASE_START_VALID | WRITE_START_VALID | READ_STOP_VALID
The READ_START_VALID should be enabled for sending PAGE_READ
command. READ_STOP_VALID should be cleared since normal NAND
page read does not require READ_STOP command.
Fixes: c76b78d8ec05a ("mtd: nand: Qualcomm NAND controller driver")
Cc: [email protected]
Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/mtd/nand/qcom_nandc.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 0e727d7..e5cb8f1 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -109,7 +109,11 @@
#define READ_ADDR 0
/* NAND_DEV_CMD_VLD bits */
-#define READ_START_VLD 0
+#define READ_START_VLD BIT(0)
+#define READ_STOP_VLD BIT(1)
+#define WRITE_START_VLD BIT(2)
+#define ERASE_START_VLD BIT(3)
+#define SEQ_READ_START_VLD BIT(4)
/* NAND_EBI2_ECC_BUF_CFG bits */
#define NUM_STEPS 0
@@ -148,6 +152,10 @@
#define FETCH_ID 0xb
#define RESET_DEVICE 0xd
+/* Default Value for NAND_DEV_CMD_VLD */
+#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \
+ ERASE_START_VLD | SEQ_READ_START_VLD)
+
/*
* the NAND controller performs reads/writes with ECC in 516 byte chunks.
* the driver calls the chunks 'step' or 'codeword' interchangeably
@@ -695,8 +703,7 @@ static int nandc_param(struct qcom_nand_host *host)
/* configure CMD1 and VLD for ONFI param probing */
nandc_set_reg(nandc, NAND_DEV_CMD_VLD,
- (nandc->vld & ~(1 << READ_START_VLD))
- | 0 << READ_START_VLD);
+ (nandc->vld & ~READ_START_VLD));
nandc_set_reg(nandc, NAND_DEV_CMD1,
(nandc->cmd1 & ~(0xFF << READ_ADDR))
| NAND_CMD_PARAM << READ_ADDR);
@@ -1995,13 +2002,14 @@ 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);
/* enable ADM DMA */
nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
/* save the original values of these registers */
nandc->cmd1 = nandc_read(nandc, NAND_DEV_CMD1);
- nandc->vld = nandc_read(nandc, NAND_DEV_CMD_VLD);
+ nandc->vld = NAND_DEV_CMD_VLD_VAL;
return 0;
}
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Le Fri, 11 Aug 2017 17:09:15 +0530,
Abhishek Sahu <[email protected]> a écrit :
> * 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 (20):
> mtd: nand: qcom: fix read failure without complete bootchain
> mtd: nand: qcom: support for NAND controller properties
> mtd: nand: qcom: add bam property for QPIC NAND controller
> mtd: nand: qcom: add and initialize QPIC DMA resources
Applied patches 1 to 4. Still waiting a bit to let Archit review the
remaining ones.
Regards,
Boris
> 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 transfer 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 | 866 ++++++++++++++++++---
> 2 files changed, 814 insertions(+), 115 deletions(-)
>
On 2017-08-13 13:17, Boris Brezillon wrote:
> Le Fri, 11 Aug 2017 17:09:15 +0530,
> Abhishek Sahu <[email protected]> a écrit :
>
>> * 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 (20):
>> mtd: nand: qcom: fix read failure without complete bootchain
>> mtd: nand: qcom: support for NAND controller properties
>> mtd: nand: qcom: add bam property for QPIC NAND controller
>> mtd: nand: qcom: add and initialize QPIC DMA resources
>
> Applied patches 1 to 4. Still waiting a bit to let Archit review the
> remaining ones.
Thanks Boris.
Sure. That would be better if Archit reviews the remaining patches.
>> 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 transfer 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 | 866
>> ++++++++++++++++++---
>> 2 files changed, 814 insertions(+), 115 deletions(-)
>>
On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
> 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]>
Thanks,
Archit
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> 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 Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
> - 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]>
Thanks,
Archit
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> 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 Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
> The BAM has multiple flags to control the transfer. This patch
> adds flags parameter in register and data transfer 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.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/mtd/nand/qcom_nandc.c | 122 ++++++++++++++++++++++++------------------
> 1 file changed, 70 insertions(+), 52 deletions(-)
>
> diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
> index f52a692..d9c8a6b 100644
> --- a/drivers/mtd/nand/qcom_nandc.c
> +++ b/drivers/mtd/nand/qcom_nandc.c
> @@ -180,6 +180,14 @@
> #define QPIC_PER_CW_CMD_SGL 32
> #define QPIC_PER_CW_DATA_SGL 8
>
> +/* Flags used for BAM DMA desc preparation*/
> +/* Don't set the EOT in current tx sgl */
> +#define NAND_BAM_NO_EOT BIT(0)
> +/* Set the NWD flag in current sgl */
> +#define NAND_BAM_NWD BIT(1)
> +/* Finish writing in the current sgl and start writing in another sgl */
> +#define NAND_BAM_NEXT_SGL BIT(2)
> +
> /*
> * This data type corresponds to the BAM transaction which will be used for all
> * NAND transfers.
> @@ -731,7 +739,7 @@ static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
> * @num_regs: number of registers to read
Minor comment: the read_reg_dma/write_reg/read_data/write_data_dma funcs add a new arg, so it
would be nice to update the comment describing the function and its arguments. It would also
be nice to mention that the flags are presently used only for controllers using BAM.
With that,
Reviewed-by: Archit Taneja <[email protected]>
Thanks,
Archit
> */
> 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;
> @@ -755,7 +763,7 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
> * @num_regs: number of registers to write
> */
> 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 +775,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;
>
> @@ -788,7 +799,7 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
> * @size: DMA transaction size in bytes
> */
> 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);
> }
> @@ -802,7 +813,7 @@ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
> * @size: DMA transaction size in bytes
> */
> 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 +824,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 +835,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 +859,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 +871,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 +924,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 +933,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 +959,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 +986,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 +1003,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 +1402,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 +1418,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 +1460,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 +1529,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 +1608,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 +1622,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 +1677,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 +1746,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 +1831,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 Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
> 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.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/mtd/nand/qcom_nandc.c | 64 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 64 insertions(+)
>
> diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
> index d9c8a6b..b452cfb 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_readl(nandc, reg, offset, size, is_last) \
Minor nit, readl makes one think it's 'read long'. If it isn't too
much effort, could you s/nandc_set_readl/nandc_set_read_loc ?
> +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
>
> @@ -260,6 +273,11 @@ struct nandc_regs {
> __le32 orig_vld;
>
> __le32 ecc_buf_cfg;
> + __le32 read_location0;
> + __le32 read_location1;
> + __le32 read_location2;
> + __le32 read_location3;
> +
> };
>
> /*
> @@ -516,6 +534,14 @@ static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
> return ®s->orig_vld;
> case NAND_EBI2_ECC_BUF_CFG:
> return ®s->ecc_buf_cfg;
> + case NAND_READ_LOCATION_0:
> + return ®s->read_location0;
> + case NAND_READ_LOCATION_1:
> + return ®s->read_location1;
> + case NAND_READ_LOCATION_2:
> + return ®s->read_location2;
> + case NAND_READ_LOCATION_3:
> + return ®s->read_location3;
> default:
> return NULL;
> }
> @@ -590,6 +616,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_readl(nandc, 0, 0, host->use_ecc ?
> + host->cw_data : host->cw_size, 1);
> }
>
> /*
> @@ -835,6 +865,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);
>
> @@ -923,6 +957,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_readl(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);
> @@ -1398,6 +1433,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_readl(nandc, 0, 0, data_size, 0);
> + nandc_set_readl(nandc, 1, data_size,
> + oob_size, 1);
> + } else if (data_buf) {
> + nandc_set_readl(nandc, 0, 0, data_size, 1);
> + } else {
> + nandc_set_readl(nandc, 0, data_size,
> + oob_size, 1);
> + }
> + }
> +
> config_nand_cw_read(nandc);
>
> if (data_buf)
> @@ -1457,6 +1505,7 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)
>
> set_address(host, host->cw_size * (ecc->steps - 1), page);
> update_rw_regs(host, 1, true);
> + nandc_set_readl(nandc, 0, 0, size, 1);
The READ_LOC reg should already be set up in update_rw_regs, right? If so, we could
drop this line.
With that,
Reviewed-by: Archit Taneja <[email protected]>
Thanks,
Archit
>
> config_nand_single_cw_page_read(nandc);
>
> @@ -1502,6 +1551,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;
> @@ -1527,6 +1577,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_readl(nandc, 0, read_loc, data_size1, 0);
> + read_loc += data_size1;
> +
> + nandc_set_readl(nandc, 1, read_loc, oob_size1, 0);
> + read_loc += oob_size1;
> +
> + nandc_set_readl(nandc, 2, read_loc, data_size2, 0);
> + read_loc += data_size2;
> +
> + nandc_set_readl(nandc, 3, read_loc, oob_size2, 1);
> + }
> +
> config_nand_cw_read(nandc);
>
> read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
>
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
> 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]>
Thanks,
Archit
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> 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 b452cfb..3d9fd7f 100644
> --- a/drivers/mtd/nand/qcom_nandc.c
> +++ b/drivers/mtd/nand/qcom_nandc.c
> @@ -200,6 +200,11 @@
> #define NAND_BAM_NWD BIT(1)
> /* Finish writing in the current sgl and start writing in another 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
> @@ -278,6 +283,8 @@ struct nandc_regs {
> __le32 read_location2;
> __le32 read_location3;
>
> + __le32 erased_cw_detect_cfg_clr;
> + __le32 erased_cw_detect_cfg_set;
> };
>
> /*
> @@ -805,6 +812,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 = ®s->erased_cw_detect_cfg_set;
> + else
> + vaddr = ®s->erased_cw_detect_cfg_clr;
> + }
> +
> if (first == NAND_EXEC_CMD)
> flags |= NAND_BAM_NWD;
>
> @@ -857,6 +871,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);
> }
>
> /*
> @@ -2258,6 +2275,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 Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
> 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.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> 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 3d9fd7f..ae873d3 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
> @@ -1035,7 +1038,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);
I'm not sure why the above register was configured in read_id in the first place. Would
it be required later if we want the controller to support multiple NAND chips? If not,
then we could consider dropping this. Anyway, that can be posted as a separate patch
later.
Reviewed-by: Archit Taneja <[email protected]>
Thanks,
Archit
> nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
>
> write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
> @@ -2408,12 +2412,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 Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
> 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]>
Thanks,
Archit
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> 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 ae873d3..85fbe00 100644
> --- a/drivers/mtd/nand/qcom_nandc.c
> +++ b/drivers/mtd/nand/qcom_nandc.c
> @@ -470,6 +470,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);
> @@ -701,6 +722,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)
> @@ -848,6 +904,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);
> }
>
> @@ -862,6 +921,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);
> }
>
> @@ -1149,6 +1211,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);
> }
>
> /*
> @@ -1553,6 +1619,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");
> @@ -1578,6 +1645,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);
>
> @@ -1649,6 +1718,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);
> @@ -1672,6 +1742,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;
> @@ -1737,6 +1808,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;
> @@ -1813,11 +1885,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);
> @@ -1870,6 +1944,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;
> @@ -1900,6 +1975,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 Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
> 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.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/mtd/nand/qcom_nandc.c | 22 ++++++++++++++++------
> 1 file changed, 16 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
> index 85fbe00..c0c140b 100644
> --- a/drivers/mtd/nand/qcom_nandc.c
> +++ b/drivers/mtd/nand/qcom_nandc.c
> @@ -193,6 +193,9 @@
> ((size) << READ_LOCATION_SIZE) | \
> ((is_last) << READ_LOCATION_LAST))
>
> +/* Returns the actual register address for NAND_FLASH_DEV_* */
There aren't any registers starting with NAND_FLASH_DEV_* in the registers
defined above, it might get confusing for someone who doesn't have access
to the HW docs. Could you explicitly mention in this comment all the register
names that are required to go through this translation, it should make things
more readable. With that:
Reviewed-by: Archit Taneja <[email protected]>
Thanks,
Archit
> +#define nandc_dev_addr(nandc, reg) ((nandc)->props->flash_dev_offset + (reg))
> +
> #define QPIC_PER_CW_CMD_SGL 32
> #define QPIC_PER_CW_DATA_SGL 8
>
> @@ -426,10 +429,12 @@ struct qcom_nand_host {
> * among different NAND controllers.
> * @ecc_modes - ecc mode for NAND
> * @is_bam - whether NAND controller is using BAM
> + * @flash_dev_offset - NAND_FLASH_DEV_* registers start offset
> */
> struct qcom_nandc_props {
> u32 ecc_modes;
> bool is_bam;
> + u32 flash_dev_offset;
> };
>
> /* Frees the BAM transaction memory */
> @@ -844,6 +849,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 = nandc_dev_addr(nandc, first);
> +
> size = num_regs * sizeof(u32);
> vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
> nandc->reg_read_pos += num_regs;
> @@ -881,11 +889,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 = nandc_dev_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 = nandc_dev_addr(nandc, NAND_DEV_CMD_VLD);
>
> size = num_regs * sizeof(u32);
>
> @@ -2492,7 +2500,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, nandc_dev_addr(nandc, NAND_DEV_CMD_VLD),
> + NAND_DEV_CMD_VLD_VAL);
>
> /* enable ADM or BAM DMA */
> if (nandc->props->is_bam) {
> @@ -2503,7 +2512,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, nandc_dev_addr(nandc, NAND_DEV_CMD1));
> nandc->vld = NAND_DEV_CMD_VLD_VAL;
>
> return 0;
> @@ -2752,6 +2761,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,
> + .flash_dev_offset = 0x0,
> };
>
> /*
>
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, 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]>
Thanks,
Archit
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> 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 c0c140b..d17c466 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
> @@ -196,6 +197,7 @@
> /* Returns the actual register address for NAND_FLASH_DEV_* */
> #define nandc_dev_addr(nandc, reg) ((nandc)->props->flash_dev_offset + (reg))
>
> +#define QPIC_PER_CW_CMD_ELEMENTS 32
> #define QPIC_PER_CW_CMD_SGL 32
> #define QPIC_PER_CW_DATA_SGL 8
>
> @@ -215,8 +217,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.
> @@ -225,8 +232,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;
> @@ -456,7 +466,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);
> @@ -466,6 +477,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;
> @@ -483,6 +498,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 Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, 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]>
Thanks,
Archit
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> 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 d17c466..f2cb696 100644
> --- a/drivers/mtd/nand/qcom_nandc.c
> +++ b/drivers/mtd/nand/qcom_nandc.c
> @@ -194,6 +194,14 @@
> ((size) << READ_LOCATION_SIZE) | \
> ((is_last) << READ_LOCATION_LAST))
>
> +/* 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))
> +
> /* Returns the actual register address for NAND_FLASH_DEV_* */
> #define nandc_dev_addr(nandc, reg) ((nandc)->props->flash_dev_offset + (reg))
>
> @@ -311,7 +319,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
> *
> @@ -344,6 +353,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;
> @@ -745,6 +755,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.
> */
> @@ -861,19 +931,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 = nandc_dev_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);
> }
>
> /*
> @@ -889,13 +962,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 = ®s->erased_cw_detect_cfg_set;
> @@ -912,10 +981,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 = nandc_dev_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);
> }
>
> /*
> @@ -1177,7 +1251,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;
> }
> @@ -2713,6 +2788,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 Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
> Add the compatible string for IPQ4019 QPIC NAND controller
> version 1.4.0 which uses BAM DMA.
Reviewed-by: Archit Taneja <[email protected]>
Thanks,
Archit
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> 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 f2cb696..03e671e 100644
> --- a/drivers/mtd/nand/qcom_nandc.c
> +++ b/drivers/mtd/nand/qcom_nandc.c
> @@ -2857,6 +2857,12 @@ static int qcom_nandc_remove(struct platform_device *pdev)
> .flash_dev_offset = 0x0,
> };
>
> +static const struct qcom_nandc_props ipq4019_nandc_props = {
> + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
> + .is_bam = true,
> + .flash_dev_offset = 0x0,
> +};
> +
> /*
> * data will hold a struct pointer containing more differences once we support
> * more controller variants
> @@ -2866,6 +2872,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 Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
> 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]>
Thanks,
Archit
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> 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 03e671e..1484fb4 100644
> --- a/drivers/mtd/nand/qcom_nandc.c
> +++ b/drivers/mtd/nand/qcom_nandc.c
> @@ -2863,6 +2863,12 @@ static int qcom_nandc_remove(struct platform_device *pdev)
> .flash_dev_offset = 0x0,
> };
>
> +static const struct qcom_nandc_props ipq8074_nandc_props = {
> + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
> + .is_bam = true,
> + .flash_dev_offset = 0x7000,
> +};
> +
> /*
> * data will hold a struct pointer containing more differences once we support
> * more controller variants
> @@ -2876,6 +2882,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 Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
On 2017-08-16 09:48, Archit Taneja wrote:
> On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
>> The BAM has multiple flags to control the transfer. This patch
>> adds flags parameter in register and data transfer 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.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> drivers/mtd/nand/qcom_nandc.c | 122
>> ++++++++++++++++++++++++------------------
>> 1 file changed, 70 insertions(+), 52 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/qcom_nandc.c
>> b/drivers/mtd/nand/qcom_nandc.c
>> index f52a692..d9c8a6b 100644
>> --- a/drivers/mtd/nand/qcom_nandc.c
>> +++ b/drivers/mtd/nand/qcom_nandc.c
>> @@ -180,6 +180,14 @@
>> #define QPIC_PER_CW_CMD_SGL 32
>> #define QPIC_PER_CW_DATA_SGL 8
>> +/* Flags used for BAM DMA desc preparation*/
We can remove BAM from here since later on
we will pass flag for erase code word and it will
be used by ADM also.
>> +/* Don't set the EOT in current tx sgl */
>> +#define NAND_BAM_NO_EOT BIT(0)
>> +/* Set the NWD flag in current sgl */
>> +#define NAND_BAM_NWD BIT(1)
>> +/* Finish writing in the current sgl and start writing in another sgl
>> */
>> +#define NAND_BAM_NEXT_SGL BIT(2)
>> +
>> /*
>> * This data type corresponds to the BAM transaction which will be
>> used for all
>> * NAND transfers.
>> @@ -731,7 +739,7 @@ static int prep_adm_dma_desc(struct
>> qcom_nand_controller *nandc, bool read,
>> * @num_regs: number of registers to read
>
> Minor comment: the read_reg_dma/write_reg/read_data/write_data_dma
> funcs add a new arg, so it
> would be nice to update the comment describing the function and its
> arguments. It would also
Thanks. I will update the comment to include this new arg.
> be nice to mention that the flags are presently used only for
> controllers using BAM.
>
We can use this flag for ADM also so I will make the
comment generic.
> With that,
>
> Reviewed-by: Archit Taneja <[email protected]>
>
> Thanks,
> Archit
>
>> */
>> 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;
>> @@ -755,7 +763,7 @@ static int read_reg_dma(struct
>> qcom_nand_controller *nandc, int first,
>> * @num_regs: number of registers to write
>> */
>> 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 +775,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;
>> @@ -788,7 +799,7 @@ static int write_reg_dma(struct
>> qcom_nand_controller *nandc, int first,
>> * @size: DMA transaction size in bytes
>> */
>> 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);
>> }
>> @@ -802,7 +813,7 @@ static int read_data_dma(struct
>> qcom_nand_controller *nandc, int reg_off,
>> * @size: DMA transaction size in bytes
>> */
>> 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 +824,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 +835,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 +859,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 +871,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 +924,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 +933,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 +959,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 +986,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 +1003,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 +1402,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 +1418,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 +1460,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 +1529,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 +1608,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 +1622,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 +1677,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 +1746,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 +1831,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);
>>
On 2017-08-16 10:04, Archit Taneja wrote:
> On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
>> 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.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> drivers/mtd/nand/qcom_nandc.c | 64
>> +++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 64 insertions(+)
>>
>> diff --git a/drivers/mtd/nand/qcom_nandc.c
>> b/drivers/mtd/nand/qcom_nandc.c
>> index d9c8a6b..b452cfb 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_readl(nandc, reg, offset, size, is_last) \
>
> Minor nit, readl makes one think it's 'read long'. If it isn't too
> much effort, could you s/nandc_set_readl/nandc_set_read_loc ?
>
Thanks Archit.
Yes that would be better. I will change the name.
>> +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
>> @@ -260,6 +273,11 @@ struct nandc_regs {
>> __le32 orig_vld;
>> __le32 ecc_buf_cfg;
>> + __le32 read_location0;
>> + __le32 read_location1;
>> + __le32 read_location2;
>> + __le32 read_location3;
>> +
>> };
>> /*
>> @@ -516,6 +534,14 @@ static __le32 *offset_to_nandc_reg(struct
>> nandc_regs *regs, int offset)
>> return ®s->orig_vld;
>> case NAND_EBI2_ECC_BUF_CFG:
>> return ®s->ecc_buf_cfg;
>> + case NAND_READ_LOCATION_0:
>> + return ®s->read_location0;
>> + case NAND_READ_LOCATION_1:
>> + return ®s->read_location1;
>> + case NAND_READ_LOCATION_2:
>> + return ®s->read_location2;
>> + case NAND_READ_LOCATION_3:
>> + return ®s->read_location3;
>> default:
>> return NULL;
>> }
>> @@ -590,6 +616,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_readl(nandc, 0, 0, host->use_ecc ?
>> + host->cw_data : host->cw_size, 1);
>> }
>> /*
>> @@ -835,6 +865,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);
>> @@ -923,6 +957,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_readl(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);
>> @@ -1398,6 +1433,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_readl(nandc, 0, 0, data_size, 0);
>> + nandc_set_readl(nandc, 1, data_size,
>> + oob_size, 1);
>> + } else if (data_buf) {
>> + nandc_set_readl(nandc, 0, 0, data_size, 1);
>> + } else {
>> + nandc_set_readl(nandc, 0, data_size,
>> + oob_size, 1);
>> + }
>> + }
>> +
>> config_nand_cw_read(nandc);
>> if (data_buf)
>> @@ -1457,6 +1505,7 @@ static int copy_last_cw(struct qcom_nand_host
>> *host, int page)
>> set_address(host, host->cw_size * (ecc->steps - 1), page);
>> update_rw_regs(host, 1, true);
>> + nandc_set_readl(nandc, 0, 0, size, 1);
>
> The READ_LOC reg should already be set up in update_rw_regs, right? If
> so, we could
> drop this line.
Yes. this line is redundant. I will remove the same.
>
> With that,
>
> Reviewed-by: Archit Taneja <[email protected]>
>
> Thanks,
> Archit
>
>> config_nand_single_cw_page_read(nandc);
>> @@ -1502,6 +1551,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;
>> @@ -1527,6 +1577,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_readl(nandc, 0, read_loc, data_size1, 0);
>> + read_loc += data_size1;
>> +
>> + nandc_set_readl(nandc, 1, read_loc, oob_size1, 0);
>> + read_loc += oob_size1;
>> +
>> + nandc_set_readl(nandc, 2, read_loc, data_size2, 0);
>> + read_loc += data_size2;
>> +
>> + nandc_set_readl(nandc, 3, read_loc, oob_size2, 1);
>> + }
>> +
>> config_nand_cw_read(nandc);
>> read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
>>
On 2017-08-16 10:20, Archit Taneja wrote:
> On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
>> 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.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> 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 3d9fd7f..ae873d3 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
>> @@ -1035,7 +1038,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);
>
> I'm not sure why the above register was configured in read_id in the
> first place. Would
> it be required later if we want the controller to support multiple
> NAND chips? If not,
> then we could consider dropping this. Anyway, that can be posted as a
> separate patch
> later.
Correct. It seems the current driver does not have fully support
second NAND chip select since it need to program NAND_DEV1_CFG1 and
NAND_DEV1_CFG0 also.
We can have separate patch series which will add the full support
for multiple NAND chips and this line can be removed in that patch
series.
>
> Reviewed-by: Archit Taneja <[email protected]>
>
> Thanks,
> Archit
>
>> nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
>> write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
>> @@ -2408,12 +2412,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);
>>
On 2017-08-16 11:22, Archit Taneja wrote:
> On 08/11/2017 05:09 PM, Abhishek Sahu wrote:
>> 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.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> drivers/mtd/nand/qcom_nandc.c | 22 ++++++++++++++++------
>> 1 file changed, 16 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/qcom_nandc.c
>> b/drivers/mtd/nand/qcom_nandc.c
>> index 85fbe00..c0c140b 100644
>> --- a/drivers/mtd/nand/qcom_nandc.c
>> +++ b/drivers/mtd/nand/qcom_nandc.c
>> @@ -193,6 +193,9 @@
>> ((size) << READ_LOCATION_SIZE) | \
>> ((is_last) << READ_LOCATION_LAST))
>> +/* Returns the actual register address for NAND_FLASH_DEV_* */
>
> There aren't any registers starting with NAND_FLASH_DEV_* in the
> registers
> defined above, it might get confusing for someone who doesn't have
> access
> to the HW docs. Could you explicitly mention in this comment all the
> register
> names that are required to go through this translation, it should make
> things
> more readable. With that:
Thanks Archit. Following are the registers
#define NAND_DEV_CMD0 0xa0
#define NAND_DEV_CMD1 0xa4
#define NAND_DEV_CMD2 0xa8
#define NAND_DEV_CMD_VLD 0xac
I will update the comment.
>
> Reviewed-by: Archit Taneja <[email protected]>
>
> Thanks,
> Archit
>
>
>> +#define nandc_dev_addr(nandc, reg) ((nandc)->props->flash_dev_offset
>> + (reg))
>> +
>> #define QPIC_PER_CW_CMD_SGL 32
>> #define QPIC_PER_CW_DATA_SGL 8
>> @@ -426,10 +429,12 @@ struct qcom_nand_host {
>> * among different NAND controllers.
>> * @ecc_modes - ecc mode for NAND
>> * @is_bam - whether NAND controller is using BAM
>> + * @flash_dev_offset - NAND_FLASH_DEV_* registers start offset
>> */
>> struct qcom_nandc_props {
>> u32 ecc_modes;
>> bool is_bam;
>> + u32 flash_dev_offset;
>> };
>> /* Frees the BAM transaction memory */
>> @@ -844,6 +849,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 = nandc_dev_addr(nandc, first);
>> +
>> size = num_regs * sizeof(u32);
>> vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
>> nandc->reg_read_pos += num_regs;
>> @@ -881,11 +889,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 = nandc_dev_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 = nandc_dev_addr(nandc, NAND_DEV_CMD_VLD);
>> size = num_regs * sizeof(u32);
>> @@ -2492,7 +2500,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, nandc_dev_addr(nandc, NAND_DEV_CMD_VLD),
>> + NAND_DEV_CMD_VLD_VAL);
>> /* enable ADM or BAM DMA */
>> if (nandc->props->is_bam) {
>> @@ -2503,7 +2512,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, nandc_dev_addr(nandc,
>> NAND_DEV_CMD1));
>> nandc->vld = NAND_DEV_CMD_VLD_VAL;
>> return 0;
>> @@ -2752,6 +2761,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,
>> + .flash_dev_offset = 0x0,
>> };
>> /*
>>