Default kernel doesn't change and stays on 3.18 for now. Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> SVN-Revision: 46554master
parent
991e641a76
commit
4d1656e813
@ -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 <agross@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 4432401
|
|
||||||
Message-Id: <1403816781-31008-1-git-send-email-agross@codeaurora.org>
|
|
||||||
To: Mark Brown <broonie@kernel.org>
|
|
||||||
Cc: linux-spi@vger.kernel.org, Sagar Dharia <sdharia@codeaurora.org>,
|
|
||||||
Daniel Sneddon <dsneddon@codeaurora.org>,
|
|
||||||
Bjorn Andersson <bjorn.andersson@sonymobile.com>,
|
|
||||||
"Ivan T. Ivanov" <iivanov@mm-sol.com>,
|
|
||||||
linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
linux-arm-msm@vger.kernel.org, Andy Gross <agross@codeaurora.org>
|
|
||||||
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 <agross@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
.../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 = <index> 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 <linux/platform_device.h>
|
|
||||||
#include <linux/pm_runtime.h>
|
|
||||||
#include <linux/spi/spi.h>
|
|
||||||
+#include <linux/dmaengine.h>
|
|
||||||
+#include <linux/dma-mapping.h>
|
|
||||||
|
|
||||||
#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, control;
|
|
||||||
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);
|
|
||||||
|
|
||||||
@@ -428,6 +679,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 */
|
|
||||||
@@ -461,25 +720,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);
|
|
||||||
@@ -563,6 +826,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);
|
|
||||||
|
|
||||||
@@ -628,6 +892,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);
|
|
||||||
|
|
||||||
@@ -740,6 +1054,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);
|
|
||||||
|
|
@ -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 <agross@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 5007321
|
|
||||||
Message-Id: <1412112088-25928-1-git-send-email-agross@codeaurora.org>
|
|
||||||
To: Mark Brown <broonie@kernel.org>
|
|
||||||
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" <iivanov@mm-sol.com>,
|
|
||||||
Bjorn Andersson <bjorn.andersson@sonymobile.com>,
|
|
||||||
Kumar Gala <galak@codeaurora.org>, Andy Gross <agross@codeaurora.org>
|
|
||||||
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 <agross@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
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, control;
|
|
||||||
+ u32 config, iomode, control;
|
|
||||||
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);
|
|
||||||
|
|
||||||
@@ -733,7 +827,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");
|
|
||||||
@@ -750,6 +845,7 @@ exit:
|
|
||||||
if (!ret)
|
|
||||||
ret = controller->error;
|
|
||||||
spin_unlock_irqrestore(&controller->lock, flags);
|
|
||||||
+
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@ |
|||||||
From 4faba89e3ffbb1c5f6232651375b9b3212b50f02 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Andy Gross <agross@codeaurora.org>
|
|
||||||
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 <agross@codeaurora.org>
|
|
||||||
---
|
|
||||||
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);
|
|
||||||
}
|
|
@ -1,67 +0,0 @@ |
|||||||
From fded70251b1b58f68de1d3757ece9965f0b75452 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
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 <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
drivers/watchdog/qcom-wdt.c | 21 +++++++++++++++------
|
|
||||||
1 file changed, 15 insertions(+), 6 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/watchdog/qcom-wdt.c
|
|
||||||
+++ b/drivers/watchdog/qcom-wdt.c
|
|
||||||
@@ -20,9 +20,9 @@
|
|
||||||
#include <linux/reboot.h>
|
|
||||||
#include <linux/watchdog.h>
|
|
||||||
|
|
||||||
-#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 platfor
|
|
||||||
{
|
|
||||||
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 platfor
|
|
||||||
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 platfo
|
|
||||||
}
|
|
||||||
|
|
||||||
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,48 +0,0 @@ |
|||||||
From 297cf8136ecd6a56520888fd28948393766b8ee7 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
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 <joshc@codeaurora.org>
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
arch/arm/boot/dts/qcom-ipq8064.dtsi | 14 +++++++++++++-
|
|
||||||
1 file changed, 13 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
--- 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,50 +0,0 @@ |
|||||||
From e535f01dffb6dd9e09934fa219be52af3437a8f6 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
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 <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
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 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>;
|
|
||||||
};
|
|
@ -1,71 +0,0 @@ |
|||||||
From 4d54b0adfa67476e6509bc8646b9dbac642e8a29 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
Date: Thu, 26 Mar 2015 11:29:26 -0700
|
|
||||||
Subject: [PATCH] mfd: qcom_rpm: Add support for IPQ8064
|
|
||||||
|
|
||||||
The IPQ8064 also includes an RPM following the same message structure as
|
|
||||||
other chips. In addition, it supports a few new resource types to
|
|
||||||
support the NSS fabric clocks and the SMB208/SMB209 regulators found on
|
|
||||||
the reference boards.
|
|
||||||
|
|
||||||
Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Signed-off-by: Lee Jones <lee.jones@linaro.org>
|
|
||||||
---
|
|
||||||
drivers/mfd/qcom_rpm.c | 41 +++++++++++++++++++++++++++++++++++++++++
|
|
||||||
1 file changed, 41 insertions(+)
|
|
||||||
|
|
||||||
--- a/drivers/mfd/qcom_rpm.c
|
|
||||||
+++ b/drivers/mfd/qcom_rpm.c
|
|
||||||
@@ -323,10 +323,51 @@ static const struct qcom_rpm_data msm896
|
|
||||||
.n_resources = ARRAY_SIZE(msm8960_rpm_resource_table),
|
|
||||||
};
|
|
||||||
|
|
||||||
+static const struct qcom_rpm_resource ipq806x_rpm_resource_table[] = {
|
|
||||||
+ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 },
|
|
||||||
+ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 },
|
|
||||||
+ [QCOM_RPM_NSS_FABRIC_0_CLK] = { 29, 13, 10, 1 },
|
|
||||||
+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 },
|
|
||||||
+ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 },
|
|
||||||
+ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 },
|
|
||||||
+ [QCOM_RPM_NSS_FABRIC_1_CLK] = { 33, 17, 14, 1 },
|
|
||||||
+ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 2 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 3 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 2 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 3 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 2 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 3 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 2 },
|
|
||||||
+ [QCOM_RPM_CXO_BUFFERS] = { 209, 33, 31, 1 },
|
|
||||||
+ [QCOM_RPM_USB_OTG_SWITCH] = { 210, 34, 32, 1 },
|
|
||||||
+ [QCOM_RPM_HDMI_SWITCH] = { 211, 35, 33, 1 },
|
|
||||||
+ [QCOM_RPM_DDR_DMM] = { 212, 36, 34, 2 },
|
|
||||||
+ [QCOM_RPM_VDDMIN_GPIO] = { 215, 40, 39, 1 },
|
|
||||||
+ [QCOM_RPM_SMB208_S1a] = { 216, 41, 90, 2 },
|
|
||||||
+ [QCOM_RPM_SMB208_S1b] = { 218, 43, 91, 2 },
|
|
||||||
+ [QCOM_RPM_SMB208_S2a] = { 220, 45, 92, 2 },
|
|
||||||
+ [QCOM_RPM_SMB208_S2b] = { 222, 47, 93, 2 },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct qcom_rpm_data ipq806x_template = {
|
|
||||||
+ .version = 3,
|
|
||||||
+ .resource_table = ipq806x_rpm_resource_table,
|
|
||||||
+ .n_resources = ARRAY_SIZE(ipq806x_rpm_resource_table),
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
static const struct of_device_id qcom_rpm_of_match[] = {
|
|
||||||
{ .compatible = "qcom,rpm-apq8064", .data = &apq8064_template },
|
|
||||||
{ .compatible = "qcom,rpm-msm8660", .data = &msm8660_template },
|
|
||||||
{ .compatible = "qcom,rpm-msm8960", .data = &msm8960_template },
|
|
||||||
+ { .compatible = "qcom,rpm-ipq8064", .data = &ipq806x_template },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
|
|
@ -1,42 +0,0 @@ |
|||||||
From 30bc3aa5c4ed3072bdff7d915772df1b91307ed4 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
Date: Thu, 26 Mar 2015 11:29:25 -0700
|
|
||||||
Subject: [PATCH] mfd: devicetree: qcom_rpm: Document IPQ8064 resources
|
|
||||||
|
|
||||||
The IPQ8064 SoC has several RPM-controlled resources, an NSS fabrick
|
|
||||||
clock and four regulator resources. Provide definitions for them.
|
|
||||||
|
|
||||||
Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
[sboyd@codeaurora.org: Drop regulator part of binding]
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Signed-off-by: Lee Jones <lee.jones@linaro.org>
|
|
||||||
---
|
|
||||||
Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 1 +
|
|
||||||
include/dt-bindings/mfd/qcom-rpm.h | 6 ++++++
|
|
||||||
2 files changed, 7 insertions(+)
|
|
||||||
|
|
||||||
--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
|
|
||||||
+++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
|
|
||||||
@@ -12,6 +12,7 @@ frequencies.
|
|
||||||
"qcom,rpm-apq8064"
|
|
||||||
"qcom,rpm-msm8660"
|
|
||||||
"qcom,rpm-msm8960"
|
|
||||||
+ "qcom,rpm-ipq8064"
|
|
||||||
|
|
||||||
- reg:
|
|
||||||
Usage: required
|
|
||||||
--- a/include/dt-bindings/mfd/qcom-rpm.h
|
|
||||||
+++ b/include/dt-bindings/mfd/qcom-rpm.h
|
|
||||||
@@ -141,6 +141,12 @@
|
|
||||||
#define QCOM_RPM_SYS_FABRIC_MODE 131
|
|
||||||
#define QCOM_RPM_USB_OTG_SWITCH 132
|
|
||||||
#define QCOM_RPM_VDDMIN_GPIO 133
|
|
||||||
+#define QCOM_RPM_NSS_FABRIC_0_CLK 134
|
|
||||||
+#define QCOM_RPM_NSS_FABRIC_1_CLK 135
|
|
||||||
+#define QCOM_RPM_SMB208_S1a 136
|
|
||||||
+#define QCOM_RPM_SMB208_S1b 137
|
|
||||||
+#define QCOM_RPM_SMB208_S2a 138
|
|
||||||
+#define QCOM_RPM_SMB208_S2b 139
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Constants used to select force mode for regulators.
|
|
@ -1,175 +0,0 @@ |
|||||||
From df944689d491e6af533173bf2ef448c3dd334f15 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Mon, 11 May 2015 15:15:25 -0700
|
|
||||||
Subject: [PATCH 6/8] net: stmmac: create one debugfs dir per net-device
|
|
||||||
|
|
||||||
stmmac DebugFS entries are currently global to the driver. As a result,
|
|
||||||
having more than one stmmac device in the system creates the following
|
|
||||||
error:
|
|
||||||
* ERROR stmmaceth, debugfs create directory failed
|
|
||||||
* stmmac_hw_setup: failed debugFS registration
|
|
||||||
|
|
||||||
This also results in being able to access the debugfs information for
|
|
||||||
the first registered device only.
|
|
||||||
|
|
||||||
This patch changes the debugfs structure to have one sub-directory per
|
|
||||||
net-device. Files under "/sys/kernel/debug/stmmaceth" will now show-up
|
|
||||||
under /sys/kernel/debug/stmmaceth/ethN/.
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 6 ++
|
|
||||||
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 76 ++++++++++++++++-------
|
|
||||||
2 files changed, 59 insertions(+), 23 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
|
|
||||||
@@ -116,6 +116,12 @@ struct stmmac_priv {
|
|
||||||
int use_riwt;
|
|
||||||
int irq_wake;
|
|
||||||
spinlock_t ptp_lock;
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_DEBUG_FS
|
|
||||||
+ struct dentry *dbgfs_dir;
|
|
||||||
+ struct dentry *dbgfs_rings_status;
|
|
||||||
+ struct dentry *dbgfs_dma_cap;
|
|
||||||
+#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
int stmmac_mdio_unregister(struct net_device *ndev);
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
||||||
@@ -119,7 +119,7 @@ static irqreturn_t stmmac_interrupt(int
|
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
|
||||||
static int stmmac_init_fs(struct net_device *dev);
|
|
||||||
-static void stmmac_exit_fs(void);
|
|
||||||
+static void stmmac_exit_fs(struct net_device *dev);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x))
|
|
||||||
@@ -1918,7 +1918,7 @@ static int stmmac_release(struct net_dev
|
|
||||||
netif_carrier_off(dev);
|
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
|
||||||
- stmmac_exit_fs();
|
|
||||||
+ stmmac_exit_fs(dev);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
stmmac_release_ptp(priv);
|
|
||||||
@@ -2510,8 +2510,6 @@ static int stmmac_ioctl(struct net_devic
|
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
|
||||||
static struct dentry *stmmac_fs_dir;
|
|
||||||
-static struct dentry *stmmac_rings_status;
|
|
||||||
-static struct dentry *stmmac_dma_cap;
|
|
||||||
|
|
||||||
static void sysfs_display_ring(void *head, int size, int extend_desc,
|
|
||||||
struct seq_file *seq)
|
|
||||||
@@ -2650,36 +2648,39 @@ static const struct file_operations stmm
|
|
||||||
|
|
||||||
static int stmmac_init_fs(struct net_device *dev)
|
|
||||||
{
|
|
||||||
- /* Create debugfs entries */
|
|
||||||
- stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
|
|
||||||
+ struct stmmac_priv *priv = netdev_priv(dev);
|
|
||||||
+
|
|
||||||
+ /* Create per netdev entries */
|
|
||||||
+ priv->dbgfs_dir = debugfs_create_dir(dev->name, stmmac_fs_dir);
|
|
||||||
|
|
||||||
- if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
|
|
||||||
- pr_err("ERROR %s, debugfs create directory failed\n",
|
|
||||||
- STMMAC_RESOURCE_NAME);
|
|
||||||
+ if (!priv->dbgfs_dir || IS_ERR(priv->dbgfs_dir)) {
|
|
||||||
+ pr_err("ERROR %s/%s, debugfs create directory failed\n",
|
|
||||||
+ STMMAC_RESOURCE_NAME, dev->name);
|
|
||||||
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Entry to report DMA RX/TX rings */
|
|
||||||
- stmmac_rings_status = debugfs_create_file("descriptors_status",
|
|
||||||
- S_IRUGO, stmmac_fs_dir, dev,
|
|
||||||
- &stmmac_rings_status_fops);
|
|
||||||
+ priv->dbgfs_rings_status =
|
|
||||||
+ debugfs_create_file("descriptors_status", S_IRUGO,
|
|
||||||
+ priv->dbgfs_dir, dev,
|
|
||||||
+ &stmmac_rings_status_fops);
|
|
||||||
|
|
||||||
- if (!stmmac_rings_status || IS_ERR(stmmac_rings_status)) {
|
|
||||||
+ if (!priv->dbgfs_rings_status || IS_ERR(priv->dbgfs_rings_status)) {
|
|
||||||
pr_info("ERROR creating stmmac ring debugfs file\n");
|
|
||||||
- debugfs_remove(stmmac_fs_dir);
|
|
||||||
+ debugfs_remove_recursive(priv->dbgfs_dir);
|
|
||||||
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Entry to report the DMA HW features */
|
|
||||||
- stmmac_dma_cap = debugfs_create_file("dma_cap", S_IRUGO, stmmac_fs_dir,
|
|
||||||
- dev, &stmmac_dma_cap_fops);
|
|
||||||
+ priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", S_IRUGO,
|
|
||||||
+ priv->dbgfs_dir,
|
|
||||||
+ dev, &stmmac_dma_cap_fops);
|
|
||||||
|
|
||||||
- if (!stmmac_dma_cap || IS_ERR(stmmac_dma_cap)) {
|
|
||||||
+ if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) {
|
|
||||||
pr_info("ERROR creating stmmac MMC debugfs file\n");
|
|
||||||
- debugfs_remove(stmmac_rings_status);
|
|
||||||
- debugfs_remove(stmmac_fs_dir);
|
|
||||||
+ debugfs_remove_recursive(priv->dbgfs_dir);
|
|
||||||
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
@@ -2687,11 +2688,11 @@ static int stmmac_init_fs(struct net_dev
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static void stmmac_exit_fs(void)
|
|
||||||
+static void stmmac_exit_fs(struct net_device *dev)
|
|
||||||
{
|
|
||||||
- debugfs_remove(stmmac_rings_status);
|
|
||||||
- debugfs_remove(stmmac_dma_cap);
|
|
||||||
- debugfs_remove(stmmac_fs_dir);
|
|
||||||
+ struct stmmac_priv *priv = netdev_priv(dev);
|
|
||||||
+
|
|
||||||
+ debugfs_remove_recursive(priv->dbgfs_dir);
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_DEBUG_FS */
|
|
||||||
|
|
||||||
@@ -3136,6 +3137,35 @@ err:
|
|
||||||
__setup("stmmaceth=", stmmac_cmdline_opt);
|
|
||||||
#endif /* MODULE */
|
|
||||||
|
|
||||||
+static int __init stmmac_init(void)
|
|
||||||
+{
|
|
||||||
+#ifdef CONFIG_DEBUG_FS
|
|
||||||
+ /* Create debugfs main directory if it doesn't exist yet */
|
|
||||||
+ if (stmmac_fs_dir == NULL) {
|
|
||||||
+ stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
|
|
||||||
+
|
|
||||||
+ if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
|
|
||||||
+ pr_err("ERROR %s, debugfs create directory failed\n",
|
|
||||||
+ STMMAC_RESOURCE_NAME);
|
|
||||||
+
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit stmmac_exit(void)
|
|
||||||
+{
|
|
||||||
+#ifdef CONFIG_DEBUG_FS
|
|
||||||
+ debugfs_remove_recursive(stmmac_fs_dir);
|
|
||||||
+#endif
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(stmmac_init)
|
|
||||||
+module_exit(stmmac_exit)
|
|
||||||
+
|
|
||||||
MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet device driver");
|
|
||||||
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
Loading…
Reference in new issue