mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-05 00:20:32 +00:00
Merge remote-tracking branch 'spi/topic/img-spfi' into spi-next
This commit is contained in:
commit
19655dd083
2 changed files with 101 additions and 83 deletions
|
@ -14,6 +14,7 @@ Required properties:
|
||||||
- dma-names: Must include the following entries:
|
- dma-names: Must include the following entries:
|
||||||
- rx
|
- rx
|
||||||
- tx
|
- tx
|
||||||
|
- cs-gpios: Must specify the GPIOs used for chipselect lines.
|
||||||
- #address-cells: Must be 1.
|
- #address-cells: Must be 1.
|
||||||
- #size-cells: Must be 0.
|
- #size-cells: Must be 0.
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
@ -122,36 +123,31 @@ static inline void spfi_start(struct img_spfi *spfi)
|
||||||
spfi_writel(spfi, val, SPFI_CONTROL);
|
spfi_writel(spfi, val, SPFI_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void spfi_stop(struct img_spfi *spfi)
|
|
||||||
{
|
|
||||||
u32 val;
|
|
||||||
|
|
||||||
val = spfi_readl(spfi, SPFI_CONTROL);
|
|
||||||
val &= ~SPFI_CONTROL_SPFI_EN;
|
|
||||||
spfi_writel(spfi, val, SPFI_CONTROL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void spfi_reset(struct img_spfi *spfi)
|
static inline void spfi_reset(struct img_spfi *spfi)
|
||||||
{
|
{
|
||||||
spfi_writel(spfi, SPFI_CONTROL_SOFT_RESET, SPFI_CONTROL);
|
spfi_writel(spfi, SPFI_CONTROL_SOFT_RESET, SPFI_CONTROL);
|
||||||
udelay(1);
|
|
||||||
spfi_writel(spfi, 0, SPFI_CONTROL);
|
spfi_writel(spfi, 0, SPFI_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spfi_flush_tx_fifo(struct img_spfi *spfi)
|
static int spfi_wait_all_done(struct img_spfi *spfi)
|
||||||
{
|
{
|
||||||
unsigned long timeout = jiffies + msecs_to_jiffies(10);
|
unsigned long timeout = jiffies + msecs_to_jiffies(50);
|
||||||
|
|
||||||
spfi_writel(spfi, SPFI_INTERRUPT_SDE, SPFI_INTERRUPT_CLEAR);
|
|
||||||
while (time_before(jiffies, timeout)) {
|
while (time_before(jiffies, timeout)) {
|
||||||
if (spfi_readl(spfi, SPFI_INTERRUPT_STATUS) &
|
u32 status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS);
|
||||||
SPFI_INTERRUPT_SDE)
|
|
||||||
return;
|
if (status & SPFI_INTERRUPT_ALLDONETRIG) {
|
||||||
|
spfi_writel(spfi, SPFI_INTERRUPT_ALLDONETRIG,
|
||||||
|
SPFI_INTERRUPT_CLEAR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_err(spfi->dev, "Timed out waiting for FIFO to drain\n");
|
dev_err(spfi->dev, "Timed out waiting for transaction to complete\n");
|
||||||
spfi_reset(spfi);
|
spfi_reset(spfi);
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int spfi_pio_write32(struct img_spfi *spfi, const u32 *buf,
|
static unsigned int spfi_pio_write32(struct img_spfi *spfi, const u32 *buf,
|
||||||
|
@ -237,6 +233,7 @@ static int img_spfi_start_pio(struct spi_master *master,
|
||||||
const void *tx_buf = xfer->tx_buf;
|
const void *tx_buf = xfer->tx_buf;
|
||||||
void *rx_buf = xfer->rx_buf;
|
void *rx_buf = xfer->rx_buf;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (tx_buf)
|
if (tx_buf)
|
||||||
tx_bytes = xfer->len;
|
tx_bytes = xfer->len;
|
||||||
|
@ -269,16 +266,15 @@ static int img_spfi_start_pio(struct spi_master *master,
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = spfi_wait_all_done(spfi);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (rx_bytes > 0 || tx_bytes > 0) {
|
if (rx_bytes > 0 || tx_bytes > 0) {
|
||||||
dev_err(spfi->dev, "PIO transfer timed out\n");
|
dev_err(spfi->dev, "PIO transfer timed out\n");
|
||||||
spfi_reset(spfi);
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx_buf)
|
|
||||||
spfi_flush_tx_fifo(spfi);
|
|
||||||
spfi_stop(spfi);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,14 +283,12 @@ static void img_spfi_dma_rx_cb(void *data)
|
||||||
struct img_spfi *spfi = data;
|
struct img_spfi *spfi = data;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
spfi_wait_all_done(spfi);
|
||||||
|
|
||||||
spin_lock_irqsave(&spfi->lock, flags);
|
spin_lock_irqsave(&spfi->lock, flags);
|
||||||
|
|
||||||
spfi->rx_dma_busy = false;
|
spfi->rx_dma_busy = false;
|
||||||
if (!spfi->tx_dma_busy) {
|
if (!spfi->tx_dma_busy)
|
||||||
spfi_stop(spfi);
|
|
||||||
spi_finalize_current_transfer(spfi->master);
|
spi_finalize_current_transfer(spfi->master);
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&spfi->lock, flags);
|
spin_unlock_irqrestore(&spfi->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,16 +297,12 @@ static void img_spfi_dma_tx_cb(void *data)
|
||||||
struct img_spfi *spfi = data;
|
struct img_spfi *spfi = data;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spfi_flush_tx_fifo(spfi);
|
spfi_wait_all_done(spfi);
|
||||||
|
|
||||||
spin_lock_irqsave(&spfi->lock, flags);
|
spin_lock_irqsave(&spfi->lock, flags);
|
||||||
|
|
||||||
spfi->tx_dma_busy = false;
|
spfi->tx_dma_busy = false;
|
||||||
if (!spfi->rx_dma_busy) {
|
if (!spfi->rx_dma_busy)
|
||||||
spfi_stop(spfi);
|
|
||||||
spi_finalize_current_transfer(spfi->master);
|
spi_finalize_current_transfer(spfi->master);
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&spfi->lock, flags);
|
spin_unlock_irqrestore(&spfi->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,6 +387,75 @@ static int img_spfi_start_dma(struct spi_master *master,
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void img_spfi_handle_err(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct img_spfi *spfi = spi_master_get_devdata(master);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop all DMA and reset the controller if the previous transaction
|
||||||
|
* timed-out and never completed it's DMA.
|
||||||
|
*/
|
||||||
|
spin_lock_irqsave(&spfi->lock, flags);
|
||||||
|
if (spfi->tx_dma_busy || spfi->rx_dma_busy) {
|
||||||
|
spfi->tx_dma_busy = false;
|
||||||
|
spfi->rx_dma_busy = false;
|
||||||
|
|
||||||
|
dmaengine_terminate_all(spfi->tx_ch);
|
||||||
|
dmaengine_terminate_all(spfi->rx_ch);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&spfi->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int img_spfi_prepare(struct spi_master *master, struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct img_spfi *spfi = spi_master_get_devdata(master);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = spfi_readl(spfi, SPFI_PORT_STATE);
|
||||||
|
if (msg->spi->mode & SPI_CPHA)
|
||||||
|
val |= SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select);
|
||||||
|
else
|
||||||
|
val &= ~SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select);
|
||||||
|
if (msg->spi->mode & SPI_CPOL)
|
||||||
|
val |= SPFI_PORT_STATE_CK_POL(msg->spi->chip_select);
|
||||||
|
else
|
||||||
|
val &= ~SPFI_PORT_STATE_CK_POL(msg->spi->chip_select);
|
||||||
|
spfi_writel(spfi, val, SPFI_PORT_STATE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int img_spfi_unprepare(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct img_spfi *spfi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
spfi_reset(spfi);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int img_spfi_setup(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = gpio_request_one(spi->cs_gpio, (spi->mode & SPI_CS_HIGH) ?
|
||||||
|
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
|
||||||
|
dev_name(&spi->dev));
|
||||||
|
if (ret)
|
||||||
|
dev_err(&spi->dev, "can't request chipselect gpio %d\n",
|
||||||
|
spi->cs_gpio);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void img_spfi_cleanup(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
gpio_free(spi->cs_gpio);
|
||||||
|
}
|
||||||
|
|
||||||
static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
|
@ -416,6 +475,9 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
||||||
val |= div << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT;
|
val |= div << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT;
|
||||||
spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(spi->chip_select));
|
spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(spi->chip_select));
|
||||||
|
|
||||||
|
spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT,
|
||||||
|
SPFI_TRANSACTION);
|
||||||
|
|
||||||
val = spfi_readl(spfi, SPFI_CONTROL);
|
val = spfi_readl(spfi, SPFI_CONTROL);
|
||||||
val &= ~(SPFI_CONTROL_SEND_DMA | SPFI_CONTROL_GET_DMA);
|
val &= ~(SPFI_CONTROL_SEND_DMA | SPFI_CONTROL_GET_DMA);
|
||||||
if (xfer->tx_buf)
|
if (xfer->tx_buf)
|
||||||
|
@ -429,25 +491,7 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
||||||
else if (xfer->tx_nbits == SPI_NBITS_QUAD &&
|
else if (xfer->tx_nbits == SPI_NBITS_QUAD &&
|
||||||
xfer->rx_nbits == SPI_NBITS_QUAD)
|
xfer->rx_nbits == SPI_NBITS_QUAD)
|
||||||
val |= SPFI_CONTROL_TMODE_QUAD << SPFI_CONTROL_TMODE_SHIFT;
|
val |= SPFI_CONTROL_TMODE_QUAD << SPFI_CONTROL_TMODE_SHIFT;
|
||||||
val &= ~SPFI_CONTROL_CONTINUE;
|
|
||||||
if (!xfer->cs_change && !list_is_last(&xfer->transfer_list,
|
|
||||||
&master->cur_msg->transfers))
|
|
||||||
val |= SPFI_CONTROL_CONTINUE;
|
|
||||||
spfi_writel(spfi, val, SPFI_CONTROL);
|
spfi_writel(spfi, val, SPFI_CONTROL);
|
||||||
|
|
||||||
val = spfi_readl(spfi, SPFI_PORT_STATE);
|
|
||||||
if (spi->mode & SPI_CPHA)
|
|
||||||
val |= SPFI_PORT_STATE_CK_PHASE(spi->chip_select);
|
|
||||||
else
|
|
||||||
val &= ~SPFI_PORT_STATE_CK_PHASE(spi->chip_select);
|
|
||||||
if (spi->mode & SPI_CPOL)
|
|
||||||
val |= SPFI_PORT_STATE_CK_POL(spi->chip_select);
|
|
||||||
else
|
|
||||||
val &= ~SPFI_PORT_STATE_CK_POL(spi->chip_select);
|
|
||||||
spfi_writel(spfi, val, SPFI_PORT_STATE);
|
|
||||||
|
|
||||||
spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT,
|
|
||||||
SPFI_TRANSACTION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int img_spfi_transfer_one(struct spi_master *master,
|
static int img_spfi_transfer_one(struct spi_master *master,
|
||||||
|
@ -455,8 +499,6 @@ static int img_spfi_transfer_one(struct spi_master *master,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct img_spfi *spfi = spi_master_get_devdata(spi->master);
|
struct img_spfi *spfi = spi_master_get_devdata(spi->master);
|
||||||
bool dma_reset = false;
|
|
||||||
unsigned long flags;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (xfer->len > SPFI_TRANSACTION_TSIZE_MASK) {
|
if (xfer->len > SPFI_TRANSACTION_TSIZE_MASK) {
|
||||||
|
@ -466,23 +508,6 @@ static int img_spfi_transfer_one(struct spi_master *master,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Stop all DMA and reset the controller if the previous transaction
|
|
||||||
* timed-out and never completed it's DMA.
|
|
||||||
*/
|
|
||||||
spin_lock_irqsave(&spfi->lock, flags);
|
|
||||||
if (spfi->tx_dma_busy || spfi->rx_dma_busy) {
|
|
||||||
dev_err(spfi->dev, "SPI DMA still busy\n");
|
|
||||||
dma_reset = true;
|
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(&spfi->lock, flags);
|
|
||||||
|
|
||||||
if (dma_reset) {
|
|
||||||
dmaengine_terminate_all(spfi->tx_ch);
|
|
||||||
dmaengine_terminate_all(spfi->rx_ch);
|
|
||||||
spfi_reset(spfi);
|
|
||||||
}
|
|
||||||
|
|
||||||
img_spfi_config(master, spi, xfer);
|
img_spfi_config(master, spi, xfer);
|
||||||
if (master->can_dma && master->can_dma(master, spi, xfer))
|
if (master->can_dma && master->can_dma(master, spi, xfer))
|
||||||
ret = img_spfi_start_dma(master, spi, xfer);
|
ret = img_spfi_start_dma(master, spi, xfer);
|
||||||
|
@ -492,17 +517,6 @@ static int img_spfi_transfer_one(struct spi_master *master,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void img_spfi_set_cs(struct spi_device *spi, bool enable)
|
|
||||||
{
|
|
||||||
struct img_spfi *spfi = spi_master_get_devdata(spi->master);
|
|
||||||
u32 val;
|
|
||||||
|
|
||||||
val = spfi_readl(spfi, SPFI_PORT_STATE);
|
|
||||||
val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK << SPFI_PORT_STATE_DEV_SEL_SHIFT);
|
|
||||||
val |= spi->chip_select << SPFI_PORT_STATE_DEV_SEL_SHIFT;
|
|
||||||
spfi_writel(spfi, val, SPFI_PORT_STATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool img_spfi_can_dma(struct spi_master *master, struct spi_device *spi,
|
static bool img_spfi_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
|
@ -591,14 +605,17 @@ static int img_spfi_probe(struct platform_device *pdev)
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_DUAL | SPI_RX_DUAL;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_DUAL | SPI_RX_DUAL;
|
||||||
if (of_property_read_bool(spfi->dev->of_node, "img,supports-quad-mode"))
|
if (of_property_read_bool(spfi->dev->of_node, "img,supports-quad-mode"))
|
||||||
master->mode_bits |= SPI_TX_QUAD | SPI_RX_QUAD;
|
master->mode_bits |= SPI_TX_QUAD | SPI_RX_QUAD;
|
||||||
master->num_chipselect = 5;
|
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(8);
|
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(8);
|
||||||
master->max_speed_hz = clk_get_rate(spfi->spfi_clk) / 4;
|
master->max_speed_hz = clk_get_rate(spfi->spfi_clk) / 4;
|
||||||
master->min_speed_hz = clk_get_rate(spfi->spfi_clk) / 512;
|
master->min_speed_hz = clk_get_rate(spfi->spfi_clk) / 512;
|
||||||
|
|
||||||
master->set_cs = img_spfi_set_cs;
|
master->setup = img_spfi_setup;
|
||||||
|
master->cleanup = img_spfi_cleanup;
|
||||||
master->transfer_one = img_spfi_transfer_one;
|
master->transfer_one = img_spfi_transfer_one;
|
||||||
|
master->prepare_message = img_spfi_prepare;
|
||||||
|
master->unprepare_message = img_spfi_unprepare;
|
||||||
|
master->handle_err = img_spfi_handle_err;
|
||||||
|
|
||||||
spfi->tx_ch = dma_request_slave_channel(spfi->dev, "tx");
|
spfi->tx_ch = dma_request_slave_channel(spfi->dev, "tx");
|
||||||
spfi->rx_ch = dma_request_slave_channel(spfi->dev, "rx");
|
spfi->rx_ch = dma_request_slave_channel(spfi->dev, "rx");
|
||||||
|
|
Loading…
Reference in a new issue