diff options
author | Feng Tang <feng.tang@intel.com> | 2010-01-20 15:49:45 -0500 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2010-01-20 15:49:45 -0500 |
commit | 552e450929a7298cc8834fd2824a60b2e914f70e (patch) | |
tree | 1b9568370d1b40f23dd4d73765e98ff74665eb07 | |
parent | 51f921c1eb1124fb99ab0728c19e8e14c82c81be (diff) |
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) | |||
358 | static irqreturn_t interrupt_transfer(struct dw_spi *dws) | 358 | static irqreturn_t interrupt_transfer(struct dw_spi *dws) |
359 | { | 359 | { |
360 | u16 irq_status, irq_mask = 0x3f; | 360 | u16 irq_status, irq_mask = 0x3f; |
361 | u32 int_level = dws->fifo_len / 2; | ||
362 | u32 left; | ||
361 | 363 | ||
362 | irq_status = dw_readw(dws, isr) & irq_mask; | 364 | irq_status = dw_readw(dws, isr) & irq_mask; |
363 | /* Error handling */ | 365 | /* Error handling */ |
@@ -369,22 +371,23 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) | |||
369 | return IRQ_HANDLED; | 371 | return IRQ_HANDLED; |
370 | } | 372 | } |
371 | 373 | ||
372 | /* INT comes from tx */ | 374 | if (irq_status & SPI_INT_TXEI) { |
373 | if (dws->tx && (irq_status & SPI_INT_TXEI)) { | 375 | spi_mask_intr(dws, SPI_INT_TXEI); |
374 | while (dws->tx < dws->tx_end) | 376 | |
377 | left = (dws->tx_end - dws->tx) / dws->n_bytes; | ||
378 | left = (left > int_level) ? int_level : left; | ||
379 | |||
380 | while (left--) | ||
375 | dws->write(dws); | 381 | dws->write(dws); |
382 | dws->read(dws); | ||
376 | 383 | ||
377 | if (dws->tx == dws->tx_end) { | 384 | /* Re-enable the IRQ if there is still data left to tx */ |
378 | spi_mask_intr(dws, SPI_INT_TXEI); | 385 | if (dws->tx_end > dws->tx) |
386 | spi_umask_intr(dws, SPI_INT_TXEI); | ||
387 | else | ||
379 | transfer_complete(dws); | 388 | transfer_complete(dws); |
380 | } | ||
381 | } | 389 | } |
382 | 390 | ||
383 | /* INT comes from rx */ | ||
384 | if (dws->rx && (irq_status & SPI_INT_RXFI)) { | ||
385 | if (dws->read(dws)) | ||
386 | transfer_complete(dws); | ||
387 | } | ||
388 | return IRQ_HANDLED; | 391 | return IRQ_HANDLED; |
389 | } | 392 | } |
390 | 393 | ||
@@ -428,6 +431,7 @@ static void pump_transfers(unsigned long data) | |||
428 | u8 bits = 0; | 431 | u8 bits = 0; |
429 | u8 imask = 0; | 432 | u8 imask = 0; |
430 | u8 cs_change = 0; | 433 | u8 cs_change = 0; |
434 | u16 txint_level = 0; | ||
431 | u16 clk_div = 0; | 435 | u16 clk_div = 0; |
432 | u32 speed = 0; | 436 | u32 speed = 0; |
433 | u32 cr0 = 0; | 437 | u32 cr0 = 0; |
@@ -438,6 +442,9 @@ static void pump_transfers(unsigned long data) | |||
438 | chip = dws->cur_chip; | 442 | chip = dws->cur_chip; |
439 | spi = message->spi; | 443 | spi = message->spi; |
440 | 444 | ||
445 | if (unlikely(!chip->clk_div)) | ||
446 | chip->clk_div = dws->max_freq / chip->speed_hz; | ||
447 | |||
441 | if (message->state == ERROR_STATE) { | 448 | if (message->state == ERROR_STATE) { |
442 | message->status = -EIO; | 449 | message->status = -EIO; |
443 | goto early_exit; | 450 | goto early_exit; |
@@ -492,7 +499,7 @@ static void pump_transfers(unsigned long data) | |||
492 | 499 | ||
493 | /* clk_div doesn't support odd number */ | 500 | /* clk_div doesn't support odd number */ |
494 | clk_div = dws->max_freq / speed; | 501 | clk_div = dws->max_freq / speed; |
495 | clk_div = (clk_div >> 1) << 1; | 502 | clk_div = (clk_div + 1) & 0xfffe; |
496 | 503 | ||
497 | chip->speed_hz = speed; | 504 | chip->speed_hz = speed; |
498 | chip->clk_div = clk_div; | 505 | chip->clk_div = clk_div; |
@@ -535,11 +542,16 @@ static void pump_transfers(unsigned long data) | |||
535 | /* Check if current transfer is a DMA transaction */ | 542 | /* Check if current transfer is a DMA transaction */ |
536 | dws->dma_mapped = map_dma_buffers(dws); | 543 | dws->dma_mapped = map_dma_buffers(dws); |
537 | 544 | ||
545 | /* | ||
546 | * Interrupt mode | ||
547 | * we only need set the TXEI IRQ, as TX/RX always happen syncronizely | ||
548 | */ | ||
538 | if (!dws->dma_mapped && !chip->poll_mode) { | 549 | if (!dws->dma_mapped && !chip->poll_mode) { |
539 | if (dws->rx) | 550 | int templen = dws->len / dws->n_bytes; |
540 | imask |= SPI_INT_RXFI; | 551 | txint_level = dws->fifo_len / 2; |
541 | if (dws->tx) | 552 | txint_level = (templen > txint_level) ? txint_level : templen; |
542 | imask |= SPI_INT_TXEI; | 553 | |
554 | imask |= SPI_INT_TXEI; | ||
543 | dws->transfer_handler = interrupt_transfer; | 555 | dws->transfer_handler = interrupt_transfer; |
544 | } | 556 | } |
545 | 557 | ||
@@ -549,21 +561,23 @@ static void pump_transfers(unsigned long data) | |||
549 | * 2. clk_div is changed | 561 | * 2. clk_div is changed |
550 | * 3. control value changes | 562 | * 3. control value changes |
551 | */ | 563 | */ |
552 | if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div) { | 564 | if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div || imask) { |
553 | spi_enable_chip(dws, 0); | 565 | spi_enable_chip(dws, 0); |
554 | 566 | ||
555 | if (dw_readw(dws, ctrl0) != cr0) | 567 | if (dw_readw(dws, ctrl0) != cr0) |
556 | dw_writew(dws, ctrl0, cr0); | 568 | dw_writew(dws, ctrl0, cr0); |
557 | 569 | ||
570 | spi_set_clk(dws, clk_div ? clk_div : chip->clk_div); | ||
571 | spi_chip_sel(dws, spi->chip_select); | ||
572 | |||
558 | /* Set the interrupt mask, for poll mode just diable all int */ | 573 | /* Set the interrupt mask, for poll mode just diable all int */ |
559 | spi_mask_intr(dws, 0xff); | 574 | spi_mask_intr(dws, 0xff); |
560 | if (!chip->poll_mode) | 575 | if (imask) |
561 | spi_umask_intr(dws, imask); | 576 | spi_umask_intr(dws, imask); |
577 | if (txint_level) | ||
578 | dw_writew(dws, txfltr, txint_level); | ||
562 | 579 | ||
563 | spi_set_clk(dws, clk_div ? clk_div : chip->clk_div); | ||
564 | spi_chip_sel(dws, spi->chip_select); | ||
565 | spi_enable_chip(dws, 1); | 580 | spi_enable_chip(dws, 1); |
566 | |||
567 | if (cs_change) | 581 | if (cs_change) |
568 | dws->prev_chip = chip; | 582 | dws->prev_chip = chip; |
569 | } | 583 | } |
@@ -712,11 +726,11 @@ static int dw_spi_setup(struct spi_device *spi) | |||
712 | } | 726 | } |
713 | chip->bits_per_word = spi->bits_per_word; | 727 | chip->bits_per_word = spi->bits_per_word; |
714 | 728 | ||
729 | if (!spi->max_speed_hz) { | ||
730 | dev_err(&spi->dev, "No max speed HZ parameter\n"); | ||
731 | return -EINVAL; | ||
732 | } | ||
715 | chip->speed_hz = spi->max_speed_hz; | 733 | chip->speed_hz = spi->max_speed_hz; |
716 | if (chip->speed_hz) | ||
717 | chip->clk_div = 25000000 / chip->speed_hz; | ||
718 | else | ||
719 | chip->clk_div = 8; /* default value */ | ||
720 | 734 | ||
721 | chip->tmode = 0; /* Tx & Rx */ | 735 | chip->tmode = 0; /* Tx & Rx */ |
722 | /* Default SPI mode is SCPOL = 0, SCPH = 0 */ | 736 | /* 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, | |||
73 | dws->num_cs = 4; | 73 | dws->num_cs = 4; |
74 | dws->max_freq = 25000000; /* for Moorestwon */ | 74 | dws->max_freq = 25000000; /* for Moorestwon */ |
75 | dws->irq = pdev->irq; | 75 | dws->irq = pdev->irq; |
76 | dws->fifo_len = 40; /* FIFO has 40 words buffer */ | ||
76 | 77 | ||
77 | ret = dw_spi_add_host(dws); | 78 | ret = dw_spi_add_host(dws); |
78 | if (ret) | 79 | 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 { | |||
90 | unsigned long paddr; | 90 | unsigned long paddr; |
91 | u32 iolen; | 91 | u32 iolen; |
92 | int irq; | 92 | int irq; |
93 | u32 fifo_len; /* depth of the FIFO buffer */ | ||
93 | u32 max_freq; /* max bus freq supported */ | 94 | u32 max_freq; /* max bus freq supported */ |
94 | 95 | ||
95 | u16 bus_num; | 96 | u16 bus_num; |