/*
* OMAP2 McSPI controller driver
*
* Copyright (C) 2005, 2006 Nokia Corporation
* Author: Samuel Ortiz <samuel.ortiz@nokia.com> and
* Juha Yrj�l� <juha.yrjola@nokia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.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/pinctrl/consumer.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 <linux/gpio.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
#define OMAP2_MCSPI_MAX_FREQ 48000000
#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
#define OMAP2_MCSPI_IRQSTATUS 0x18
#define OMAP2_MCSPI_IRQENABLE 0x1c
#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
#define OMAP2_MCSPI_CHSTAT0 0x30
#define OMAP2_MCSPI_CHCTRL0 0x34
#define OMAP2_MCSPI_TX0 0x38
#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)
#define OMAP2_MCSPI_MODULCTRL_STEST BIT(3)
#define OMAP2_MCSPI_CHCONF_PHA BIT(0)
#define OMAP2_MCSPI_CHCONF_POL BIT(1)
#define OMAP2_MCSPI_CHCONF_CLKD_MASK (0x0f << 2)
#define OMAP2_MCSPI_CHCONF_EPOL BIT(6)
#define OMAP2_MCSPI_CHCONF_WL_MASK (0x1f << 7)
#define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY BIT(12)
#define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY BIT(13)
#define OMAP2_MCSPI_CHCONF_TRM_MASK (0x03 << 12)
#define OMAP2_MCSPI_CHCONF_DMAW BIT(14)
#define OMAP2_MCSPI_CHCONF_DMAR BIT(15)
#define OMAP2_MCSPI_CHCONF_DPE0 BIT(16)
#define OMAP2_MCSPI_CHCONF_DPE1 BIT(17)
#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 {
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
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
* cache operations; better heuristics consider wordsize and bitrate.
*/
#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 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 omap2_mcspi_regs ctx;
int fifo_depth;
unsigned int pin_dir:1;
};
struct omap2_mcspi_cs {
void __iomem *base;
unsigned long phys;
int word_len;
u16 mode;
struct list_head node;
/* Context save and restore shadow register */
u32 chconf0, chctrl0;
};
static inline void mcspi_write_reg(struct spi_master *master,
int idx, u32 val)
{
struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
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 readl_relaxed(mcspi->base + idx);
}
static inline void mcspi_write_cs_reg(const struct spi_device *spi,
int idx, u32 val)
{
struct omap2_mcspi_cs *cs = spi->controller_state;
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 readl_relaxed(cs->base + idx);
}
static inline u32 mcspi_cached_chconf0(const struct spi_device *spi)
{
struct omap2_mcspi_cs *cs = spi->controller_state;
return cs->chconf0;
}
static inline void mcspi_write_chconf0(const struct spi_device *spi, u32 val)
{
struct omap2_mcspi_cs *cs = spi->controller_state;
cs->chconf0 = val;
mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, 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)
{
u32 l, rw;
l = mcspi_cached_chconf0(spi);
if (is_read) /* 1 is read, 0 write */
rw = OMAP2_MCSPI_CHCONF_DMAR;
else
rw = OMAP2_MCSPI_CHCONF_DMAW;
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 = 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);
}
static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
{
struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
u32 l;
/* The controller handles the inverted chip selects
* using the OMAP2_MCSPI_CHCONF_EPOL bit so revert
* the inversion from the core spi_set_cs function.
*/
if (spi->mode & SPI_CS_HIGH)
enable = !enable;
if (spi->controller_state) {
int err = pm_runtime_get_sync(mcspi->dev);
if (err < 0) {
dev_err(mcspi->dev, "failed to get sync: %d\n", err);
return;
}
l = mcspi_cached_chconf0(spi);
if (enable)
l &= ~OMAP2_MCSPI_CHCONF_FORCE;
else
l |= OMAP2_MCSPI_CHCONF_FORCE;
mcspi_write_chconf0(spi, l);
pm_runtime_mark_last_busy(mcspi->dev);
pm_runtime_put_autosuspend(mcspi->dev);
}
}
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 swi
|