diff options
| author | Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> | 2018-09-29 03:46:36 -0400 |
|---|---|---|
| committer | Vinod Koul <vkoul@kernel.org> | 2018-10-05 11:00:57 -0400 |
| commit | d64e1b3f5cce41dd878a3ea008dd696a4d7d07d9 (patch) | |
| tree | d1f745215db992ee0846a6945f6058675bc2f2f9 | |
| parent | 5b394b2ddf0347bef56e50c69a58773c94343ff3 (diff) | |
dmaengine: owl: Add Slave and Cyclic mode support for Actions Semi Owl S900 SoC
Add Slave and Cyclic mode support for Actions Semi Owl S900 SoC. The slave
mode supports bus width of 4 bytes common for all peripherals and 1 byte
specific for UART.
The cyclic mode supports only block mode transfer.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
| -rw-r--r-- | drivers/dma/owl-dma.c | 279 |
1 files changed, 272 insertions, 7 deletions
diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c index 7812a6338acd..1d26db4c9229 100644 --- a/drivers/dma/owl-dma.c +++ b/drivers/dma/owl-dma.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include <linux/mm.h> | 21 | #include <linux/mm.h> |
| 22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
| 23 | #include <linux/of_device.h> | 23 | #include <linux/of_device.h> |
| 24 | #include <linux/of_dma.h> | ||
| 24 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
| 25 | #include "virt-dma.h" | 26 | #include "virt-dma.h" |
| 26 | 27 | ||
| @@ -165,6 +166,7 @@ struct owl_dma_lli { | |||
| 165 | struct owl_dma_txd { | 166 | struct owl_dma_txd { |
| 166 | struct virt_dma_desc vd; | 167 | struct virt_dma_desc vd; |
| 167 | struct list_head lli_list; | 168 | struct list_head lli_list; |
| 169 | bool cyclic; | ||
| 168 | }; | 170 | }; |
| 169 | 171 | ||
| 170 | /** | 172 | /** |
| @@ -191,6 +193,8 @@ struct owl_dma_vchan { | |||
| 191 | struct virt_dma_chan vc; | 193 | struct virt_dma_chan vc; |
| 192 | struct owl_dma_pchan *pchan; | 194 | struct owl_dma_pchan *pchan; |
| 193 | struct owl_dma_txd *txd; | 195 | struct owl_dma_txd *txd; |
| 196 | struct dma_slave_config cfg; | ||
| 197 | u8 drq; | ||
| 194 | }; | 198 | }; |
| 195 | 199 | ||
| 196 | /** | 200 | /** |
| @@ -336,9 +340,11 @@ static struct owl_dma_lli *owl_dma_alloc_lli(struct owl_dma *od) | |||
| 336 | 340 | ||
| 337 | static struct owl_dma_lli *owl_dma_add_lli(struct owl_dma_txd *txd, | 341 | static struct owl_dma_lli *owl_dma_add_lli(struct owl_dma_txd *txd, |
| 338 | struct owl_dma_lli *prev, | 342 | struct owl_dma_lli *prev, |
| 339 | struct owl_dma_lli *next) | 343 | struct owl_dma_lli *next, |
| 344 | bool is_cyclic) | ||
| 340 | { | 345 | { |
| 341 | list_add_tail(&next->node, &txd->lli_list); | 346 | if (!is_cyclic) |
| 347 | list_add_tail(&next->node, &txd->lli_list); | ||
| 342 | 348 | ||
| 343 | if (prev) { | 349 | if (prev) { |
| 344 | prev->hw.next_lli = next->phys; | 350 | prev->hw.next_lli = next->phys; |
| @@ -351,7 +357,9 @@ static struct owl_dma_lli *owl_dma_add_lli(struct owl_dma_txd *txd, | |||
| 351 | static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, | 357 | static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, |
| 352 | struct owl_dma_lli *lli, | 358 | struct owl_dma_lli *lli, |
| 353 | dma_addr_t src, dma_addr_t dst, | 359 | dma_addr_t src, dma_addr_t dst, |
| 354 | u32 len, enum dma_transfer_direction dir) | 360 | u32 len, enum dma_transfer_direction dir, |
| 361 | struct dma_slave_config *sconfig, | ||
| 362 | bool is_cyclic) | ||
| 355 | { | 363 | { |
| 356 | struct owl_dma_lli_hw *hw = &lli->hw; | 364 | struct owl_dma_lli_hw *hw = &lli->hw; |
| 357 | u32 mode; | 365 | u32 mode; |
| @@ -365,6 +373,32 @@ static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, | |||
| 365 | OWL_DMA_MODE_DAM_INC; | 373 | OWL_DMA_MODE_DAM_INC; |
| 366 | 374 | ||
| 367 | break; | 375 | break; |
| 376 | case DMA_MEM_TO_DEV: | ||
| 377 | mode |= OWL_DMA_MODE_TS(vchan->drq) | ||
| 378 | | OWL_DMA_MODE_ST_DCU | OWL_DMA_MODE_DT_DEV | ||
| 379 | | OWL_DMA_MODE_SAM_INC | OWL_DMA_MODE_DAM_CONST; | ||
| 380 | |||
| 381 | /* | ||
| 382 | * Hardware only supports 32bit and 8bit buswidth. Since the | ||
| 383 | * default is 32bit, select 8bit only when requested. | ||
| 384 | */ | ||
| 385 | if (sconfig->dst_addr_width == DMA_SLAVE_BUSWIDTH_1_BYTE) | ||
| 386 | mode |= OWL_DMA_MODE_NDDBW_8BIT; | ||
| 387 | |||
| 388 | break; | ||
| 389 | case DMA_DEV_TO_MEM: | ||
| 390 | mode |= OWL_DMA_MODE_TS(vchan->drq) | ||
| 391 | | OWL_DMA_MODE_ST_DEV | OWL_DMA_MODE_DT_DCU | ||
| 392 | | OWL_DMA_MODE_SAM_CONST | OWL_DMA_MODE_DAM_INC; | ||
| 393 | |||
| 394 | /* | ||
| 395 | * Hardware only supports 32bit and 8bit buswidth. Since the | ||
| 396 | * default is 32bit, select 8bit only when requested. | ||
| 397 | */ | ||
| 398 | if (sconfig->src_addr_width == DMA_SLAVE_BUSWIDTH_1_BYTE) | ||
| 399 | mode |= OWL_DMA_MODE_NDDBW_8BIT; | ||
| 400 | |||
| 401 | break; | ||
| 368 | default: | 402 | default: |
| 369 | return -EINVAL; | 403 | return -EINVAL; |
| 370 | } | 404 | } |
| @@ -381,7 +415,10 @@ static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, | |||
| 381 | OWL_DMA_LLC_SAV_LOAD_NEXT | | 415 | OWL_DMA_LLC_SAV_LOAD_NEXT | |
| 382 | OWL_DMA_LLC_DAV_LOAD_NEXT); | 416 | OWL_DMA_LLC_DAV_LOAD_NEXT); |
| 383 | 417 | ||
| 384 | hw->ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_SUPER_BLOCK); | 418 | if (is_cyclic) |
| 419 | hw->ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_BLOCK); | ||
| 420 | else | ||
| 421 | hw->ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_SUPER_BLOCK); | ||
| 385 | 422 | ||
| 386 | return 0; | 423 | return 0; |
| 387 | } | 424 | } |
| @@ -443,6 +480,16 @@ static void owl_dma_terminate_pchan(struct owl_dma *od, | |||
| 443 | spin_unlock_irqrestore(&od->lock, flags); | 480 | spin_unlock_irqrestore(&od->lock, flags); |
| 444 | } | 481 | } |
| 445 | 482 | ||
| 483 | static void owl_dma_pause_pchan(struct owl_dma_pchan *pchan) | ||
| 484 | { | ||
| 485 | pchan_writel(pchan, 1, OWL_DMAX_PAUSE); | ||
| 486 | } | ||
| 487 | |||
| 488 | static void owl_dma_resume_pchan(struct owl_dma_pchan *pchan) | ||
| 489 | { | ||
| 490 | pchan_writel(pchan, 0, OWL_DMAX_PAUSE); | ||
| 491 | } | ||
| 492 | |||
| 446 | static int owl_dma_start_next_txd(struct owl_dma_vchan *vchan) | 493 | static int owl_dma_start_next_txd(struct owl_dma_vchan *vchan) |
| 447 | { | 494 | { |
| 448 | struct owl_dma *od = to_owl_dma(vchan->vc.chan.device); | 495 | struct owl_dma *od = to_owl_dma(vchan->vc.chan.device); |
| @@ -464,7 +511,10 @@ static int owl_dma_start_next_txd(struct owl_dma_vchan *vchan) | |||
| 464 | lli = list_first_entry(&txd->lli_list, | 511 | lli = list_first_entry(&txd->lli_list, |
| 465 | struct owl_dma_lli, node); | 512 | struct owl_dma_lli, node); |
| 466 | 513 | ||
| 467 | int_ctl = OWL_DMA_INTCTL_SUPER_BLOCK; | 514 | if (txd->cyclic) |
| 515 | int_ctl = OWL_DMA_INTCTL_BLOCK; | ||
| 516 | else | ||
| 517 | int_ctl = OWL_DMA_INTCTL_SUPER_BLOCK; | ||
| 468 | 518 | ||
| 469 | pchan_writel(pchan, OWL_DMAX_MODE, OWL_DMA_MODE_LME); | 519 | pchan_writel(pchan, OWL_DMAX_MODE, OWL_DMA_MODE_LME); |
| 470 | pchan_writel(pchan, OWL_DMAX_LINKLIST_CTL, | 520 | pchan_writel(pchan, OWL_DMAX_LINKLIST_CTL, |
| @@ -627,6 +677,54 @@ static int owl_dma_terminate_all(struct dma_chan *chan) | |||
| 627 | return 0; | 677 | return 0; |
| 628 | } | 678 | } |
| 629 | 679 | ||
| 680 | static int owl_dma_config(struct dma_chan *chan, | ||
| 681 | struct dma_slave_config *config) | ||
| 682 | { | ||
| 683 | struct owl_dma_vchan *vchan = to_owl_vchan(chan); | ||
| 684 | |||
| 685 | /* Reject definitely invalid configurations */ | ||
| 686 | if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || | ||
| 687 | config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) | ||
| 688 | return -EINVAL; | ||
| 689 | |||
| 690 | memcpy(&vchan->cfg, config, sizeof(struct dma_slave_config)); | ||
| 691 | |||
| 692 | return 0; | ||
| 693 | } | ||
| 694 | |||
| 695 | static int owl_dma_pause(struct dma_chan *chan) | ||
| 696 | { | ||
| 697 | struct owl_dma_vchan *vchan = to_owl_vchan(chan); | ||
| 698 | unsigned long flags; | ||
| 699 | |||
| 700 | spin_lock_irqsave(&vchan->vc.lock, flags); | ||
| 701 | |||
| 702 | owl_dma_pause_pchan(vchan->pchan); | ||
| 703 | |||
| 704 | spin_unlock_irqrestore(&vchan->vc.lock, flags); | ||
| 705 | |||
| 706 | return 0; | ||
| 707 | } | ||
| 708 | |||
| 709 | static int owl_dma_resume(struct dma_chan *chan) | ||
| 710 | { | ||
| 711 | struct owl_dma_vchan *vchan = to_owl_vchan(chan); | ||
| 712 | unsigned long flags; | ||
| 713 | |||
| 714 | if (!vchan->pchan && !vchan->txd) | ||
| 715 | return 0; | ||
| 716 | |||
| 717 | dev_dbg(chan2dev(chan), "vchan %p: resume\n", &vchan->vc); | ||
| 718 | |||
| 719 | spin_lock_irqsave(&vchan->vc.lock, flags); | ||
| 720 | |||
| 721 | owl_dma_resume_pchan(vchan->pchan); | ||
| 722 | |||
| 723 | spin_unlock_irqrestore(&vchan->vc.lock, flags); | ||
| 724 | |||
| 725 | return 0; | ||
| 726 | } | ||
| 727 | |||
| 630 | static u32 owl_dma_getbytes_chan(struct owl_dma_vchan *vchan) | 728 | static u32 owl_dma_getbytes_chan(struct owl_dma_vchan *vchan) |
| 631 | { | 729 | { |
| 632 | struct owl_dma_pchan *pchan; | 730 | struct owl_dma_pchan *pchan; |
| @@ -754,13 +852,14 @@ static struct dma_async_tx_descriptor | |||
| 754 | bytes = min_t(size_t, (len - offset), OWL_DMA_FRAME_MAX_LENGTH); | 852 | bytes = min_t(size_t, (len - offset), OWL_DMA_FRAME_MAX_LENGTH); |
| 755 | 853 | ||
| 756 | ret = owl_dma_cfg_lli(vchan, lli, src + offset, dst + offset, | 854 | ret = owl_dma_cfg_lli(vchan, lli, src + offset, dst + offset, |
| 757 | bytes, DMA_MEM_TO_MEM); | 855 | bytes, DMA_MEM_TO_MEM, |
| 856 | &vchan->cfg, txd->cyclic); | ||
| 758 | if (ret) { | 857 | if (ret) { |
