From: Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Cc: Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>,
Fabio Estevam
<fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org>,
Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>,
Wolfgang Denk <wd-ynQEQJNshbs@public.gmane.org>,
Detlev Zundel <dzu-ynQEQJNshbs@public.gmane.org>,
Rob Herring <rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>,
spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org,
Chris Ball <cjb-2X9k7bc8m7Mdnm+yROfE0A@public.gmane.org>,
Dong Aisheng <b29396-KZfg59tc24xl57MIdRCFDg@public.gmane.org>,
Stefano Babic <sbabic-ynQEQJNshbs@public.gmane.org>
Subject: [PATCH 08/10 V2] spi: Add DMA support into SPI driver
Date: Fri, 6 Jul 2012 08:17:27 +0200 [thread overview]
Message-ID: <1341555449-17507-8-git-send-email-marex@denx.de> (raw)
In-Reply-To: <1341555449-17507-1-git-send-email-marex-ynQEQJNshbs@public.gmane.org>
Signed-off-by: Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
Cc: Chris Ball <cjb-2X9k7bc8m7Mdnm+yROfE0A@public.gmane.org>
Cc: Detlev Zundel <dzu-ynQEQJNshbs@public.gmane.org>
CC: Dong Aisheng <b29396-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Cc: Fabio Estevam <fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Cc: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
Cc: Linux ARM kernel <linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org>
Cc: Rob Herring <rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>
CC: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Stefano Babic <sbabic-ynQEQJNshbs@public.gmane.org>
Cc: Wolfgang Denk <wd-ynQEQJNshbs@public.gmane.org>
---
drivers/spi/spi-mxs.c | 230 +++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 215 insertions(+), 15 deletions(-)
V2: Only get DMA resource of we don't do DT configuration (based on observation
by Fabio, thanks!)
diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c
index a7d2ebc..e90200f 100644
--- a/drivers/spi/spi-mxs.c
+++ b/drivers/spi/spi-mxs.c
@@ -55,8 +55,12 @@
#define SSP_TIMEOUT 1000 /* 1000 ms */
+#define SG_NUM 4
+#define SG_MAXLEN 0xff00
+
struct mxs_spi {
struct mxs_ssp ssp;
+ struct completion c;
};
static int mxs_spi_setup_transfer(struct spi_device *dev,
@@ -192,6 +196,115 @@ static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set)
return 0;
}
+static void mxs_ssp_dma_irq_callback(void *param)
+{
+ struct mxs_spi *spi = param;
+ complete(&spi->c);
+}
+
+static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id)
+{
+ struct mxs_ssp *ssp = dev_id;
+ dev_err(ssp->dev, "%s[%i] CTRL1=%08x STATUS=%08x\n",
+ __func__, __LINE__,
+ readl(ssp->base + HW_SSP_CTRL1(ssp)),
+ readl(ssp->base + HW_SSP_STATUS(ssp)));
+ return IRQ_HANDLED;
+}
+
+static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs,
+ unsigned char *buf, int len,
+ int *first, int *last, int write)
+{
+ struct mxs_ssp *ssp = &spi->ssp;
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist sg[SG_NUM];
+ int sg_count;
+ uint32_t pio = BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs);
+ int ret;
+
+ if (len > SG_NUM * SG_MAXLEN) {
+ dev_err(ssp->dev, "Data chunk too big for DMA\n");
+ return -EINVAL;
+ }
+
+ init_completion(&spi->c);
+
+ if (*first)
+ pio |= BM_SSP_CTRL0_LOCK_CS;
+ if (*last)
+ pio |= BM_SSP_CTRL0_IGNORE_CRC;
+ if (!write)
+ pio |= BM_SSP_CTRL0_READ;
+
+ if (ssp->devid == IMX23_SSP)
+ pio |= len;
+ else
+ writel(len, ssp->base + HW_SSP_XFER_SIZE);
+
+ /* Queue the PIO register write transfer. */
+ desc = dmaengine_prep_slave_sg(ssp->dmach,
+ (struct scatterlist *)&pio,
+ 1, DMA_TRANS_NONE, 0);
+ if (!desc) {
+ dev_err(ssp->dev,
+ "Failed to get PIO reg. write descriptor.\n");
+ return -EINVAL;
+ }
+
+ /* Queue the DMA data transfer. */
+ sg_init_table(sg, (len / SG_MAXLEN) + 1);
+ sg_count = 0;
+ while (len) {
+ sg_set_buf(&sg[sg_count++], buf, min(len, SG_MAXLEN));
+ len -= min(len, SG_MAXLEN);
+ buf += min(len, SG_MAXLEN);
+ }
+ dma_map_sg(ssp->dev, sg, sg_count,
+ write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ desc = dmaengine_prep_slave_sg(ssp->dmach, sg, sg_count,
+ write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+ if (!desc) {
+ dev_err(ssp->dev,
+ "Failed to get DMA data write descriptor.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /*
+ * The last descriptor must have this callback,
+ * to finish the DMA transaction.
+ */
+ desc->callback = mxs_ssp_dma_irq_callback;
+ desc->callback_param = spi;
+
+ /* Start the transfer. */
+ dmaengine_submit(desc);
+ dma_async_issue_pending(ssp->dmach);
+
+ ret = wait_for_completion_timeout(&spi->c,
+ msecs_to_jiffies(SSP_TIMEOUT));
+
+ if (!ret) {
+ dev_err(ssp->dev, "DMA transfer timeout\n");
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ ret = 0;
+
+err:
+ for (--sg_count; sg_count >= 0; sg_count--) {
+ dma_unmap_sg(ssp->dev, &sg[sg_count], 1,
+ write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ }
+
+ return ret;
+}
+
static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs,
unsigned char *buf, int len,
int *first, int *last, int write)
@@ -276,18 +389,48 @@ static int mxs_spi_transfer_one(struct spi_master *host, struct spi_message *m)
first = 1;
if (&t->transfer_list == m->transfers.prev)
last = 1;
- if (t->rx_buf && t->tx_buf) {
+ if ((t->rx_buf && t->tx_buf) || (t->rx_dma && t->tx_dma)) {
dev_err(ssp->dev,
"Cannot send and receive simultaneously\n");
return -EINVAL;
}
- if (t->tx_buf)
- status = mxs_spi_txrx_pio(spi, cs, (void *)t->tx_buf,
- t->len, &first, &last, 1);
- if (t->rx_buf)
- status = mxs_spi_txrx_pio(spi, cs, t->rx_buf,
- t->len, &first, &last, 0);
+ /*
+ * Small blocks can be transfered via PIO.
+ * Measured by empiric means:
+ *
+ * dd if=/dev/mtdblock0 of=/dev/null bs=1024k count=1
+ *
+ * DMA only: 2.164808 seconds, 473.0KB/s
+ * Combined: 1.676276 seconds, 610.9KB/s
+ */
+ if (t->len <= 256) {
+ writel(BM_SSP_CTRL1_DMA_ENABLE,
+ ssp->base + HW_SSP_CTRL1(ssp) +
+ STMP_OFFSET_REG_CLR);
+
+ if (t->tx_buf)
+ status = mxs_spi_txrx_pio(spi, cs,
+ (void *)t->tx_buf,
+ t->len, &first, &last, 1);
+ if (t->rx_buf)
+ status = mxs_spi_txrx_pio(spi, cs,
+ t->rx_buf, t->len,
+ &first, &last, 0);
+ } else {
+ writel(BM_SSP_CTRL1_DMA_ENABLE,
+ ssp->base + HW_SSP_CTRL1(ssp) +
+ STMP_OFFSET_REG_SET);
+
+ if (t->tx_buf)
+ status = mxs_spi_txrx_dma(spi, cs,
+ (void *)t->tx_buf, t->len,
+ &first, &last, 1);
+ if (t->rx_buf)
+ status = mxs_spi_txrx_dma(spi, cs,
+ t->rx_buf, t->len,
+ &first, &last, 0);
+ }
m->actual_length += t->len;
if (status)
@@ -302,6 +445,21 @@ static int mxs_spi_transfer_one(struct spi_master *host, struct spi_message *m)
return status;
}
+static bool mxs_ssp_dma_filter(struct dma_chan *chan, void *param)
+{
+ struct mxs_ssp *ssp = param;
+
+ if (!mxs_dma_is_apbh(chan))
+ return false;
+
+ if (chan->chan_id != ssp->dma_channel)
+ return false;
+
+ chan->private = &ssp->dma_data;
+
+ return true;
+}
+
static const struct of_device_id mxs_spi_dt_ids[] = {
{ .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, },
{ .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, },
@@ -317,15 +475,18 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev)
struct spi_master *host;
struct mxs_spi *spi;
struct mxs_ssp *ssp;
- struct resource *iores;
+ struct resource *iores, *dmares;
struct pinctrl *pinctrl;
struct clk *clk;
void __iomem *base;
- int devid;
- int ret = 0;
+ int devid, dma_channel;
+ int ret = 0, irq_err, irq_dma;
+ dma_cap_mask_t mask;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!iores)
+ irq_err = platform_get_irq(pdev, 0);
+ irq_dma = platform_get_irq(pdev, 1);
+ if (!iores || irq_err < 0 || irq_dma < 0)
return -EINVAL;
base = devm_request_and_ioremap(&pdev->dev, iores);
@@ -340,10 +501,26 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev)
if (IS_ERR(clk))
return PTR_ERR(clk);
- if (np)
+ if (np) {
devid = (enum mxs_ssp_id) of_id->data;
- else
+ /*
+ * TODO: This is a temporary solution and should be changed
+ * to use generic DMA binding later when the helpers get in.
+ */
+ ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
+ &dma_channel);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to get DMA channel\n");
+ return -EINVAL;
+ }
+ } else {
+ dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmares)
+ return -EINVAL;
devid = pdev->id_entry->driver_data;
+ dma_channel = dmares->start;
+ }
host = spi_alloc_master(&pdev->dev, sizeof(*spi));
if (!host)
@@ -363,8 +540,28 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev)
ssp->clk = clk;
ssp->base = base;
ssp->devid = devid;
+ ssp->dma_channel = dma_channel;
+
+ ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0,
+ DRIVER_NAME, ssp);
+ if (ret)
+ goto out_host_free;
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ ssp->dma_data.chan_irq = irq_dma;
+ ssp->dmach = dma_request_channel(mask, mxs_ssp_dma_filter, ssp);
+ if (!ssp->dmach) {
+ dev_err(ssp->dev, "Failed to request DMA\n");
+ goto out_host_free;
+ }
+
+ /*
+ * Crank up the clock to 120MHz, this will be further divided onto a
+ * proper speed.
+ */
clk_prepare_enable(ssp->clk);
+ clk_set_rate(ssp->clk, 120 * 1000 * 1000);
ssp->clk_rate = clk_get_rate(ssp->clk) / 1000;
stmp_reset_block(ssp->base);
@@ -374,15 +571,18 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev)
ret = spi_register_master(host);
if (ret) {
dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret);
- goto out_host_free;
+ goto out_free_dma;
}
return 0;
-out_host_free:
+out_free_dma:
+ dma_release_channel(ssp->dmach);
clk_disable_unprepare(ssp->clk);
+out_host_free:
spi_master_put(host);
kfree(host);
+
return ret;
}
--
1.7.10
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
next prev parent reply other threads:[~2012-07-06 6:17 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-07-06 6:17 [PATCH 01/10 RESEND] mmc: spi: Move SSP register definitions into separate file Marek Vasut
[not found] ` <1341555449-17507-1-git-send-email-marex-ynQEQJNshbs@public.gmane.org>
2012-07-06 6:17 ` [PATCH 02/10 RESEND] mmc: spi: Rename IMX2[38]_MMC to IMX2[38]_SSP Marek Vasut
2012-07-06 6:17 ` [PATCH 03/10 RESEND] mmc: spi: Add necessary bits into mxs-spi.h Marek Vasut
2012-07-06 6:17 ` [PATCH 04/10 RESEND] mmc: spi: Pull out parts shared between MMC and SPI Marek Vasut
[not found] ` <1341555449-17507-4-git-send-email-marex-ynQEQJNshbs@public.gmane.org>
2012-07-16 7:57 ` Attila Kinali
[not found] ` <20120716095741.85d959fc81ab759b10adcf25-HB9FjVmMKa7tRgLqZ5aouw@public.gmane.org>
2012-07-16 10:59 ` Marek Vasut
[not found] ` <201207161259.15034.marex-ynQEQJNshbs@public.gmane.org>
2012-07-16 11:32 ` Attila Kinali
2012-07-06 6:17 ` [PATCH 05/10 RESEND] mmc: spi: Pull out the SSP clock configuration function Marek Vasut
2012-07-06 6:17 ` [PATCH 06/10 V2] spi: Add SPI driver for mx233/mx28 Marek Vasut
2012-07-31 20:53 ` [06/10,V2] " Guenter Roeck
[not found] ` <20120731205300.GA25721-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2012-08-01 2:31 ` Marek Vasut
[not found] ` <201208010431.04399.marex-ynQEQJNshbs@public.gmane.org>
2012-08-01 3:34 ` Guenter Roeck
2012-08-01 3:53 ` Shawn Guo
[not found] ` <20120801035333.GB1672-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-08-01 3:35 ` Guenter Roeck
[not found] ` <20120801033559.GB2323-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2012-08-01 4:50 ` Shawn Guo
[not found] ` <20120801045010.GC1672-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-08-01 5:00 ` Marek Vasut
[not found] ` <201208010700.54829.marex-ynQEQJNshbs@public.gmane.org>
2012-08-01 5:29 ` Guenter Roeck
[not found] ` <20120801052947.GA2400-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2012-08-01 5:46 ` Shubhrajyoti Datta
[not found] ` <CAM=Q2csm1m8DCd8__NAUCmk6AN=Czg_=nm4c3ervAgOhOtoMTw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-08-01 6:36 ` Guenter Roeck
[not found] ` <20120801063639.GB2764-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2012-08-01 6:41 ` Marek Vasut
2012-08-01 5:58 ` Shawn Guo
[not found] ` <20120801055854.GE1672-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-08-01 5:42 ` Guenter Roeck
[not found] ` <20120801054228.GA2645-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2012-08-01 6:28 ` Shawn Guo
[not found] ` <20120801062838.GF1672-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-08-01 6:10 ` Marek Vasut
[not found] ` <201208010810.37652.marex-ynQEQJNshbs@public.gmane.org>
2012-08-01 6:39 ` Guenter Roeck
[not found] ` <20120801063936.GC2764-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2012-08-01 6:45 ` Marek Vasut
[not found] ` <201208010845.19361.marex-ynQEQJNshbs@public.gmane.org>
2012-08-01 6:56 ` Guenter Roeck
[not found] ` <20120801065650.GA2928-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2012-08-01 7:50 ` Shawn Guo
[not found] ` <20120801075010.GI1672-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-08-01 14:58 ` Guenter Roeck
2012-08-01 7:40 ` Shawn Guo
2012-08-01 7:38 ` Lothar Waßmann
[not found] ` <20504.56543.699097.320114-VjFSrY7JcPWvSplVBqRQBQ@public.gmane.org>
2012-08-01 8:20 ` Shawn Guo
[not found] ` <20120801074051.GG1672-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-08-01 7:47 ` Shawn Guo
2012-08-01 6:33 ` Guenter Roeck
[not found] ` <20120801063326.GA2764-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2012-08-01 6:40 ` Marek Vasut
2012-08-01 4:48 ` Marek Vasut
2012-08-01 3:48 ` Shawn Guo
2012-07-06 6:17 ` [PATCH 07/10 RESEND] mmc: spi: Pull out common DMA parts from MXS MMC Marek Vasut
2012-07-06 6:17 ` Marek Vasut [this message]
[not found] ` <1341555449-17507-8-git-send-email-marex-ynQEQJNshbs@public.gmane.org>
2012-08-01 6:29 ` [PATCH 08/10 V2] spi: Add DMA support into SPI driver Marek Vasut
2012-07-06 6:17 ` [PATCH 09/10] spi: Add SSP/SPI device tree documentation Marek Vasut
2012-07-06 6:17 ` [PATCH 10/10] ARM: mx28: Add SPI pinmux into imx28.dtsi Marek Vasut
2012-07-09 16:53 ` [PATCH 01/10 RESEND] mmc: spi: Move SSP register definitions into separate file Marek Vasut
-- strict thread matches above, loose matches on Subject: below --
2012-08-03 15:26 [PATCH 00/10 V3] MXS SPI driver Marek Vasut
[not found] ` <1344007575-11448-1-git-send-email-marex-ynQEQJNshbs@public.gmane.org>
2012-08-03 15:26 ` [PATCH 08/10 V2] spi: Add DMA support into " Marek Vasut
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1341555449-17507-8-git-send-email-marex@denx.de \
--to=marex-ynqeqjnshbs@public.gmane.org \
--cc=b29396-KZfg59tc24xl57MIdRCFDg@public.gmane.org \
--cc=cjb-2X9k7bc8m7Mdnm+yROfE0A@public.gmane.org \
--cc=dzu-ynQEQJNshbs@public.gmane.org \
--cc=fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org \
--cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
--cc=rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org \
--cc=sbabic-ynQEQJNshbs@public.gmane.org \
--cc=shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \
--cc=spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org \
--cc=wd-ynQEQJNshbs@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).