From 275876e208e28abf4b96ec89030e482b1331ee75 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Tue, 15 Jul 2014 12:23:03 +0800
Subject: regmap: Add the DT binding documentation for endianness
Device-Tree binding for device endianness
Index Device Endianness properties
---------------------------------------------------
1 BE 'big-endian'
2 LE 'little-endian'
For one device driver, which will run in different scenarios above
on different SoCs using the devicetree, we need one way to simplify
this.
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
---
.../devicetree/bindings/regmap/regmap.txt | 47 ++++++++++++++++++++++
1 file changed, 47 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regmap/regmap.txt
(limited to 'Documentation/devicetree')
diff --git a/Documentation/devicetree/bindings/regmap/regmap.txt b/Documentation/devicetree/bindings/regmap/regmap.txt
new file mode 100644
index 00000000000..b494f8b8ef7
--- /dev/null
+++ b/Documentation/devicetree/bindings/regmap/regmap.txt
@@ -0,0 +1,47 @@
+Device-Tree binding for regmap
+
+The endianness mode of CPU & Device scenarios:
+Index Device Endianness properties
+---------------------------------------------------
+1 BE 'big-endian'
+2 LE 'little-endian'
+
+For one device driver, which will run in different scenarios above
+on different SoCs using the devicetree, we need one way to simplify
+this.
+
+Required properties:
+- {big,little}-endian: these are boolean properties, if absent
+ meaning that the CPU and the Device are in the same endianness mode,
+ these properties are for register values and all the buffers only.
+
+Examples:
+Scenario 1 : CPU in LE mode & device in LE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+};
+
+Scenario 2 : CPU in LE mode & device in BE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+ big-endian;
+};
+
+Scenario 3 : CPU in BE mode & device in BE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+};
+
+Scenario 4 : CPU in BE mode & device in LE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+ little-endian;
+};
--
cgit v1.2.3-70-g09d2
From c99428d035908b9c0b8be452f9b091bc5e090256 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Mon, 18 Aug 2014 15:48:20 +0800
Subject: spi: fsl-dspi: Convert to use regmap framework's endianness method.
Signed-off-by: Xiubo Li
Acked-by: Chao Fu
Signed-off-by: Mark Brown
---
Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt | 7 ++++++-
drivers/spi/spi-fsl-dspi.c | 3 ---
2 files changed, 6 insertions(+), 4 deletions(-)
(limited to 'Documentation/devicetree')
diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt
index 5376de40f10..cbbe16ed387 100644
--- a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt
+++ b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt
@@ -10,7 +10,12 @@ Required properties:
- pinctrl-names: must contain a "default" entry.
- spi-num-chipselects : the number of the chipselect signals.
- bus-num : the slave chip chipselect signal number.
-- big-endian : if DSPI modudle is big endian, the bool will be set in node.
+
+Optional property:
+- big-endian: If present the dspi device's registers are implemented
+ in big endian mode, otherwise in native mode(same with CPU), for more
+ detail please see: Documentation/devicetree/bindings/regmap/regmap.txt.
+
Example:
dspi0@4002c000 {
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 5021ddf03f6..ebc4d1fd76e 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -493,9 +493,6 @@ static int dspi_probe(struct platform_device *pdev)
}
dspi_regmap_config.lock_arg = dspi;
- dspi_regmap_config.val_format_endian =
- of_property_read_bool(np, "big-endian")
- ? REGMAP_ENDIAN_BIG : REGMAP_ENDIAN_DEFAULT;
dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base,
&dspi_regmap_config);
if (IS_ERR(dspi->regmap)) {
--
cgit v1.2.3-70-g09d2
From f62caccd12c17e4cb516d43a6e4dd8a3abc1f7e0 Mon Sep 17 00:00:00 2001
From: Robin Gong
Date: Thu, 11 Sep 2014 09:18:44 +0800
Subject: spi: spi-imx: add DMA support
Enable DMA support on i.mx6. The read speed can increase from 600KB/s
to 1.2MB/s on i.mx6q. You can disable or enable dma function in dts.
If not set "dma-names" in dts, spi will use PIO mode. This patch only
validate on i.mx6, not i.mx5, but encourage ones to apply this patch
on i.mx5 since they share the same IP.
Note:
Sometime, there is a weid data in rxfifo after one full tx/rx
transfer finish by DMA on i.mx6dl, so we disable dma functhion on
i.mx6dl.
Signed-off-by: Frank Li
Signed-off-by: Robin Gong
Acked-by: Marek Vasut
Signed-off-by: Mark Brown
---
.../devicetree/bindings/spi/fsl-imx-cspi.txt | 5 +
drivers/spi/spi-imx.c | 286 ++++++++++++++++++++-
2 files changed, 285 insertions(+), 6 deletions(-)
(limited to 'Documentation/devicetree')
diff --git a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt
index 4256a6df9b7..aad527b357a 100644
--- a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt
+++ b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt
@@ -7,6 +7,9 @@ Required properties:
- interrupts : Should contain CSPI/eCSPI interrupt
- fsl,spi-num-chipselects : Contains the number of the chipselect
- cs-gpios : Specifies the gpio pins to be used for chipselects.
+- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
+ Documentation/devicetree/bindings/dma/dma.txt
+- dma-names: DMA request names should include "tx" and "rx" if present.
Example:
@@ -19,4 +22,6 @@ ecspi@70010000 {
fsl,spi-num-chipselects = <2>;
cs-gpios = <&gpio3 24 0>, /* GPIO3_24 */
<&gpio3 25 0>; /* GPIO3_25 */
+ dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
+ dma-names = "rx", "tx";
};
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 5daff2054ae..3637847b537 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -21,6 +21,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -37,6 +39,7 @@
#include
#include
+#include
#include
#define DRIVER_NAME "spi_imx"
@@ -51,6 +54,9 @@
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
+/* The maximum bytes that a sdma BD can transfer.*/
+#define MAX_SDMA_BD_BYTES (1 << 15)
+#define IMX_DMA_TIMEOUT (msecs_to_jiffies(3000))
struct spi_imx_config {
unsigned int speed_hz;
unsigned int bpw;
@@ -95,6 +101,16 @@ struct spi_imx_data {
const void *tx_buf;
unsigned int txfifo; /* number of words pushed in tx FIFO */
+ /* DMA */
+ unsigned int dma_is_inited;
+ unsigned int dma_finished;
+ bool usedma;
+ u32 rx_wml;
+ u32 tx_wml;
+ u32 rxt_wml;
+ struct completion dma_rx_completion;
+ struct completion dma_tx_completion;
+
const struct spi_imx_devtype_data *devtype_data;
int chipselect[0];
};
@@ -181,9 +197,21 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
return 7;
}
+static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
+ struct spi_transfer *transfer)
+{
+ struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
+
+ if (spi_imx->dma_is_inited && (transfer->len > spi_imx->rx_wml)
+ && (transfer->len > spi_imx->tx_wml))
+ return true;
+ return false;
+}
+
#define MX51_ECSPI_CTRL 0x08
#define MX51_ECSPI_CTRL_ENABLE (1 << 0)
#define MX51_ECSPI_CTRL_XCH (1 << 2)
+#define MX51_ECSPI_CTRL_SMC (1 << 3)
#define MX51_ECSPI_CTRL_MODE_MASK (0xf << 4)
#define MX51_ECSPI_CTRL_POSTDIV_OFFSET 8
#define MX51_ECSPI_CTRL_PREDIV_OFFSET 12
@@ -201,6 +229,18 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
#define MX51_ECSPI_INT_TEEN (1 << 0)
#define MX51_ECSPI_INT_RREN (1 << 3)
+#define MX51_ECSPI_DMA 0x14
+#define MX51_ECSPI_DMA_TX_WML_OFFSET 0
+#define MX51_ECSPI_DMA_TX_WML_MASK 0x3F
+#define MX51_ECSPI_DMA_RX_WML_OFFSET 16
+#define MX51_ECSPI_DMA_RX_WML_MASK (0x3F << 16)
+#define MX51_ECSPI_DMA_RXT_WML_OFFSET 24
+#define MX51_ECSPI_DMA_RXT_WML_MASK (0x3F << 24)
+
+#define MX51_ECSPI_DMA_TEDEN_OFFSET 7
+#define MX51_ECSPI_DMA_RXDEN_OFFSET 23
+#define MX51_ECSPI_DMA_RXTDEN_OFFSET 31
+
#define MX51_ECSPI_STAT 0x18
#define MX51_ECSPI_STAT_RR (1 << 3)
@@ -257,17 +297,22 @@ static void __maybe_unused mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int
static void __maybe_unused mx51_ecspi_trigger(struct spi_imx_data *spi_imx)
{
- u32 reg;
-
- reg = readl(spi_imx->base + MX51_ECSPI_CTRL);
- reg |= MX51_ECSPI_CTRL_XCH;
+ u32 reg = readl(spi_imx->base + MX51_ECSPI_CTRL);
+
+ if (!spi_imx->usedma)
+ reg |= MX51_ECSPI_CTRL_XCH;
+ else if (!spi_imx->dma_finished)
+ reg |= MX51_ECSPI_CTRL_SMC;
+ else
+ reg &= ~MX51_ECSPI_CTRL_SMC;
writel(reg, spi_imx->base + MX51_ECSPI_CTRL);
}
static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
struct spi_imx_config *config)
{
- u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0;
+ u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0;
+ u32 tx_wml_cfg, rx_wml_cfg, rxt_wml_cfg;
u32 clk = config->speed_hz, delay;
/*
@@ -319,6 +364,30 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
else /* SCLK is _very_ slow */
usleep_range(delay, delay + 10);
+ /*
+ * Configure the DMA register: setup the watermark
+ * and enable DMA request.
+ */
+ if (spi_imx->dma_is_inited) {
+ dma = readl(spi_imx->base + MX51_ECSPI_DMA);
+
+ spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2;
+ spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2;
+ spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2;
+ rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
+ tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
+ rxt_wml_cfg = spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
+ dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK
+ & ~MX51_ECSPI_DMA_RX_WML_MASK
+ & ~MX51_ECSPI_DMA_RXT_WML_MASK)
+ | rx_wml_cfg | tx_wml_cfg | rxt_wml_cfg
+ |(1 << MX51_ECSPI_DMA_TEDEN_OFFSET)
+ |(1 << MX51_ECSPI_DMA_RXDEN_OFFSET)
+ |(1 << MX51_ECSPI_DMA_RXTDEN_OFFSET);
+
+ writel(dma, spi_imx->base + MX51_ECSPI_DMA);
+ }
+
return 0;
}
@@ -730,7 +799,186 @@ static int spi_imx_setupxfer(struct spi_device *spi,
return 0;
}
-static int spi_imx_transfer(struct spi_device *spi,
+static void spi_imx_sdma_exit(struct spi_imx_data *spi_imx)
+{
+ struct spi_master *master = spi_imx->bitbang.master;
+
+ if (master->dma_rx) {
+ dma_release_channel(master->dma_rx);
+ master->dma_rx = NULL;
+ }
+
+ if (master->dma_tx) {
+ dma_release_channel(master->dma_tx);
+ master->dma_tx = NULL;
+ }
+
+ spi_imx->dma_is_inited = 0;
+}
+
+static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
+ struct spi_master *master,
+ const struct resource *res)
+{
+ struct dma_slave_config slave_config = {};
+ int ret;
+
+ /* Prepare for TX DMA: */
+ master->dma_tx = dma_request_slave_channel(dev, "tx");
+ if (!master->dma_tx) {
+ dev_err(dev, "cannot get the TX DMA channel!\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ slave_config.direction = DMA_MEM_TO_DEV;
+ slave_config.dst_addr = res->start + MXC_CSPITXDATA;
+ slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ slave_config.dst_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
+ ret = dmaengine_slave_config(master->dma_tx, &slave_config);
+ if (ret) {
+ dev_err(dev, "error in TX dma configuration.\n");
+ goto err;
+ }
+
+ /* Prepare for RX : */
+ master->dma_rx = dma_request_slave_channel(dev, "rx");
+ if (!master->dma_rx) {
+ dev_dbg(dev, "cannot get the DMA channel.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ slave_config.direction = DMA_DEV_TO_MEM;
+ slave_config.src_addr = res->start + MXC_CSPIRXDATA;
+ slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ slave_config.src_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
+ ret = dmaengine_slave_config(master->dma_rx, &slave_config);
+ if (ret) {
+ dev_err(dev, "error in RX dma configuration.\n");
+ goto err;
+ }
+
+ init_completion(&spi_imx->dma_rx_completion);
+ init_completion(&spi_imx->dma_tx_completion);
+ master->can_dma = spi_imx_can_dma;
+ master->max_dma_len = MAX_SDMA_BD_BYTES;
+ spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX |
+ SPI_MASTER_MUST_TX;
+ spi_imx->dma_is_inited = 1;
+
+ return 0;
+err:
+ spi_imx_sdma_exit(spi_imx);
+ return ret;
+}
+
+static void spi_imx_dma_rx_callback(void *cookie)
+{
+ struct spi_imx_data *spi_imx = (struct spi_imx_data *)cookie;
+
+ complete(&spi_imx->dma_rx_completion);
+}
+
+static void spi_imx_dma_tx_callback(void *cookie)
+{
+ struct spi_imx_data *spi_imx = (struct spi_imx_data *)cookie;
+
+ complete(&spi_imx->dma_tx_completion);
+}
+
+static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
+ struct spi_transfer *transfer)
+{
+ struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL;
+ int ret;
+ u32 dma;
+ int left;
+ struct spi_master *master = spi_imx->bitbang.master;
+ struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg;
+
+ if (tx) {
+ desc_tx = dmaengine_prep_slave_sg(master->dma_tx,
+ tx->sgl, tx->nents, DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_tx)
+ goto no_dma;
+
+ desc_tx->callback = spi_imx_dma_tx_callback;
+ desc_tx->callback_param = (void *)spi_imx;
+ dmaengine_submit(desc_tx);
+ }
+
+ if (rx) {
+ desc_rx = dmaengine_prep_slave_sg(master->dma_rx,
+ rx->sgl, rx->nents, DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_rx)
+ goto no_dma;
+
+ desc_rx->callback = spi_imx_dma_rx_callback;
+ desc_rx->callback_param = (void *)spi_imx;
+ dmaengine_submit(desc_rx);
+ }
+
+ reinit_completion(&spi_imx->dma_rx_completion);
+ reinit_completion(&spi_imx->dma_tx_completion);
+
+ /* Trigger the cspi module. */
+ spi_imx->dma_finished = 0;
+
+ dma = readl(spi_imx->base + MX51_ECSPI_DMA);
+ dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK);
+ /* Change RX_DMA_LENGTH trigger dma fetch tail data */
+ left = transfer->len % spi_imx->rxt_wml;
+ if (left)
+ writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET),
+ spi_imx->base + MX51_ECSPI_DMA);
+ spi_imx->devtype_data->trigger(spi_imx);
+
+ dma_async_issue_pending(master->dma_tx);
+ dma_async_issue_pending(master->dma_rx);
+ /* Wait SDMA to finish the data transfer.*/
+ ret = wait_for_completion_timeout(&spi_imx->dma_tx_completion,
+ IMX_DMA_TIMEOUT);
+ if (!ret) {
+ pr_warn("%s %s: I/O Error in DMA TX\n",
+ dev_driver_string(&master->dev),
+ dev_name(&master->dev));
+ dmaengine_terminate_all(master->dma_tx);
+ } else {
+ ret = wait_for_completion_timeout(&spi_imx->dma_rx_completion,
+ IMX_DMA_TIMEOUT);
+ if (!ret) {
+ pr_warn("%s %s: I/O Error in DMA RX\n",
+ dev_driver_string(&master->dev),
+ dev_name(&master->dev));
+ spi_imx->devtype_data->reset(spi_imx);
+ dmaengine_terminate_all(master->dma_rx);
+ }
+ writel(dma |
+ spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET,
+ spi_imx->base + MX51_ECSPI_DMA);
+ }
+
+ spi_imx->dma_finished = 1;
+ spi_imx->devtype_data->trigger(spi_imx);
+
+ if (!ret)
+ ret = -ETIMEDOUT;
+ else if (ret > 0)
+ ret = transfer->len;
+
+ return ret;
+
+no_dma:
+ pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
+ dev_driver_string(&master->dev),
+ dev_name(&master->dev));
+ return -EAGAIN;
+}
+
+static int spi_imx_pio_transfer(struct spi_device *spi,
struct spi_transfer *transfer)
{
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
@@ -751,6 +999,24 @@ static int spi_imx_transfer(struct spi_device *spi,
return transfer->len;
}
+static int spi_imx_transfer(struct spi_device *spi,
+ struct spi_transfer *transfer)
+{
+ int ret;
+ struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
+
+ if (spi_imx->bitbang.master->can_dma &&
+ spi_imx_can_dma(spi_imx->bitbang.master, spi, transfer)) {
+ spi_imx->usedma = true;
+ ret = spi_imx_dma_transfer(spi_imx, transfer);
+ if (ret != -EAGAIN)
+ return ret;
+ }
+ spi_imx->usedma = false;
+
+ return spi_imx_pio_transfer(spi, transfer);
+}
+
static int spi_imx_setup(struct spi_device *spi)
{
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
@@ -911,6 +1177,13 @@ static int spi_imx_probe(struct platform_device *pdev)
goto out_put_per;
spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per);
+ /*
+ * Only validated on i.mx6 now, can remove the constrain if validated on
+ * other chips.
+ */
+ if (spi_imx->devtype_data == &imx51_ecspi_devtype_data
+ && spi_imx_sdma_init(&pdev->dev, spi_imx, master, res))
+ dev_err(&pdev->dev, "dma setup error,use pio instead\n");
spi_imx->devtype_data->reset(spi_imx);
@@ -949,6 +1222,7 @@ static int spi_imx_remove(struct platform_device *pdev)
writel(0, spi_imx->base + MXC_CSPICTRL);
clk_unprepare(spi_imx->clk_ipg);
clk_unprepare(spi_imx->clk_per);
+ spi_imx_sdma_exit(spi_imx);
spi_master_put(master);
return 0;
--
cgit v1.2.3-70-g09d2
From df59fa7f4bca9658b75f0f5fee225b3a057475c5 Mon Sep 17 00:00:00 2001
From: Greg Ungerer
Date: Sun, 28 Sep 2014 23:24:04 +1000
Subject: spi: orion: support armada extended baud rates
The Armada SoC family implementation of this SPI hardware module has
extended the configuration register to allow for a wider range of SPI
clock rates. Specifically the Serial Baud Rate Pre-selection bits in the
SPI Interface Configuration Register now also use bits 6 and 7 as well.
Modify the baud rate calculation to handle these differences for the
Armada case. Potentially a baud rate can be setup using a number of
different pre-scalar and scalar combinations. This code tries all
possible pre-scalar divisors (8 in total) to try and find the most
accurate set.
This change introduces (and documents) a new device tree compatible
device name "armada-370-spi" to support this.
Signed-off-by: Greg Ungerer
Tested-by: Ezequiel Garcia
Reviewed-by: Ezequiel Garcia
Signed-off-by: Mark Brown
---
.../devicetree/bindings/spi/spi-orion.txt | 2 +-
drivers/spi/spi-orion.c | 116 +++++++++++++++++----
2 files changed, 95 insertions(+), 23 deletions(-)
(limited to 'Documentation/devicetree')
diff --git a/Documentation/devicetree/bindings/spi/spi-orion.txt b/Documentation/devicetree/bindings/spi/spi-orion.txt
index a3ff50fc76f..50c3a3de61c 100644
--- a/Documentation/devicetree/bindings/spi/spi-orion.txt
+++ b/Documentation/devicetree/bindings/spi/spi-orion.txt
@@ -1,7 +1,7 @@
Marvell Orion SPI device
Required properties:
-- compatible : should be "marvell,orion-spi".
+- compatible : should be "marvell,orion-spi" or "marvell,armada-370-spi".
- reg : offset and length of the register set for the device
- cell-index : Which of multiple SPI controllers is this.
Optional properties:
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index c4675fa8b64..acf8e48db16 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -18,6 +18,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -40,13 +41,27 @@
#define ORION_SPI_MODE_CPHA (1 << 12)
#define ORION_SPI_IF_8_16_BIT_MODE (1 << 5)
#define ORION_SPI_CLK_PRESCALE_MASK 0x1F
+#define ARMADA_SPI_CLK_PRESCALE_MASK 0xDF
#define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \
ORION_SPI_MODE_CPHA)
+enum orion_spi_type {
+ ORION_SPI,
+ ARMADA_SPI,
+};
+
+struct orion_spi_dev {
+ enum orion_spi_type typ;
+ unsigned int min_divisor;
+ unsigned int max_divisor;
+ u32 prescale_mask;
+};
+
struct orion_spi {
struct spi_master *master;
void __iomem *base;
struct clk *clk;
+ const struct orion_spi_dev *devdata;
};
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
@@ -83,30 +98,66 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
u32 prescale;
u32 reg;
struct orion_spi *orion_spi;
+ const struct orion_spi_dev *devdata;
orion_spi = spi_master_get_devdata(spi->master);
+ devdata = orion_spi->devdata;
tclk_hz = clk_get_rate(orion_spi->clk);
- /*
- * the supported rates are: 4,6,8...30
- * round up as we look for equal or less speed
- */
- rate = DIV_ROUND_UP(tclk_hz, speed);
- rate = roundup(rate, 2);
+ if (devdata->typ == ARMADA_SPI) {
+ unsigned int clk, spr, sppr, sppr2, err;
+ unsigned int best_spr, best_sppr, best_err;
+
+ best_err = speed;
+ best_spr = 0;
+ best_sppr = 0;
+
+ /* Iterate over the valid range looking for best fit */
+ for (sppr = 0; sppr < 8; sppr++) {
+ sppr2 = 0x1 << sppr;
+
+ spr = tclk_hz / sppr2;
+ spr = DIV_ROUND_UP(spr, speed);
+ if ((spr == 0) || (spr > 15))
+ continue;
- /* check if requested speed is too small */
- if (rate > 30)
- return -EINVAL;
+ clk = tclk_hz / (spr * sppr2);
+ err = speed - clk;
- if (rate < 4)
- rate = 4;
+ if (err < best_err) {
+ best_spr = spr;
+ best_sppr = sppr;
+ best_err = err;
+ }
+ }
+
+ if ((best_sppr == 0) && (best_spr == 0))
+ return -EINVAL;
+
+ prescale = ((best_sppr & 0x6) << 5) |
+ ((best_sppr & 0x1) << 4) | best_spr;
+ } else {
+ /*
+ * the supported rates are: 4,6,8...30
+ * round up as we look for equal or less speed
+ */
+ rate = DIV_ROUND_UP(tclk_hz, speed);
+ rate = roundup(rate, 2);
+
+ /* check if requested speed is too small */
+ if (rate > 30)
+ return -EINVAL;
- /* Convert the rate to SPI clock divisor value. */
- prescale = 0x10 + rate/2;
+ if (rate < 4)
+ rate = 4;
+
+ /* Convert the rate to SPI clock divisor value. */
+ prescale = 0x10 + rate/2;
+ }
reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
- reg = ((reg & ~ORION_SPI_CLK_PRESCALE_MASK) | prescale);
+ reg = ((reg & ~devdata->prescale_mask) | prescale);
writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
return 0;
@@ -342,8 +393,31 @@ static int orion_spi_reset(struct orion_spi *orion_spi)
return 0;
}
+static const struct orion_spi_dev orion_spi_dev_data = {
+ .typ = ORION_SPI,
+ .min_divisor = 4,
+ .max_divisor = 30,
+ .prescale_mask = ORION_SPI_CLK_PRESCALE_MASK,
+};
+
+static const struct orion_spi_dev armada_spi_dev_data = {
+ .typ = ARMADA_SPI,
+ .min_divisor = 1,
+ .max_divisor = 1920,
+ .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
+};
+
+static const struct of_device_id orion_spi_of_match_table[] = {
+ { .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, },
+ { .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, },
+ {}
+};
+MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
+
static int orion_spi_probe(struct platform_device *pdev)
{
+ const struct of_device_id *of_id;
+ const struct orion_spi_dev *devdata;
struct spi_master *master;
struct orion_spi *spi;
struct resource *r;
@@ -378,6 +452,10 @@ static int orion_spi_probe(struct platform_device *pdev)
spi = spi_master_get_devdata(master);
spi->master = master;
+ of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
+ devdata = of_id->data;
+ spi->devdata = devdata;
+
spi->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(spi->clk)) {
status = PTR_ERR(spi->clk);
@@ -389,8 +467,8 @@ static int orion_spi_probe(struct platform_device *pdev)
goto out;
tclk_hz = clk_get_rate(spi->clk);
- master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4);
- master->min_speed_hz = DIV_ROUND_UP(tclk_hz, 30);
+ master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
+ master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi->base = devm_ioremap_resource(&pdev->dev, r);
@@ -469,12 +547,6 @@ static const struct dev_pm_ops orion_spi_pm_ops = {
NULL)
};
-static const struct of_device_id orion_spi_of_match_table[] = {
- { .compatible = "marvell,orion-spi", },
- {}
-};
-MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
-
static struct platform_driver orion_spi_driver = {
.driver = {
.name = DRIVER_NAME,
--
cgit v1.2.3-70-g09d2