aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-rockchip.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-rockchip.c')
-rw-r--r--drivers/spi/spi-rockchip.c71
1 files changed, 48 insertions, 23 deletions
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 79a8bc4f6cec..8f50a4020f6f 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -13,20 +13,14 @@
*
*/
-#include <linux/init.h>
-#include <linux/module.h>
#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <linux/spi/spi.h>
-#include <linux/scatterlist.h>
-#include <linux/of.h>
#include <linux/pm_runtime.h>
-#include <linux/io.h>
-#include <linux/dmaengine.h>
+#include <linux/scatterlist.h>
#define DRIVER_NAME "rockchip-spi"
@@ -179,7 +173,7 @@ struct rockchip_spi {
u8 tmode;
u8 bpw;
u8 n_bytes;
- u8 rsd_nsecs;
+ u32 rsd_nsecs;
unsigned len;
u32 speed;
@@ -192,13 +186,12 @@ struct rockchip_spi {
/* protect state */
spinlock_t lock;
- struct completion xfer_completion;
-
u32 use_dma;
struct sg_table tx_sg;
struct sg_table rx_sg;
struct rockchip_spi_dma_data dma_rx;
struct rockchip_spi_dma_data dma_tx;
+ struct dma_slave_caps dma_caps;
};
static inline void spi_enable_chip(struct rockchip_spi *rs, int enable)
@@ -265,7 +258,10 @@ static inline u32 rx_max(struct rockchip_spi *rs)
static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
{
u32 ser;
- struct rockchip_spi *rs = spi_master_get_devdata(spi->master);
+ struct spi_master *master = spi->master;
+ struct rockchip_spi *rs = spi_master_get_devdata(master);
+
+ pm_runtime_get_sync(rs->dev);
ser = readl_relaxed(rs->regs + ROCKCHIP_SPI_SER) & SER_MASK;
@@ -290,6 +286,8 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
ser &= ~(1 << spi->chip_select);
writel_relaxed(ser, rs->regs + ROCKCHIP_SPI_SER);
+
+ pm_runtime_put_sync(rs->dev);
}
static int rockchip_spi_prepare_message(struct spi_master *master,
@@ -319,12 +317,12 @@ static void rockchip_spi_handle_err(struct spi_master *master,
*/
if (rs->use_dma) {
if (rs->state & RXBUSY) {
- dmaengine_terminate_all(rs->dma_rx.ch);
+ dmaengine_terminate_async(rs->dma_rx.ch);
flush_fifo(rs);
}
if (rs->state & TXBUSY)
- dmaengine_terminate_all(rs->dma_tx.ch);
+ dmaengine_terminate_async(rs->dma_tx.ch);
}
spin_unlock_irqrestore(&rs->lock, flags);
@@ -433,7 +431,7 @@ static void rockchip_spi_dma_txcb(void *data)
spin_unlock_irqrestore(&rs->lock, flags);
}
-static void rockchip_spi_prepare_dma(struct rockchip_spi *rs)
+static int rockchip_spi_prepare_dma(struct rockchip_spi *rs)
{
unsigned long flags;
struct dma_slave_config rxconf, txconf;
@@ -449,13 +447,18 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs)
rxconf.direction = rs->dma_rx.direction;
rxconf.src_addr = rs->dma_rx.addr;
rxconf.src_addr_width = rs->n_bytes;
- rxconf.src_maxburst = rs->n_bytes;
+ if (rs->dma_caps.max_burst > 4)
+ rxconf.src_maxburst = 4;
+ else
+ rxconf.src_maxburst = 1;
dmaengine_slave_config(rs->dma_rx.ch, &rxconf);
rxdesc = dmaengine_prep_slave_sg(
rs->dma_rx.ch,
rs->rx_sg.sgl, rs->rx_sg.nents,
rs->dma_rx.direction, DMA_PREP_INTERRUPT);
+ if (!rxdesc)
+ return -EINVAL;
rxdesc->callback = rockchip_spi_dma_rxcb;
rxdesc->callback_param = rs;
@@ -466,13 +469,21 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs)
txconf.direction = rs->dma_tx.direction;
txconf.dst_addr = rs->dma_tx.addr;
txconf.dst_addr_width = rs->n_bytes;
- txconf.dst_maxburst = rs->n_bytes;
+ if (rs->dma_caps.max_burst > 4)
+ txconf.dst_maxburst = 4;
+ else
+ txconf.dst_maxburst = 1;
dmaengine_slave_config(rs->dma_tx.ch, &txconf);
txdesc = dmaengine_prep_slave_sg(
rs->dma_tx.ch,
rs->tx_sg.sgl, rs->tx_sg.nents,
rs->dma_tx.direction, DMA_PREP_INTERRUPT);
+ if (!txdesc) {
+ if (rxdesc)
+ dmaengine_terminate_sync(rs->dma_rx.ch);
+ return -EINVAL;
+ }
txdesc->callback = rockchip_spi_dma_txcb;
txdesc->callback_param = rs;
@@ -494,6 +505,8 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs)
dmaengine_submit(txdesc);
dma_async_issue_pending(rs->dma_tx.ch);
}
+
+ return 0;
}
static void rockchip_spi_config(struct rockchip_spi *rs)
@@ -503,7 +516,8 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
int rsd = 0;
u32 cr0 = (CR0_BHT_8BIT << CR0_BHT_OFFSET)
- | (CR0_SSD_ONE << CR0_SSD_OFFSET);
+ | (CR0_SSD_ONE << CR0_SSD_OFFSET)
+ | (CR0_EM_BIG << CR0_EM_OFFSET);
cr0 |= (rs->n_bytes << CR0_DFS_OFFSET);
cr0 |= ((rs->mode & 0x3) << CR0_SCPH_OFFSET);
@@ -606,12 +620,12 @@ static int rockchip_spi_transfer_one(
if (rs->use_dma) {
if (rs->tmode == CR0_XFM_RO) {
/* rx: dma must be prepared first */
- rockchip_spi_prepare_dma(rs);
+ ret = rockchip_spi_prepare_dma(rs);
spi_enable_chip(rs, 1);
} else {
/* tx or tr: spi must be enabled first */
spi_enable_chip(rs, 1);
- rockchip_spi_prepare_dma(rs);
+ ret = rockchip_spi_prepare_dma(rs);
}
} else {
spi_enable_chip(rs, 1);
@@ -717,8 +731,14 @@ static int rockchip_spi_probe(struct platform_device *pdev)
master->handle_err = rockchip_spi_handle_err;
rs->dma_tx.ch = dma_request_slave_channel(rs->dev, "tx");
- if (!rs->dma_tx.ch)
+ if (IS_ERR_OR_NULL(rs->dma_tx.ch)) {
+ /* Check tx to see if we need defer probing driver */
+ if (PTR_ERR(rs->dma_tx.ch) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_get_fifo_len;
+ }
dev_warn(rs->dev, "Failed to request TX DMA channel\n");
+ }
rs->dma_rx.ch = dma_request_slave_channel(rs->dev, "rx");
if (!rs->dma_rx.ch) {
@@ -730,6 +750,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
}
if (rs->dma_tx.ch && rs->dma_rx.ch) {
+ dma_get_slave_caps(rs->dma_rx.ch, &(rs->dma_caps));
rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR);
rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR);
rs->dma_tx.direction = DMA_MEM_TO_DEV;
@@ -749,6 +770,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
return 0;
err_register_master:
+ pm_runtime_disable(&pdev->dev);
if (rs->dma_tx.ch)
dma_release_channel(rs->dma_tx.ch);
if (rs->dma_rx.ch)
@@ -778,6 +800,8 @@ static int rockchip_spi_remove(struct platform_device *pdev)
if (rs->dma_rx.ch)
dma_release_channel(rs->dma_rx.ch);
+ spi_master_put(master);
+
return 0;
}
@@ -868,6 +892,7 @@ static const struct of_device_id rockchip_spi_dt_match[] = {
{ .compatible = "rockchip,rk3066-spi", },
{ .compatible = "rockchip,rk3188-spi", },
{ .compatible = "rockchip,rk3288-spi", },
+ { .compatible = "rockchip,rk3399-spi", },
{ },
};
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);