| .. | .. |
|---|
| 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 | |
|---|