Skip to content

Commit

Permalink
spi: rspi: Add support for more than one interrupt
Browse files Browse the repository at this point in the history
Add support for multiple interrupts, based on the SDK reference code.
This is needed for RZ/A1H, which supports 3 interrupts.

When using multiple interrupts, they must be called "rx" (SPRI) and "tx"
(SPTI). The error interrupt (SPEI) is not used, as it matters for slave
mode only.

When using a single interrupt, it may be called "mux". If it cannot be
found, the first interrupt in the device's resources will be used.

Signed-off-by: Geert Uytterhoeven <geert+renesas@linux-m68k.org>
Signed-off-by: Mark Brown <broonie@linaro.org>
  • Loading branch information
Geert Uytterhoeven authored and Mark Brown committed Jan 27, 2014
1 parent 340a15e commit 9372220
Showing 1 changed file with 87 additions and 19 deletions.
106 changes: 87 additions & 19 deletions drivers/spi/spi-rspi.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* SH RSPI driver
*
* Copyright (C) 2012 Renesas Solutions Corp.
* Copyright (C) 2012, 2013 Renesas Solutions Corp.
*
* Based on spi-sh.c:
* Copyright (C) 2011 Renesas Solutions Corp.
Expand Down Expand Up @@ -183,12 +183,12 @@ struct rspi_data {
struct clk *clk;
u8 spsr;
u16 spcmd;
int rx_irq, tx_irq;
const struct spi_ops *ops;

/* for dmaengine */
struct dma_chan *chan_tx;
struct dma_chan *chan_rx;
int irq;

unsigned dma_width_16bit:1;
unsigned dma_callbacked:1;
Expand Down Expand Up @@ -440,7 +440,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t)
struct scatterlist sg;
const void *buf = NULL;
struct dma_async_tx_descriptor *desc;
unsigned len;
unsigned int len;
int ret = 0;

if (rspi->dma_width_16bit) {
Expand Down Expand Up @@ -478,7 +478,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t)
* DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be
* called. So, this driver disables the IRQ while DMA transfer.
*/
disable_irq(rspi->irq);
disable_irq(rspi->tx_irq);

rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD, RSPI_SPCR);
rspi_enable_irq(rspi, SPCR_SPTIE);
Expand All @@ -497,7 +497,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t)
ret = -ETIMEDOUT;
rspi_disable_irq(rspi, SPCR_SPTIE);

enable_irq(rspi->irq);
enable_irq(rspi->tx_irq);

end:
rspi_dma_unmap_sg(&sg, rspi->chan_tx, DMA_TO_DEVICE);
Expand Down Expand Up @@ -536,7 +536,7 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t)
struct scatterlist sg, sg_dummy;
void *dummy = NULL, *rx_buf = NULL;
struct dma_async_tx_descriptor *desc, *desc_dummy;
unsigned len;
unsigned int len;
int ret = 0;

if (rspi->dma_width_16bit) {
Expand Down Expand Up @@ -594,7 +594,9 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t)
* DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be
* called. So, this driver disables the IRQ while DMA transfer.
*/
disable_irq(rspi->irq);
disable_irq(rspi->tx_irq);
if (rspi->rx_irq != rspi->tx_irq)
disable_irq(rspi->rx_irq);

rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_TXMD, RSPI_SPCR);
rspi_enable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE);
Expand All @@ -617,7 +619,9 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t)
ret = -ETIMEDOUT;
rspi_disable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE);

enable_irq(rspi->irq);
enable_irq(rspi->tx_irq);
if (rspi->rx_irq != rspi->tx_irq)
enable_irq(rspi->rx_irq);

end:
rspi_dma_unmap_sg(&sg, rspi->chan_rx, DMA_FROM_DEVICE);
Expand Down Expand Up @@ -775,7 +779,7 @@ static int rspi_unprepare_message(struct spi_master *master,
return 0;
}

static irqreturn_t rspi_irq(int irq, void *_sr)
static irqreturn_t rspi_irq_mux(int irq, void *_sr)
{
struct rspi_data *rspi = _sr;
u8 spsr;
Expand All @@ -797,6 +801,36 @@ static irqreturn_t rspi_irq(int irq, void *_sr)
return ret;
}

static irqreturn_t rspi_irq_rx(int irq, void *_sr)
{
struct rspi_data *rspi = _sr;
u8 spsr;

rspi->spsr = spsr = rspi_read8(rspi, RSPI_SPSR);
if (spsr & SPSR_SPRF) {
rspi_disable_irq(rspi, SPCR_SPRIE);
wake_up(&rspi->wait);
return IRQ_HANDLED;
}

return 0;
}

static irqreturn_t rspi_irq_tx(int irq, void *_sr)
{
struct rspi_data *rspi = _sr;
u8 spsr;

rspi->spsr = spsr = rspi_read8(rspi, RSPI_SPSR);
if (spsr & SPSR_SPTEF) {
rspi_disable_irq(rspi, SPCR_SPTIE);
wake_up(&rspi->wait);
return IRQ_HANDLED;
}

return 0;
}

static int rspi_request_dma(struct rspi_data *rspi,
struct platform_device *pdev)
{
Expand Down Expand Up @@ -868,12 +902,25 @@ static int rspi_remove(struct platform_device *pdev)
return 0;
}

static int rspi_request_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, const char *suffix,
void *dev_id)
{
const char *base = dev_name(dev);
size_t len = strlen(base) + strlen(suffix) + 2;
char *name = devm_kzalloc(dev, len, GFP_KERNEL);
if (!name)
return -ENOMEM;
snprintf(name, len, "%s:%s", base, suffix);
return devm_request_irq(dev, irq, handler, 0, name, dev_id);
}

static int rspi_probe(struct platform_device *pdev)
{
struct resource *res;
struct spi_master *master;
struct rspi_data *rspi;
int ret, irq;
int ret;
char clk_name[16];
const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev);
const struct spi_ops *ops;
Expand All @@ -886,12 +933,6 @@ static int rspi_probe(struct platform_device *pdev)
return -ENODEV;
}

irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "platform_get_irq error\n");
return -ENODEV;
}

master = spi_alloc_master(&pdev->dev, sizeof(struct rspi_data));
if (master == NULL) {
dev_err(&pdev->dev, "spi_alloc_master error.\n");
Expand Down Expand Up @@ -934,14 +975,41 @@ static int rspi_probe(struct platform_device *pdev)
master->unprepare_message = rspi_unprepare_message;
master->mode_bits = SPI_CPHA | SPI_CPOL;

ret = devm_request_irq(&pdev->dev, irq, rspi_irq, 0,
dev_name(&pdev->dev), rspi);
ret = platform_get_irq_byname(pdev, "rx");
if (ret < 0) {
ret = platform_get_irq_byname(pdev, "mux");
if (ret < 0)
ret = platform_get_irq(pdev, 0);
if (ret >= 0)
rspi->rx_irq = rspi->tx_irq = ret;
} else {
rspi->rx_irq = ret;
ret = platform_get_irq_byname(pdev, "tx");
if (ret >= 0)
rspi->tx_irq = ret;
}
if (ret < 0) {
dev_err(&pdev->dev, "platform_get_irq error\n");
goto error2;
}

if (rspi->rx_irq == rspi->tx_irq) {
/* Single multiplexed interrupt */
ret = rspi_request_irq(&pdev->dev, rspi->rx_irq, rspi_irq_mux,
"mux", rspi);
} else {
/* Multi-interrupt mode, only SPRI and SPTI are used */
ret = rspi_request_irq(&pdev->dev, rspi->rx_irq, rspi_irq_rx,
"rx", rspi);
if (!ret)
ret = rspi_request_irq(&pdev->dev, rspi->tx_irq,
rspi_irq_tx, "tx", rspi);
}
if (ret < 0) {
dev_err(&pdev->dev, "request_irq error\n");
goto error2;
}

rspi->irq = irq;
ret = rspi_request_dma(rspi, pdev);
if (ret < 0) {
dev_err(&pdev->dev, "rspi_request_dma failed.\n");
Expand Down

0 comments on commit 9372220

Please sign in to comment.