diff options
author | Feng Tang <feng.tang@intel.com> | 2010-01-20 13:49:45 -0700 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2010-01-20 13:49:45 -0700 |
commit | 552e450929a7298cc8834fd2824a60b2e914f70e (patch) | |
tree | 1b9568370d1b40f23dd4d73765e98ff74665eb07 | |
parent | 51f921c1eb1124fb99ab0728c19e8e14c82c81be (diff) | |
download | linux-552e450929a7298cc8834fd2824a60b2e914f70e.tar.bz2 |
spi/dw_spi: refine the IRQ mode working flow
Now dw_spi core fully supports 3 transfer modes: pure polling,
DMA and IRQ mode. IRQ mode will use the FIFO half empty as
the IRQ trigger, so each interface driver need set the fifo_len,
so that core driver can handle it properly
Signed-off-by: Feng Tang <feng.tang@intel.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
-rw-r--r-- | drivers/spi/dw_spi.c | 64 | ||||
-rw-r--r-- | drivers/spi/dw_spi_pci.c | 1 | ||||
-rw-r--r-- | include/linux/spi/dw_spi.h | 1 |
3 files changed, 41 insertions, 25 deletions
diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 521d680af289..1bb709b3920f 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -358,6 +358,8 @@ static void transfer_complete(struct dw_spi *dws) static irqreturn_t interrupt_transfer(struct dw_spi *dws) { u16 irq_status, irq_mask = 0x3f; + u32 int_level = dws->fifo_len / 2; + u32 left; irq_status = dw_readw(dws, isr) & irq_mask; /* Error handling */ @@ -369,22 +371,23 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) return IRQ_HANDLED; } - /* INT comes from tx */ - if (dws->tx && (irq_status & SPI_INT_TXEI)) { - while (dws->tx < dws->tx_end) + if (irq_status & SPI_INT_TXEI) { + spi_mask_intr(dws, SPI_INT_TXEI); + + left = (dws->tx_end - dws->tx) / dws->n_bytes; + left = (left > int_level) ? int_level : left; + + while (left--) dws->write(dws); + dws->read(dws); - if (dws->tx == dws->tx_end) { - spi_mask_intr(dws, SPI_INT_TXEI); + /* Re-enable the IRQ if there is still data left to tx */ + if (dws->tx_end > dws->tx) + spi_umask_intr(dws, SPI_INT_TXEI); + else transfer_complete(dws); - } } - /* INT comes from rx */ - if (dws->rx && (irq_status & SPI_INT_RXFI)) { - if (dws->read(dws)) - transfer_complete(dws); - } return IRQ_HANDLED; } @@ -428,6 +431,7 @@ static void pump_transfers(unsigned long data) u8 bits = 0; u8 imask = 0; u8 cs_change = 0; + u16 txint_level = 0; u16 clk_div = 0; u32 speed = 0; u32 cr0 = 0; @@ -438,6 +442,9 @@ static void pump_transfers(unsigned long data) chip = dws->cur_chip; spi = message->spi; + if (unlikely(!chip->clk_div)) + chip->clk_div = dws->max_freq / chip->speed_hz; + if (message->state == ERROR_STATE) { message->status = -EIO; goto early_exit; @@ -492,7 +499,7 @@ static void pump_transfers(unsigned long data) /* clk_div doesn't support odd number */ clk_div = dws->max_freq / speed; - clk_div = (clk_div >> 1) << 1; + clk_div = (clk_div + 1) & 0xfffe; chip->speed_hz = speed; chip->clk_div = clk_div; @@ -535,11 +542,16 @@ static void pump_transfers(unsigned long data) /* Check if current transfer is a DMA transaction */ dws->dma_mapped = map_dma_buffers(dws); + /* + * Interrupt mode + * we only need set the TXEI IRQ, as TX/RX always happen syncronizely + */ if (!dws->dma_mapped && !chip->poll_mode) { - if (dws->rx) - imask |= SPI_INT_RXFI; - if (dws->tx) - imask |= SPI_INT_TXEI; + int templen = dws->len / dws->n_bytes; + txint_level = dws->fifo_len / 2; + txint_level = (templen > txint_level) ? txint_level : templen; + + imask |= SPI_INT_TXEI; dws->transfer_handler = interrupt_transfer; } @@ -549,21 +561,23 @@ static void pump_transfers(unsigned long data) * 2. clk_div is changed * 3. control value changes */ - if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div) { + if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div || imask) { spi_enable_chip(dws, 0); if (dw_readw(dws, ctrl0) != cr0) dw_writew(dws, ctrl0, cr0); + spi_set_clk(dws, clk_div ? clk_div : chip->clk_div); + spi_chip_sel(dws, spi->chip_select); + /* Set the interrupt mask, for poll mode just diable all int */ spi_mask_intr(dws, 0xff); - if (!chip->poll_mode) + if (imask) spi_umask_intr(dws, imask); + if (txint_level) + dw_writew(dws, txfltr, txint_level); - spi_set_clk(dws, clk_div ? clk_div : chip->clk_div); - spi_chip_sel(dws, spi->chip_select); spi_enable_chip(dws, 1); - if (cs_change) dws->prev_chip = chip; } @@ -712,11 +726,11 @@ static int dw_spi_setup(struct spi_device *spi) } chip->bits_per_word = spi->bits_per_word; + if (!spi->max_speed_hz) { + dev_err(&spi->dev, "No max speed HZ parameter\n"); + return -EINVAL; + } chip->speed_hz = spi->max_speed_hz; - if (chip->speed_hz) - chip->clk_div = 25000000 / chip->speed_hz; - else - chip->clk_div = 8; /* default value */ chip->tmode = 0; /* Tx & Rx */ /* Default SPI mode is SCPOL = 0, SCPH = 0 */ diff --git a/drivers/spi/dw_spi_pci.c b/drivers/spi/dw_spi_pci.c index 7980f1443ce1..1f0735f9cc76 100644 --- a/drivers/spi/dw_spi_pci.c +++ b/drivers/spi/dw_spi_pci.c @@ -73,6 +73,7 @@ static int __devinit spi_pci_probe(struct pci_dev *pdev, dws->num_cs = 4; dws->max_freq = 25000000; /* for Moorestwon */ dws->irq = pdev->irq; + dws->fifo_len = 40; /* FIFO has 40 words buffer */ ret = dw_spi_add_host(dws); if (ret) diff --git a/include/linux/spi/dw_spi.h b/include/linux/spi/dw_spi.h index 51b3e771a9a3..1a127a31e017 100644 --- a/include/linux/spi/dw_spi.h +++ b/include/linux/spi/dw_spi.h @@ -90,6 +90,7 @@ struct dw_spi { unsigned long paddr; u32 iolen; int irq; + u32 fifo_len; /* depth of the FIFO buffer */ u32 max_freq; /* max bus freq supported */ u16 bus_num; |