You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
370 lines
11 KiB
370 lines
11 KiB
From 074036f9de6b8c5fc642e8e2540950f6a35aa804 Mon Sep 17 00:00:00 2001
|
|
From: Ram Chandra Jangir <rjangir@codeaurora.org>
|
|
Date: Thu, 20 Apr 2017 10:31:10 +0530
|
|
Subject: [PATCH] qcom: mtd: nand: Add bam_dma support in qcom_nand driver
|
|
|
|
The current driver only support ADM DMA so this patch adds the
|
|
BAM DMA support in current NAND driver with compatible string
|
|
qcom,ebi2-nandc-bam.
|
|
Added bam channels and data buffers, NAND BAM uses 3 channels:
|
|
command, data tx and data rx, while ADM uses only single channel.
|
|
So this patch adds the BAM channel in device tree and using the
|
|
same in NAND driver allocation function.
|
|
|
|
Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
|
|
---
|
|
.../devicetree/bindings/mtd/qcom_nandc.txt | 69 +++++++--
|
|
drivers/mtd/nand/qcom_nandc.c | 160 +++++++++++++++++----
|
|
2 files changed, 190 insertions(+), 39 deletions(-)
|
|
|
|
--- a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
|
|
+++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
|
|
@@ -1,21 +1,26 @@
|
|
* Qualcomm NAND controller
|
|
|
|
Required properties:
|
|
-- compatible: should be "qcom,ipq806x-nand"
|
|
+- compatible: "qcom,ipq806x-nand" for IPQ8064 which uses
|
|
+ ADM DMA.
|
|
+ "qcom,ebi2-nand-bam" - nand drivers using BAM DMA
|
|
+ like IPQ4019.
|
|
- 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
|
|
- 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
|
|
-- dma-names: must be "rxtx"
|
|
-- qcom,cmd-crci: must contain the ADM command type CRCI block instance
|
|
- number specified for the NAND controller on the given
|
|
- platform
|
|
-- qcom,data-crci: must contain the ADM data type CRCI block instance
|
|
- number specified for the NAND controller on the given
|
|
- platform
|
|
+ or BAM DMA controller node and the channel number to
|
|
+ be used for NAND. Refer to dma.txt, qcom_adm.txt(ADM)
|
|
+ and qcom_bam_dma.txt(BAM) for more details
|
|
+- dma-names: "rxtx" - ADM
|
|
+ "tx", "rx", "cmd" - BAM
|
|
+- qcom,cmd-crci: Only required for ADM DMA. must contain the ADM command
|
|
+ type CRCI block instance number specified for the NAND
|
|
+ controller on the given platform.
|
|
+- qcom,data-crci: Only required for ADM DMA. must contain the ADM data
|
|
+ type CRCI block instance number specified for the NAND
|
|
+ controller on the given platform.
|
|
- #address-cells: <1> - subnodes give the chip-select number
|
|
- #size-cells: <0>
|
|
|
|
@@ -44,7 +49,7 @@ partition.txt for more detail.
|
|
Example:
|
|
|
|
nand@1ac00000 {
|
|
- compatible = "qcom,ebi2-nandc";
|
|
+ compatible = "qcom,ipq806x-nand","qcom.qcom_nand";
|
|
reg = <0x1ac00000 0x800>;
|
|
|
|
clocks = <&gcc EBI2_CLK>,
|
|
@@ -58,6 +63,48 @@ nand@1ac00000 {
|
|
|
|
#address-cells = <1>;
|
|
#size-cells = <0>;
|
|
+
|
|
+ nandcs@0 {
|
|
+ compatible = "qcom,nandcs";
|
|
+ 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>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
+nand@79B0000 {
|
|
+ compatible = "qcom,ebi2-nandc-bam";
|
|
+ reg = <0x79B0000 0x1000>;
|
|
+
|
|
+ clocks = <&gcc EBI2_CLK>,
|
|
+ <&gcc EBI2_AON_CLK>;
|
|
+ clock-names = "core", "aon";
|
|
+
|
|
+ dmas = <&qpicbam 0>,
|
|
+ <&qpicbam 1>,
|
|
+ <&qpicbam 2>;
|
|
+ dma-names = "tx", "rx", "cmd";
|
|
+
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
|
|
nandcs@0 {
|
|
compatible = "qcom,nandcs";
|
|
--- 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_buf_phys: 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
|
|
@@ -242,7 +243,10 @@ struct nandc_regs {
|
|
* @cmd1/vld: some fixed controller register values
|
|
* @ecc_modes: supported ECC modes by the current controller,
|
|
* initialized via DT match data
|
|
- */
|
|
+ * @bch_enabled: flag to tell whether BCH or RS ECC mode is used
|
|
+ * @dma_bam_enabled: flag to tell whether nand controller is using
|
|
+ * bam dma
|
|
+*/
|
|
struct qcom_nand_controller {
|
|
struct nand_hw_control controller;
|
|
struct list_head host_list;
|
|
@@ -255,17 +259,28 @@ struct qcom_nand_controller {
|
|
struct clk *core_clk;
|
|
struct clk *aon_clk;
|
|
|
|
- struct dma_chan *chan;
|
|
- unsigned int cmd_crci;
|
|
- unsigned int data_crci;
|
|
struct list_head desc_list;
|
|
+ union {
|
|
+ struct {
|
|
+ struct dma_chan *tx_chan;
|
|
+ struct dma_chan *rx_chan;
|
|
+ struct dma_chan *cmd_chan;
|
|
+ };
|
|
+ struct {
|
|
+ struct dma_chan *chan;
|
|
+ unsigned int cmd_crci;
|
|
+ unsigned int data_crci;
|
|
+ };
|
|
+ };
|
|
|
|
u8 *data_buffer;
|
|
+ bool dma_bam_enabled;
|
|
int buf_size;
|
|
int buf_count;
|
|
int buf_start;
|
|
|
|
__le32 *reg_read_buf;
|
|
+ dma_addr_t reg_read_buf_phys;
|
|
int reg_read_pos;
|
|
|
|
struct nandc_regs *regs;
|
|
@@ -324,6 +339,17 @@ struct qcom_nand_host {
|
|
u32 clrreadstatus;
|
|
};
|
|
|
|
+/*
|
|
+ * This data type corresponds to the nand driver data which will be used at
|
|
+ * driver probe time
|
|
+ * @ecc_modes - ecc mode for nand
|
|
+ * @dma_bam_enabled - whether this driver is using bam
|
|
+ */
|
|
+struct qcom_nand_driver_data {
|
|
+ u32 ecc_modes;
|
|
+ bool dma_bam_enabled;
|
|
+};
|
|
+
|
|
static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
|
|
{
|
|
return container_of(chip, struct qcom_nand_host, chip);
|
|
@@ -1949,16 +1975,46 @@ static int qcom_nandc_alloc(struct qcom_
|
|
if (!nandc->regs)
|
|
return -ENOMEM;
|
|
|
|
- nandc->reg_read_buf = devm_kzalloc(nandc->dev,
|
|
- MAX_REG_RD * sizeof(*nandc->reg_read_buf),
|
|
- GFP_KERNEL);
|
|
- if (!nandc->reg_read_buf)
|
|
- return -ENOMEM;
|
|
+ if (!nandc->dma_bam_enabled) {
|
|
+ nandc->reg_read_buf = devm_kzalloc(nandc->dev,
|
|
+ MAX_REG_RD *
|
|
+ sizeof(*nandc->reg_read_buf),
|
|
+ GFP_KERNEL);
|
|
|
|
- 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->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;
|
|
+ }
|
|
+ } else {
|
|
+ nandc->reg_read_buf = dmam_alloc_coherent(nandc->dev,
|
|
+ MAX_REG_RD *
|
|
+ sizeof(*nandc->reg_read_buf),
|
|
+ &nandc->reg_read_buf_phys, GFP_KERNEL);
|
|
+
|
|
+ if (!nandc->reg_read_buf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
}
|
|
|
|
INIT_LIST_HEAD(&nandc->desc_list);
|
|
@@ -1971,8 +2027,35 @@ static int qcom_nandc_alloc(struct qcom_
|
|
|
|
static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
|
|
{
|
|
- dma_release_channel(nandc->chan);
|
|
-}
|
|
+ if (nandc->dma_bam_enabled) {
|
|
+ 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->tx_chan);
|
|
+
|
|
+ if (nandc->reg_read_buf)
|
|
+ dmam_free_coherent(nandc->dev, MAX_REG_RD *
|
|
+ sizeof(*nandc->reg_read_buf),
|
|
+ nandc->reg_read_buf,
|
|
+ nandc->reg_read_buf_phys);
|
|
+ } else {
|
|
+ if (nandc->chan)
|
|
+ dma_release_channel(nandc->chan);
|
|
+
|
|
+ if (nandc->reg_read_buf)
|
|
+ devm_kfree(nandc->dev, nandc->reg_read_buf);
|
|
+ }
|
|
+
|
|
+ if (nandc->regs)
|
|
+ devm_kfree(nandc->dev, nandc->regs);
|
|
+
|
|
+ if (nandc->data_buffer)
|
|
+ devm_kfree(nandc->dev, nandc->data_buffer);
|
|
+ }
|
|
|
|
/* one time setup of a few nand controller registers */
|
|
static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
|
|
@@ -2010,6 +2093,8 @@ static int qcom_nand_host_init(struct qc
|
|
mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs);
|
|
mtd->owner = THIS_MODULE;
|
|
mtd->dev.parent = dev;
|
|
+ mtd->priv = chip;
|
|
+ chip->priv = nandc;
|
|
|
|
chip->cmdfunc = qcom_nandc_command;
|
|
chip->select_chip = qcom_nandc_select_chip;
|
|
@@ -2057,16 +2142,20 @@ static int qcom_nandc_parse_dt(struct pl
|
|
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->dma_bam_enabled) {
|
|
+ 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;
|
|
@@ -2081,6 +2170,7 @@ static int qcom_nandc_probe(struct platf
|
|
struct device_node *dn = dev->of_node, *child;
|
|
struct resource *res;
|
|
int ret;
|
|
+ struct qcom_nand_driver_data *driver_data;
|
|
|
|
nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL);
|
|
if (!nandc)
|
|
@@ -2095,7 +2185,10 @@ static int qcom_nandc_probe(struct platf
|
|
return -ENODEV;
|
|
}
|
|
|
|
- nandc->ecc_modes = (unsigned long)dev_data;
|
|
+ driver_data = (struct qcom_nand_driver_data *)dev_data;
|
|
+
|
|
+ nandc->ecc_modes = driver_data->ecc_modes;
|
|
+ nandc->dma_bam_enabled = driver_data->dma_bam_enabled;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
nandc->base = devm_ioremap_resource(dev, res);
|
|
@@ -2187,7 +2280,15 @@ static int qcom_nandc_remove(struct plat
|
|
return 0;
|
|
}
|
|
|
|
-#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT)
|
|
+struct qcom_nand_driver_data ebi2_nandc_bam_data = {
|
|
+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
|
|
+ .dma_bam_enabled = true,
|
|
+};
|
|
+
|
|
+struct qcom_nand_driver_data ebi2_nandc_data = {
|
|
+ .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
|
|
+ .dma_bam_enabled = false,
|
|
+};
|
|
|
|
/*
|
|
* data will hold a struct pointer containing more differences once we support
|
|
@@ -2195,7 +2296,10 @@ static int qcom_nandc_remove(struct plat
|
|
*/
|
|
static const struct of_device_id qcom_nandc_of_match[] = {
|
|
{ .compatible = "qcom,ipq806x-nand",
|
|
- .data = (void *)EBI2_NANDC_ECC_MODES,
|
|
+ .data = (void *) &ebi2_nandc_data,
|
|
+ },
|
|
+ { .compatible = "qcom,ebi2-nandc-bam",
|
|
+ .data = (void *) &ebi2_nandc_bam_data,
|
|
},
|
|
{}
|
|
};
|
|
|