From: John Crispin Date: Fri, 6 Mar 2015 07:56:58 +0000 (+0000) Subject: ipq806x: rename patches in patches-3.18 X-Git-Tag: reboot~4110 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=3ddd2b49a9c59d0b7a1b73bdc177d4f294beb526;p=oweals%2Fopenwrt.git ipq806x: rename patches in patches-3.18 This will allow ipq806x to support multiple kernel version more easily. Signed-off-by: Mathieu Olivari SVN-Revision: 44616 --- diff --git a/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch b/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch new file mode 100644 index 0000000000..62badd5387 --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch @@ -0,0 +1,522 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: spi: qup: Add DMA capabilities +From: Andy Gross +X-Patchwork-Id: 4432401 +Message-Id: <1403816781-31008-1-git-send-email-agross@codeaurora.org> +To: Mark Brown +Cc: linux-spi@vger.kernel.org, Sagar Dharia , + Daniel Sneddon , + Bjorn Andersson , + "Ivan T. Ivanov" , + linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + linux-arm-msm@vger.kernel.org, Andy Gross +Date: Thu, 26 Jun 2014 16:06:21 -0500 + +This patch adds DMA capabilities to the spi-qup driver. If DMA channels are +present, the QUP will use DMA instead of block mode for transfers to/from SPI +peripherals for transactions larger than the length of a block. + +Signed-off-by: Andy Gross + +--- +.../devicetree/bindings/spi/qcom,spi-qup.txt | 10 + + drivers/spi/spi-qup.c | 361 ++++++++++++++++++-- + 2 files changed, 350 insertions(+), 21 deletions(-) + +--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt ++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +@@ -27,6 +27,11 @@ Optional properties: + - spi-max-frequency: Specifies maximum SPI clock frequency, + Units - Hz. Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt ++- dmas : Two DMA channel specifiers following the convention outlined ++ in bindings/dma/dma.txt ++- dma-names: Names for the dma channels, if present. There must be at ++ least one channel named "tx" for transmit and named "rx" for ++ receive. + - num-cs: total number of chipselects + - cs-gpios: should specify GPIOs used for chipselects. + The gpios will be referred to as reg = in the SPI child +@@ -51,6 +56,10 @@ Example: + clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>; + clock-names = "core", "iface"; + ++ dmas = <&blsp2_bam 2>, ++ <&blsp2_bam 3>; ++ dma-names = "rx", "tx"; ++ + pinctrl-names = "default"; + pinctrl-0 = <&spi8_default>; + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -22,6 +22,8 @@ + #include + #include + #include ++#include ++#include + + #define QUP_CONFIG 0x0000 + #define QUP_STATE 0x0004 +@@ -116,6 +118,8 @@ + + #define SPI_NUM_CHIPSELECTS 4 + ++#define SPI_MAX_XFER (SZ_64K - 64) ++ + /* high speed mode is when bus rate is greater then 26MHz */ + #define SPI_HS_MIN_RATE 26000000 + #define SPI_MAX_RATE 50000000 +@@ -143,6 +147,17 @@ struct spi_qup { + int tx_bytes; + int rx_bytes; + int qup_v1; ++ ++ int use_dma; ++ ++ struct dma_chan *rx_chan; ++ struct dma_slave_config rx_conf; ++ struct dma_chan *tx_chan; ++ struct dma_slave_config tx_conf; ++ dma_addr_t rx_dma; ++ dma_addr_t tx_dma; ++ void *dummy; ++ atomic_t dma_outstanding; + }; + + +@@ -266,6 +281,221 @@ static void spi_qup_fifo_write(struct sp + } + } + ++static void qup_dma_callback(void *data) ++{ ++ struct spi_qup *controller = data; ++ ++ if (atomic_dec_and_test(&controller->dma_outstanding)) ++ complete(&controller->done); ++} ++ ++static int spi_qup_do_dma(struct spi_qup *controller, struct spi_transfer *xfer) ++{ ++ struct dma_async_tx_descriptor *rxd, *txd; ++ dma_cookie_t rx_cookie, tx_cookie; ++ u32 xfer_len, rx_align = 0, tx_align = 0, n_words; ++ struct scatterlist tx_sg[2], rx_sg[2]; ++ int ret = 0; ++ u32 bytes_to_xfer = xfer->len; ++ u32 offset = 0; ++ u32 rx_nents = 0, tx_nents = 0; ++ dma_addr_t rx_dma = 0, tx_dma = 0, rx_dummy_dma = 0, tx_dummy_dma = 0; ++ ++ ++ if (xfer->rx_buf) { ++ rx_dma = dma_map_single(controller->dev, xfer->rx_buf, ++ xfer->len, DMA_FROM_DEVICE); ++ ++ if (dma_mapping_error(controller->dev, rx_dma)) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ /* check to see if we need dummy buffer for leftover bytes */ ++ rx_align = xfer->len % controller->in_blk_sz; ++ if (rx_align) { ++ rx_dummy_dma = dma_map_single(controller->dev, ++ controller->dummy, controller->in_fifo_sz, ++ DMA_FROM_DEVICE); ++ ++ if (dma_mapping_error(controller->dev, rx_dummy_dma)) { ++ ret = -ENOMEM; ++ goto err_map_rx_dummy; ++ } ++ } ++ } ++ ++ if (xfer->tx_buf) { ++ tx_dma = dma_map_single(controller->dev, ++ (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE); ++ ++ if (dma_mapping_error(controller->dev, tx_dma)) { ++ ret = -ENOMEM; ++ goto err_map_tx; ++ } ++ ++ /* check to see if we need dummy buffer for leftover bytes */ ++ tx_align = xfer->len % controller->out_blk_sz; ++ if (tx_align) { ++ memcpy(controller->dummy + SZ_1K, ++ xfer->tx_buf + xfer->len - tx_align, ++ tx_align); ++ memset(controller->dummy + SZ_1K + tx_align, 0, ++ controller->out_blk_sz - tx_align); ++ ++ tx_dummy_dma = dma_map_single(controller->dev, ++ controller->dummy + SZ_1K, ++ controller->out_blk_sz, DMA_TO_DEVICE); ++ ++ if (dma_mapping_error(controller->dev, tx_dummy_dma)) { ++ ret = -ENOMEM; ++ goto err_map_tx_dummy; ++ } ++ } ++ } ++ ++ atomic_set(&controller->dma_outstanding, 0); ++ ++ while (bytes_to_xfer > 0) { ++ xfer_len = min_t(u32, bytes_to_xfer, SPI_MAX_XFER); ++ n_words = DIV_ROUND_UP(xfer_len, controller->w_size); ++ ++ /* write out current word count to controller */ ++ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); ++ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); ++ ++ reinit_completion(&controller->done); ++ ++ if (xfer->tx_buf) { ++ /* recalc align for each transaction */ ++ tx_align = xfer_len % controller->out_blk_sz; ++ ++ if (tx_align) ++ tx_nents = 2; ++ else ++ tx_nents = 1; ++ ++ /* initialize scatterlists */ ++ sg_init_table(tx_sg, tx_nents); ++ sg_dma_len(&tx_sg[0]) = xfer_len - tx_align; ++ sg_dma_address(&tx_sg[0]) = tx_dma + offset; ++ ++ /* account for non block size transfer */ ++ if (tx_align) { ++ sg_dma_len(&tx_sg[1]) = controller->out_blk_sz; ++ sg_dma_address(&tx_sg[1]) = tx_dummy_dma; ++ } ++ ++ txd = dmaengine_prep_slave_sg(controller->tx_chan, ++ tx_sg, tx_nents, DMA_MEM_TO_DEV, 0); ++ if (!txd) { ++ ret = -ENOMEM; ++ goto err_unmap; ++ } ++ ++ atomic_inc(&controller->dma_outstanding); ++ ++ txd->callback = qup_dma_callback; ++ txd->callback_param = controller; ++ ++ tx_cookie = dmaengine_submit(txd); ++ ++ dma_async_issue_pending(controller->tx_chan); ++ } ++ ++ if (xfer->rx_buf) { ++ /* recalc align for each transaction */ ++ rx_align = xfer_len % controller->in_blk_sz; ++ ++ if (rx_align) ++ rx_nents = 2; ++ else ++ rx_nents = 1; ++ ++ /* initialize scatterlists */ ++ sg_init_table(rx_sg, rx_nents); ++ sg_dma_address(&rx_sg[0]) = rx_dma + offset; ++ sg_dma_len(&rx_sg[0]) = xfer_len - rx_align; ++ ++ /* account for non block size transfer */ ++ if (rx_align) { ++ sg_dma_len(&rx_sg[1]) = controller->in_blk_sz; ++ sg_dma_address(&rx_sg[1]) = rx_dummy_dma; ++ } ++ ++ rxd = dmaengine_prep_slave_sg(controller->rx_chan, ++ rx_sg, rx_nents, DMA_DEV_TO_MEM, 0); ++ if (!rxd) { ++ ret = -ENOMEM; ++ goto err_unmap; ++ } ++ ++ atomic_inc(&controller->dma_outstanding); ++ ++ rxd->callback = qup_dma_callback; ++ rxd->callback_param = controller; ++ ++ rx_cookie = dmaengine_submit(rxd); ++ ++ dma_async_issue_pending(controller->rx_chan); ++ } ++ ++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { ++ dev_warn(controller->dev, "cannot set EXECUTE state\n"); ++ goto err_unmap; ++ } ++ ++ if (!wait_for_completion_timeout(&controller->done, ++ msecs_to_jiffies(1000))) { ++ ret = -ETIMEDOUT; ++ ++ /* clear out all the DMA transactions */ ++ if (xfer->tx_buf) ++ dmaengine_terminate_all(controller->tx_chan); ++ if (xfer->rx_buf) ++ dmaengine_terminate_all(controller->rx_chan); ++ ++ goto err_unmap; ++ } ++ ++ if (rx_align) ++ memcpy(xfer->rx_buf + offset + xfer->len - rx_align, ++ controller->dummy, rx_align); ++ ++ /* adjust remaining bytes to transfer */ ++ bytes_to_xfer -= xfer_len; ++ offset += xfer_len; ++ ++ ++ /* reset mini-core state so we can program next transaction */ ++ if (spi_qup_set_state(controller, QUP_STATE_RESET)) { ++ dev_err(controller->dev, "cannot set RESET state\n"); ++ goto err_unmap; ++ } ++ } ++ ++ ret = 0; ++ ++err_unmap: ++ if (tx_align) ++ dma_unmap_single(controller->dev, tx_dummy_dma, ++ controller->out_fifo_sz, DMA_TO_DEVICE); ++err_map_tx_dummy: ++ if (xfer->tx_buf) ++ dma_unmap_single(controller->dev, tx_dma, xfer->len, ++ DMA_TO_DEVICE); ++err_map_tx: ++ if (rx_align) ++ dma_unmap_single(controller->dev, rx_dummy_dma, ++ controller->in_fifo_sz, DMA_FROM_DEVICE); ++err_map_rx_dummy: ++ if (xfer->rx_buf) ++ dma_unmap_single(controller->dev, rx_dma, xfer->len, ++ DMA_FROM_DEVICE); ++ ++ return ret; ++} ++ + static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) + { + struct spi_qup *controller = dev_id; +@@ -315,11 +545,13 @@ static irqreturn_t spi_qup_qup_irq(int i + error = -EIO; + } + +- if (opflags & QUP_OP_IN_SERVICE_FLAG) +- spi_qup_fifo_read(controller, xfer); ++ if (!controller->use_dma) { ++ if (opflags & QUP_OP_IN_SERVICE_FLAG) ++ spi_qup_fifo_read(controller, xfer); + +- if (opflags & QUP_OP_OUT_SERVICE_FLAG) +- spi_qup_fifo_write(controller, xfer); ++ if (opflags & QUP_OP_OUT_SERVICE_FLAG) ++ spi_qup_fifo_write(controller, xfer); ++ } + + spin_lock_irqsave(&controller->lock, flags); + controller->error = error; +@@ -339,6 +571,8 @@ static int spi_qup_io_config(struct spi_ + struct spi_qup *controller = spi_master_get_devdata(spi->master); + u32 config, iomode, mode; + int ret, n_words, w_size; ++ size_t dma_align = dma_get_cache_alignment(); ++ u32 dma_available = 0; + + if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { + dev_err(controller->dev, "too big size for loopback %d > %d\n", +@@ -367,6 +601,11 @@ static int spi_qup_io_config(struct spi_ + n_words = xfer->len / w_size; + controller->w_size = w_size; + ++ if (controller->rx_chan && ++ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) && ++ IS_ALIGNED((size_t)xfer->rx_buf, dma_align)) ++ dma_available = 1; ++ + if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { + mode = QUP_IO_M_MODE_FIFO; + writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); +@@ -374,19 +613,31 @@ static int spi_qup_io_config(struct spi_ + /* must be zero for FIFO */ + writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); +- } else { ++ controller->use_dma = 0; ++ } else if (!dma_available) { + mode = QUP_IO_M_MODE_BLOCK; + writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); + /* must be zero for BLOCK and BAM */ + writel_relaxed(0, controller->base + QUP_MX_READ_CNT); + writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); ++ controller->use_dma = 0; ++ } else { ++ mode = QUP_IO_M_MODE_DMOV; ++ writel_relaxed(0, controller->base + QUP_MX_READ_CNT); ++ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); ++ controller->use_dma = 1; + } + + iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); + /* Set input and output transfer mode */ + iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); +- iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); ++ ++ if (!controller->use_dma) ++ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); ++ else ++ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; ++ + iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); + iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); + +@@ -419,6 +670,14 @@ static int spi_qup_io_config(struct spi_ + config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); + config |= xfer->bits_per_word - 1; + config |= QUP_CONFIG_SPI_MODE; ++ ++ if (controller->use_dma) { ++ if (!xfer->tx_buf) ++ config |= QUP_CONFIG_NO_OUTPUT; ++ if (!xfer->rx_buf) ++ config |= QUP_CONFIG_NO_INPUT; ++ } ++ + writel_relaxed(config, controller->base + QUP_CONFIG); + + /* only write to OPERATIONAL_MASK when register is present */ +@@ -452,25 +711,29 @@ static int spi_qup_transfer_one(struct s + controller->tx_bytes = 0; + spin_unlock_irqrestore(&controller->lock, flags); + +- if (spi_qup_set_state(controller, QUP_STATE_RUN)) { +- dev_warn(controller->dev, "cannot set RUN state\n"); +- goto exit; +- } ++ if (controller->use_dma) { ++ ret = spi_qup_do_dma(controller, xfer); ++ } else { ++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { ++ dev_warn(controller->dev, "cannot set RUN state\n"); ++ goto exit; ++ } + +- if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { +- dev_warn(controller->dev, "cannot set PAUSE state\n"); +- goto exit; +- } ++ if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { ++ dev_warn(controller->dev, "cannot set PAUSE state\n"); ++ goto exit; ++ } + +- spi_qup_fifo_write(controller, xfer); ++ spi_qup_fifo_write(controller, xfer); + +- if (spi_qup_set_state(controller, QUP_STATE_RUN)) { +- dev_warn(controller->dev, "cannot set EXECUTE state\n"); +- goto exit; +- } ++ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { ++ dev_warn(controller->dev, "cannot set EXECUTE state\n"); ++ goto exit; ++ } + +- if (!wait_for_completion_timeout(&controller->done, timeout)) +- ret = -ETIMEDOUT; ++ if (!wait_for_completion_timeout(&controller->done, timeout)) ++ ret = -ETIMEDOUT; ++ } + exit: + spi_qup_set_state(controller, QUP_STATE_RESET); + spin_lock_irqsave(&controller->lock, flags); +@@ -553,6 +816,7 @@ static int spi_qup_probe(struct platform + master->transfer_one = spi_qup_transfer_one; + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; ++ master->dma_alignment = dma_get_cache_alignment(); + + platform_set_drvdata(pdev, master); + +@@ -618,6 +882,56 @@ static int spi_qup_probe(struct platform + QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN, + base + QUP_ERROR_FLAGS_EN); + ++ /* allocate dma resources, if available */ ++ controller->rx_chan = dma_request_slave_channel(&pdev->dev, "rx"); ++ if (controller->rx_chan) { ++ controller->tx_chan = ++ dma_request_slave_channel(&pdev->dev, "tx"); ++ ++ if (!controller->tx_chan) { ++ dev_err(&pdev->dev, "Failed to allocate dma tx chan"); ++ dma_release_channel(controller->rx_chan); ++ } ++ ++ /* set DMA parameters */ ++ controller->rx_conf.device_fc = 1; ++ controller->rx_conf.src_addr = res->start + QUP_INPUT_FIFO; ++ controller->rx_conf.src_maxburst = controller->in_blk_sz; ++ ++ controller->tx_conf.device_fc = 1; ++ controller->tx_conf.dst_addr = res->start + QUP_OUTPUT_FIFO; ++ controller->tx_conf.dst_maxburst = controller->out_blk_sz; ++ ++ if (dmaengine_slave_config(controller->rx_chan, ++ &controller->rx_conf)) { ++ dev_err(&pdev->dev, "failed to configure RX channel\n"); ++ ++ dma_release_channel(controller->rx_chan); ++ dma_release_channel(controller->tx_chan); ++ controller->tx_chan = NULL; ++ controller->rx_chan = NULL; ++ } else if (dmaengine_slave_config(controller->tx_chan, ++ &controller->tx_conf)) { ++ dev_err(&pdev->dev, "failed to configure TX channel\n"); ++ ++ dma_release_channel(controller->rx_chan); ++ dma_release_channel(controller->tx_chan); ++ controller->tx_chan = NULL; ++ controller->rx_chan = NULL; ++ } ++ ++ controller->dummy = devm_kmalloc(controller->dev, PAGE_SIZE, ++ GFP_KERNEL); ++ ++ if (!controller->dummy) { ++ dma_release_channel(controller->rx_chan); ++ dma_release_channel(controller->tx_chan); ++ controller->tx_chan = NULL; ++ controller->rx_chan = NULL; ++ } ++ } ++ ++ + writel_relaxed(0, base + SPI_CONFIG); + writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL); + +@@ -730,6 +1044,11 @@ static int spi_qup_remove(struct platfor + if (ret) + return ret; + ++ if (controller->rx_chan) ++ dma_release_channel(controller->rx_chan); ++ if (controller->tx_chan) ++ dma_release_channel(controller->tx_chan); ++ + clk_disable_unprepare(controller->cclk); + clk_disable_unprepare(controller->iclk); + diff --git a/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch b/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch new file mode 100644 index 0000000000..62ee5b4fad --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch @@ -0,0 +1,376 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3] spi: qup: Fix incorrect block transfers +From: Andy Gross +X-Patchwork-Id: 5007321 +Message-Id: <1412112088-25928-1-git-send-email-agross@codeaurora.org> +To: Mark Brown +Cc: linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org, + "Ivan T. Ivanov" , + Bjorn Andersson , + Kumar Gala , Andy Gross +Date: Tue, 30 Sep 2014 16:21:28 -0500 + +This patch fixes a number of errors with the QUP block transfer mode. Errors +manifested themselves as input underruns, output overruns, and timed out +transactions. + +The block mode does not require the priming that occurs in FIFO mode. At the +moment that the QUP is placed into the RUN state, the QUP will immediately raise +an interrupt if the request is a write. Therefore, there is no need to prime +the pump. + +In addition, the block transfers require that whole blocks of data are +read/written at a time. The last block of data that completes a transaction may +contain less than a full blocks worth of data. + +Each block of data results in an input/output service interrupt accompanied with +a input/output block flag set. Additional block reads/writes require clearing +of the service flag. It is ok to check for additional blocks of data in the +ISR, but you have to ack every block you transfer. Imbalanced acks result in +early return from complete transactions with pending interrupts that still have +to be ack'd. The next transaction can be affected by these interrupts. +Transactions are deemed complete when the MAX_INPUT or MAX_OUTPUT flag are set. + +Changes from v2: +- Added in additional completion check so that transaction done is not + prematurely signaled. +- Fixed various review comments. + +Changes from v1: +- Split out read/write block function. +- Removed extraneous checks for transfer length + +Signed-off-by: Andy Gross + +--- +drivers/spi/spi-qup.c | 201 ++++++++++++++++++++++++++++++++++++------------- + 1 file changed, 148 insertions(+), 53 deletions(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -82,6 +82,8 @@ + #define QUP_IO_M_MODE_BAM 3 + + /* QUP_OPERATIONAL fields */ ++#define QUP_OP_IN_BLOCK_READ_REQ BIT(13) ++#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12) + #define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) + #define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) + #define QUP_OP_IN_SERVICE_FLAG BIT(9) +@@ -147,6 +149,7 @@ struct spi_qup { + int tx_bytes; + int rx_bytes; + int qup_v1; ++ int mode; + + int use_dma; + +@@ -213,30 +216,14 @@ static int spi_qup_set_state(struct spi_ + return 0; + } + +- +-static void spi_qup_fifo_read(struct spi_qup *controller, +- struct spi_transfer *xfer) ++static void spi_qup_fill_read_buffer(struct spi_qup *controller, ++ struct spi_transfer *xfer, u32 data) + { + u8 *rx_buf = xfer->rx_buf; +- u32 word, state; +- int idx, shift, w_size; +- +- w_size = controller->w_size; +- +- while (controller->rx_bytes < xfer->len) { +- +- state = readl_relaxed(controller->base + QUP_OPERATIONAL); +- if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY)) +- break; +- +- word = readl_relaxed(controller->base + QUP_INPUT_FIFO); +- +- if (!rx_buf) { +- controller->rx_bytes += w_size; +- continue; +- } ++ int idx, shift; + +- for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) { ++ if (rx_buf) ++ for (idx = 0; idx < controller->w_size; idx++) { + /* + * The data format depends on bytes per SPI word: + * 4 bytes: 0x12345678 +@@ -244,41 +231,139 @@ static void spi_qup_fifo_read(struct spi + * 1 byte : 0x00000012 + */ + shift = BITS_PER_BYTE; +- shift *= (w_size - idx - 1); +- rx_buf[controller->rx_bytes] = word >> shift; ++ shift *= (controller->w_size - idx - 1); ++ rx_buf[controller->rx_bytes + idx] = data >> shift; ++ } ++ ++ controller->rx_bytes += controller->w_size; ++} ++ ++static void spi_qup_prepare_write_data(struct spi_qup *controller, ++ struct spi_transfer *xfer, u32 *data) ++{ ++ const u8 *tx_buf = xfer->tx_buf; ++ u32 val; ++ int idx; ++ ++ *data = 0; ++ ++ if (tx_buf) ++ for (idx = 0; idx < controller->w_size; idx++) { ++ val = tx_buf[controller->tx_bytes + idx]; ++ *data |= val << (BITS_PER_BYTE * (3 - idx)); + } ++ ++ controller->tx_bytes += controller->w_size; ++} ++ ++static void spi_qup_fifo_read(struct spi_qup *controller, ++ struct spi_transfer *xfer) ++{ ++ u32 data; ++ ++ /* clear service request */ ++ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, ++ controller->base + QUP_OPERATIONAL); ++ ++ while (controller->rx_bytes < xfer->len) { ++ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) & ++ QUP_OP_IN_FIFO_NOT_EMPTY)) ++ break; ++ ++ data = readl_relaxed(controller->base + QUP_INPUT_FIFO); ++ ++ spi_qup_fill_read_buffer(controller, xfer, data); + } + } + + static void spi_qup_fifo_write(struct spi_qup *controller, +- struct spi_transfer *xfer) ++ struct spi_transfer *xfer) + { +- const u8 *tx_buf = xfer->tx_buf; +- u32 word, state, data; +- int idx, w_size; ++ u32 data; + +- w_size = controller->w_size; ++ /* clear service request */ ++ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, ++ controller->base + QUP_OPERATIONAL); + + while (controller->tx_bytes < xfer->len) { + +- state = readl_relaxed(controller->base + QUP_OPERATIONAL); +- if (state & QUP_OP_OUT_FIFO_FULL) ++ if (readl_relaxed(controller->base + QUP_OPERATIONAL) & ++ QUP_OP_OUT_FIFO_FULL) + break; + +- word = 0; +- for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) { ++ spi_qup_prepare_write_data(controller, xfer, &data); ++ writel_relaxed(data, controller->base + QUP_OUTPUT_FIFO); + +- if (!tx_buf) { +- controller->tx_bytes += w_size; +- break; +- } ++ } ++} + +- data = tx_buf[controller->tx_bytes]; +- word |= data << (BITS_PER_BYTE * (3 - idx)); +- } ++static void spi_qup_block_read(struct spi_qup *controller, ++ struct spi_transfer *xfer) ++{ ++ u32 data; ++ u32 reads_per_blk = controller->in_blk_sz >> 2; ++ u32 num_words = (xfer->len - controller->rx_bytes) / controller->w_size; ++ int i; ++ ++ do { ++ /* ACK by clearing service flag */ ++ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, ++ controller->base + QUP_OPERATIONAL); ++ ++ /* transfer up to a block size of data in a single pass */ ++ for (i = 0; num_words && i < reads_per_blk; i++, num_words--) { ++ ++ /* read data and fill up rx buffer */ ++ data = readl_relaxed(controller->base + QUP_INPUT_FIFO); ++ spi_qup_fill_read_buffer(controller, xfer, data); ++ } ++ ++ /* check to see if next block is ready */ ++ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) & ++ QUP_OP_IN_BLOCK_READ_REQ)) ++ break; + +- writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); +- } ++ } while (num_words); ++ ++ /* ++ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block ++ * reads, it has to be cleared again at the very end ++ */ ++ if (readl_relaxed(controller->base + QUP_OPERATIONAL) & ++ QUP_OP_MAX_INPUT_DONE_FLAG) ++ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, ++ controller->base + QUP_OPERATIONAL); ++ ++} ++ ++static void spi_qup_block_write(struct spi_qup *controller, ++ struct spi_transfer *xfer) ++{ ++ u32 data; ++ u32 writes_per_blk = controller->out_blk_sz >> 2; ++ u32 num_words = (xfer->len - controller->tx_bytes) / controller->w_size; ++ int i; ++ ++ do { ++ /* ACK by clearing service flag */ ++ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, ++ controller->base + QUP_OPERATIONAL); ++ ++ /* transfer up to a block size of data in a single pass */ ++ for (i = 0; num_words && i < writes_per_blk; i++, num_words--) { ++ ++ /* swizzle the bytes for output and write out */ ++ spi_qup_prepare_write_data(controller, xfer, &data); ++ writel_relaxed(data, ++ controller->base + QUP_OUTPUT_FIFO); ++ } ++ ++ /* check to see if next block is ready */ ++ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) & ++ QUP_OP_OUT_BLOCK_WRITE_REQ)) ++ break; ++ ++ } while (num_words); + } + + static void qup_dma_callback(void *data) +@@ -515,9 +600,9 @@ static irqreturn_t spi_qup_qup_irq(int i + + writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); + writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); +- writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); + + if (!xfer) { ++ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); + dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n", + qup_err, spi_err, opflags); + return IRQ_HANDLED; +@@ -546,11 +631,19 @@ static irqreturn_t spi_qup_qup_irq(int i + } + + if (!controller->use_dma) { +- if (opflags & QUP_OP_IN_SERVICE_FLAG) +- spi_qup_fifo_read(controller, xfer); ++ if (opflags & QUP_OP_IN_SERVICE_FLAG) { ++ if (opflags & QUP_OP_IN_BLOCK_READ_REQ) ++ spi_qup_block_read(controller, xfer); ++ else ++ spi_qup_fifo_read(controller, xfer); ++ } + +- if (opflags & QUP_OP_OUT_SERVICE_FLAG) +- spi_qup_fifo_write(controller, xfer); ++ if (opflags & QUP_OP_OUT_SERVICE_FLAG) { ++ if (opflags & QUP_OP_OUT_BLOCK_WRITE_REQ) ++ spi_qup_block_write(controller, xfer); ++ else ++ spi_qup_fifo_write(controller, xfer); ++ } + } + + spin_lock_irqsave(&controller->lock, flags); +@@ -558,7 +651,8 @@ static irqreturn_t spi_qup_qup_irq(int i + controller->xfer = xfer; + spin_unlock_irqrestore(&controller->lock, flags); + +- if (controller->rx_bytes == xfer->len || error) ++ if ((controller->rx_bytes == xfer->len && ++ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) + complete(&controller->done); + + return IRQ_HANDLED; +@@ -569,7 +663,7 @@ static irqreturn_t spi_qup_qup_irq(int i + static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + { + struct spi_qup *controller = spi_master_get_devdata(spi->master); +- u32 config, iomode, mode; ++ u32 config, iomode; + int ret, n_words, w_size; + size_t dma_align = dma_get_cache_alignment(); + u32 dma_available = 0; +@@ -607,7 +701,7 @@ static int spi_qup_io_config(struct spi_ + dma_available = 1; + + if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { +- mode = QUP_IO_M_MODE_FIFO; ++ controller->mode = QUP_IO_M_MODE_FIFO; + writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); + /* must be zero for FIFO */ +@@ -615,7 +709,7 @@ static int spi_qup_io_config(struct spi_ + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); + controller->use_dma = 0; + } else if (!dma_available) { +- mode = QUP_IO_M_MODE_BLOCK; ++ controller->mode = QUP_IO_M_MODE_BLOCK; + writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); + /* must be zero for BLOCK and BAM */ +@@ -623,7 +717,7 @@ static int spi_qup_io_config(struct spi_ + writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); + controller->use_dma = 0; + } else { +- mode = QUP_IO_M_MODE_DMOV; ++ controller->mode = QUP_IO_M_MODE_DMOV; + writel_relaxed(0, controller->base + QUP_MX_READ_CNT); + writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); + controller->use_dma = 1; +@@ -638,8 +732,8 @@ static int spi_qup_io_config(struct spi_ + else + iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; + +- iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); +- iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); ++ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); ++ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); + + writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); + +@@ -724,7 +818,8 @@ static int spi_qup_transfer_one(struct s + goto exit; + } + +- spi_qup_fifo_write(controller, xfer); ++ if (controller->mode == QUP_IO_M_MODE_FIFO) ++ spi_qup_fifo_write(controller, xfer); + + if (spi_qup_set_state(controller, QUP_STATE_RUN)) { + dev_warn(controller->dev, "cannot set EXECUTE state\n"); +@@ -741,6 +836,7 @@ exit: + if (!ret) + ret = controller->error; + spin_unlock_irqrestore(&controller->lock, flags); ++ + return ret; + } + diff --git a/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch b/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch new file mode 100644 index 0000000000..7052227810 --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch @@ -0,0 +1,56 @@ +From 4faba89e3ffbb1c5f6232651375b9b3212b50f02 Mon Sep 17 00:00:00 2001 +From: Andy Gross +Date: Thu, 15 Jan 2015 17:56:02 -0800 +Subject: [PATCH] spi: qup: Ensure done detection + +This patch fixes an issue where a SPI transaction has completed, but the done +condition is missed. This occurs because at the time of interrupt the +MAX_INPUT_DONE_FLAG is not asserted. However, in the process of reading blocks +of data from the FIFO, the last portion of data comes in. + +The opflags read at the beginning of the irq handler no longer matches the +current opflag state. To get around this condition, the block read function +should update the opflags so that done detection is correct after the return. + +Change-Id: If109e0eeb432f96000d765c4b34dbb2269f8093f +Signed-off-by: Andy Gross +--- + drivers/spi/spi-qup.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -298,7 +298,7 @@ static void spi_qup_fifo_write(struct sp + } + + static void spi_qup_block_read(struct spi_qup *controller, +- struct spi_transfer *xfer) ++ struct spi_transfer *xfer, u32 *opflags) + { + u32 data; + u32 reads_per_blk = controller->in_blk_sz >> 2; +@@ -327,10 +327,12 @@ static void spi_qup_block_read(struct sp + + /* + * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block +- * reads, it has to be cleared again at the very end ++ * reads, it has to be cleared again at the very end. However, be sure ++ * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be ++ * present and this is used to determine if transaction is complete + */ +- if (readl_relaxed(controller->base + QUP_OPERATIONAL) & +- QUP_OP_MAX_INPUT_DONE_FLAG) ++ *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); ++ if (*opflags & QUP_OP_MAX_INPUT_DONE_FLAG) + writel_relaxed(QUP_OP_IN_SERVICE_FLAG, + controller->base + QUP_OPERATIONAL); + +@@ -633,7 +635,7 @@ static irqreturn_t spi_qup_qup_irq(int i + if (!controller->use_dma) { + if (opflags & QUP_OP_IN_SERVICE_FLAG) { + if (opflags & QUP_OP_IN_BLOCK_READ_REQ) +- spi_qup_block_read(controller, xfer); ++ spi_qup_block_read(controller, xfer, &opflags); + else + spi_qup_fifo_read(controller, xfer); + } diff --git a/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch b/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch new file mode 100644 index 0000000000..0cd7da1f6d --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch @@ -0,0 +1,72 @@ +From fded70251b1b58f68de1d3757ece9965f0b75452 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Thu, 19 Feb 2015 20:19:30 -0800 +Subject: [PATCH 1/3] watchdog: qcom: use timer devicetree binding + +MSM watchdog configuration happens in the same register block as the +timer, so we'll use the same binding as the existing timer. + +The qcom-wdt will now be probed when devicetree has an entry compatible +with "qcom,kpss-timer" or "qcom-scss-timer". + +Signed-off-by: Mathieu Olivari +--- + drivers/watchdog/qcom-wdt.c | 21 +++++++++++++++------ + 1 file changed, 15 insertions(+), 6 deletions(-) + +diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c +index aa85618..aa03ca8 100644 +--- a/drivers/watchdog/qcom-wdt.c ++++ b/drivers/watchdog/qcom-wdt.c +@@ -20,9 +20,9 @@ + #include + #include + +-#define WDT_RST 0x0 +-#define WDT_EN 0x8 +-#define WDT_BITE_TIME 0x24 ++#define WDT_RST 0x38 ++#define WDT_EN 0x40 ++#define WDT_BITE_TIME 0x5C + + struct qcom_wdt { + struct watchdog_device wdd; +@@ -117,6 +117,8 @@ static int qcom_wdt_probe(struct platform_device *pdev) + { + struct qcom_wdt *wdt; + struct resource *res; ++ struct device_node *np = pdev->dev.of_node; ++ u32 percpu_offset; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); +@@ -124,6 +126,14 @@ static int qcom_wdt_probe(struct platform_device *pdev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ /* We use CPU0's DGT for the watchdog */ ++ if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) ++ percpu_offset = 0; ++ ++ res->start += percpu_offset; ++ res->end += percpu_offset; ++ + wdt->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); +@@ -203,9 +213,8 @@ static int qcom_wdt_remove(struct platform_device *pdev) + } + + static const struct of_device_id qcom_wdt_of_table[] = { +- { .compatible = "qcom,kpss-wdt-msm8960", }, +- { .compatible = "qcom,kpss-wdt-apq8064", }, +- { .compatible = "qcom,kpss-wdt-ipq8064", }, ++ { .compatible = "qcom,kpss-timer" }, ++ { .compatible = "qcom,scss-timer" }, + { }, + }; + MODULE_DEVICE_TABLE(of, qcom_wdt_of_table); +-- +1.9.1 + diff --git a/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch b/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch new file mode 100644 index 0000000000..24a093a812 --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch @@ -0,0 +1,53 @@ +From 297cf8136ecd6a56520888fd28948393766b8ee7 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Thu, 19 Feb 2015 20:27:39 -0800 +Subject: [PATCH 2/3] ARM: qcom: add description of KPSS WDT for IPQ8064 + +Add the watchdog related entries to the Krait Processor Sub-system +(KPSS) timer IPQ8064 devicetree section. Also, add a fixed-clock +description of SLEEP_CLK, which will do for now. + +Signed-off-by: Josh Cartwright +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064.dtsi | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi +index cb225da..d01f618 100644 +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -60,6 +60,14 @@ + }; + }; + ++ clocks { ++ sleep_clk: sleep_clk { ++ compatible = "fixed-clock"; ++ clock-frequency = <32768>; ++ #clock-cells = <0>; ++ }; ++ }; ++ + soc: soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -89,10 +97,14 @@ + compatible = "qcom,kpss-timer", "qcom,msm-timer"; + interrupts = <1 1 0x301>, + <1 2 0x301>, +- <1 3 0x301>; ++ <1 3 0x301>, ++ <1 4 0x301>, ++ <1 5 0x301>; + reg = <0x0200a000 0x100>; + clock-frequency = <25000000>, + <32768>; ++ clocks = <&sleep_clk>; ++ clock-names = "sleep"; + cpu-offset = <0x80000>; + }; + +-- +1.9.1 + diff --git a/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch b/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch new file mode 100644 index 0000000000..6876768962 --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch @@ -0,0 +1,50 @@ +From e535f01dffb6dd9e09934fa219be52af3437a8f6 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Thu, 19 Feb 2015 20:36:27 -0800 +Subject: [PATCH 3/3] ARM: msm: add watchdog entries to DT timer binding doc + +The watchdog has been reworked to use the same DT node as the timer. +This change is updating the device tree doc accordingly. + +Signed-off-by: Mathieu Olivari +--- + Documentation/devicetree/bindings/arm/msm/timer.txt | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +--- a/Documentation/devicetree/bindings/arm/msm/timer.txt ++++ b/Documentation/devicetree/bindings/arm/msm/timer.txt +@@ -9,11 +9,17 @@ Properties: + "qcom,scss-timer" - scorpion subsystem + + - interrupts : Interrupts for the the debug timer, the first general purpose +- timer, and optionally a second general purpose timer in that +- order. ++ timer, and optionally a second general purpose timer, and ++ optionally as well, 2 watchdog interrupts, in that order. + + - reg : Specifies the base address of the timer registers. + ++- clocks: Reference to the parent clocks, one per output clock. The parents ++ must appear in the same order as the clock names. ++ ++- clock-names: The name of the clocks as free-form strings. They should be in ++ the same order as the clocks. ++ + - clock-frequency : The frequency of the debug timer and the general purpose + timer(s) in Hz in that order. + +@@ -29,9 +35,13 @@ Example: + compatible = "qcom,scss-timer", "qcom,msm-timer"; + interrupts = <1 1 0x301>, + <1 2 0x301>, +- <1 3 0x301>; ++ <1 3 0x301>, ++ <1 4 0x301>, ++ <1 5 0x301>; + reg = <0x0200a000 0x100>; + clock-frequency = <19200000>, + <32768>; ++ clocks = <&sleep_clk>; ++ clock-names = "sleep"; + cpu-offset = <0x40000>; + }; diff --git a/target/linux/ipq806x/patches-3.18/700-add-gmac-dts-suport.patch b/target/linux/ipq806x/patches-3.18/700-add-gmac-dts-suport.patch new file mode 100644 index 0000000000..89ebe66946 --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/700-add-gmac-dts-suport.patch @@ -0,0 +1,172 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -18,8 +18,15 @@ + bootargs = "console=ttyMSM0,115200 root=/dev/mtdblock12 rootfstype=squashfs,jffs2"; + }; + ++ aliases { ++ mdio-gpio0 = &mdio0; ++ }; ++ + soc { + pinmux@800000 { ++ pinctrl-0 = <&mdio0_pins &rgmii2_pins>; ++ pinctrl-names = "default"; ++ + i2c4_pins: i2c4_pinmux { + pins = "gpio12", "gpio13"; + function = "gsbi4"; +@@ -34,6 +41,25 @@ + bias-none; + }; + }; ++ ++ mdio0_pins: mdio0_pins { ++ mux { ++ pins = "gpio0", "gpio1"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; ++ ++ rgmii2_pins: rgmii2_pins { ++ mux { ++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", ++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; ++ function = "rgmii2"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi@16300000 { +@@ -72,6 +98,7 @@ + #size-cells = <1>; + spi-max-frequency = <50000000>; + reg = <0>; ++ m25p,fast-read; + + partition@0 { + label = "0:SBL1"; +@@ -148,5 +175,66 @@ + sata@29000000 { + status = "ok"; + }; ++ ++ mdio0: mdio { ++ compatible = "virtual,mdio-gpio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; ++ ++ phy0: ethernet-phy@0 { ++ device_type = "ethernet-phy"; ++ reg = <0>; ++ qca,ar8327-initvals = < ++ 0x00004 0x7600000 /* PAD0_MODE */ ++ 0x00008 0x1000000 /* PAD5_MODE */ ++ 0x0000c 0x80 /* PAD6_MODE */ ++ 0x000e4 0xaa545 /* MAC_POWER_SEL */ ++ 0x000e0 0xc74164de /* SGMII_CTRL */ ++ 0x0007c 0x4e /* PORT0_STATUS */ ++ 0x00094 0x4e /* PORT6_STATUS */ ++ >; ++ }; ++ ++ phy4: ethernet-phy@4 { ++ device_type = "ethernet-phy"; ++ reg = <4>; ++ }; ++ }; ++ ++ nss-gmac-common { ++ reg = <0x03000000 0x0000FFFF 0x1bb00000 0x0000FFFF 0x00900000 0x00004000>; ++ reg-names = "nss_reg_base" , "qsgmii_reg_base", "clk_ctl_base"; ++ }; ++ ++ gmac1: ethernet@37200000 { ++ status = "ok"; ++ phy-mode = "rgmii"; ++ qcom,id = <1>; ++ qcom,phy_mdio_addr = <4>; ++ qcom,poll_required = <1>; ++ qcom,rgmii_delay = <0>; ++ qcom,emulation = <0>; ++ qcom,forced_speed = <1000>; ++ qcom,forced_duplex = <1>; ++ qcom,socver = <0>; ++ local-mac-address = [000000000000]; ++ mdiobus = <&mdio0>; ++ }; ++ ++ gmac2: ethernet@37400000 { ++ status = "ok"; ++ phy-mode = "sgmii"; ++ qcom,id = <2>; ++ qcom,phy_mdio_addr = <0>; ++ qcom,poll_required = <0>; ++ qcom,rgmii_delay = <0>; ++ qcom,emulation = <0>; ++ qcom,forced_speed = <1000>; ++ qcom,forced_duplex = <1>; ++ qcom,socver = <0>; ++ local-mac-address = [000000000000]; ++ mdiobus = <&mdio0>; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -3,6 +3,7 @@ + #include "skeleton.dtsi" + #include + #include ++#include + + / { + model = "Qualcomm IPQ8064"; +@@ -279,5 +280,42 @@ + #clock-cells = <1>; + #reset-cells = <1>; + }; ++ ++ nss-gmac-common { ++ reg = <0x03000000 0x0000FFFF 0x1bb00000 0x0000FFFF 0x00900000 0x00004000>; ++ reg-names = "nss_reg_base" , "qsgmii_reg_base", "clk_ctl_base"; ++ }; ++ ++ gmac0: ethernet@37000000 { ++ device_type = "network"; ++ compatible = "qcom,nss-gmac"; ++ reg = <0x37000000 0x200000>; ++ interrupts = ; ++ status = "disabled"; ++ }; ++ ++ gmac1: ethernet@37200000 { ++ device_type = "network"; ++ compatible = "qcom,nss-gmac"; ++ reg = <0x37200000 0x200000>; ++ interrupts = ; ++ status = "disabled"; ++ }; ++ ++ gmac2: ethernet@37400000 { ++ device_type = "network"; ++ compatible = "qcom,nss-gmac"; ++ reg = <0x37400000 0x200000>; ++ interrupts = ; ++ status = "disabled"; ++ }; ++ ++ gmac3: ethernet@37600000 { ++ device_type = "network"; ++ compatible = "qcom,nss-gmac"; ++ reg = <0x37600000 0x200000>; ++ interrupts = ; ++ status = "disabled"; ++ }; + }; + }; diff --git a/target/linux/ipq806x/patches/001-spi-qup-Add-DMA-capabilities.patch b/target/linux/ipq806x/patches/001-spi-qup-Add-DMA-capabilities.patch deleted file mode 100644 index 62badd5387..0000000000 --- a/target/linux/ipq806x/patches/001-spi-qup-Add-DMA-capabilities.patch +++ /dev/null @@ -1,522 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: spi: qup: Add DMA capabilities -From: Andy Gross -X-Patchwork-Id: 4432401 -Message-Id: <1403816781-31008-1-git-send-email-agross@codeaurora.org> -To: Mark Brown -Cc: linux-spi@vger.kernel.org, Sagar Dharia , - Daniel Sneddon , - Bjorn Andersson , - "Ivan T. Ivanov" , - linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - linux-arm-msm@vger.kernel.org, Andy Gross -Date: Thu, 26 Jun 2014 16:06:21 -0500 - -This patch adds DMA capabilities to the spi-qup driver. If DMA channels are -present, the QUP will use DMA instead of block mode for transfers to/from SPI -peripherals for transactions larger than the length of a block. - -Signed-off-by: Andy Gross - ---- -.../devicetree/bindings/spi/qcom,spi-qup.txt | 10 + - drivers/spi/spi-qup.c | 361 ++++++++++++++++++-- - 2 files changed, 350 insertions(+), 21 deletions(-) - ---- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt -+++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt -@@ -27,6 +27,11 @@ Optional properties: - - spi-max-frequency: Specifies maximum SPI clock frequency, - Units - Hz. Definition as per - Documentation/devicetree/bindings/spi/spi-bus.txt -+- dmas : Two DMA channel specifiers following the convention outlined -+ in bindings/dma/dma.txt -+- dma-names: Names for the dma channels, if present. There must be at -+ least one channel named "tx" for transmit and named "rx" for -+ receive. - - num-cs: total number of chipselects - - cs-gpios: should specify GPIOs used for chipselects. - The gpios will be referred to as reg = in the SPI child -@@ -51,6 +56,10 @@ Example: - clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>; - clock-names = "core", "iface"; - -+ dmas = <&blsp2_bam 2>, -+ <&blsp2_bam 3>; -+ dma-names = "rx", "tx"; -+ - pinctrl-names = "default"; - pinctrl-0 = <&spi8_default>; - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -22,6 +22,8 @@ - #include - #include - #include -+#include -+#include - - #define QUP_CONFIG 0x0000 - #define QUP_STATE 0x0004 -@@ -116,6 +118,8 @@ - - #define SPI_NUM_CHIPSELECTS 4 - -+#define SPI_MAX_XFER (SZ_64K - 64) -+ - /* high speed mode is when bus rate is greater then 26MHz */ - #define SPI_HS_MIN_RATE 26000000 - #define SPI_MAX_RATE 50000000 -@@ -143,6 +147,17 @@ struct spi_qup { - int tx_bytes; - int rx_bytes; - int qup_v1; -+ -+ int use_dma; -+ -+ struct dma_chan *rx_chan; -+ struct dma_slave_config rx_conf; -+ struct dma_chan *tx_chan; -+ struct dma_slave_config tx_conf; -+ dma_addr_t rx_dma; -+ dma_addr_t tx_dma; -+ void *dummy; -+ atomic_t dma_outstanding; - }; - - -@@ -266,6 +281,221 @@ static void spi_qup_fifo_write(struct sp - } - } - -+static void qup_dma_callback(void *data) -+{ -+ struct spi_qup *controller = data; -+ -+ if (atomic_dec_and_test(&controller->dma_outstanding)) -+ complete(&controller->done); -+} -+ -+static int spi_qup_do_dma(struct spi_qup *controller, struct spi_transfer *xfer) -+{ -+ struct dma_async_tx_descriptor *rxd, *txd; -+ dma_cookie_t rx_cookie, tx_cookie; -+ u32 xfer_len, rx_align = 0, tx_align = 0, n_words; -+ struct scatterlist tx_sg[2], rx_sg[2]; -+ int ret = 0; -+ u32 bytes_to_xfer = xfer->len; -+ u32 offset = 0; -+ u32 rx_nents = 0, tx_nents = 0; -+ dma_addr_t rx_dma = 0, tx_dma = 0, rx_dummy_dma = 0, tx_dummy_dma = 0; -+ -+ -+ if (xfer->rx_buf) { -+ rx_dma = dma_map_single(controller->dev, xfer->rx_buf, -+ xfer->len, DMA_FROM_DEVICE); -+ -+ if (dma_mapping_error(controller->dev, rx_dma)) { -+ ret = -ENOMEM; -+ return ret; -+ } -+ -+ /* check to see if we need dummy buffer for leftover bytes */ -+ rx_align = xfer->len % controller->in_blk_sz; -+ if (rx_align) { -+ rx_dummy_dma = dma_map_single(controller->dev, -+ controller->dummy, controller->in_fifo_sz, -+ DMA_FROM_DEVICE); -+ -+ if (dma_mapping_error(controller->dev, rx_dummy_dma)) { -+ ret = -ENOMEM; -+ goto err_map_rx_dummy; -+ } -+ } -+ } -+ -+ if (xfer->tx_buf) { -+ tx_dma = dma_map_single(controller->dev, -+ (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE); -+ -+ if (dma_mapping_error(controller->dev, tx_dma)) { -+ ret = -ENOMEM; -+ goto err_map_tx; -+ } -+ -+ /* check to see if we need dummy buffer for leftover bytes */ -+ tx_align = xfer->len % controller->out_blk_sz; -+ if (tx_align) { -+ memcpy(controller->dummy + SZ_1K, -+ xfer->tx_buf + xfer->len - tx_align, -+ tx_align); -+ memset(controller->dummy + SZ_1K + tx_align, 0, -+ controller->out_blk_sz - tx_align); -+ -+ tx_dummy_dma = dma_map_single(controller->dev, -+ controller->dummy + SZ_1K, -+ controller->out_blk_sz, DMA_TO_DEVICE); -+ -+ if (dma_mapping_error(controller->dev, tx_dummy_dma)) { -+ ret = -ENOMEM; -+ goto err_map_tx_dummy; -+ } -+ } -+ } -+ -+ atomic_set(&controller->dma_outstanding, 0); -+ -+ while (bytes_to_xfer > 0) { -+ xfer_len = min_t(u32, bytes_to_xfer, SPI_MAX_XFER); -+ n_words = DIV_ROUND_UP(xfer_len, controller->w_size); -+ -+ /* write out current word count to controller */ -+ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); -+ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); -+ -+ reinit_completion(&controller->done); -+ -+ if (xfer->tx_buf) { -+ /* recalc align for each transaction */ -+ tx_align = xfer_len % controller->out_blk_sz; -+ -+ if (tx_align) -+ tx_nents = 2; -+ else -+ tx_nents = 1; -+ -+ /* initialize scatterlists */ -+ sg_init_table(tx_sg, tx_nents); -+ sg_dma_len(&tx_sg[0]) = xfer_len - tx_align; -+ sg_dma_address(&tx_sg[0]) = tx_dma + offset; -+ -+ /* account for non block size transfer */ -+ if (tx_align) { -+ sg_dma_len(&tx_sg[1]) = controller->out_blk_sz; -+ sg_dma_address(&tx_sg[1]) = tx_dummy_dma; -+ } -+ -+ txd = dmaengine_prep_slave_sg(controller->tx_chan, -+ tx_sg, tx_nents, DMA_MEM_TO_DEV, 0); -+ if (!txd) { -+ ret = -ENOMEM; -+ goto err_unmap; -+ } -+ -+ atomic_inc(&controller->dma_outstanding); -+ -+ txd->callback = qup_dma_callback; -+ txd->callback_param = controller; -+ -+ tx_cookie = dmaengine_submit(txd); -+ -+ dma_async_issue_pending(controller->tx_chan); -+ } -+ -+ if (xfer->rx_buf) { -+ /* recalc align for each transaction */ -+ rx_align = xfer_len % controller->in_blk_sz; -+ -+ if (rx_align) -+ rx_nents = 2; -+ else -+ rx_nents = 1; -+ -+ /* initialize scatterlists */ -+ sg_init_table(rx_sg, rx_nents); -+ sg_dma_address(&rx_sg[0]) = rx_dma + offset; -+ sg_dma_len(&rx_sg[0]) = xfer_len - rx_align; -+ -+ /* account for non block size transfer */ -+ if (rx_align) { -+ sg_dma_len(&rx_sg[1]) = controller->in_blk_sz; -+ sg_dma_address(&rx_sg[1]) = rx_dummy_dma; -+ } -+ -+ rxd = dmaengine_prep_slave_sg(controller->rx_chan, -+ rx_sg, rx_nents, DMA_DEV_TO_MEM, 0); -+ if (!rxd) { -+ ret = -ENOMEM; -+ goto err_unmap; -+ } -+ -+ atomic_inc(&controller->dma_outstanding); -+ -+ rxd->callback = qup_dma_callback; -+ rxd->callback_param = controller; -+ -+ rx_cookie = dmaengine_submit(rxd); -+ -+ dma_async_issue_pending(controller->rx_chan); -+ } -+ -+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -+ dev_warn(controller->dev, "cannot set EXECUTE state\n"); -+ goto err_unmap; -+ } -+ -+ if (!wait_for_completion_timeout(&controller->done, -+ msecs_to_jiffies(1000))) { -+ ret = -ETIMEDOUT; -+ -+ /* clear out all the DMA transactions */ -+ if (xfer->tx_buf) -+ dmaengine_terminate_all(controller->tx_chan); -+ if (xfer->rx_buf) -+ dmaengine_terminate_all(controller->rx_chan); -+ -+ goto err_unmap; -+ } -+ -+ if (rx_align) -+ memcpy(xfer->rx_buf + offset + xfer->len - rx_align, -+ controller->dummy, rx_align); -+ -+ /* adjust remaining bytes to transfer */ -+ bytes_to_xfer -= xfer_len; -+ offset += xfer_len; -+ -+ -+ /* reset mini-core state so we can program next transaction */ -+ if (spi_qup_set_state(controller, QUP_STATE_RESET)) { -+ dev_err(controller->dev, "cannot set RESET state\n"); -+ goto err_unmap; -+ } -+ } -+ -+ ret = 0; -+ -+err_unmap: -+ if (tx_align) -+ dma_unmap_single(controller->dev, tx_dummy_dma, -+ controller->out_fifo_sz, DMA_TO_DEVICE); -+err_map_tx_dummy: -+ if (xfer->tx_buf) -+ dma_unmap_single(controller->dev, tx_dma, xfer->len, -+ DMA_TO_DEVICE); -+err_map_tx: -+ if (rx_align) -+ dma_unmap_single(controller->dev, rx_dummy_dma, -+ controller->in_fifo_sz, DMA_FROM_DEVICE); -+err_map_rx_dummy: -+ if (xfer->rx_buf) -+ dma_unmap_single(controller->dev, rx_dma, xfer->len, -+ DMA_FROM_DEVICE); -+ -+ return ret; -+} -+ - static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) - { - struct spi_qup *controller = dev_id; -@@ -315,11 +545,13 @@ static irqreturn_t spi_qup_qup_irq(int i - error = -EIO; - } - -- if (opflags & QUP_OP_IN_SERVICE_FLAG) -- spi_qup_fifo_read(controller, xfer); -+ if (!controller->use_dma) { -+ if (opflags & QUP_OP_IN_SERVICE_FLAG) -+ spi_qup_fifo_read(controller, xfer); - -- if (opflags & QUP_OP_OUT_SERVICE_FLAG) -- spi_qup_fifo_write(controller, xfer); -+ if (opflags & QUP_OP_OUT_SERVICE_FLAG) -+ spi_qup_fifo_write(controller, xfer); -+ } - - spin_lock_irqsave(&controller->lock, flags); - controller->error = error; -@@ -339,6 +571,8 @@ static int spi_qup_io_config(struct spi_ - struct spi_qup *controller = spi_master_get_devdata(spi->master); - u32 config, iomode, mode; - int ret, n_words, w_size; -+ size_t dma_align = dma_get_cache_alignment(); -+ u32 dma_available = 0; - - if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { - dev_err(controller->dev, "too big size for loopback %d > %d\n", -@@ -367,6 +601,11 @@ static int spi_qup_io_config(struct spi_ - n_words = xfer->len / w_size; - controller->w_size = w_size; - -+ if (controller->rx_chan && -+ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) && -+ IS_ALIGNED((size_t)xfer->rx_buf, dma_align)) -+ dma_available = 1; -+ - if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { - mode = QUP_IO_M_MODE_FIFO; - writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); -@@ -374,19 +613,31 @@ static int spi_qup_io_config(struct spi_ - /* must be zero for FIFO */ - writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); - writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); -- } else { -+ controller->use_dma = 0; -+ } else if (!dma_available) { - mode = QUP_IO_M_MODE_BLOCK; - writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); - writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); - /* must be zero for BLOCK and BAM */ - writel_relaxed(0, controller->base + QUP_MX_READ_CNT); - writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); -+ controller->use_dma = 0; -+ } else { -+ mode = QUP_IO_M_MODE_DMOV; -+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT); -+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); -+ controller->use_dma = 1; - } - - iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); - /* Set input and output transfer mode */ - iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); -- iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); -+ -+ if (!controller->use_dma) -+ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); -+ else -+ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; -+ - iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); - iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); - -@@ -419,6 +670,14 @@ static int spi_qup_io_config(struct spi_ - config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); - config |= xfer->bits_per_word - 1; - config |= QUP_CONFIG_SPI_MODE; -+ -+ if (controller->use_dma) { -+ if (!xfer->tx_buf) -+ config |= QUP_CONFIG_NO_OUTPUT; -+ if (!xfer->rx_buf) -+ config |= QUP_CONFIG_NO_INPUT; -+ } -+ - writel_relaxed(config, controller->base + QUP_CONFIG); - - /* only write to OPERATIONAL_MASK when register is present */ -@@ -452,25 +711,29 @@ static int spi_qup_transfer_one(struct s - controller->tx_bytes = 0; - spin_unlock_irqrestore(&controller->lock, flags); - -- if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -- dev_warn(controller->dev, "cannot set RUN state\n"); -- goto exit; -- } -+ if (controller->use_dma) { -+ ret = spi_qup_do_dma(controller, xfer); -+ } else { -+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -+ dev_warn(controller->dev, "cannot set RUN state\n"); -+ goto exit; -+ } - -- if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { -- dev_warn(controller->dev, "cannot set PAUSE state\n"); -- goto exit; -- } -+ if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { -+ dev_warn(controller->dev, "cannot set PAUSE state\n"); -+ goto exit; -+ } - -- spi_qup_fifo_write(controller, xfer); -+ spi_qup_fifo_write(controller, xfer); - -- if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -- dev_warn(controller->dev, "cannot set EXECUTE state\n"); -- goto exit; -- } -+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -+ dev_warn(controller->dev, "cannot set EXECUTE state\n"); -+ goto exit; -+ } - -- if (!wait_for_completion_timeout(&controller->done, timeout)) -- ret = -ETIMEDOUT; -+ if (!wait_for_completion_timeout(&controller->done, timeout)) -+ ret = -ETIMEDOUT; -+ } - exit: - spi_qup_set_state(controller, QUP_STATE_RESET); - spin_lock_irqsave(&controller->lock, flags); -@@ -553,6 +816,7 @@ static int spi_qup_probe(struct platform - master->transfer_one = spi_qup_transfer_one; - master->dev.of_node = pdev->dev.of_node; - master->auto_runtime_pm = true; -+ master->dma_alignment = dma_get_cache_alignment(); - - platform_set_drvdata(pdev, master); - -@@ -618,6 +882,56 @@ static int spi_qup_probe(struct platform - QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN, - base + QUP_ERROR_FLAGS_EN); - -+ /* allocate dma resources, if available */ -+ controller->rx_chan = dma_request_slave_channel(&pdev->dev, "rx"); -+ if (controller->rx_chan) { -+ controller->tx_chan = -+ dma_request_slave_channel(&pdev->dev, "tx"); -+ -+ if (!controller->tx_chan) { -+ dev_err(&pdev->dev, "Failed to allocate dma tx chan"); -+ dma_release_channel(controller->rx_chan); -+ } -+ -+ /* set DMA parameters */ -+ controller->rx_conf.device_fc = 1; -+ controller->rx_conf.src_addr = res->start + QUP_INPUT_FIFO; -+ controller->rx_conf.src_maxburst = controller->in_blk_sz; -+ -+ controller->tx_conf.device_fc = 1; -+ controller->tx_conf.dst_addr = res->start + QUP_OUTPUT_FIFO; -+ controller->tx_conf.dst_maxburst = controller->out_blk_sz; -+ -+ if (dmaengine_slave_config(controller->rx_chan, -+ &controller->rx_conf)) { -+ dev_err(&pdev->dev, "failed to configure RX channel\n"); -+ -+ dma_release_channel(controller->rx_chan); -+ dma_release_channel(controller->tx_chan); -+ controller->tx_chan = NULL; -+ controller->rx_chan = NULL; -+ } else if (dmaengine_slave_config(controller->tx_chan, -+ &controller->tx_conf)) { -+ dev_err(&pdev->dev, "failed to configure TX channel\n"); -+ -+ dma_release_channel(controller->rx_chan); -+ dma_release_channel(controller->tx_chan); -+ controller->tx_chan = NULL; -+ controller->rx_chan = NULL; -+ } -+ -+ controller->dummy = devm_kmalloc(controller->dev, PAGE_SIZE, -+ GFP_KERNEL); -+ -+ if (!controller->dummy) { -+ dma_release_channel(controller->rx_chan); -+ dma_release_channel(controller->tx_chan); -+ controller->tx_chan = NULL; -+ controller->rx_chan = NULL; -+ } -+ } -+ -+ - writel_relaxed(0, base + SPI_CONFIG); - writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL); - -@@ -730,6 +1044,11 @@ static int spi_qup_remove(struct platfor - if (ret) - return ret; - -+ if (controller->rx_chan) -+ dma_release_channel(controller->rx_chan); -+ if (controller->tx_chan) -+ dma_release_channel(controller->tx_chan); -+ - clk_disable_unprepare(controller->cclk); - clk_disable_unprepare(controller->iclk); - diff --git a/target/linux/ipq806x/patches/002-v3-spi-qup-Fix-incorrect-block-transfers.patch b/target/linux/ipq806x/patches/002-v3-spi-qup-Fix-incorrect-block-transfers.patch deleted file mode 100644 index 62ee5b4fad..0000000000 --- a/target/linux/ipq806x/patches/002-v3-spi-qup-Fix-incorrect-block-transfers.patch +++ /dev/null @@ -1,376 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3] spi: qup: Fix incorrect block transfers -From: Andy Gross -X-Patchwork-Id: 5007321 -Message-Id: <1412112088-25928-1-git-send-email-agross@codeaurora.org> -To: Mark Brown -Cc: linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org, - "Ivan T. Ivanov" , - Bjorn Andersson , - Kumar Gala , Andy Gross -Date: Tue, 30 Sep 2014 16:21:28 -0500 - -This patch fixes a number of errors with the QUP block transfer mode. Errors -manifested themselves as input underruns, output overruns, and timed out -transactions. - -The block mode does not require the priming that occurs in FIFO mode. At the -moment that the QUP is placed into the RUN state, the QUP will immediately raise -an interrupt if the request is a write. Therefore, there is no need to prime -the pump. - -In addition, the block transfers require that whole blocks of data are -read/written at a time. The last block of data that completes a transaction may -contain less than a full blocks worth of data. - -Each block of data results in an input/output service interrupt accompanied with -a input/output block flag set. Additional block reads/writes require clearing -of the service flag. It is ok to check for additional blocks of data in the -ISR, but you have to ack every block you transfer. Imbalanced acks result in -early return from complete transactions with pending interrupts that still have -to be ack'd. The next transaction can be affected by these interrupts. -Transactions are deemed complete when the MAX_INPUT or MAX_OUTPUT flag are set. - -Changes from v2: -- Added in additional completion check so that transaction done is not - prematurely signaled. -- Fixed various review comments. - -Changes from v1: -- Split out read/write block function. -- Removed extraneous checks for transfer length - -Signed-off-by: Andy Gross - ---- -drivers/spi/spi-qup.c | 201 ++++++++++++++++++++++++++++++++++++------------- - 1 file changed, 148 insertions(+), 53 deletions(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -82,6 +82,8 @@ - #define QUP_IO_M_MODE_BAM 3 - - /* QUP_OPERATIONAL fields */ -+#define QUP_OP_IN_BLOCK_READ_REQ BIT(13) -+#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12) - #define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) - #define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) - #define QUP_OP_IN_SERVICE_FLAG BIT(9) -@@ -147,6 +149,7 @@ struct spi_qup { - int tx_bytes; - int rx_bytes; - int qup_v1; -+ int mode; - - int use_dma; - -@@ -213,30 +216,14 @@ static int spi_qup_set_state(struct spi_ - return 0; - } - -- --static void spi_qup_fifo_read(struct spi_qup *controller, -- struct spi_transfer *xfer) -+static void spi_qup_fill_read_buffer(struct spi_qup *controller, -+ struct spi_transfer *xfer, u32 data) - { - u8 *rx_buf = xfer->rx_buf; -- u32 word, state; -- int idx, shift, w_size; -- -- w_size = controller->w_size; -- -- while (controller->rx_bytes < xfer->len) { -- -- state = readl_relaxed(controller->base + QUP_OPERATIONAL); -- if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY)) -- break; -- -- word = readl_relaxed(controller->base + QUP_INPUT_FIFO); -- -- if (!rx_buf) { -- controller->rx_bytes += w_size; -- continue; -- } -+ int idx, shift; - -- for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) { -+ if (rx_buf) -+ for (idx = 0; idx < controller->w_size; idx++) { - /* - * The data format depends on bytes per SPI word: - * 4 bytes: 0x12345678 -@@ -244,41 +231,139 @@ static void spi_qup_fifo_read(struct spi - * 1 byte : 0x00000012 - */ - shift = BITS_PER_BYTE; -- shift *= (w_size - idx - 1); -- rx_buf[controller->rx_bytes] = word >> shift; -+ shift *= (controller->w_size - idx - 1); -+ rx_buf[controller->rx_bytes + idx] = data >> shift; -+ } -+ -+ controller->rx_bytes += controller->w_size; -+} -+ -+static void spi_qup_prepare_write_data(struct spi_qup *controller, -+ struct spi_transfer *xfer, u32 *data) -+{ -+ const u8 *tx_buf = xfer->tx_buf; -+ u32 val; -+ int idx; -+ -+ *data = 0; -+ -+ if (tx_buf) -+ for (idx = 0; idx < controller->w_size; idx++) { -+ val = tx_buf[controller->tx_bytes + idx]; -+ *data |= val << (BITS_PER_BYTE * (3 - idx)); - } -+ -+ controller->tx_bytes += controller->w_size; -+} -+ -+static void spi_qup_fifo_read(struct spi_qup *controller, -+ struct spi_transfer *xfer) -+{ -+ u32 data; -+ -+ /* clear service request */ -+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+ while (controller->rx_bytes < xfer->len) { -+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) & -+ QUP_OP_IN_FIFO_NOT_EMPTY)) -+ break; -+ -+ data = readl_relaxed(controller->base + QUP_INPUT_FIFO); -+ -+ spi_qup_fill_read_buffer(controller, xfer, data); - } - } - - static void spi_qup_fifo_write(struct spi_qup *controller, -- struct spi_transfer *xfer) -+ struct spi_transfer *xfer) - { -- const u8 *tx_buf = xfer->tx_buf; -- u32 word, state, data; -- int idx, w_size; -+ u32 data; - -- w_size = controller->w_size; -+ /* clear service request */ -+ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); - - while (controller->tx_bytes < xfer->len) { - -- state = readl_relaxed(controller->base + QUP_OPERATIONAL); -- if (state & QUP_OP_OUT_FIFO_FULL) -+ if (readl_relaxed(controller->base + QUP_OPERATIONAL) & -+ QUP_OP_OUT_FIFO_FULL) - break; - -- word = 0; -- for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) { -+ spi_qup_prepare_write_data(controller, xfer, &data); -+ writel_relaxed(data, controller->base + QUP_OUTPUT_FIFO); - -- if (!tx_buf) { -- controller->tx_bytes += w_size; -- break; -- } -+ } -+} - -- data = tx_buf[controller->tx_bytes]; -- word |= data << (BITS_PER_BYTE * (3 - idx)); -- } -+static void spi_qup_block_read(struct spi_qup *controller, -+ struct spi_transfer *xfer) -+{ -+ u32 data; -+ u32 reads_per_blk = controller->in_blk_sz >> 2; -+ u32 num_words = (xfer->len - controller->rx_bytes) / controller->w_size; -+ int i; -+ -+ do { -+ /* ACK by clearing service flag */ -+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+ /* transfer up to a block size of data in a single pass */ -+ for (i = 0; num_words && i < reads_per_blk; i++, num_words--) { -+ -+ /* read data and fill up rx buffer */ -+ data = readl_relaxed(controller->base + QUP_INPUT_FIFO); -+ spi_qup_fill_read_buffer(controller, xfer, data); -+ } -+ -+ /* check to see if next block is ready */ -+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) & -+ QUP_OP_IN_BLOCK_READ_REQ)) -+ break; - -- writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); -- } -+ } while (num_words); -+ -+ /* -+ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block -+ * reads, it has to be cleared again at the very end -+ */ -+ if (readl_relaxed(controller->base + QUP_OPERATIONAL) & -+ QUP_OP_MAX_INPUT_DONE_FLAG) -+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+} -+ -+static void spi_qup_block_write(struct spi_qup *controller, -+ struct spi_transfer *xfer) -+{ -+ u32 data; -+ u32 writes_per_blk = controller->out_blk_sz >> 2; -+ u32 num_words = (xfer->len - controller->tx_bytes) / controller->w_size; -+ int i; -+ -+ do { -+ /* ACK by clearing service flag */ -+ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+ /* transfer up to a block size of data in a single pass */ -+ for (i = 0; num_words && i < writes_per_blk; i++, num_words--) { -+ -+ /* swizzle the bytes for output and write out */ -+ spi_qup_prepare_write_data(controller, xfer, &data); -+ writel_relaxed(data, -+ controller->base + QUP_OUTPUT_FIFO); -+ } -+ -+ /* check to see if next block is ready */ -+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) & -+ QUP_OP_OUT_BLOCK_WRITE_REQ)) -+ break; -+ -+ } while (num_words); - } - - static void qup_dma_callback(void *data) -@@ -515,9 +600,9 @@ static irqreturn_t spi_qup_qup_irq(int i - - writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); - writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); -- writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); - - if (!xfer) { -+ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); - dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n", - qup_err, spi_err, opflags); - return IRQ_HANDLED; -@@ -546,11 +631,19 @@ static irqreturn_t spi_qup_qup_irq(int i - } - - if (!controller->use_dma) { -- if (opflags & QUP_OP_IN_SERVICE_FLAG) -- spi_qup_fifo_read(controller, xfer); -+ if (opflags & QUP_OP_IN_SERVICE_FLAG) { -+ if (opflags & QUP_OP_IN_BLOCK_READ_REQ) -+ spi_qup_block_read(controller, xfer); -+ else -+ spi_qup_fifo_read(controller, xfer); -+ } - -- if (opflags & QUP_OP_OUT_SERVICE_FLAG) -- spi_qup_fifo_write(controller, xfer); -+ if (opflags & QUP_OP_OUT_SERVICE_FLAG) { -+ if (opflags & QUP_OP_OUT_BLOCK_WRITE_REQ) -+ spi_qup_block_write(controller, xfer); -+ else -+ spi_qup_fifo_write(controller, xfer); -+ } - } - - spin_lock_irqsave(&controller->lock, flags); -@@ -558,7 +651,8 @@ static irqreturn_t spi_qup_qup_irq(int i - controller->xfer = xfer; - spin_unlock_irqrestore(&controller->lock, flags); - -- if (controller->rx_bytes == xfer->len || error) -+ if ((controller->rx_bytes == xfer->len && -+ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) - complete(&controller->done); - - return IRQ_HANDLED; -@@ -569,7 +663,7 @@ static irqreturn_t spi_qup_qup_irq(int i - static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) - { - struct spi_qup *controller = spi_master_get_devdata(spi->master); -- u32 config, iomode, mode; -+ u32 config, iomode; - int ret, n_words, w_size; - size_t dma_align = dma_get_cache_alignment(); - u32 dma_available = 0; -@@ -607,7 +701,7 @@ static int spi_qup_io_config(struct spi_ - dma_available = 1; - - if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { -- mode = QUP_IO_M_MODE_FIFO; -+ controller->mode = QUP_IO_M_MODE_FIFO; - writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); - writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); - /* must be zero for FIFO */ -@@ -615,7 +709,7 @@ static int spi_qup_io_config(struct spi_ - writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); - controller->use_dma = 0; - } else if (!dma_available) { -- mode = QUP_IO_M_MODE_BLOCK; -+ controller->mode = QUP_IO_M_MODE_BLOCK; - writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); - writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); - /* must be zero for BLOCK and BAM */ -@@ -623,7 +717,7 @@ static int spi_qup_io_config(struct spi_ - writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); - controller->use_dma = 0; - } else { -- mode = QUP_IO_M_MODE_DMOV; -+ controller->mode = QUP_IO_M_MODE_DMOV; - writel_relaxed(0, controller->base + QUP_MX_READ_CNT); - writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); - controller->use_dma = 1; -@@ -638,8 +732,8 @@ static int spi_qup_io_config(struct spi_ - else - iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; - -- iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); -- iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); -+ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); -+ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); - - writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); - -@@ -724,7 +818,8 @@ static int spi_qup_transfer_one(struct s - goto exit; - } - -- spi_qup_fifo_write(controller, xfer); -+ if (controller->mode == QUP_IO_M_MODE_FIFO) -+ spi_qup_fifo_write(controller, xfer); - - if (spi_qup_set_state(controller, QUP_STATE_RUN)) { - dev_warn(controller->dev, "cannot set EXECUTE state\n"); -@@ -741,6 +836,7 @@ exit: - if (!ret) - ret = controller->error; - spin_unlock_irqrestore(&controller->lock, flags); -+ - return ret; - } - diff --git a/target/linux/ipq806x/patches/003-spi-qup-Ensure-done-detection.patch b/target/linux/ipq806x/patches/003-spi-qup-Ensure-done-detection.patch deleted file mode 100644 index 7052227810..0000000000 --- a/target/linux/ipq806x/patches/003-spi-qup-Ensure-done-detection.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 4faba89e3ffbb1c5f6232651375b9b3212b50f02 Mon Sep 17 00:00:00 2001 -From: Andy Gross -Date: Thu, 15 Jan 2015 17:56:02 -0800 -Subject: [PATCH] spi: qup: Ensure done detection - -This patch fixes an issue where a SPI transaction has completed, but the done -condition is missed. This occurs because at the time of interrupt the -MAX_INPUT_DONE_FLAG is not asserted. However, in the process of reading blocks -of data from the FIFO, the last portion of data comes in. - -The opflags read at the beginning of the irq handler no longer matches the -current opflag state. To get around this condition, the block read function -should update the opflags so that done detection is correct after the return. - -Change-Id: If109e0eeb432f96000d765c4b34dbb2269f8093f -Signed-off-by: Andy Gross ---- - drivers/spi/spi-qup.c | 12 +++++++----- - 1 file changed, 7 insertions(+), 5 deletions(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -298,7 +298,7 @@ static void spi_qup_fifo_write(struct sp - } - - static void spi_qup_block_read(struct spi_qup *controller, -- struct spi_transfer *xfer) -+ struct spi_transfer *xfer, u32 *opflags) - { - u32 data; - u32 reads_per_blk = controller->in_blk_sz >> 2; -@@ -327,10 +327,12 @@ static void spi_qup_block_read(struct sp - - /* - * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block -- * reads, it has to be cleared again at the very end -+ * reads, it has to be cleared again at the very end. However, be sure -+ * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be -+ * present and this is used to determine if transaction is complete - */ -- if (readl_relaxed(controller->base + QUP_OPERATIONAL) & -- QUP_OP_MAX_INPUT_DONE_FLAG) -+ *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); -+ if (*opflags & QUP_OP_MAX_INPUT_DONE_FLAG) - writel_relaxed(QUP_OP_IN_SERVICE_FLAG, - controller->base + QUP_OPERATIONAL); - -@@ -633,7 +635,7 @@ static irqreturn_t spi_qup_qup_irq(int i - if (!controller->use_dma) { - if (opflags & QUP_OP_IN_SERVICE_FLAG) { - if (opflags & QUP_OP_IN_BLOCK_READ_REQ) -- spi_qup_block_read(controller, xfer); -+ spi_qup_block_read(controller, xfer, &opflags); - else - spi_qup_fifo_read(controller, xfer); - } diff --git a/target/linux/ipq806x/patches/011-watchdog-qcom-use-timer-devicetree-binding.patch b/target/linux/ipq806x/patches/011-watchdog-qcom-use-timer-devicetree-binding.patch deleted file mode 100644 index 0cd7da1f6d..0000000000 --- a/target/linux/ipq806x/patches/011-watchdog-qcom-use-timer-devicetree-binding.patch +++ /dev/null @@ -1,72 +0,0 @@ -From fded70251b1b58f68de1d3757ece9965f0b75452 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 19 Feb 2015 20:19:30 -0800 -Subject: [PATCH 1/3] watchdog: qcom: use timer devicetree binding - -MSM watchdog configuration happens in the same register block as the -timer, so we'll use the same binding as the existing timer. - -The qcom-wdt will now be probed when devicetree has an entry compatible -with "qcom,kpss-timer" or "qcom-scss-timer". - -Signed-off-by: Mathieu Olivari ---- - drivers/watchdog/qcom-wdt.c | 21 +++++++++++++++------ - 1 file changed, 15 insertions(+), 6 deletions(-) - -diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c -index aa85618..aa03ca8 100644 ---- a/drivers/watchdog/qcom-wdt.c -+++ b/drivers/watchdog/qcom-wdt.c -@@ -20,9 +20,9 @@ - #include - #include - --#define WDT_RST 0x0 --#define WDT_EN 0x8 --#define WDT_BITE_TIME 0x24 -+#define WDT_RST 0x38 -+#define WDT_EN 0x40 -+#define WDT_BITE_TIME 0x5C - - struct qcom_wdt { - struct watchdog_device wdd; -@@ -117,6 +117,8 @@ static int qcom_wdt_probe(struct platform_device *pdev) - { - struct qcom_wdt *wdt; - struct resource *res; -+ struct device_node *np = pdev->dev.of_node; -+ u32 percpu_offset; - int ret; - - wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); -@@ -124,6 +126,14 @@ static int qcom_wdt_probe(struct platform_device *pdev) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ -+ /* We use CPU0's DGT for the watchdog */ -+ if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) -+ percpu_offset = 0; -+ -+ res->start += percpu_offset; -+ res->end += percpu_offset; -+ - wdt->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(wdt->base)) - return PTR_ERR(wdt->base); -@@ -203,9 +213,8 @@ static int qcom_wdt_remove(struct platform_device *pdev) - } - - static const struct of_device_id qcom_wdt_of_table[] = { -- { .compatible = "qcom,kpss-wdt-msm8960", }, -- { .compatible = "qcom,kpss-wdt-apq8064", }, -- { .compatible = "qcom,kpss-wdt-ipq8064", }, -+ { .compatible = "qcom,kpss-timer" }, -+ { .compatible = "qcom,scss-timer" }, - { }, - }; - MODULE_DEVICE_TABLE(of, qcom_wdt_of_table); --- -1.9.1 - diff --git a/target/linux/ipq806x/patches/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch b/target/linux/ipq806x/patches/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch deleted file mode 100644 index 24a093a812..0000000000 --- a/target/linux/ipq806x/patches/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 297cf8136ecd6a56520888fd28948393766b8ee7 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 19 Feb 2015 20:27:39 -0800 -Subject: [PATCH 2/3] ARM: qcom: add description of KPSS WDT for IPQ8064 - -Add the watchdog related entries to the Krait Processor Sub-system -(KPSS) timer IPQ8064 devicetree section. Also, add a fixed-clock -description of SLEEP_CLK, which will do for now. - -Signed-off-by: Josh Cartwright -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 14 +++++++++++++- - 1 file changed, 13 insertions(+), 1 deletion(-) - -diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi -index cb225da..d01f618 100644 ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -60,6 +60,14 @@ - }; - }; - -+ clocks { -+ sleep_clk: sleep_clk { -+ compatible = "fixed-clock"; -+ clock-frequency = <32768>; -+ #clock-cells = <0>; -+ }; -+ }; -+ - soc: soc { - #address-cells = <1>; - #size-cells = <1>; -@@ -89,10 +97,14 @@ - compatible = "qcom,kpss-timer", "qcom,msm-timer"; - interrupts = <1 1 0x301>, - <1 2 0x301>, -- <1 3 0x301>; -+ <1 3 0x301>, -+ <1 4 0x301>, -+ <1 5 0x301>; - reg = <0x0200a000 0x100>; - clock-frequency = <25000000>, - <32768>; -+ clocks = <&sleep_clk>; -+ clock-names = "sleep"; - cpu-offset = <0x80000>; - }; - --- -1.9.1 - diff --git a/target/linux/ipq806x/patches/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch b/target/linux/ipq806x/patches/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch deleted file mode 100644 index 6876768962..0000000000 --- a/target/linux/ipq806x/patches/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch +++ /dev/null @@ -1,50 +0,0 @@ -From e535f01dffb6dd9e09934fa219be52af3437a8f6 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 19 Feb 2015 20:36:27 -0800 -Subject: [PATCH 3/3] ARM: msm: add watchdog entries to DT timer binding doc - -The watchdog has been reworked to use the same DT node as the timer. -This change is updating the device tree doc accordingly. - -Signed-off-by: Mathieu Olivari ---- - Documentation/devicetree/bindings/arm/msm/timer.txt | 16 +++++++++++++--- - 1 file changed, 13 insertions(+), 3 deletions(-) - ---- a/Documentation/devicetree/bindings/arm/msm/timer.txt -+++ b/Documentation/devicetree/bindings/arm/msm/timer.txt -@@ -9,11 +9,17 @@ Properties: - "qcom,scss-timer" - scorpion subsystem - - - interrupts : Interrupts for the the debug timer, the first general purpose -- timer, and optionally a second general purpose timer in that -- order. -+ timer, and optionally a second general purpose timer, and -+ optionally as well, 2 watchdog interrupts, in that order. - - - reg : Specifies the base address of the timer registers. - -+- clocks: Reference to the parent clocks, one per output clock. The parents -+ must appear in the same order as the clock names. -+ -+- clock-names: The name of the clocks as free-form strings. They should be in -+ the same order as the clocks. -+ - - clock-frequency : The frequency of the debug timer and the general purpose - timer(s) in Hz in that order. - -@@ -29,9 +35,13 @@ Example: - compatible = "qcom,scss-timer", "qcom,msm-timer"; - interrupts = <1 1 0x301>, - <1 2 0x301>, -- <1 3 0x301>; -+ <1 3 0x301>, -+ <1 4 0x301>, -+ <1 5 0x301>; - reg = <0x0200a000 0x100>; - clock-frequency = <19200000>, - <32768>; -+ clocks = <&sleep_clk>; -+ clock-names = "sleep"; - cpu-offset = <0x40000>; - }; diff --git a/target/linux/ipq806x/patches/700-add-gmac-dts-suport.patch b/target/linux/ipq806x/patches/700-add-gmac-dts-suport.patch deleted file mode 100644 index 89ebe66946..0000000000 --- a/target/linux/ipq806x/patches/700-add-gmac-dts-suport.patch +++ /dev/null @@ -1,172 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -18,8 +18,15 @@ - bootargs = "console=ttyMSM0,115200 root=/dev/mtdblock12 rootfstype=squashfs,jffs2"; - }; - -+ aliases { -+ mdio-gpio0 = &mdio0; -+ }; -+ - soc { - pinmux@800000 { -+ pinctrl-0 = <&mdio0_pins &rgmii2_pins>; -+ pinctrl-names = "default"; -+ - i2c4_pins: i2c4_pinmux { - pins = "gpio12", "gpio13"; - function = "gsbi4"; -@@ -34,6 +41,25 @@ - bias-none; - }; - }; -+ -+ mdio0_pins: mdio0_pins { -+ mux { -+ pins = "gpio0", "gpio1"; -+ function = "gpio"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; -+ -+ rgmii2_pins: rgmii2_pins { -+ mux { -+ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", -+ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; -+ function = "rgmii2"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi@16300000 { -@@ -72,6 +98,7 @@ - #size-cells = <1>; - spi-max-frequency = <50000000>; - reg = <0>; -+ m25p,fast-read; - - partition@0 { - label = "0:SBL1"; -@@ -148,5 +175,66 @@ - sata@29000000 { - status = "ok"; - }; -+ -+ mdio0: mdio { -+ compatible = "virtual,mdio-gpio"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; -+ -+ phy0: ethernet-phy@0 { -+ device_type = "ethernet-phy"; -+ reg = <0>; -+ qca,ar8327-initvals = < -+ 0x00004 0x7600000 /* PAD0_MODE */ -+ 0x00008 0x1000000 /* PAD5_MODE */ -+ 0x0000c 0x80 /* PAD6_MODE */ -+ 0x000e4 0xaa545 /* MAC_POWER_SEL */ -+ 0x000e0 0xc74164de /* SGMII_CTRL */ -+ 0x0007c 0x4e /* PORT0_STATUS */ -+ 0x00094 0x4e /* PORT6_STATUS */ -+ >; -+ }; -+ -+ phy4: ethernet-phy@4 { -+ device_type = "ethernet-phy"; -+ reg = <4>; -+ }; -+ }; -+ -+ nss-gmac-common { -+ reg = <0x03000000 0x0000FFFF 0x1bb00000 0x0000FFFF 0x00900000 0x00004000>; -+ reg-names = "nss_reg_base" , "qsgmii_reg_base", "clk_ctl_base"; -+ }; -+ -+ gmac1: ethernet@37200000 { -+ status = "ok"; -+ phy-mode = "rgmii"; -+ qcom,id = <1>; -+ qcom,phy_mdio_addr = <4>; -+ qcom,poll_required = <1>; -+ qcom,rgmii_delay = <0>; -+ qcom,emulation = <0>; -+ qcom,forced_speed = <1000>; -+ qcom,forced_duplex = <1>; -+ qcom,socver = <0>; -+ local-mac-address = [000000000000]; -+ mdiobus = <&mdio0>; -+ }; -+ -+ gmac2: ethernet@37400000 { -+ status = "ok"; -+ phy-mode = "sgmii"; -+ qcom,id = <2>; -+ qcom,phy_mdio_addr = <0>; -+ qcom,poll_required = <0>; -+ qcom,rgmii_delay = <0>; -+ qcom,emulation = <0>; -+ qcom,forced_speed = <1000>; -+ qcom,forced_duplex = <1>; -+ qcom,socver = <0>; -+ local-mac-address = [000000000000]; -+ mdiobus = <&mdio0>; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -3,6 +3,7 @@ - #include "skeleton.dtsi" - #include - #include -+#include - - / { - model = "Qualcomm IPQ8064"; -@@ -279,5 +280,42 @@ - #clock-cells = <1>; - #reset-cells = <1>; - }; -+ -+ nss-gmac-common { -+ reg = <0x03000000 0x0000FFFF 0x1bb00000 0x0000FFFF 0x00900000 0x00004000>; -+ reg-names = "nss_reg_base" , "qsgmii_reg_base", "clk_ctl_base"; -+ }; -+ -+ gmac0: ethernet@37000000 { -+ device_type = "network"; -+ compatible = "qcom,nss-gmac"; -+ reg = <0x37000000 0x200000>; -+ interrupts = ; -+ status = "disabled"; -+ }; -+ -+ gmac1: ethernet@37200000 { -+ device_type = "network"; -+ compatible = "qcom,nss-gmac"; -+ reg = <0x37200000 0x200000>; -+ interrupts = ; -+ status = "disabled"; -+ }; -+ -+ gmac2: ethernet@37400000 { -+ device_type = "network"; -+ compatible = "qcom,nss-gmac"; -+ reg = <0x37400000 0x200000>; -+ interrupts = ; -+ status = "disabled"; -+ }; -+ -+ gmac3: ethernet@37600000 { -+ device_type = "network"; -+ compatible = "qcom,nss-gmac"; -+ reg = <0x37600000 0x200000>; -+ interrupts = ; -+ status = "disabled"; -+ }; - }; - };