.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2013 - 2015 Linaro Ltd. |
---|
3 | 4 | * Copyright (c) 2013 Hisilicon Limited. |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or modify |
---|
6 | | - * it under the terms of the GNU General Public License version 2 as |
---|
7 | | - * published by the Free Software Foundation. |
---|
8 | 5 | */ |
---|
9 | 6 | #include <linux/sched.h> |
---|
10 | 7 | #include <linux/device.h> |
---|
.. | .. |
---|
52 | 49 | #define CX_SRC 0x814 |
---|
53 | 50 | #define CX_DST 0x818 |
---|
54 | 51 | #define CX_CFG 0x81c |
---|
55 | | -#define AXI_CFG 0x820 |
---|
56 | | -#define AXI_CFG_DEFAULT 0x201201 |
---|
57 | 52 | |
---|
58 | 53 | #define CX_LLI_CHAIN_EN 0x2 |
---|
59 | 54 | #define CX_CFG_EN 0x1 |
---|
.. | .. |
---|
87 | 82 | struct virt_dma_chan vc; |
---|
88 | 83 | struct k3_dma_phy *phy; |
---|
89 | 84 | struct list_head node; |
---|
90 | | - enum dma_transfer_direction dir; |
---|
91 | 85 | dma_addr_t dev_addr; |
---|
92 | 86 | enum dma_status status; |
---|
93 | 87 | bool cyclic; |
---|
| 88 | + struct dma_slave_config slave_config; |
---|
94 | 89 | }; |
---|
95 | 90 | |
---|
96 | 91 | struct k3_dma_phy { |
---|
.. | .. |
---|
113 | 108 | struct dma_pool *pool; |
---|
114 | 109 | u32 dma_channels; |
---|
115 | 110 | u32 dma_requests; |
---|
| 111 | + u32 dma_channel_mask; |
---|
116 | 112 | unsigned int irq; |
---|
117 | 113 | }; |
---|
118 | 114 | |
---|
| 115 | + |
---|
| 116 | +#define K3_FLAG_NOCLK BIT(1) |
---|
| 117 | + |
---|
| 118 | +struct k3dma_soc_data { |
---|
| 119 | + unsigned long flags; |
---|
| 120 | +}; |
---|
| 121 | + |
---|
| 122 | + |
---|
119 | 123 | #define to_k3_dma(dmadev) container_of(dmadev, struct k3_dma_dev, slave) |
---|
| 124 | + |
---|
| 125 | +static int k3_dma_config_write(struct dma_chan *chan, |
---|
| 126 | + enum dma_transfer_direction dir, |
---|
| 127 | + struct dma_slave_config *cfg); |
---|
120 | 128 | |
---|
121 | 129 | static struct k3_dma_chan *to_k3_chan(struct dma_chan *chan) |
---|
122 | 130 | { |
---|
.. | .. |
---|
157 | 165 | writel_relaxed(hw->count, phy->base + CX_CNT0); |
---|
158 | 166 | writel_relaxed(hw->saddr, phy->base + CX_SRC); |
---|
159 | 167 | writel_relaxed(hw->daddr, phy->base + CX_DST); |
---|
160 | | - writel_relaxed(AXI_CFG_DEFAULT, phy->base + AXI_CFG); |
---|
161 | 168 | writel_relaxed(hw->config, phy->base + CX_CFG); |
---|
162 | 169 | } |
---|
163 | 170 | |
---|
.. | .. |
---|
290 | 297 | return -EAGAIN; |
---|
291 | 298 | } |
---|
292 | 299 | |
---|
293 | | -static void k3_dma_tasklet(unsigned long arg) |
---|
| 300 | +static void k3_dma_tasklet(struct tasklet_struct *t) |
---|
294 | 301 | { |
---|
295 | | - struct k3_dma_dev *d = (struct k3_dma_dev *)arg; |
---|
| 302 | + struct k3_dma_dev *d = from_tasklet(d, t, task); |
---|
296 | 303 | struct k3_dma_phy *p; |
---|
297 | 304 | struct k3_dma_chan *c, *cn; |
---|
298 | 305 | unsigned pch, pch_alloc = 0; |
---|
.. | .. |
---|
316 | 323 | /* check new channel request in d->chan_pending */ |
---|
317 | 324 | spin_lock_irq(&d->lock); |
---|
318 | 325 | for (pch = 0; pch < d->dma_channels; pch++) { |
---|
| 326 | + if (!(d->dma_channel_mask & (1 << pch))) |
---|
| 327 | + continue; |
---|
| 328 | + |
---|
319 | 329 | p = &d->phy[pch]; |
---|
320 | 330 | |
---|
321 | 331 | if (p->vchan == NULL && !list_empty(&d->chan_pending)) { |
---|
.. | .. |
---|
333 | 343 | spin_unlock_irq(&d->lock); |
---|
334 | 344 | |
---|
335 | 345 | for (pch = 0; pch < d->dma_channels; pch++) { |
---|
| 346 | + if (!(d->dma_channel_mask & (1 << pch))) |
---|
| 347 | + continue; |
---|
| 348 | + |
---|
336 | 349 | if (pch_alloc & (1 << pch)) { |
---|
337 | 350 | p = &d->phy[pch]; |
---|
338 | 351 | c = p->vchan; |
---|
.. | .. |
---|
507 | 520 | copy = min_t(size_t, len, DMA_MAX_SIZE); |
---|
508 | 521 | k3_dma_fill_desc(ds, dst, src, copy, num++, c->ccfg); |
---|
509 | 522 | |
---|
510 | | - if (c->dir == DMA_MEM_TO_DEV) { |
---|
511 | | - src += copy; |
---|
512 | | - } else if (c->dir == DMA_DEV_TO_MEM) { |
---|
513 | | - dst += copy; |
---|
514 | | - } else { |
---|
515 | | - src += copy; |
---|
516 | | - dst += copy; |
---|
517 | | - } |
---|
| 523 | + src += copy; |
---|
| 524 | + dst += copy; |
---|
518 | 525 | len -= copy; |
---|
519 | 526 | } while (len); |
---|
520 | 527 | |
---|
.. | .. |
---|
548 | 555 | if (!ds) |
---|
549 | 556 | return NULL; |
---|
550 | 557 | num = 0; |
---|
| 558 | + k3_dma_config_write(chan, dir, &c->slave_config); |
---|
551 | 559 | |
---|
552 | 560 | for_each_sg(sgl, sg, sglen, i) { |
---|
553 | 561 | addr = sg_dma_address(sg); |
---|
.. | .. |
---|
608 | 616 | avail = buf_len; |
---|
609 | 617 | total = avail; |
---|
610 | 618 | num = 0; |
---|
| 619 | + k3_dma_config_write(chan, dir, &c->slave_config); |
---|
611 | 620 | |
---|
612 | 621 | if (period_len < modulo) |
---|
613 | 622 | modulo = period_len; |
---|
.. | .. |
---|
648 | 657 | struct dma_slave_config *cfg) |
---|
649 | 658 | { |
---|
650 | 659 | struct k3_dma_chan *c = to_k3_chan(chan); |
---|
| 660 | + |
---|
| 661 | + memcpy(&c->slave_config, cfg, sizeof(*cfg)); |
---|
| 662 | + |
---|
| 663 | + return 0; |
---|
| 664 | +} |
---|
| 665 | + |
---|
| 666 | +static int k3_dma_config_write(struct dma_chan *chan, |
---|
| 667 | + enum dma_transfer_direction dir, |
---|
| 668 | + struct dma_slave_config *cfg) |
---|
| 669 | +{ |
---|
| 670 | + struct k3_dma_chan *c = to_k3_chan(chan); |
---|
651 | 671 | u32 maxburst = 0, val = 0; |
---|
652 | 672 | enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED; |
---|
653 | 673 | |
---|
654 | | - if (cfg == NULL) |
---|
655 | | - return -EINVAL; |
---|
656 | | - c->dir = cfg->direction; |
---|
657 | | - if (c->dir == DMA_DEV_TO_MEM) { |
---|
| 674 | + if (dir == DMA_DEV_TO_MEM) { |
---|
658 | 675 | c->ccfg = CX_CFG_DSTINCR; |
---|
659 | 676 | c->dev_addr = cfg->src_addr; |
---|
660 | 677 | maxburst = cfg->src_maxburst; |
---|
661 | 678 | width = cfg->src_addr_width; |
---|
662 | | - } else if (c->dir == DMA_MEM_TO_DEV) { |
---|
| 679 | + } else if (dir == DMA_MEM_TO_DEV) { |
---|
663 | 680 | c->ccfg = CX_CFG_SRCINCR; |
---|
664 | 681 | c->dev_addr = cfg->dst_addr; |
---|
665 | 682 | maxburst = cfg->dst_maxburst; |
---|
.. | .. |
---|
788 | 805 | return 0; |
---|
789 | 806 | } |
---|
790 | 807 | |
---|
| 808 | +static const struct k3dma_soc_data k3_v1_dma_data = { |
---|
| 809 | + .flags = 0, |
---|
| 810 | +}; |
---|
| 811 | + |
---|
| 812 | +static const struct k3dma_soc_data asp_v1_dma_data = { |
---|
| 813 | + .flags = K3_FLAG_NOCLK, |
---|
| 814 | +}; |
---|
| 815 | + |
---|
791 | 816 | static const struct of_device_id k3_pdma_dt_ids[] = { |
---|
792 | | - { .compatible = "hisilicon,k3-dma-1.0", }, |
---|
| 817 | + { .compatible = "hisilicon,k3-dma-1.0", |
---|
| 818 | + .data = &k3_v1_dma_data |
---|
| 819 | + }, |
---|
| 820 | + { .compatible = "hisilicon,hisi-pcm-asp-dma-1.0", |
---|
| 821 | + .data = &asp_v1_dma_data |
---|
| 822 | + }, |
---|
793 | 823 | {} |
---|
794 | 824 | }; |
---|
795 | 825 | MODULE_DEVICE_TABLE(of, k3_pdma_dt_ids); |
---|
.. | .. |
---|
808 | 838 | |
---|
809 | 839 | static int k3_dma_probe(struct platform_device *op) |
---|
810 | 840 | { |
---|
| 841 | + const struct k3dma_soc_data *soc_data; |
---|
811 | 842 | struct k3_dma_dev *d; |
---|
812 | 843 | const struct of_device_id *of_id; |
---|
813 | | - struct resource *iores; |
---|
814 | 844 | int i, ret, irq = 0; |
---|
815 | | - |
---|
816 | | - iores = platform_get_resource(op, IORESOURCE_MEM, 0); |
---|
817 | | - if (!iores) |
---|
818 | | - return -EINVAL; |
---|
819 | 845 | |
---|
820 | 846 | d = devm_kzalloc(&op->dev, sizeof(*d), GFP_KERNEL); |
---|
821 | 847 | if (!d) |
---|
822 | 848 | return -ENOMEM; |
---|
823 | 849 | |
---|
824 | | - d->base = devm_ioremap_resource(&op->dev, iores); |
---|
| 850 | + soc_data = device_get_match_data(&op->dev); |
---|
| 851 | + if (!soc_data) |
---|
| 852 | + return -EINVAL; |
---|
| 853 | + |
---|
| 854 | + d->base = devm_platform_ioremap_resource(op, 0); |
---|
825 | 855 | if (IS_ERR(d->base)) |
---|
826 | 856 | return PTR_ERR(d->base); |
---|
827 | 857 | |
---|
.. | .. |
---|
831 | 861 | "dma-channels", &d->dma_channels); |
---|
832 | 862 | of_property_read_u32((&op->dev)->of_node, |
---|
833 | 863 | "dma-requests", &d->dma_requests); |
---|
| 864 | + ret = of_property_read_u32((&op->dev)->of_node, |
---|
| 865 | + "dma-channel-mask", &d->dma_channel_mask); |
---|
| 866 | + if (ret) { |
---|
| 867 | + dev_warn(&op->dev, |
---|
| 868 | + "dma-channel-mask doesn't exist, considering all as available.\n"); |
---|
| 869 | + d->dma_channel_mask = (u32)~0UL; |
---|
| 870 | + } |
---|
834 | 871 | } |
---|
835 | 872 | |
---|
836 | | - d->clk = devm_clk_get(&op->dev, NULL); |
---|
837 | | - if (IS_ERR(d->clk)) { |
---|
838 | | - dev_err(&op->dev, "no dma clk\n"); |
---|
839 | | - return PTR_ERR(d->clk); |
---|
| 873 | + if (!(soc_data->flags & K3_FLAG_NOCLK)) { |
---|
| 874 | + d->clk = devm_clk_get(&op->dev, NULL); |
---|
| 875 | + if (IS_ERR(d->clk)) { |
---|
| 876 | + dev_err(&op->dev, "no dma clk\n"); |
---|
| 877 | + return PTR_ERR(d->clk); |
---|
| 878 | + } |
---|
840 | 879 | } |
---|
841 | 880 | |
---|
842 | 881 | irq = platform_get_irq(op, 0); |
---|
.. | .. |
---|
860 | 899 | return -ENOMEM; |
---|
861 | 900 | |
---|
862 | 901 | for (i = 0; i < d->dma_channels; i++) { |
---|
863 | | - struct k3_dma_phy *p = &d->phy[i]; |
---|
| 902 | + struct k3_dma_phy *p; |
---|
864 | 903 | |
---|
| 904 | + if (!(d->dma_channel_mask & BIT(i))) |
---|
| 905 | + continue; |
---|
| 906 | + |
---|
| 907 | + p = &d->phy[i]; |
---|
865 | 908 | p->idx = i; |
---|
866 | 909 | p->base = d->base + i * 0x40; |
---|
867 | 910 | } |
---|
.. | .. |
---|
919 | 962 | |
---|
920 | 963 | spin_lock_init(&d->lock); |
---|
921 | 964 | INIT_LIST_HEAD(&d->chan_pending); |
---|
922 | | - tasklet_init(&d->task, k3_dma_tasklet, (unsigned long)d); |
---|
| 965 | + tasklet_setup(&d->task, k3_dma_tasklet); |
---|
923 | 966 | platform_set_drvdata(op, d); |
---|
924 | 967 | dev_info(&op->dev, "initialized\n"); |
---|
925 | 968 | |
---|