diff options
author | Yi Li <yi.li@analog.com> | 2009-06-03 05:46:22 -0400 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2010-10-18 02:49:29 -0400 |
commit | f6a6d96685be6e784849d067b44acb831f595417 (patch) | |
tree | 74676924942c6823b0b1517569394239d834943a /drivers/spi | |
parent | bb8beecd98de45f821a3360e0b061fc1f8da947c (diff) |
spi/bfin_spi: utilize the SPI interrupt in PIO mode
The current behavior in PIO mode is to poll the SPI status registers which
can obviously lead to higher latencies when doing a lot of SPI traffic.
There is a SPI interrupt which can be used instead to signal individual
completion of transactions.
Signed-off-by: Yi Li <yi.li@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/spi_bfin5xx.c | 235 |
1 files changed, 185 insertions, 50 deletions
diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index b8352546c589..3736c3596a41 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c | |||
@@ -92,6 +92,9 @@ struct driver_data { | |||
92 | dma_addr_t rx_dma; | 92 | dma_addr_t rx_dma; |
93 | dma_addr_t tx_dma; | 93 | dma_addr_t tx_dma; |
94 | 94 | ||
95 | int irq_requested; | ||
96 | int spi_irq; | ||
97 | |||
95 | size_t rx_map_len; | 98 | size_t rx_map_len; |
96 | size_t tx_map_len; | 99 | size_t tx_map_len; |
97 | u8 n_bytes; | 100 | u8 n_bytes; |
@@ -115,6 +118,7 @@ struct chip_data { | |||
115 | u16 cs_chg_udelay; /* Some devices require > 255usec delay */ | 118 | u16 cs_chg_udelay; /* Some devices require > 255usec delay */ |
116 | u32 cs_gpio; | 119 | u32 cs_gpio; |
117 | u16 idle_tx_val; | 120 | u16 idle_tx_val; |
121 | u8 pio_interrupt; /* use spi data irq */ | ||
118 | void (*write) (struct driver_data *); | 122 | void (*write) (struct driver_data *); |
119 | void (*read) (struct driver_data *); | 123 | void (*read) (struct driver_data *); |
120 | void (*duplex) (struct driver_data *); | 124 | void (*duplex) (struct driver_data *); |
@@ -525,6 +529,79 @@ static void bfin_spi_giveback(struct driver_data *drv_data) | |||
525 | msg->complete(msg->context); | 529 | msg->complete(msg->context); |
526 | } | 530 | } |
527 | 531 | ||
532 | /* spi data irq handler */ | ||
533 | static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) | ||
534 | { | ||
535 | struct driver_data *drv_data = dev_id; | ||
536 | struct chip_data *chip = drv_data->cur_chip; | ||
537 | struct spi_message *msg = drv_data->cur_msg; | ||
538 | int n_bytes = drv_data->n_bytes; | ||
539 | |||
540 | /* wait until transfer finished. */ | ||
541 | while (!(read_STAT(drv_data) & BIT_STAT_RXS)) | ||
542 | cpu_relax(); | ||
543 | |||
544 | if ((drv_data->tx && drv_data->tx >= drv_data->tx_end) || | ||
545 | (drv_data->rx && drv_data->rx >= (drv_data->rx_end - n_bytes))) { | ||
546 | /* last read */ | ||
547 | if (drv_data->rx) { | ||
548 | dev_dbg(&drv_data->pdev->dev, "last read\n"); | ||
549 | if (n_bytes == 2) | ||
550 | *(u16 *) (drv_data->rx) = read_RDBR(drv_data); | ||
551 | else if (n_bytes == 1) | ||
552 | *(u8 *) (drv_data->rx) = read_RDBR(drv_data); | ||
553 | drv_data->rx += n_bytes; | ||
554 | } | ||
555 | |||
556 | msg->actual_length += drv_data->len_in_bytes; | ||
557 | if (drv_data->cs_change) | ||
558 | bfin_spi_cs_deactive(drv_data, chip); | ||
559 | /* Move to next transfer */ | ||
560 | msg->state = bfin_spi_next_transfer(drv_data); | ||
561 | |||
562 | disable_irq(drv_data->spi_irq); | ||
563 | |||
564 | /* Schedule transfer tasklet */ | ||
565 | tasklet_schedule(&drv_data->pump_transfers); | ||
566 | return IRQ_HANDLED; | ||
567 | } | ||
568 | |||
569 | if (drv_data->rx && drv_data->tx) { | ||
570 | /* duplex */ | ||
571 | dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n"); | ||
572 | if (drv_data->n_bytes == 2) { | ||
573 | *(u16 *) (drv_data->rx) = read_RDBR(drv_data); | ||
574 | write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); | ||
575 | } else if (drv_data->n_bytes == 1) { | ||
576 | *(u8 *) (drv_data->rx) = read_RDBR(drv_data); | ||
577 | write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); | ||
578 | } | ||
579 | } else if (drv_data->rx) { | ||
580 | /* read */ | ||
581 | dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n"); | ||
582 | if (drv_data->n_bytes == 2) | ||
583 | *(u16 *) (drv_data->rx) = read_RDBR(drv_data); | ||
584 | else if (drv_data->n_bytes == 1) | ||
585 | *(u8 *) (drv_data->rx) = read_RDBR(drv_data); | ||
586 | write_TDBR(drv_data, chip->idle_tx_val); | ||
587 | } else if (drv_data->tx) { | ||
588 | /* write */ | ||
589 | dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n"); | ||
590 | bfin_spi_dummy_read(drv_data); | ||
591 | if (drv_data->n_bytes == 2) | ||
592 | write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); | ||
593 | else if (drv_data->n_bytes == 1) | ||
594 | write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); | ||
595 | } | ||
596 | |||
597 | if (drv_data->tx) | ||
598 | drv_data->tx += n_bytes; | ||
599 | if (drv_data->rx) | ||
600 | drv_data->rx += n_bytes; | ||
601 | |||
602 | return IRQ_HANDLED; | ||
603 | } | ||
604 | |||
528 | static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) | 605 | static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) |
529 | { | 606 | { |
530 | struct driver_data *drv_data = dev_id; | 607 | struct driver_data *drv_data = dev_id; |
@@ -700,6 +777,7 @@ static void bfin_spi_pump_transfers(unsigned long data) | |||
700 | 777 | ||
701 | default: | 778 | default: |
702 | /* No change, the same as default setting */ | 779 | /* No change, the same as default setting */ |
780 | transfer->bits_per_word = chip->bits_per_word; | ||
703 | drv_data->n_bytes = chip->n_bytes; | 781 | drv_data->n_bytes = chip->n_bytes; |
704 | width = chip->width; | 782 | width = chip->width; |
705 | drv_data->write = drv_data->tx ? chip->write : bfin_spi_null_writer; | 783 | drv_data->write = drv_data->tx ? chip->write : bfin_spi_null_writer; |
@@ -842,60 +920,86 @@ static void bfin_spi_pump_transfers(unsigned long data) | |||
842 | dma_enable_irq(drv_data->dma_channel); | 920 | dma_enable_irq(drv_data->dma_channel); |
843 | local_irq_restore(flags); | 921 | local_irq_restore(flags); |
844 | 922 | ||
845 | } else { | 923 | return; |
846 | /* IO mode write then read */ | 924 | } |
847 | dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n"); | ||
848 | 925 | ||
849 | /* we always use SPI_WRITE mode. SPI_READ mode | 926 | if (chip->pio_interrupt) { |
850 | seems to have problems with setting up the | 927 | /* use write mode. spi irq should have been disabled */ |
851 | output value in TDBR prior to the transfer. */ | 928 | cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD)); |
852 | write_CTRL(drv_data, (cr | CFG_SPI_WRITE)); | 929 | write_CTRL(drv_data, (cr | CFG_SPI_WRITE)); |
853 | 930 | ||
854 | if (full_duplex) { | 931 | /* discard old RX data and clear RXS */ |
855 | /* full duplex mode */ | 932 | bfin_spi_dummy_read(drv_data); |
856 | BUG_ON((drv_data->tx_end - drv_data->tx) != | ||
857 | (drv_data->rx_end - drv_data->rx)); | ||
858 | dev_dbg(&drv_data->pdev->dev, | ||
859 | "IO duplex: cr is 0x%x\n", cr); | ||
860 | |||
861 | drv_data->duplex(drv_data); | ||
862 | |||
863 | if (drv_data->tx != drv_data->tx_end) | ||
864 | tranf_success = 0; | ||
865 | } else if (drv_data->tx != NULL) { | ||
866 | /* write only half duplex */ | ||
867 | dev_dbg(&drv_data->pdev->dev, | ||
868 | "IO write: cr is 0x%x\n", cr); | ||
869 | 933 | ||
870 | drv_data->write(drv_data); | 934 | /* start transfer */ |
935 | if (drv_data->tx == NULL) | ||
936 | write_TDBR(drv_data, chip->idle_tx_val); | ||
937 | else { | ||
938 | if (transfer->bits_per_word == 8) | ||
939 | write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); | ||
940 | else if (transfer->bits_per_word == 16) | ||
941 | write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); | ||
942 | drv_data->tx += drv_data->n_bytes; | ||
943 | } | ||
871 | 944 | ||
872 | if (drv_data->tx != drv_data->tx_end) | 945 | /* once TDBR is empty, interrupt is triggered */ |
873 | tranf_success = 0; | 946 | enable_irq(drv_data->spi_irq); |
874 | } else if (drv_data->rx != NULL) { | 947 | return; |
875 | /* read only half duplex */ | 948 | } |
876 | dev_dbg(&drv_data->pdev->dev, | ||
877 | "IO read: cr is 0x%x\n", cr); | ||
878 | 949 | ||
879 | drv_data->read(drv_data); | 950 | /* IO mode */ |
880 | if (drv_data->rx != drv_data->rx_end) | 951 | dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n"); |
881 | tranf_success = 0; | 952 | |
882 | } | 953 | /* we always use SPI_WRITE mode. SPI_READ mode |
954 | seems to have problems with setting up the | ||
955 | output value in TDBR prior to the transfer. */ | ||
956 | write_CTRL(drv_data, (cr | CFG_SPI_WRITE)); | ||
957 | |||
958 | if (full_duplex) { | ||
959 | /* full duplex mode */ | ||
960 | BUG_ON((drv_data->tx_end - drv_data->tx) != | ||
961 | (drv_data->rx_end - drv_data->rx)); | ||
962 | dev_dbg(&drv_data->pdev->dev, | ||
963 | "IO duplex: cr is 0x%x\n", cr); | ||
964 | |||
965 | drv_data->duplex(drv_data); | ||
966 | |||
967 | if (drv_data->tx != drv_data->tx_end) | ||
968 | tranf_success = 0; | ||
969 | } else if (drv_data->tx != NULL) { | ||
970 | /* write only half duplex */ | ||
971 | dev_dbg(&drv_data->pdev->dev, | ||
972 | "IO write: cr is 0x%x\n", cr); | ||
973 | |||
974 | drv_data->write(drv_data); | ||
975 | |||
976 | if (drv_data->tx != drv_data->tx_end) | ||
977 | tranf_success = 0; | ||
978 | } else if (drv_data->rx != NULL) { | ||
979 | /* read only half duplex */ | ||
980 | dev_dbg(&drv_data->pdev->dev, | ||
981 | "IO read: cr is 0x%x\n", cr); | ||
982 | |||
983 | drv_data->read(drv_data); | ||
984 | if (drv_data->rx != drv_data->rx_end) | ||
985 | tranf_success = 0; | ||
986 | } | ||
883 | 987 | ||
884 | if (!tranf_success) { | 988 | if (!tranf_success) { |
885 | dev_dbg(&drv_data->pdev->dev, | 989 | dev_dbg(&drv_data->pdev->dev, |
886 | "IO write error!\n"); | 990 | "IO write error!\n"); |
887 | message->state = ERROR_STATE; | 991 | message->state = ERROR_STATE; |
888 | } else { | 992 | } else { |
889 | /* Update total byte transfered */ | 993 | /* Update total byte transfered */ |
890 | message->actual_length += drv_data->len_in_bytes; | 994 | message->actual_length += drv_data->len_in_bytes; |
891 | /* Move to next transfer of this msg */ | 995 | /* Move to next transfer of this msg */ |
892 | message->state = bfin_spi_next_transfer(drv_data); | 996 | message->state = bfin_spi_next_transfer(drv_data); |
893 | if (drv_data->cs_change) | 997 | if (drv_data->cs_change) |
894 | bfin_spi_cs_deactive(drv_data, chip); | 998 | bfin_spi_cs_deactive(drv_data, chip); |
895 | } | ||
896 | /* Schedule next transfer tasklet */ | ||
897 | tasklet_schedule(&drv_data->pump_transfers); | ||
898 | } | 999 | } |
1000 | |||
1001 | /* Schedule next transfer tasklet */ | ||
1002 | tasklet_schedule(&drv_data->pump_transfers); | ||
899 | } | 1003 | } |
900 | 1004 | ||
901 | /* pop a msg from queue and kick off real transfer */ | 1005 | /* pop a msg from queue and kick off real transfer */ |
@@ -1047,6 +1151,7 @@ static int bfin_spi_setup(struct spi_device *spi) | |||
1047 | chip->cs_chg_udelay = chip_info->cs_chg_udelay; | 1151 | chip->cs_chg_udelay = chip_info->cs_chg_udelay; |
1048 | chip->cs_gpio = chip_info->cs_gpio; | 1152 | chip->cs_gpio = chip_info->cs_gpio; |
1049 | chip->idle_tx_val = chip_info->idle_tx_val; | 1153 | chip->idle_tx_val = chip_info->idle_tx_val; |
1154 | chip->pio_interrupt = chip_info->pio_interrupt; | ||
1050 | } | 1155 | } |
1051 | 1156 | ||
1052 | /* translate common spi framework into our register */ | 1157 | /* translate common spi framework into our register */ |
@@ -1096,6 +1201,11 @@ static int bfin_spi_setup(struct spi_device *spi) | |||
1096 | goto error; | 1201 | goto error; |
1097 | } | 1202 | } |
1098 | 1203 | ||
1204 | if (chip->enable_dma && chip->pio_interrupt) { | ||
1205 | dev_err(&spi->dev, "enable_dma is set, " | ||
1206 | "do not set pio_interrupt\n"); | ||
1207 | goto error; | ||
1208 | } | ||
1099 | /* | 1209 | /* |
1100 | * if any one SPI chip is registered and wants DMA, request the | 1210 | * if any one SPI chip is registered and wants DMA, request the |
1101 | * DMA channel for it | 1211 | * DMA channel for it |
@@ -1119,6 +1229,18 @@ static int bfin_spi_setup(struct spi_device *spi) | |||
1119 | dma_disable_irq(drv_data->dma_channel); | 1229 | dma_disable_irq(drv_data->dma_channel); |
1120 | } | 1230 | } |
1121 | 1231 | ||
1232 | if (chip->pio_interrupt && !drv_data->irq_requested) { | ||
1233 | ret = request_irq(drv_data->spi_irq, bfin_spi_pio_irq_handler, | ||
1234 | IRQF_DISABLED, "BFIN_SPI", drv_data); | ||
1235 | if (ret) { | ||
1236 | dev_err(&spi->dev, "Unable to register spi IRQ\n"); | ||
1237 | goto error; | ||
1238 | } | ||
1239 | drv_data->irq_requested = 1; | ||
1240 | /* we use write mode, spi irq has to be disabled here */ | ||
1241 | disable_irq(drv_data->spi_irq); | ||
1242 | } | ||
1243 | |||
1122 | if (chip->chip_select_num == 0) { | 1244 | if (chip->chip_select_num == 0) { |
1123 | ret = gpio_request(chip->cs_gpio, spi->modalias); | 1245 | ret = gpio_request(chip->cs_gpio, spi->modalias); |
1124 | if (ret) { | 1246 | if (ret) { |
@@ -1328,11 +1450,19 @@ static int __init bfin_spi_probe(struct platform_device *pdev) | |||
1328 | goto out_error_ioremap; | 1450 | goto out_error_ioremap; |
1329 | } | 1451 | } |
1330 | 1452 | ||
1331 | drv_data->dma_channel = platform_get_irq(pdev, 0); | 1453 | res = platform_get_resource(pdev, IORESOURCE_DMA, 0); |
1332 | if (drv_data->dma_channel < 0) { | 1454 | if (res == NULL) { |
1333 | dev_err(dev, "No DMA channel specified\n"); | 1455 | dev_err(dev, "No DMA channel specified\n"); |
1334 | status = -ENOENT; | 1456 | status = -ENOENT; |
1335 | goto out_error_no_dma_ch; | 1457 | goto out_error_free_io; |
1458 | } | ||
1459 | drv_data->dma_channel = res->start; | ||
1460 | |||
1461 | drv_data->spi_irq = platform_get_irq(pdev, 0); | ||
1462 | if (drv_data->spi_irq < 0) { | ||
1463 | dev_err(dev, "No spi pio irq specified\n"); | ||
1464 | status = -ENOENT; | ||
1465 | goto out_error_free_io; | ||
1336 | } | 1466 | } |
1337 | 1467 | ||
1338 | /* Initial and start queue */ | 1468 | /* Initial and start queue */ |
@@ -1375,7 +1505,7 @@ static int __init bfin_spi_probe(struct platform_device *pdev) | |||
1375 | 1505 | ||
1376 | out_error_queue_alloc: | 1506 | out_error_queue_alloc: |
1377 | bfin_spi_destroy_queue(drv_data); | 1507 | bfin_spi_destroy_queue(drv_data); |
1378 | out_error_no_dma_ch: | 1508 | out_error_free_io: |
1379 | iounmap((void *) drv_data->regs_base); | 1509 | iounmap((void *) drv_data->regs_base); |
1380 | out_error_ioremap: | 1510 | out_error_ioremap: |
1381 | out_error_get_res: | 1511 | out_error_get_res: |
@@ -1407,6 +1537,11 @@ static int __devexit bfin_spi_remove(struct platform_device *pdev) | |||
1407 | free_dma(drv_data->dma_channel); | 1537 | free_dma(drv_data->dma_channel); |
1408 | } | 1538 | } |
1409 | 1539 | ||
1540 | if (drv_data->irq_requested) { | ||
1541 | free_irq(drv_data->spi_irq, drv_data); | ||
1542 | drv_data->irq_requested = 0; | ||
1543 | } | ||
1544 | |||
1410 | /* Disconnect from the SPI framework */ | 1545 | /* Disconnect from the SPI framework */ |
1411 | spi_unregister_master(drv_data->master); | 1546 | spi_unregister_master(drv_data->master); |
1412 | 1547 | ||