diff options
Diffstat (limited to 'drivers/spi/spi-omap2-mcspi.c')
| -rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 1241 |
1 files changed, 726 insertions, 515 deletions
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 0b0dfb71c64..4dc77df3886 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -22,29 +22,32 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/omap-dma.h> #include <linux/platform_device.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/gcd.h> #include <linux/spi/spi.h> -#include <plat/dma.h> -#include <plat/clock.h> -#include <plat/mcspi.h> +#include <linux/platform_data/spi-omap2-mcspi.h> #define OMAP2_MCSPI_MAX_FREQ 48000000 - -/* OMAP2 has 3 SPI controllers, while OMAP3 has 4 */ -#define OMAP2_MCSPI_MAX_CTRL 4 +#define OMAP2_MCSPI_MAX_DIVIDER 4096 +#define OMAP2_MCSPI_MAX_FIFODEPTH 64 +#define OMAP2_MCSPI_MAX_FIFOWCNT 0xFFFF +#define SPI_AUTOSUSPEND_TIMEOUT 2000 #define OMAP2_MCSPI_REVISION 0x00 #define OMAP2_MCSPI_SYSSTATUS 0x14 @@ -53,6 +56,7 @@ #define OMAP2_MCSPI_WAKEUPENABLE 0x20 #define OMAP2_MCSPI_SYST 0x24 #define OMAP2_MCSPI_MODULCTRL 0x28 +#define OMAP2_MCSPI_XFERLEVEL 0x7c /* per-channel banks, 0x14 bytes each, first is: */ #define OMAP2_MCSPI_CHCONF0 0x2c @@ -62,6 +66,7 @@ #define OMAP2_MCSPI_RX0 0x3c /* per-register bitmasks: */ +#define OMAP2_MCSPI_IRQSTATUS_EOW BIT(17) #define OMAP2_MCSPI_MODULCTRL_SINGLE BIT(0) #define OMAP2_MCSPI_MODULCTRL_MS BIT(2) @@ -82,25 +87,33 @@ #define OMAP2_MCSPI_CHCONF_IS BIT(18) #define OMAP2_MCSPI_CHCONF_TURBO BIT(19) #define OMAP2_MCSPI_CHCONF_FORCE BIT(20) +#define OMAP2_MCSPI_CHCONF_FFET BIT(27) +#define OMAP2_MCSPI_CHCONF_FFER BIT(28) +#define OMAP2_MCSPI_CHCONF_CLKG BIT(29) #define OMAP2_MCSPI_CHSTAT_RXS BIT(0) #define OMAP2_MCSPI_CHSTAT_TXS BIT(1) #define OMAP2_MCSPI_CHSTAT_EOT BIT(2) +#define OMAP2_MCSPI_CHSTAT_TXFFE BIT(3) #define OMAP2_MCSPI_CHCTRL_EN BIT(0) +#define OMAP2_MCSPI_CHCTRL_EXTCLK_MASK (0xff << 8) #define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0) /* We have 2 DMA channels per CS, one for RX and one for TX */ struct omap2_mcspi_dma { - int dma_tx_channel; - int dma_rx_channel; + struct dma_chan *dma_tx; + struct dma_chan *dma_rx; int dma_tx_sync_dev; int dma_rx_sync_dev; struct completion dma_tx_completion; struct completion dma_rx_completion; + + char dma_rx_ch_name[14]; + char dma_tx_ch_name[14]; }; /* use PIO for small transfers, avoiding DMA setup/teardown overhead and @@ -109,19 +122,27 @@ struct omap2_mcspi_dma { #define DMA_MIN_BYTES 160 +/* + * Used for context save and restore, structure members to be updated whenever + * corresponding registers are modified. + */ +struct omap2_mcspi_regs { + u32 modulctrl; + u32 wakeupenable; + struct list_head cs; +}; + struct omap2_mcspi { - struct work_struct work; - /* lock protects queue and registers */ - spinlock_t lock; - struct list_head msg_queue; struct spi_master *master; /* Virtual base address of the controller */ void __iomem *base; unsigned long phys; /* SPI1 has 4 channels, while SPI2 has 2 */ struct omap2_mcspi_dma *dma_channels; - struct device *dev; - struct workqueue_struct *wq; + struct device *dev; + struct omap2_mcspi_regs ctx; + int fifo_depth; + unsigned int pin_dir:1; }; struct omap2_mcspi_cs { @@ -130,40 +151,22 @@ struct omap2_mcspi_cs { int word_len; struct list_head node; /* Context save and restore shadow register */ - u32 chconf0; -}; - -/* used for context save and restore, structure members to be updated whenever - * corresponding registers are modified. - */ -struct omap2_mcspi_regs { - u32 modulctrl; - u32 wakeupenable; - struct list_head cs; + u32 chconf0, chctrl0; }; -static struct omap2_mcspi_regs omap2_mcspi_ctx[OMAP2_MCSPI_MAX_CTRL]; - -#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) { struct omap2_mcspi *mcspi = spi_master_get_devdata(master); - __raw_writel(val, mcspi->base + idx); + writel_relaxed(val, mcspi->base + idx); } static inline u32 mcspi_read_reg(struct spi_master *master, int idx) { struct omap2_mcspi *mcspi = spi_master_get_devdata(master); - return __raw_readl(mcspi->base + idx); + return readl_relaxed(mcspi->base + idx); } static inline void mcspi_write_cs_reg(const struct spi_device *spi, @@ -171,14 +174,14 @@ static inline void mcspi_write_cs_reg(const struct spi_device *spi, { struct omap2_mcspi_cs *cs = spi->controller_state; - __raw_writel(val, cs->base + idx); + writel_relaxed(val, cs->base + idx); } static inline u32 mcspi_read_cs_reg(const struct spi_device *spi, int idx) { struct omap2_mcspi_cs *cs = spi->controller_state; - return __raw_readl(cs->base + idx); + return readl_relaxed(cs->base + idx); } static inline u32 mcspi_cached_chconf0(const struct spi_device *spi) @@ -197,6 +200,16 @@ static inline void mcspi_write_chconf0(const struct spi_device *spi, u32 val) mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); } +static inline int mcspi_bytes_per_word(int word_len) +{ + if (word_len <= 8) + return 1; + else if (word_len <= 16) + return 2; + else /* word_len <= 32 */ + return 4; +} + static void omap2_mcspi_set_dma_req(const struct spi_device *spi, int is_read, int enable) { @@ -209,16 +222,26 @@ 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); } static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) { + struct omap2_mcspi_cs *cs = spi->controller_state; u32 l; - l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0; - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l); + l = cs->chctrl0; + if (enable) + l |= OMAP2_MCSPI_CHCTRL_EN; + else + l &= ~OMAP2_MCSPI_CHCTRL_EN; + cs->chctrl0 = l; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, cs->chctrl0); /* Flash post-writes */ mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0); } @@ -228,51 +251,102 @@ 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); } static void omap2_mcspi_set_master_mode(struct spi_master *master) { + struct omap2_mcspi *mcspi = spi_master_get_devdata(master); + struct omap2_mcspi_regs *ctx = &mcspi->ctx; u32 l; - /* setup when switching from (reset default) slave mode + /* + * Setup when switching from (reset default) slave mode * 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); - omap2_mcspi_ctx[master->bus_num - 1].modulctrl = l; + ctx->modulctrl = l; } -static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) +static void omap2_mcspi_set_fifo(const struct spi_device *spi, + struct spi_transfer *t, int enable) { - struct spi_master *spi_cntrl; - struct omap2_mcspi_cs *cs; - spi_cntrl = mcspi->master; + struct spi_master *master = spi->master; + struct omap2_mcspi_cs *cs = spi->controller_state; + struct omap2_mcspi *mcspi; + unsigned int wcnt; + int max_fifo_depth, fifo_depth, bytes_per_word; + u32 chconf, xferlevel; - /* McSPI: context restore */ - mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_MODULCTRL, - omap2_mcspi_ctx[spi_cntrl->bus_num - 1].modulctrl); + mcspi = spi_master_get_devdata(master); - mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_WAKEUPENABLE, - omap2_mcspi_ctx[spi_cntrl->bus_num - 1].wakeupenable); + chconf = mcspi_cached_chconf0(spi); + if (enable) { + bytes_per_word = mcspi_bytes_per_word(cs->word_len); + if (t->len % bytes_per_word != 0) + goto disable_fifo; + + if (t->rx_buf != NULL && t->tx_buf != NULL) + max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH / 2; + else + max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH; + + fifo_depth = gcd(t->len, max_fifo_depth); + if (fifo_depth < 2 || fifo_depth % bytes_per_word != 0) + goto disable_fifo; + + wcnt = t->len / bytes_per_word; + if (wcnt > OMAP2_MCSPI_MAX_FIFOWCNT) + goto disable_fifo; + + xferlevel = wcnt << 16; + if (t->rx_buf != NULL) { + chconf |= OMAP2_MCSPI_CHCONF_FFER; + xferlevel |= (fifo_depth - 1) << 8; + } + if (t->tx_buf != NULL) { + chconf |= OMAP2_MCSPI_CHCONF_FFET; + xferlevel |= fifo_depth - 1; + } - list_for_each_entry(cs, &omap2_mcspi_ctx[spi_cntrl->bus_num - 1].cs, - node) - __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); -} -static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi) -{ - pm_runtime_put_sync(mcspi->dev); + mcspi_write_reg(master, OMAP2_MCSPI_XFERLEVEL, xferlevel); + mcspi_write_chconf0(spi, chconf); + mcspi->fifo_depth = fifo_depth; + + return; + } + +disable_fifo: + if (t->rx_buf != NULL) + chconf &= ~OMAP2_MCSPI_CHCONF_FFER; + else + chconf &= ~OMAP2_MCSPI_CHCONF_FFET; + + mcspi_write_chconf0(spi, chconf); + mcspi->fifo_depth = 0; } -static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi) +static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) { - return pm_runtime_get_sync(mcspi->dev); + struct spi_master *spi_cntrl = mcspi->master; + struct omap2_mcspi_regs *ctx = &mcspi->ctx; + struct omap2_mcspi_cs *cs; + + /* McSPI: context restore */ + mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_MODULCTRL, ctx->modulctrl); + mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_WAKEUPENABLE, ctx->wakeupenable); + + list_for_each_entry(cs, &ctx->cs, node) + writel_relaxed(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); } static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) @@ -280,162 +354,285 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) unsigned long timeout; timeout = jiffies + msecs_to_jiffies(1000); - while (!(__raw_readl(reg) & bit)) { - if (time_after(jiffies, timeout)) - return -1; + while (!(readl_relaxed(reg) & bit)) { + if (time_after(jiffies, timeout)) { + if (!(readl_relaxed(reg) & bit)) + return -ETIMEDOUT; + else + return 0; + } cpu_relax(); } return 0; } -static unsigned -omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) +static void omap2_mcspi_rx_callback(void *data) +{ + struct spi_device *spi = data; + struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); + struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + + /* We must disable the DMA RX request */ + omap2_mcspi_set_dma_req(spi, 1, 0); + + complete(&mcspi_dma->dma_rx_completion); +} + +static void omap2_mcspi_tx_callback(void *data) +{ + struct spi_device *spi = data; + struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); + struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + + /* We must disable the DMA TX request */ + omap2_mcspi_set_dma_req(spi, 0, 0); + + complete(&mcspi_dma->dma_tx_completion); +} + +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, c; - unsigned long base, tx_reg, rx_reg; - int word_len, data_type, element_count; - int elements = 0; - u32 l; - u8 * rx; - const u8 * tx; - void __iomem *chstat_reg; + unsigned int count; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - l = mcspi_cached_chconf0(spi); + count = xfer->len; + + if (mcspi_dma->dma_tx) { + struct dma_async_tx_descriptor *tx; + struct scatterlist sg; + + dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); - chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; + sg_init_table(&sg, 1); + sg_dma_address(&sg) = xfer->tx_dma; + 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); + if (tx) { + tx->callback = omap2_mcspi_tx_callback; + tx->callback_param = spi; + dmaengine_submit(tx); + } else { + /* FIXME: fall back to PIO? */ + } + } + dma_async_issue_pending(mcspi_dma->dma_tx); + omap2_mcspi_set_dma_req(spi, 0, 1); + +} + +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, dma_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; - c = count; - word_len = cs->word_len; + dma_count = xfer->len; - base = cs->phys; - tx_reg = base + OMAP2_MCSPI_TX0; - rx_reg = base + OMAP2_MCSPI_RX0; - rx = xfer->rx_buf; - tx = xfer->tx_buf; + if (mcspi->fifo_depth == 0) + dma_count -= es; - if (word_len <= 8) { - data_type = OMAP_DMA_DATA_TYPE_S8; + word_len = cs->word_len; + l = mcspi_cached_chconf0(spi); + + if (word_len <= 8) element_count = count; - } else if (word_len <= 16) { - data_type = OMAP_DMA_DATA_TYPE_S16; + else if (word_len <= 16) element_count = count >> 1; - } else /* word_len <= 32 */ { - data_type = OMAP_DMA_DATA_TYPE_S32; + else /* word_len <= 32 */ element_count = count >> 2; - } - if (tx != NULL) { - omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel, - data_type, element_count, 1, - OMAP_DMA_SYNC_ELEMENT, - mcspi_dma->dma_tx_sync_dev, 0); - - omap_set_dma_dest_params(mcspi_dma->dma_tx_channel, 0, - OMAP_DMA_AMODE_CONSTANT, - tx_reg, 0, 0); - - omap_set_dma_src_params(mcspi_dma->dma_tx_channel, 0, - OMAP_DMA_AMODE_POST_INC, - xfer->tx_dma, 0, 0); - } + if (mcspi_dma->dma_rx) { + struct dma_async_tx_descriptor *tx; + struct scatterlist sg; - if (rx != NULL) { - elements = element_count - 1; - if (l & OMAP2_MCSPI_CHCONF_TURBO) - elements--; + dmaengine_slave_config(mcspi_dma->dma_rx, &cfg); - omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, - data_type, elements, 1, - OMAP_DMA_SYNC_ELEMENT, - mcspi_dma->dma_rx_sync_dev, 1); + if ((l & OMAP2_MCSPI_CHCONF_TURBO) && mcspi->fifo_depth == 0) + dma_count -= es; - omap_set_dma_src_params(mcspi_dma->dma_rx_channel, 0, - OMAP_DMA_AMODE_CONSTANT, - rx_reg, 0, 0); - - omap_set_dma_dest_params(mcspi_dma->dma_rx_channel, 0, - OMAP_DMA_AMODE_POST_INC, - xfer->rx_dma, 0, 0); - } + sg_init_table(&sg, 1); + sg_dma_address(&sg) = xfer->rx_dma; + sg_dma_len(&sg) = dma_count; - if (tx != NULL) { - omap_start_dma(mcspi_dma->dma_tx_channel); - omap2_mcspi_set_dma_req(spi, 0, 1); + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, + 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? */ + } } - if (rx != NULL) { - omap_start_dma(mcspi_dma->dma_rx_channel); - omap2_mcspi_set_dma_req(spi, 1, 1); - } + 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(&spi->dev, xfer->tx_dma, count, DMA_TO_DEVICE); + wait_for_completion(&mcspi_dma->dma_rx_completion); + dma_unmap_single(mcspi->dev, xfer->rx_dma, count, + DMA_FROM_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 (mcspi->fifo_depth > 0) + return count; - if (rx != NULL) { - wait_for_completion(&mcspi_dma->dma_rx_completion); - dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE); - omap2_mcspi_set_enable(spi, 0); + omap2_mcspi_set_enable(spi, 0); - if (l & OMAP2_MCSPI_CHCONF_TURBO) { + 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; + int bytes_per_word = mcspi_bytes_per_word(word_len); + dev_err(&spi->dev, "DMA RX penultimate word empty\n"); + count -= (bytes_per_word << 1); + omap2_mcspi_set_enable(spi, 1); + return count; + } + } + 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\n"); + count -= mcspi_bytes_per_word(word_len); + } + 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; + u32 burst; + void __iomem *chstat_reg; + void __iomem *irqstat_reg; + int wait_res; + + 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; + } + + count = xfer->len; + burst = 1; + + if (mcspi->fifo_depth > 0) { + if (count > mcspi->fifo_depth) + burst = mcspi->fifo_depth / es; + else + burst = count / es; + } + + 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 = burst; + cfg.dst_maxburst = burst; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + + if (tx != NULL) + omap2_mcspi_tx_dma(spi, xfer, cfg); + + if (rx != NULL) + count = omap2_mcspi_rx_dma(spi, xfer, cfg, es); + + if (tx != NULL) { + wait_for_completion(&mcspi_dma->dma_tx_completion); + dma_unmap_single(mcspi->dev, xfer->tx_dma, xfer->len, + DMA_TO_DEVICE); + + if (mcspi->fifo_depth > 0) { + irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS; + + if (mcspi_wait_for_reg_bit(irqstat_reg, + OMAP2_MCSPI_IRQSTATUS_EOW) < 0) + dev_err(&spi->dev, "EOW timed out\n"); + + mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQSTATUS, + OMAP2_MCSPI_IRQSTATUS_EOW); + } + + /* for TX_ONLY mode, be sure all words have shifted out */ + if (rx == NULL) { + chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; + if (mcspi->fifo_depth > 0) { + wait_res = mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXFFE); + if (wait_res < 0) + dev_err(&spi->dev, "TXFFE timed out\n"); + } else { + wait_res = mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS); + if (wait_res < 0) + dev_err(&spi->dev, "TXS timed out\n"); + } + if (wait_res >= 0 && + (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_EOT) < 0)) + dev_err(&spi->dev, "EOT timed out\n"); } - omap2_mcspi_set_enable(spi, 1); } return count; } @@ -486,7 +683,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) } dev_vdbg(&spi->dev, "write-%d %02x\n", word_len, *tx); - __raw_writel(*tx++, tx_reg); + writel_relaxed(*tx++, tx_reg); } if (rx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, @@ -498,7 +695,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) if (c == 1 && tx == NULL && (l & OMAP2_MCSPI_CHCONF_TURBO)) { omap2_mcspi_set_enable(spi, 0); - *rx++ = __raw_readl(rx_reg); + *rx++ = readl_relaxed(rx_reg); dev_vdbg(&spi->dev, "read-%d %02x\n", word_len, *(rx - 1)); if (mcspi_wait_for_reg_bit(chstat_reg, @@ -512,7 +709,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) omap2_mcspi_set_enable(spi, 0); } - *rx++ = __raw_readl(rx_reg); + *rx++ = readl_relaxed(rx_reg); dev_vdbg(&spi->dev, "read-%d %02x\n", word_len, *(rx - 1)); } @@ -533,7 +730,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) } dev_vdbg(&spi->dev, "write-%d %04x\n", word_len, *tx); - __raw_writel(*tx++, tx_reg); + writel_relaxed(*tx++, tx_reg); } if (rx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, @@ -545,7 +742,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) if (c == 2 && tx == NULL && (l & OMAP2_MCSPI_CHCONF_TURBO)) { omap2_mcspi_set_enable(spi, 0); - *rx++ = __raw_readl(rx_reg); + *rx++ = readl_relaxed(rx_reg); dev_vdbg(&spi->dev, "read-%d %04x\n", word_len, *(rx - 1)); if (mcspi_wait_for_reg_bit(chstat_reg, @@ -559,7 +756,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) omap2_mcspi_set_enable(spi, 0); } - *rx++ = __raw_readl(rx_reg); + *rx++ = readl_relaxed(rx_reg); dev_vdbg(&spi->dev, "read-%d %04x\n", word_len, *(rx - 1)); } @@ -580,7 +777,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) } dev_vdbg(&spi->dev, "write-%d %08x\n", word_len, *tx); - __raw_writel(*tx++, tx_reg); + writel_relaxed(*tx++, tx_reg); } if (rx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, @@ -592,7 +789,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) if (c == 4 && tx == NULL && (l & OMAP2_MCSPI_CHCONF_TURBO)) { omap2_mcspi_set_enable(spi, 0); - *rx++ = __raw_readl(rx_reg); + *rx++ = readl_relaxed(rx_reg); dev_vdbg(&spi->dev, "read-%d %08x\n", word_len, *(rx - 1)); if (mcspi_wait_for_reg_bit(chstat_reg, @@ -606,7 +803,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) omap2_mcspi_set_enable(spi, 0); } - *rx++ = __raw_readl(rx_reg); + *rx++ = readl_relaxed(rx_reg); dev_vdbg(&spi->dev, "read-%d %08x\n", word_len, *(rx - 1)); } @@ -651,7 +848,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi *mcspi; struct spi_master *spi_cntrl; - u32 l = 0, div = 0; + u32 l = 0, clkd = 0, div, extclk = 0, clkg = 0; u8 word_len = spi->bits_per_word; u32 speed_hz = spi->max_speed_hz; @@ -667,15 +864,32 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, speed_hz = t->speed_hz; speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ); - div = omap2_mcspi_calc_divisor(speed_hz); + if (speed_hz < (OMAP2_MCSPI_MAX_FREQ / OMAP2_MCSPI_MAX_DIVIDER)) { + clkd = omap2_mcspi_calc_divisor(speed_hz); + speed_hz = OMAP2_MCSPI_MAX_FREQ >> clkd; + clkg = 0; + } else { + div = (OMAP2_MCSPI_MAX_FREQ + speed_hz - 1) / speed_hz; + speed_hz = OMAP2_MCSPI_MAX_FREQ / div; + clkd = (div - 1) & 0xf; + extclk = (div - 1) >> 4; + clkg = OMAP2_MCSPI_CHCONF_CLKG; + } l = mcspi_cached_chconf0(spi); /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. */ - l &= ~(OMAP2_MCSPI_CHCONF_IS|OMAP2_MCSPI_CHCONF_DPE1); - l |= OMAP2_MCSPI_CHCONF_DPE0; + if (mcspi->pin_dir == MCSPI_PINDIR_D0_IN_D1_OUT) { + l &= ~OMAP2_MCSPI_CHCONF_IS; + l &= ~OMAP2_MCSPI_CHCONF_DPE1; + l |= OMAP2_MCSPI_CHCONF_DPE0; + } else { + l |= OMAP2_MCSPI_CHCONF_IS; + l |= OMAP2_MCSPI_CHCONF_DPE1; + l &= ~OMAP2_MCSPI_CHCONF_DPE0; + } /* wordlength */ l &= ~OMAP2_MCSPI_CHCONF_WL_MASK; @@ -689,7 +903,16 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, /* set clock divisor */ l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK; - l |= div << 2; + l |= clkd << 2; + + /* set clock granularity */ + l &= ~OMAP2_MCSPI_CHCONF_CLKG; + l |= clkg; + if (clkg) { + cs->chctrl0 &= ~OMAP2_MCSPI_CHCTRL_EXTCLK_MASK; + cs->chctrl0 |= extclk << 8; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, cs->chctrl0); + } /* set SPI mode 0..3 */ if (spi->mode & SPI_CPOL) @@ -704,88 +927,69 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, mcspi_write_chconf0(spi, l); dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", - OMAP2_MCSPI_MAX_FREQ >> div, + speed_hz, (spi->mode & SPI_CPHA) ? "trailing" : "leading", (spi->mode & SPI_CPOL) ? "inverted" : "normal"); return 0; } -static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data) -{ - struct spi_device *spi = data; - struct omap2_mcspi *mcspi; - struct omap2_mcspi_dma *mcspi_dma; - - mcspi = spi_master_get_devdata(spi->master); - mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); - - complete(&mcspi_dma->dma_rx_completion); - - /* We must disable the DMA RX request */ - omap2_mcspi_set_dma_req(spi, 1, 0); -} - -static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data) -{ - struct spi_device *spi = data; - struct omap2_mcspi *mcspi; - struct omap2_mcspi_dma *mcspi_dma; - - mcspi = spi_master_get_devdata(spi->master); - mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); - - complete(&mcspi_dma->dma_tx_completion); - - /* We must disable the DMA TX request */ - omap2_mcspi_set_dma_req(spi, 0, 0); -} - +/* + * Note that we currently allow DMA only if we get a channel + * for both rx and tx. Otherwise we'll do PIO for both rx and tx. + */ static int omap2_mcspi_request_dma(struct spi_device *spi) { struct spi_master *master = spi->master; struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; + dma_cap_mask_t mask; + unsigned sig; mcspi = spi_master_get_devdata(master); mcspi_dma = mcspi->dma_channels + spi->chip_select; - if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX", - omap2_mcspi_dma_rx_callback, spi, - &mcspi_dma->dma_rx_channel)) { - dev_err(&spi->dev, "no RX DMA channel for McSPI\n"); - return -EAGAIN; - } - - if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX", - omap2_mcspi_dma_tx_callback, spi, - &mcspi_dma->dma_tx_channel)) { - omap_free_dma(mcspi_dma->dma_rx_channel); - mcspi_dma->dma_rx_channel = -1; - dev_err(&spi->dev, "no TX DMA channel for McSPI\n"); - return -EAGAIN; - } - init_completion(&mcspi_dma->dma_rx_completion); init_completion(&mcspi_dma->dma_tx_completion); + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + sig = mcspi_dma->dma_rx_sync_dev; + + mcspi_dma->dma_rx = + dma_request_slave_channel_compat(mask, omap_dma_filter_fn, + &sig, &master->dev, + mcspi_dma->dma_rx_ch_name); + if (!mcspi_dma->dma_rx) + goto no_dma; + + sig = mcspi_dma->dma_tx_sync_dev; + mcspi_dma->dma_tx = + dma_request_slave_channel_compat(mask, omap_dma_filter_fn, + &sig, &master->dev, + mcspi_dma->dma_tx_ch_name); + + if (!mcspi_dma->dma_tx) { + dma_release_channel(mcspi_dma->dma_rx); + mcspi_dma->dma_rx = NULL; + goto no_dma; + } + return 0; + +no_dma: + dev_warn(&spi->dev, "not using DMA for McSPI\n"); + return -EAGAIN; } static int omap2_mcspi_setup(struct spi_device *spi) { int ret; - struct omap2_mcspi *mcspi; + struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); + struct omap2_mcspi_regs *ctx = &mcspi->ctx; struct omap2_mcspi_dma *mcspi_dma; struct omap2_mcspi_cs *cs = spi->controller_state; - if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { - dev_dbg(&spi->dev, "setup: unsupported %d bit words\n", - spi->bits_per_word); - return -EINVAL; - } - - mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; if (!cs) { @@ -795,25 +999,25 @@ static int omap2_mcspi_setup(struct spi_device *spi) cs->base = mcspi->base + spi->chip_select * 0x14; cs->phys = mcspi->phys + spi->chip_select * 0x14; cs->chconf0 = 0; + cs->chctrl0 = 0; spi->controller_state = cs; /* Link this to context save list */ - list_add_tail(&cs->node, - &omap2_mcspi_ctx[mcspi->master->bus_num - 1].cs); + list_add_tail(&cs->node, &ctx->cs); } - if (mcspi_dma->dma_rx_channel == -1 - || mcspi_dma->dma_tx_channel == -1) { + if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) { ret = omap2_mcspi_request_dma(spi); - if (ret < 0) + if (ret < 0 && ret != -EAGAIN) 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; } @@ -831,173 +1035,185 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) cs = spi->controller_state; list_del(&cs->node); - kfree(spi->controller_state); + kfree(cs); } if (spi->chip_select < spi->master->num_chipselect) { mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - if (mcspi_dma->dma_rx_channel != -1) { - omap_free_dma(mcspi_dma->dma_rx_channel); - mcspi_dma->dma_rx_channel = -1; + if (mcspi_dma->dma_rx) { + dma_release_channel(mcspi_dma->dma_rx); + mcspi_dma->dma_rx = NULL; } - if (mcspi_dma->dma_tx_channel != -1) { - omap_free_dma(mcspi_dma->dma_tx_channel); - mcspi_dma->dma_tx_channel = -1; + if (mcspi_dma->dma_tx) { + dma_release_channel(mcspi_dma->dma_tx); + mcspi_dma->dma_tx = NULL; } } } -static void omap2_mcspi_work(struct work_struct *work) +static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) { - struct omap2_mcspi *mcspi; - - mcspi = container_of(work, struct omap2_mcspi, work); - - if (omap2_mcspi_enable_clocks(mcspi) < 0) - return; - - spin_lock_irq(&mcspi->lock); /* We only enable one channel at a time -- the one whose message is - * at the head of the queue -- although this controller would gladly + * -- although this controller would gladly * arbitrate among multiple channels. This corresponds to "single * channel" master mode. As a side effect, we need to manage the * chipselect with the FORCE bit ... CS != channel enable. */ - while (!list_empty(&mcspi->msg_queue)) { - struct spi_message *m; - struct spi_device *spi; - struct spi_transfer *t = NULL; - int cs_active = 0; - struct omap2_mcspi_cs *cs; - struct omap2_mcspi_device_config *cd; - int par_override = 0; - int status = 0; - u32 chconf; - - m = container_of(mcspi->msg_queue.next, struct spi_message, - queue); - - list_del_init(&m->queue); - spin_unlock_irq(&mcspi->lock); - - spi = m->spi; - cs = spi->controller_state; - cd = spi->controller_data; - omap2_mcspi_set_enable(spi, 1); - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { - status = -EINVAL; + struct spi_device *spi; + struct spi_transfer *t = NULL; + struct spi_master *master; + struct omap2_mcspi_dma *mcspi_dma; + int cs_active = 0; + struct omap2_mcspi_cs *cs; + struct omap2_mcspi_device_config *cd; + int par_override = 0; + int status = 0; + u32 chconf; + + spi = m->spi; + master = spi->master; + mcspi_dma = mcspi->dma_channels + spi->chip_select; + cs = spi->controller_state; + cd = spi->controller_data; + + omap2_mcspi_set_enable(spi, 0); + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { + status = -EINVAL; + break; + } + if (par_override || + (t->speed_hz != spi->max_speed_hz) || + (t->bits_per_word != spi->bits_per_word)) { + par_override = 1; + status = omap2_mcspi_setup_transfer(spi, t); + if (status < 0) break; - } - if (par_override || t->speed_hz || t->bits_per_word) { - par_override = 1; - status = omap2_mcspi_setup_transfer(spi, t); - if (status < 0) - break; - if (!t->speed_hz && !t->bits_per_word) - par_override = 0; - } + if (t->speed_hz == spi->max_speed_hz && + t->bits_per_word == spi->bits_per_word) + par_override = 0; + } + if (cd && cd->cs_per_word) { + chconf = mcspi->ctx.modulctrl; + chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE; + mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf); + mcspi->ctx.modulctrl = + mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL); + } - if (!cs_active) { - omap2_mcspi_force_cs(spi, 1); - cs_active = 1; - } - chconf = mcspi_cached_chconf0(spi); - chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; - chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; + if (!cs_active) { + omap2_mcspi_force_cs(spi, 1); + cs_active = 1; + } - if (t->tx_buf == NULL) - chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; - else if (t->rx_buf == NULL) - chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; - - if (cd && cd->turbo_mode && t->tx_buf == NULL) { - /* Turbo mode is for more than one word */ - if (t->len > ((cs->word_len + 7) >> 3)) - chconf |= OMAP2_MCSPI_CHCONF_TURBO; - } + chconf = mcspi_cached_chconf0(spi); + chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; + chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; - mcspi_write_chconf0(spi, chconf); + if (t->tx_buf == NULL) + chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; + else if (t->rx_buf == NULL) + chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; - if (t->len) { - unsigned count; + if (cd && cd->turbo_mode && t->tx_buf == NULL) { + /* Turbo mode is for more than one word */ + if (t->len > ((cs->word_len + 7) >> 3)) + chconf |= OMAP2_MCSPI_CHCONF_TURBO; + } - /* RX_ONLY mode needs dummy data in TX reg */ - if (t->tx_buf == NULL) - __raw_writel(0, cs->base - + OMAP2_MCSPI_TX0); + mcspi_write_chconf0(spi, chconf); - if (m->is_dma_mapped || t->len >= DMA_MIN_BYTES) - count = omap2_mcspi_txrx_dma(spi, t); - else - count = omap2_mcspi_txrx_pio(spi, t); - m->actual_length += count; + if (t->len) { + unsigned count; - if (count != t->len) { - status = -EIO; - break; - } - } + if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && + (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)) + omap2_mcspi_set_fifo(spi, t, 1); - if (t->delay_usecs) - udelay(t->delay_usecs); + omap2_mcspi_set_enable(spi, 1); - /* ignore the "leave it on after last xfer" hint */ - if (t->cs_change) { - omap2_mcspi_force_cs(spi, 0); - cs_active = 0; + /* RX_ONLY mode needs dummy data in TX reg */ + if (t->tx_buf == NULL) + writel_relaxed(0, cs->base + + OMAP2_MCSPI_TX0); + + if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && + (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)) + count = omap2_mcspi_txrx_dma(spi, t); + else + count = omap2_mcspi_txrx_pio(spi, t); + m->actual_length += count; + + if (count != t->len) { + status = -EIO; + break; } } - /* Restore defaults if they were overriden */ - if (par_override) { - par_override = 0; - status = omap2_mcspi_setup_transfer(spi, NULL); - } + if (t->delay_usecs) + udelay(t->delay_usecs); - if (cs_active) + /* ignore the "leave it on after last xfer" hint */ + if (t->cs_change) { omap2_mcspi_force_cs(spi, 0); + cs_active = 0; + } omap2_mcspi_set_enable(spi, 0); - m->status = status; - m->complete(m->context); + if (mcspi->fifo_depth > 0) + omap2_mcspi_set_fifo(spi, t, 0); + } + /* Restore defaults if they were overriden */ + if (par_override) { + par_override = 0; + status = omap2_mcspi_setup_transfer(spi, NULL); + } + + if (cs_active) + omap2_mcspi_force_cs(spi, 0); - spin_lock_irq(&mcspi->lock); + if (cd && cd->cs_per_word) { + chconf = mcspi->ctx.modulctrl; + chconf |= OMAP2_MCSPI_MODULCTRL_SINGLE; + mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf); + mcspi->ctx.modulctrl = + mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL); } - spin_unlock_irq(&mcspi->lock); + omap2_mcspi_set_enable(spi, 0); - omap2_mcspi_disable_clocks(mcspi); + if (mcspi->fifo_depth > 0 && t) + omap2_mcspi_set_fifo(spi, t, 0); + + m->status = status; } -static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) +static int omap2_mcspi_transfer_one_message(struct spi_master *master, + struct spi_message *m) { + struct spi_device *spi; struct omap2_mcspi *mcspi; - unsigned long flags; + struct omap2_mcspi_dma *mcspi_dma; struct spi_transfer *t; + spi = m->spi; + mcspi = spi_master_get_devdata(master); + mcspi_dma = mcspi->dma_channels + spi->chip_select; m->actual_length = 0; m->status = 0; - /* reject invalid messages and transfers */ - if (list_empty(&m->transfers) || !m->complete) - return -EINVAL; list_for_each_entry(t, &m->transfers, transfer_list) { const void *tx_buf = t->tx_buf; void *rx_buf = t->rx_buf; unsigned len = t->len; - if (t->speed_hz > OMAP2_MCSPI_MAX_FREQ - || (len && !(rx_buf || tx_buf)) - || (t->bits_per_word && - ( t->bits_per_word < 4 - || t->bits_per_word > 32))) { - dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", + if ((len && !(rx_buf || tx_buf))) { + dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", t->speed_hz, len, tx_buf ? "tx" : "", @@ -1005,65 +1221,55 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) t->bits_per_word); return -EINVAL; } - if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) { - dev_dbg(&spi->dev, "speed_hz %d below minimum %d Hz\n", - t->speed_hz, - OMAP2_MCSPI_MAX_FREQ >> 15); - return -EINVAL; - } if (m->is_dma_mapped || len < DMA_MIN_BYTES) continue; - if (tx_buf != NULL) { - t->tx_dma = dma_map_single(&spi->dev, (void *) tx_buf, + if (mcspi_dma->dma_tx && tx_buf != NULL) { + t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf, len, DMA_TO_DEVICE); - if (dma_mapping_error(&spi->dev, t->tx_dma)) { - dev_dbg(&spi->dev, "dma %cX %d bytes error\n", + if (dma_mapping_error(mcspi->dev, t->tx_dma)) { + dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", 'T', len); return -EINVAL; } } - if (rx_buf != NULL) { - t->rx_dma = dma_map_single(&spi->dev, rx_buf, t->len, + if (mcspi_dma->dma_rx && rx_buf != NULL) { + t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len, DMA_FROM_DEVICE); - if (dma_mapping_error(&spi->dev, t->rx_dma)) { - dev_dbg(&spi->dev, "dma %cX %d bytes error\n", + if (dma_mapping_error(mcspi->dev, t->rx_dma)) { + dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", 'R', len); if (tx_buf != NULL) - dma_unmap_single(&spi->dev, t->tx_dma, + dma_unmap_single(mcspi->dev, t->tx_dma, len, DMA_TO_DEVICE); return -EINVAL; } } } - mcspi = spi_master_get_devdata(spi->master); - - spin_lock_irqsave(&mcspi->lock, flags); - list_add_tail(&m->queue, &mcspi->msg_queue); - queue_work(mcspi->wq, &mcspi->work); - spin_unlock_irqrestore(&mcspi->lock, flags); - + omap2_mcspi_work(mcspi, m); + spi_finalize_current_message(master); return 0; } -static int __init omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) +static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) { struct spi_master *master = mcspi->master; - u32 tmp; - int ret = 0; + 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; - tmp = OMAP2_MCSPI_WAKEUPENABLE_WKEN; - mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, tmp); - omap2_mcspi_ctx[master->bus_num - 1].wakeupenable = tmp; + mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, + OMAP2_MCSPI_WAKEUPENABLE_WKEN); + 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; } @@ -1079,15 +1285,38 @@ static int omap_mcspi_runtime_resume(struct device *dev) return 0; } +static struct omap2_mcspi_platform_config omap2_pdata = { + .regs_offset = 0, +}; + +static struct omap2_mcspi_platform_config omap4_pdata = { + .regs_offset = OMAP4_MCSPI_REG_OFFSET, +}; -static int __init omap2_mcspi_probe(struct platform_device *pdev) +static const struct of_device_id omap_mcspi_of_match[] = { + { + .compatible = "ti,omap2-mcspi", + .data = &omap2_pdata, + }, + { + .compatible = "ti,omap4-mcspi", + .data = &omap4_pdata, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, omap_mcspi_of_match); + +static int omap2_mcspi_probe(struct platform_device *pdev) { struct spi_master *master; - struct omap2_mcspi_platform_config *pdata = pdev->dev.platform_data; + const struct omap2_mcspi_platform_config *pdata; struct omap2_mcspi *mcspi; struct resource *r; int status = 0, i; - char wq_name[20]; + u32 regs_offset = 0; + static int bus_num = 1; + struct device_node *node = pdev->dev.of_node; + const struct of_device_id *match; master = spi_alloc_master(&pdev->dev, sizeof *mcspi); if (master == NULL) { @@ -1097,26 +1326,38 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - - if (pdev->id != -1) - master->bus_num = pdev->id; - + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = omap2_mcspi_setup; - master->transfer = omap2_mcspi_transfer; + master->auto_runtime_pm = true; + master->transfer_one_message = omap2_mcspi_transfer_one_message; master->cleanup = omap2_mcspi_cleanup; - master->num_chipselect = pdata->num_cs; + master->dev.of_node = node; + master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ; + master->min_speed_hz = OMAP2_MCSPI_MAX_FREQ >> 15; - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); mcspi = spi_master_get_devdata(master); mcspi->master = master; - sprintf(wq_name, "omap2_mcspi/%d", master->bus_num); - mcspi->wq = alloc_workqueue(wq_name, WQ_MEM_RECLAIM, 1); - if (mcspi->wq == NULL) { - status = -ENOMEM; - goto free_master; + match = of_match_device(omap_mcspi_of_match, &pdev->dev); + if (match) { + u32 num_cs = 1; /* default number of chipselect */ + pdata = match->data; + + of_property_read_u32(node, "ti,spi-num-cs", &num_cs); + master->num_chipselect = num_cs; + master->bus_num = bus_num++; + if (of_get_property(node, "ti,pindir-d0-out-d1-in", NULL)) + mcspi->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN; + } else { + pdata = dev_get_platdata(&pdev->dev); + master->num_chipselect = pdata->num_cs; + if (pdev->id != -1) + master->bus_num = pdev->id; + mcspi->pin_dir = pdata->pin_dir; } + regs_offset = pdata->regs_offset; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { @@ -1124,117 +1365,98 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) goto free_master; } - r->start += pdata->regs_offset; - r->end += pdata->regs_offset; + r->start += regs_offset; + r->end += regs_offset; mcspi->phys = r->start; - if (!request_mem_region(r->start, resource_size(r), - dev_name(&pdev->dev))) { - status = -EBUSY; - goto free_master; - } - mcspi->base = ioremap(r->start, resource_size(r)); - if (!mcspi->base) { - dev_dbg(&pdev->dev, "can't ioremap MCSPI\n"); - status = -ENOMEM; - goto release_region; + mcspi->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(mcspi->base)) { + status = PTR_ERR(mcspi->base); + goto free_master; } mcspi->dev = &pdev->dev; - INIT_WORK(&mcspi->work, omap2_mcspi_work); - - spin_lock_init(&mcspi->lock); - INIT_LIST_HEAD(&mcspi->msg_queue); - INIT_LIST_HEAD(&omap2_mcspi_ctx[master->bus_num - 1].cs); - mcspi->dma_channels = kcalloc(master->num_chipselect, - sizeof(struct omap2_mcspi_dma), - GFP_KERNEL); + INIT_LIST_HEAD(&mcspi->ctx.cs); - if (mcspi->dma_channels == NULL) - goto unmap_io; + mcspi->dma_channels = devm_kcalloc(&pdev->dev, master->num_chipselect, + sizeof(struct omap2_mcspi_dma), + GFP_KERNEL); + if (mcspi->dma_channels == NULL) { + status = -ENOMEM; + goto free_master; + } for (i = 0; i < master->num_chipselect; i++) { - char dma_ch_name[14]; + char *dma_rx_ch_name = mcspi->dma_channels[i].dma_rx_ch_name; + char *dma_tx_ch_name = mcspi->dma_channels[i].dma_tx_ch_name; struct resource *dma_res; - sprintf(dma_ch_name, "rx%d", i); - dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, - dma_ch_name); - if (!dma_res) { - dev_dbg(&pdev->dev, "cannot get DMA RX channel\n"); - status = -ENODEV; - break; - } + sprintf(dma_rx_ch_name, "rx%d", i); + if (!pdev->dev.of_node) { + dma_res = + platform_get_resource_byname(pdev, + IORESOURCE_DMA, + dma_rx_ch_name); + if (!dma_res) { + dev_dbg(&pdev->dev, + "cannot get DMA RX channel\n"); + status = -ENODEV; + break; + } - mcspi->dma_channels[i].dma_rx_channel = -1; - mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start; - sprintf(dma_ch_name, "tx%d", i); - dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, - dma_ch_name); - if (!dma_res) { - dev_dbg(&pdev->dev, "cannot get DMA TX channel\n"); - status = -ENODEV; - break; + mcspi->dma_channels[i].dma_rx_sync_dev = + dma_res->start; } + sprintf(dma_tx_ch_name, "tx%d", i); + if (!pdev->dev.of_node) { + dma_res = + platform_get_resource_byname(pdev, + IORESOURCE_DMA, + dma_tx_ch_name); + if (!dma_res) { + dev_dbg(&pdev->dev, + "cannot get DMA TX channel\n"); + status = -ENODEV; + break; + } - mcspi->dma_channels[i].dma_tx_channel = -1; - mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start; + mcspi->dma_channels[i].dma_tx_sync_dev = + dma_res->start; + } } if (status < 0) - goto dma_chnl_free; + goto free_master; + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); pm_runtime_enable(&pdev->dev); - if (status || omap2_mcspi_master_setup(mcspi) < 0) + status = omap2_mcspi_master_setup(mcspi); + if (status < 0) goto disable_pm; - status = spi_register_master(master); + status = devm_spi_register_master(&pdev->dev, 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); -unmap_io: - iounmap(mcspi->base); -release_region: - release_mem_region(r->start, resource_size(r)); free_master: - kfree(master); - platform_set_drvdata(pdev, NULL); + spi_master_put(master); return status; } -static int __exit omap2_mcspi_remove(struct platform_device *pdev) +static int omap2_mcspi_remove(struct platform_device *pdev) { - struct spi_master *master; - struct omap2_mcspi *mcspi; - struct omap2_mcspi_dma *dma_channels; - struct resource *r; - void __iomem *base; - - master = dev_get_drvdata(&pdev->dev); - mcspi = spi_master_get_devdata(master); - dma_channels = mcspi->dma_channels; + struct spi_master *master = platform_get_drvdata(pdev); + struct omap2_mcspi *mcspi = spi_master_get_devdata(master); - omap2_mcspi_disable_clocks(mcspi); + pm_runtime_put_sync(mcspi->dev); pm_runtime_disable(&pdev->dev); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); - - base = mcspi->base; - spi_unregister_master(master); - iounmap(base); - kfree(dma_channels); - destroy_workqueue(mcspi->wq); - platform_set_drvdata(pdev, NULL); return 0; } @@ -1252,24 +1474,24 @@ static int omap2_mcspi_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct omap2_mcspi *mcspi = spi_master_get_devdata(master); - struct omap2_mcspi_cs *cs; + struct omap2_mcspi_regs *ctx = &mcspi->ctx; + struct omap2_mcspi_cs *cs; - omap2_mcspi_enable_clocks(mcspi); - list_for_each_entry(cs, &omap2_mcspi_ctx[master->bus_num - 1].cs, - node) { + 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); - __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); - MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 0); - __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); + cs->chconf0 |= OMAP2_MCSPI_CHCONF_FORCE; + writel_relaxed(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); + cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE; + writel_relaxed(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 @@ -1285,23 +1507,12 @@ static struct platform_driver omap2_mcspi_driver = { .driver = { .name = "omap2_mcspi", .owner = THIS_MODULE, - .pm = &omap2_mcspi_pm_ops + .pm = &omap2_mcspi_pm_ops, + .of_match_table = omap_mcspi_of_match, }, - .remove = __exit_p(omap2_mcspi_remove), + .probe = omap2_mcspi_probe, + .remove = omap2_mcspi_remove, }; - -static int __init omap2_mcspi_init(void) -{ - return platform_driver_probe(&omap2_mcspi_driver, omap2_mcspi_probe); -} -subsys_initcall(omap2_mcspi_init); - -static void __exit omap2_mcspi_exit(void) -{ - platform_driver_unregister(&omap2_mcspi_driver); - -} -module_exit(omap2_mcspi_exit); - +module_platform_driver(omap2_mcspi_driver); MODULE_LICENSE("GPL"); |
