summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-omap2-mcspi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-omap2-mcspi.c')
-rw-r--r--drivers/spi/spi-omap2-mcspi.c331
1 files changed, 180 insertions, 151 deletions
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index bc4778175e34..474e2174e08a 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -38,11 +38,13 @@
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/err.h>
#include <linux/spi/spi.h>
#include <plat/clock.h>
-#include <plat/mcspi.h>
+#include <linux/platform_data/spi-omap2-mcspi.h>
#define OMAP2_MCSPI_MAX_FREQ 48000000
#define SPI_AUTOSUSPEND_TIMEOUT 2000
@@ -140,13 +142,6 @@ struct omap2_mcspi_cs {
u32 chconf0;
};
-#define MOD_REG_BIT(val, mask, set) do { \
- if (set) \
- val |= mask; \
- else \
- val &= ~mask; \
-} while (0)
-
static inline void mcspi_write_reg(struct spi_master *master,
int idx, u32 val)
{
@@ -205,7 +200,11 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi,
else
rw = OMAP2_MCSPI_CHCONF_DMAW;
- MOD_REG_BIT(l, rw, enable);
+ if (enable)
+ l |= rw;
+ else
+ l &= ~rw;
+
mcspi_write_chconf0(spi, l);
}
@@ -224,7 +223,11 @@ static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active)
u32 l;
l = mcspi_cached_chconf0(spi);
- MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active);
+ if (cs_active)
+ l |= OMAP2_MCSPI_CHCONF_FORCE;
+ else
+ l &= ~OMAP2_MCSPI_CHCONF_FORCE;
+
mcspi_write_chconf0(spi, l);
}
@@ -239,9 +242,8 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master)
* to single-channel master mode
*/
l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL);
- MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_STEST, 0);
- MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0);
- MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1);
+ l &= ~(OMAP2_MCSPI_MODULCTRL_STEST | OMAP2_MCSPI_MODULCTRL_MS);
+ l |= OMAP2_MCSPI_MODULCTRL_SINGLE;
mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l);
ctx->modulctrl = l;
@@ -260,16 +262,6 @@ static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi)
list_for_each_entry(cs, &ctx->cs, node)
__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
}
-static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi)
-{
- pm_runtime_mark_last_busy(mcspi->dev);
- pm_runtime_put_autosuspend(mcspi->dev);
-}
-
-static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi)
-{
- return pm_runtime_get_sync(mcspi->dev);
-}
static int omap2_prepare_transfer(struct spi_master *master)
{
@@ -325,49 +317,27 @@ static void omap2_mcspi_tx_callback(void *data)
omap2_mcspi_set_dma_req(spi, 0, 0);
}
-static unsigned
-omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
+static void omap2_mcspi_tx_dma(struct spi_device *spi,
+ struct spi_transfer *xfer,
+ struct dma_slave_config cfg)
{
struct omap2_mcspi *mcspi;
- struct omap2_mcspi_cs *cs = spi->controller_state;
struct omap2_mcspi_dma *mcspi_dma;
unsigned int count;
- int word_len, element_count;
- int elements = 0;
- u32 l;
u8 * rx;
const u8 * tx;
void __iomem *chstat_reg;
- struct dma_slave_config cfg;
- enum dma_slave_buswidth width;
- unsigned es;
+ struct omap2_mcspi_cs *cs = spi->controller_state;
mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
- l = mcspi_cached_chconf0(spi);
+ count = xfer->len;
+ rx = xfer->rx_buf;
+ tx = xfer->tx_buf;
chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
- if (cs->word_len <= 8) {
- width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- es = 1;
- } else if (cs->word_len <= 16) {
- width = DMA_SLAVE_BUSWIDTH_2_BYTES;
- es = 2;
- } else {
- width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- es = 4;
- }
-
- memset(&cfg, 0, sizeof(cfg));
- cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
- cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
- cfg.src_addr_width = width;
- cfg.dst_addr_width = width;
- cfg.src_maxburst = 1;
- cfg.dst_maxburst = 1;
-
- if (xfer->tx_buf && mcspi_dma->dma_tx) {
+ if (mcspi_dma->dma_tx) {
struct dma_async_tx_descriptor *tx;
struct scatterlist sg;
@@ -378,7 +348,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
sg_dma_len(&sg) = xfer->len;
tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
- DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (tx) {
tx->callback = omap2_mcspi_tx_callback;
tx->callback_param = spi;
@@ -387,8 +357,50 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
/* FIXME: fall back to PIO? */
}
}
+ dma_async_issue_pending(mcspi_dma->dma_tx);
+ omap2_mcspi_set_dma_req(spi, 0, 1);
- if (xfer->rx_buf && mcspi_dma->dma_rx) {
+ wait_for_completion(&mcspi_dma->dma_tx_completion);
+ dma_unmap_single(mcspi->dev, xfer->tx_dma, count,
+ DMA_TO_DEVICE);
+
+ /* for TX_ONLY mode, be sure all words have shifted out */
+ if (rx == NULL) {
+ if (mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_TXS) < 0)
+ dev_err(&spi->dev, "TXS timed out\n");
+ else if (mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_EOT) < 0)
+ dev_err(&spi->dev, "EOT timed out\n");
+ }
+}
+
+static unsigned
+omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
+ struct dma_slave_config cfg,
+ unsigned es)
+{
+ struct omap2_mcspi *mcspi;
+ struct omap2_mcspi_dma *mcspi_dma;
+ unsigned int count;
+ u32 l;
+ int elements = 0;
+ int word_len, element_count;
+ struct omap2_mcspi_cs *cs = spi->controller_state;
+ mcspi = spi_master_get_devdata(spi->master);
+ mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+ count = xfer->len;
+ word_len = cs->word_len;
+ l = mcspi_cached_chconf0(spi);
+
+ if (word_len <= 8)
+ element_count = count;
+ else if (word_len <= 16)
+ element_count = count >> 1;
+ else /* word_len <= 32 */
+ element_count = count >> 2;
+
+ if (mcspi_dma->dma_rx) {
struct dma_async_tx_descriptor *tx;
struct scatterlist sg;
size_t len = xfer->len - es;
@@ -403,108 +415,120 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
sg_dma_len(&sg) = len;
tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
- DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT |
+ DMA_CTRL_ACK);
if (tx) {
tx->callback = omap2_mcspi_rx_callback;
tx->callback_param = spi;
dmaengine_submit(tx);
} else {
- /* FIXME: fall back to PIO? */
+ /* FIXME: fall back to PIO? */
}
}
- count = xfer->len;
- word_len = cs->word_len;
-
- rx = xfer->rx_buf;
- tx = xfer->tx_buf;
+ dma_async_issue_pending(mcspi_dma->dma_rx);
+ omap2_mcspi_set_dma_req(spi, 1, 1);
- if (word_len <= 8) {
- element_count = count;
- } else if (word_len <= 16) {
- element_count = count >> 1;
- } else /* word_len <= 32 */ {
- element_count = count >> 2;
- }
-
- if (tx != NULL) {
- dma_async_issue_pending(mcspi_dma->dma_tx);
- omap2_mcspi_set_dma_req(spi, 0, 1);
- }
-
- if (rx != NULL) {
- dma_async_issue_pending(mcspi_dma->dma_rx);
- omap2_mcspi_set_dma_req(spi, 1, 1);
- }
-
- if (tx != NULL) {
- wait_for_completion(&mcspi_dma->dma_tx_completion);
- dma_unmap_single(mcspi->dev, xfer->tx_dma, count,
- DMA_TO_DEVICE);
-
- /* for TX_ONLY mode, be sure all words have shifted out */
- if (rx == NULL) {
- if (mcspi_wait_for_reg_bit(chstat_reg,
- OMAP2_MCSPI_CHSTAT_TXS) < 0)
- dev_err(&spi->dev, "TXS timed out\n");
- else if (mcspi_wait_for_reg_bit(chstat_reg,
- OMAP2_MCSPI_CHSTAT_EOT) < 0)
- dev_err(&spi->dev, "EOT timed out\n");
- }
- }
-
- if (rx != NULL) {
- wait_for_completion(&mcspi_dma->dma_rx_completion);
- dma_unmap_single(mcspi->dev, xfer->rx_dma, count,
- DMA_FROM_DEVICE);
- omap2_mcspi_set_enable(spi, 0);
-
- elements = element_count - 1;
+ wait_for_completion(&mcspi_dma->dma_rx_completion);
+ dma_unmap_single(mcspi->dev, xfer->rx_dma, count,
+ DMA_FROM_DEVICE);
+ omap2_mcspi_set_enable(spi, 0);
- if (l & OMAP2_MCSPI_CHCONF_TURBO) {
- elements--;
+ elements = element_count - 1;
- if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
- & OMAP2_MCSPI_CHSTAT_RXS)) {
- u32 w;
-
- w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
- if (word_len <= 8)
- ((u8 *)xfer->rx_buf)[elements++] = w;
- else if (word_len <= 16)
- ((u16 *)xfer->rx_buf)[elements++] = w;
- else /* word_len <= 32 */
- ((u32 *)xfer->rx_buf)[elements++] = w;
- } else {
- dev_err(&spi->dev,
- "DMA RX penultimate word empty");
- count -= (word_len <= 8) ? 2 :
- (word_len <= 16) ? 4 :
- /* word_len <= 32 */ 8;
- omap2_mcspi_set_enable(spi, 1);
- return count;
- }
- }
+ if (l & OMAP2_MCSPI_CHCONF_TURBO) {
+ elements--;
if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
- & OMAP2_MCSPI_CHSTAT_RXS)) {
+ & OMAP2_MCSPI_CHSTAT_RXS)) {
u32 w;
w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
if (word_len <= 8)
- ((u8 *)xfer->rx_buf)[elements] = w;
+ ((u8 *)xfer->rx_buf)[elements++] = w;
else if (word_len <= 16)
- ((u16 *)xfer->rx_buf)[elements] = w;
+ ((u16 *)xfer->rx_buf)[elements++] = w;
else /* word_len <= 32 */
- ((u32 *)xfer->rx_buf)[elements] = w;
+ ((u32 *)xfer->rx_buf)[elements++] = w;
} else {
- dev_err(&spi->dev, "DMA RX last word empty");
- count -= (word_len <= 8) ? 1 :
- (word_len <= 16) ? 2 :
- /* word_len <= 32 */ 4;
+ dev_err(&spi->dev, "DMA RX penultimate word empty");
+ count -= (word_len <= 8) ? 2 :
+ (word_len <= 16) ? 4 :
+ /* word_len <= 32 */ 8;
+ omap2_mcspi_set_enable(spi, 1);
+ return count;
}
- omap2_mcspi_set_enable(spi, 1);
}
+ if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
+ & OMAP2_MCSPI_CHSTAT_RXS)) {
+ u32 w;
+
+ w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
+ if (word_len <= 8)
+ ((u8 *)xfer->rx_buf)[elements] = w;
+ else if (word_len <= 16)
+ ((u16 *)xfer->rx_buf)[elements] = w;
+ else /* word_len <= 32 */
+ ((u32 *)xfer->rx_buf)[elements] = w;
+ } else {
+ dev_err(&spi->dev, "DMA RX last word empty");
+ count -= (word_len <= 8) ? 1 :
+ (word_len <= 16) ? 2 :
+ /* word_len <= 32 */ 4;
+ }
+ omap2_mcspi_set_enable(spi, 1);
+ return count;
+}
+
+static unsigned
+omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
+{
+ struct omap2_mcspi *mcspi;
+ struct omap2_mcspi_cs *cs = spi->controller_state;
+ struct omap2_mcspi_dma *mcspi_dma;
+ unsigned int count;
+ u32 l;
+ u8 *rx;
+ const u8 *tx;
+ struct dma_slave_config cfg;
+ enum dma_slave_buswidth width;
+ unsigned es;
+
+ mcspi = spi_master_get_devdata(spi->master);
+ mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+ l = mcspi_cached_chconf0(spi);
+
+
+ if (cs->word_len <= 8) {
+ width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ es = 1;
+ } else if (cs->word_len <= 16) {
+ width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ es = 2;
+ } else {
+ width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ es = 4;
+ }
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
+ cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
+ cfg.src_addr_width = width;
+ cfg.dst_addr_width = width;
+ cfg.src_maxburst = 1;
+ cfg.dst_maxburst = 1;
+
+ rx = xfer->rx_buf;
+ tx = xfer->tx_buf;
+
+ count = xfer->len;
+
+ if (tx != NULL)
+ omap2_mcspi_tx_dma(spi, xfer, cfg);
+
+ if (rx != NULL)
+ return omap2_mcspi_rx_dma(spi, xfer, cfg, es);
+
return count;
}
@@ -848,12 +872,13 @@ static int omap2_mcspi_setup(struct spi_device *spi)
return ret;
}
- ret = omap2_mcspi_enable_clocks(mcspi);
+ ret = pm_runtime_get_sync(mcspi->dev);
if (ret < 0)
return ret;
ret = omap2_mcspi_setup_transfer(spi, NULL);
- omap2_mcspi_disable_clocks(mcspi);
+ pm_runtime_mark_last_busy(mcspi->dev);
+ pm_runtime_put_autosuspend(mcspi->dev);
return ret;
}
@@ -1067,7 +1092,7 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
int ret = 0;
- ret = omap2_mcspi_enable_clocks(mcspi);
+ ret = pm_runtime_get_sync(mcspi->dev);
if (ret < 0)
return ret;
@@ -1076,7 +1101,8 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN;
omap2_mcspi_set_master_mode(master);
- omap2_mcspi_disable_clocks(mcspi);
+ pm_runtime_mark_last_busy(mcspi->dev);
+ pm_runtime_put_autosuspend(mcspi->dev);
return 0;
}
@@ -1116,7 +1142,7 @@ MODULE_DEVICE_TABLE(of, omap_mcspi_of_match);
static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
{
struct spi_master *master;
- struct omap2_mcspi_platform_config *pdata;
+ const struct omap2_mcspi_platform_config *pdata;
struct omap2_mcspi *mcspi;
struct resource *r;
int status = 0, i;
@@ -1124,6 +1150,7 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
static int bus_num = 1;
struct device_node *node = pdev->dev.of_node;
const struct of_device_id *match;
+ struct pinctrl *pinctrl;
master = spi_alloc_master(&pdev->dev, sizeof *mcspi);
if (master == NULL) {
@@ -1219,6 +1246,11 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
if (status < 0)
goto dma_chnl_free;
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&pdev->dev,
+ "pins are not configured from the driver\n");
+
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
pm_runtime_enable(&pdev->dev);
@@ -1228,19 +1260,16 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
status = spi_register_master(master);
if (status < 0)
- goto err_spi_register;
+ goto disable_pm;
return status;
-err_spi_register:
- spi_master_put(master);
disable_pm:
pm_runtime_disable(&pdev->dev);
dma_chnl_free:
kfree(mcspi->dma_channels);
free_master:
- kfree(master);
- platform_set_drvdata(pdev, NULL);
+ spi_master_put(master);
return status;
}
@@ -1254,12 +1283,11 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev)
mcspi = spi_master_get_devdata(master);
dma_channels = mcspi->dma_channels;
- omap2_mcspi_disable_clocks(mcspi);
+ pm_runtime_put_sync(mcspi->dev);
pm_runtime_disable(&pdev->dev);
spi_unregister_master(master);
kfree(dma_channels);
- platform_set_drvdata(pdev, NULL);
return 0;
}
@@ -1280,20 +1308,21 @@ static int omap2_mcspi_resume(struct device *dev)
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
struct omap2_mcspi_cs *cs;
- omap2_mcspi_enable_clocks(mcspi);
+ pm_runtime_get_sync(mcspi->dev);
list_for_each_entry(cs, &ctx->cs, node) {
if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) {
/*
* We need to toggle CS state for OMAP take this
* change in account.
*/
- MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 1);
+ cs->chconf0 |= OMAP2_MCSPI_CHCONF_FORCE;
__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
- MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 0);
+ cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE;
__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
}
}
- omap2_mcspi_disable_clocks(mcspi);
+ pm_runtime_mark_last_busy(mcspi->dev);
+ pm_runtime_put_autosuspend(mcspi->dev);
return 0;
}
#else