diff options
| -rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 181 |
1 files changed, 150 insertions, 31 deletions
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 0c73dd4f43a0..9fdb7a9ae03f 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c | |||
| @@ -20,6 +20,8 @@ | |||
| 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 21 | * | 21 | * |
| 22 | */ | 22 | */ |
| 23 | #define USE_DMA_ENGINE_RX | ||
| 24 | #define USE_DMA_ENGINE_TX | ||
| 23 | 25 | ||
| 24 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
| 25 | #include <linux/init.h> | 27 | #include <linux/init.h> |
| @@ -28,6 +30,8 @@ | |||
| 28 | #include <linux/device.h> | 30 | #include <linux/device.h> |
| 29 | #include <linux/delay.h> | 31 | #include <linux/delay.h> |
| 30 | #include <linux/dma-mapping.h> | 32 | #include <linux/dma-mapping.h> |
| 33 | #include <linux/dmaengine.h> | ||
| 34 | #include <linux/omap-dma.h> | ||
| 31 | #include <linux/platform_device.h> | 35 | #include <linux/platform_device.h> |
| 32 | #include <linux/err.h> | 36 | #include <linux/err.h> |
| 33 | #include <linux/clk.h> | 37 | #include <linux/clk.h> |
| @@ -93,6 +97,8 @@ | |||
| 93 | 97 | ||
| 94 | /* We have 2 DMA channels per CS, one for RX and one for TX */ | 98 | /* We have 2 DMA channels per CS, one for RX and one for TX */ |
| 95 | struct omap2_mcspi_dma { | 99 | struct omap2_mcspi_dma { |
| 100 | struct dma_chan *dma_tx; | ||
| 101 | struct dma_chan *dma_rx; | ||
| 96 | int dma_tx_channel; | 102 | int dma_tx_channel; |
| 97 | int dma_rx_channel; | 103 | int dma_rx_channel; |
| 98 | 104 | ||
| @@ -300,6 +306,30 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) | |||
| 300 | return 0; | 306 | return 0; |
| 301 | } | 307 | } |
| 302 | 308 | ||
| 309 | static void omap2_mcspi_rx_callback(void *data) | ||
| 310 | { | ||
| 311 | struct spi_device *spi = data; | ||
| 312 | struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); | ||
| 313 | struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; | ||
| 314 | |||
| 315 | complete(&mcspi_dma->dma_rx_completion); | ||
| 316 | |||
| 317 | /* We must disable the DMA RX request */ | ||
| 318 | omap2_mcspi_set_dma_req(spi, 1, 0); | ||
| 319 | } | ||
| 320 | |||
| 321 | static void omap2_mcspi_tx_callback(void *data) | ||
| 322 | { | ||
| 323 | struct spi_device *spi = data; | ||
| 324 | struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); | ||
| 325 | struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; | ||
| 326 | |||
| 327 | complete(&mcspi_dma->dma_tx_completion); | ||
| 328 | |||
| 329 | /* We must disable the DMA TX request */ | ||
| 330 | omap2_mcspi_set_dma_req(spi, 0, 0); | ||
| 331 | } | ||
| 332 | |||
| 303 | static unsigned | 333 | static unsigned |
| 304 | omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) | 334 | omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) |
| 305 | { | 335 | { |
| @@ -314,6 +344,9 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) | |||
| 314 | u8 * rx; | 344 | u8 * rx; |
| 315 | const u8 * tx; | 345 | const u8 * tx; |
| 316 | void __iomem *chstat_reg; | 346 | void __iomem *chstat_reg; |
| 347 | struct dma_slave_config cfg; | ||
| 348 | enum dma_slave_buswidth width; | ||
| 349 | unsigned es; | ||
| 317 | 350 | ||
| 318 | mcspi = spi_master_get_devdata(spi->master); | 351 | mcspi = spi_master_get_devdata(spi->master); |
| 319 | mcspi_dma = &mcspi->dma_channels[spi->chip_select]; | 352 | mcspi_dma = &mcspi->dma_channels[spi->chip_select]; |
| @@ -321,6 +354,71 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) | |||
| 321 | 354 | ||
| 322 | chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; | 355 | chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; |
| 323 | 356 | ||
| 357 | if (cs->word_len <= 8) { | ||
| 358 | width = DMA_SLAVE_BUSWIDTH_1_BYTE; | ||
| 359 | es = 1; | ||
| 360 | } else if (cs->word_len <= 16) { | ||
| 361 | width = DMA_SLAVE_BUSWIDTH_2_BYTES; | ||
| 362 | es = 2; | ||
| 363 | } else { | ||
| 364 | width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
| 365 | es = 4; | ||
| 366 | } | ||
| 367 | |||
| 368 | memset(&cfg, 0, sizeof(cfg)); | ||
| 369 | cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; | ||
| 370 | cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; | ||
| 371 | cfg.src_addr_width = width; | ||
| 372 | cfg.dst_addr_width = width; | ||
| 373 | cfg.src_maxburst = 1; | ||
| 374 | cfg.dst_maxburst = 1; | ||
| 375 | |||
| 376 | if (xfer->tx_buf && mcspi_dma->dma_tx) { | ||
| 377 | struct dma_async_tx_descriptor *tx; | ||
| 378 | struct scatterlist sg; | ||
| 379 | |||
| 380 | dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); | ||
| 381 | |||
| 382 | sg_init_table(&sg, 1); | ||
| 383 | sg_dma_address(&sg) = xfer->tx_dma; | ||
| 384 | sg_dma_len(&sg) = xfer->len; | ||
| 385 | |||
| 386 | tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1, | ||
| 387 | DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
| 388 | if (tx) { | ||
| 389 | tx->callback = omap2_mcspi_tx_callback; | ||
| 390 | tx->callback_param = spi; | ||
| 391 | dmaengine_submit(tx); | ||
| 392 | } else { | ||
| 393 | /* FIXME: fall back to PIO? */ | ||
| 394 | } | ||
| 395 | } | ||
| 396 | |||
| 397 | if (xfer->rx_buf && mcspi_dma->dma_rx) { | ||
| 398 | struct dma_async_tx_descriptor *tx; | ||
| 399 | struct scatterlist sg; | ||
| 400 | size_t len = xfer->len - es; | ||
| 401 | |||
| 402 | dmaengine_slave_config(mcspi_dma->dma_rx, &cfg); | ||
| 403 | |||
| 404 | if (l & OMAP2_MCSPI_CHCONF_TURBO) | ||
| 405 | len -= es; | ||
| 406 | |||
| 407 | sg_init_table(&sg, 1); | ||
| 408 | sg_dma_address(&sg) = xfer->rx_dma; | ||
| 409 | sg_dma_len(&sg) = len; | ||
| 410 | |||
| 411 | tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, | ||
| 412 | DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
| 413 | if (tx) { | ||
| 414 | tx->callback = omap2_mcspi_rx_callback; | ||
| 415 | tx->callback_param = spi; | ||
| 416 | dmaengine_submit(tx); | ||
| 417 | } else { | ||
| 418 | /* FIXME: fall back to PIO? */ | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 324 | count = xfer->len; | 422 | count = xfer->len; |
| 325 | c = count; | 423 | c = count; |
| 326 | word_len = cs->word_len; | 424 | word_len = cs->word_len; |
| @@ -342,7 +440,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) | |||
| 342 | element_count = count >> 2; | 440 | element_count = count >> 2; |
| 343 | } | 441 | } |
| 344 | 442 | ||
| 345 | if (tx != NULL) { | 443 | if (tx != NULL && mcspi_dma->dma_tx_channel != -1) { |
| 346 | omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel, | 444 | omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel, |
| 347 | data_type, element_count, 1, | 445 | data_type, element_count, 1, |
| 348 | OMAP_DMA_SYNC_ELEMENT, | 446 | OMAP_DMA_SYNC_ELEMENT, |
| @@ -357,7 +455,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) | |||
| 357 | xfer->tx_dma, 0, 0); | 455 | xfer->tx_dma, 0, 0); |
| 358 | } | 456 | } |
| 359 | 457 | ||
| 360 | if (rx != NULL) { | 458 | if (rx != NULL && mcspi_dma->dma_rx_channel != -1) { |
| 361 | elements = element_count - 1; | 459 | elements = element_count - 1; |
| 362 | if (l & OMAP2_MCSPI_CHCONF_TURBO) | 460 | if (l & OMAP2_MCSPI_CHCONF_TURBO) |
| 363 | elements--; | 461 | elements--; |
| @@ -377,12 +475,18 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) | |||
| 377 | } | 475 | } |
| 378 | 476 | ||
| 379 | if (tx != NULL) { | 477 | if (tx != NULL) { |
| 380 | omap_start_dma(mcspi_dma->dma_tx_channel); | 478 | if (mcspi_dma->dma_tx) |
| 479 | dma_async_issue_pending(mcspi_dma->dma_tx); | ||
| 480 | else | ||
| 481 | omap_start_dma(mcspi_dma->dma_tx_channel); | ||
| 381 | omap2_mcspi_set_dma_req(spi, 0, 1); | 482 | omap2_mcspi_set_dma_req(spi, 0, 1); |
| 382 | } | 483 | } |
| 383 | 484 | ||
| 384 | if (rx != NULL) { | 485 | if (rx != NULL) { |
| 385 | omap_start_dma(mcspi_dma->dma_rx_channel); | 486 | if (mcspi_dma->dma_rx) |
| 487 | dma_async_issue_pending(mcspi_dma->dma_rx); | ||
| 488 | else | ||
| 489 | omap_start_dma(mcspi_dma->dma_rx_channel); | ||
| 386 | omap2_mcspi_set_dma_req(spi, 1, 1); | 490 | omap2_mcspi_set_dma_req(spi, 1, 1); |
| 387 | } | 491 | } |
| 388 | 492 | ||
| @@ -406,7 +510,10 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) | |||
| 406 | dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE); | 510 | dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE); |
| 407 | omap2_mcspi_set_enable(spi, 0); | 511 | omap2_mcspi_set_enable(spi, 0); |
| 408 | 512 | ||
| 513 | elements = element_count - 1; | ||
| 514 | |||
| 409 | if (l & OMAP2_MCSPI_CHCONF_TURBO) { | 515 | if (l & OMAP2_MCSPI_CHCONF_TURBO) { |
| 516 | elements--; | ||
| 410 | 517 | ||
| 411 | if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) | 518 | if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) |
| 412 | & OMAP2_MCSPI_CHSTAT_RXS)) { | 519 | & OMAP2_MCSPI_CHSTAT_RXS)) { |
| @@ -725,32 +832,12 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, | |||
| 725 | 832 | ||
| 726 | static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data) | 833 | static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data) |
| 727 | { | 834 | { |
| 728 | struct spi_device *spi = data; | 835 | omap2_mcspi_rx_callback(data); |
| 729 | struct omap2_mcspi *mcspi; | ||
| 730 | struct omap2_mcspi_dma *mcspi_dma; | ||
| 731 | |||
| 732 | mcspi = spi_master_get_devdata(spi->master); | ||
| 733 | mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); | ||
| 734 | |||
| 735 | complete(&mcspi_dma->dma_rx_completion); | ||
| 736 | |||
| 737 | /* We must disable the DMA RX request */ | ||
| 738 | omap2_mcspi_set_dma_req(spi, 1, 0); | ||
| 739 | } | 836 | } |
| 740 | 837 | ||
| 741 | static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data) | 838 | static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data) |
| 742 | { | 839 | { |
| 743 | struct spi_device *spi = data; | 840 | omap2_mcspi_tx_callback(data); |
| 744 | struct omap2_mcspi *mcspi; | ||
| 745 | struct omap2_mcspi_dma *mcspi_dma; | ||
| 746 | |||
| 747 | mcspi = spi_master_get_devdata(spi->master); | ||
| 748 | mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); | ||
| 749 | |||
| 750 | complete(&mcspi_dma->dma_tx_completion); | ||
| 751 | |||
| 752 | /* We must disable the DMA TX request */ | ||
| 753 | omap2_mcspi_set_dma_req(spi, 0, 0); | ||
| 754 | } | 841 | } |
| 755 | 842 | ||
| 756 | static int omap2_mcspi_request_dma(struct spi_device *spi) | 843 | static int omap2_mcspi_request_dma(struct spi_device *spi) |
| @@ -758,17 +845,43 @@ static int omap2_mcspi_request_dma(struct spi_device *spi) | |||
| 758 | struct spi_master *master = spi->master; | 845 | struct spi_master *master = spi->master; |
| 759 | struct omap2_mcspi *mcspi; | 846 | struct omap2_mcspi *mcspi; |
| 760 | struct omap2_mcspi_dma *mcspi_dma; | 847 | struct omap2_mcspi_dma *mcspi_dma; |
| 848 | dma_cap_mask_t mask; | ||
| 849 | unsigned sig; | ||
| 761 | 850 | ||
| 762 | mcspi = spi_master_get_devdata(master); | 851 | mcspi = spi_master_get_devdata(master); |
| 763 | mcspi_dma = mcspi->dma_channels + spi->chip_select; | 852 | mcspi_dma = mcspi->dma_channels + spi->chip_select; |
| 764 | 853 | ||
| 854 | init_completion(&mcspi_dma->dma_rx_completion); | ||
| 855 | init_completion(&mcspi_dma->dma_tx_completion); | ||
| 856 | |||
| 857 | dma_cap_zero(mask); | ||
| 858 | dma_cap_set(DMA_SLAVE, mask); | ||
| 859 | #ifdef USE_DMA_ENGINE_RX | ||
| 860 | sig = mcspi_dma->dma_rx_sync_dev; | ||
| 861 | mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); | ||
| 862 | if (!mcspi_dma->dma_rx) { | ||
| 863 | dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n"); | ||
| 864 | return -EAGAIN; | ||
| 865 | } | ||
| 866 | #else | ||
| 765 | if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX", | 867 | if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX", |
| 766 | omap2_mcspi_dma_rx_callback, spi, | 868 | omap2_mcspi_dma_rx_callback, spi, |
| 767 | &mcspi_dma->dma_rx_channel)) { | 869 | &mcspi_dma->dma_rx_channel)) { |
| 768 | dev_err(&spi->dev, "no RX DMA channel for McSPI\n"); | 870 | dev_err(&spi->dev, "no RX DMA channel for McSPI\n"); |
| 769 | return -EAGAIN; | 871 | return -EAGAIN; |
| 770 | } | 872 | } |
| 873 | #endif | ||
| 771 | 874 | ||
| 875 | #ifdef USE_DMA_ENGINE_TX | ||
| 876 | sig = mcspi_dma->dma_tx_sync_dev; | ||
| 877 | mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); | ||
| 878 | if (!mcspi_dma->dma_tx) { | ||
| 879 | dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n"); | ||
| 880 | dma_release_channel(mcspi_dma->dma_rx); | ||
| 881 | mcspi_dma->dma_rx = NULL; | ||
| 882 | return -EAGAIN; | ||
| 883 | } | ||
| 884 | #else | ||
| 772 | if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX", | 885 | if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX", |
| 773 | omap2_mcspi_dma_tx_callback, spi, | 886 | omap2_mcspi_dma_tx_callback, spi, |
| 774 | &mcspi_dma->dma_tx_channel)) { | 887 | &mcspi_dma->dma_tx_channel)) { |
| @@ -777,9 +890,7 @@ static int omap2_mcspi_request_dma(struct spi_device *spi) | |||
| 777 | dev_err(&spi->dev, "no TX DMA channel for McSPI\n"); | 890 | dev_err(&spi->dev, "no TX DMA channel for McSPI\n"); |
| 778 | return -EAGAIN; | 891 | return -EAGAIN; |
| 779 | } | 892 | } |
| 780 | 893 | #endif | |
| 781 | init_completion(&mcspi_dma->dma_rx_completion); | ||
| 782 | init_completion(&mcspi_dma->dma_tx_completion); | ||
| 783 | 894 | ||
| 784 | return 0; | 895 | return 0; |
| 785 | } | 896 | } |
| @@ -812,8 +923,8 @@ static int omap2_mcspi_setup(struct spi_device *spi) | |||
| 812 | list_add_tail(&cs->node, &ctx->cs); | 923 | list_add_tail(&cs->node, &ctx->cs); |
| 813 | } | 924 | } |
| 814 | 925 | ||
| 815 | if (mcspi_dma->dma_rx_channel == -1 | 926 | if ((!mcspi_dma->dma_rx && mcspi_dma->dma_rx_channel == -1) || |
| 816 | || mcspi_dma->dma_tx_channel == -1) { | 927 | (!mcspi_dma->dma_tx && mcspi_dma->dma_tx_channel == -1)) { |
| 817 | ret = omap2_mcspi_request_dma(spi); | 928 | ret = omap2_mcspi_request_dma(spi); |
| 818 | if (ret < 0) | 929 | if (ret < 0) |
| 819 | return ret; | 930 | return ret; |
| @@ -848,6 +959,14 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) | |||
| 848 | if (spi->chip_select < spi->master->num_chipselect) { | 959 | if (spi->chip_select < spi->master->num_chipselect) { |
| 849 | mcspi_dma = &mcspi->dma_channels[spi->chip_select]; | 960 | mcspi_dma = &mcspi->dma_channels[spi->chip_select]; |
| 850 | 961 | ||
| 962 | if (mcspi_dma->dma_rx) { | ||
| 963 | dma_release_channel(mcspi_dma->dma_rx); | ||
| 964 | mcspi_dma->dma_rx = NULL; | ||
| 965 | } | ||
| 966 | if (mcspi_dma->dma_tx) { | ||
| 967 | dma_release_channel(mcspi_dma->dma_tx); | ||
| 968 | mcspi_dma->dma_tx = NULL; | ||
| 969 | } | ||
| 851 | if (mcspi_dma->dma_rx_channel != -1) { | 970 | if (mcspi_dma->dma_rx_channel != -1) { |
| 852 | omap_free_dma(mcspi_dma->dma_rx_channel); | 971 | omap_free_dma(mcspi_dma->dma_rx_channel); |
| 853 | mcspi_dma->dma_rx_channel = -1; | 972 | mcspi_dma->dma_rx_channel = -1; |
