Lines Matching +full:ls1b +full:- +full:apbdma
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Driver for Loongson-1 APB DMA Controller
5 * Copyright (C) 2015-2024 Keguang Zhang <keguang.zhang@gmail.com>
9 #include <linux/dma-mapping.h>
20 #include "virt-dma.h"
22 /* Loongson-1 DMA Control Register */
94 return &chan->dev->device; in chan2dev()
100 struct dma_chan *dchan = &chan->vc.chan; in ls1x_dma_query()
105 val |= dchan->chan_id; in ls1x_dma_query()
106 writel(val, chan->reg_base + LS1X_DMA_CTRL); in ls1x_dma_query()
107 ret = readl_poll_timeout_atomic(chan->reg_base + LS1X_DMA_CTRL, val, in ls1x_dma_query()
118 struct dma_chan *dchan = &chan->vc.chan; in ls1x_dma_start()
124 val |= dchan->chan_id; in ls1x_dma_start()
125 writel(val, chan->reg_base + LS1X_DMA_CTRL); in ls1x_dma_start()
126 ret = readl_poll_timeout(chan->reg_base + LS1X_DMA_CTRL, val, in ls1x_dma_start()
138 int val = readl(chan->reg_base + LS1X_DMA_CTRL); in ls1x_dma_stop()
140 writel(val | LS1X_DMA_STOP, chan->reg_base + LS1X_DMA_CTRL); in ls1x_dma_stop()
149 chan->curr_lli, chan->curr_lli->phys); in ls1x_dma_free_chan_resources()
150 dma_pool_destroy(chan->lli_pool); in ls1x_dma_free_chan_resources()
151 chan->lli_pool = NULL; in ls1x_dma_free_chan_resources()
152 devm_free_irq(dev, chan->irq, chan); in ls1x_dma_free_chan_resources()
153 vchan_free_chan_resources(&chan->vc); in ls1x_dma_free_chan_resources()
163 ret = devm_request_irq(dev, chan->irq, ls1x_dma_irq_handler, in ls1x_dma_alloc_chan_resources()
166 dev_err(dev, "failed to request IRQ %d\n", chan->irq); in ls1x_dma_alloc_chan_resources()
170 chan->lli_pool = dma_pool_create(dma_chan_name(dchan), dev, in ls1x_dma_alloc_chan_resources()
173 if (!chan->lli_pool) in ls1x_dma_alloc_chan_resources()
174 return -ENOMEM; in ls1x_dma_alloc_chan_resources()
178 chan->curr_lli = dma_alloc_coherent(dev, sizeof(struct ls1x_dma_lli), in ls1x_dma_alloc_chan_resources()
180 if (!chan->curr_lli) { in ls1x_dma_alloc_chan_resources()
181 dma_pool_destroy(chan->lli_pool); in ls1x_dma_alloc_chan_resources()
182 return -ENOMEM; in ls1x_dma_alloc_chan_resources()
184 chan->curr_lli->phys = phys; in ls1x_dma_alloc_chan_resources()
192 struct ls1x_dma_chan *chan = to_ls1x_dma_chan(vd->tx.chan); in ls1x_dma_free_desc()
195 list_for_each_entry_safe(lli, _lli, &desc->lli_list, node) { in ls1x_dma_free_desc()
196 list_del(&lli->node); in ls1x_dma_free_desc()
197 dma_pool_free(chan->lli_pool, lli, lli->phys); in ls1x_dma_free_desc()
211 INIT_LIST_HEAD(&desc->lli_list); in ls1x_dma_alloc_desc()
229 dev_addr = chan->dst_addr; in ls1x_dma_prep_lli()
230 chan->bus_width = chan->dst_addr_width; in ls1x_dma_prep_lli()
234 dev_addr = chan->src_addr; in ls1x_dma_prep_lli()
235 chan->bus_width = chan->src_addr_width; in ls1x_dma_prep_lli()
241 return -EINVAL; in ls1x_dma_prep_lli()
249 if (!is_dma_copy_aligned(dchan->device, buf_addr, 0, buf_len)) { in ls1x_dma_prep_lli()
251 return -EINVAL; in ls1x_dma_prep_lli()
255 lli = dma_pool_zalloc(chan->lli_pool, GFP_NOWAIT, &phys); in ls1x_dma_prep_lli()
258 return -ENOMEM; in ls1x_dma_prep_lli()
262 lli->phys = phys; in ls1x_dma_prep_lli()
263 lli->hw[LS1X_DMADESC_SADDR] = buf_addr; in ls1x_dma_prep_lli()
264 lli->hw[LS1X_DMADESC_DADDR] = dev_addr; in ls1x_dma_prep_lli()
265 lli->hw[LS1X_DMADESC_LENGTH] = buf_len / chan->bus_width; in ls1x_dma_prep_lli()
266 lli->hw[LS1X_DMADESC_STRIDE] = 0; in ls1x_dma_prep_lli()
267 lli->hw[LS1X_DMADESC_CYCLES] = 1; in ls1x_dma_prep_lli()
268 lli->hw[LS1X_DMADESC_CMD] = cmd; in ls1x_dma_prep_lli()
271 prev->hw[LS1X_DMADESC_NEXT] = in ls1x_dma_prep_lli()
272 lli->phys | LS1X_DMA_NEXT_VALID; in ls1x_dma_prep_lli()
278 list_add_tail(&lli->node, &desc->lli_list); in ls1x_dma_prep_lli()
282 lli->hw[LS1X_DMADESC_NEXT] = first->phys | LS1X_DMA_NEXT_VALID; in ls1x_dma_prep_lli()
283 chan->is_cyclic = is_cyclic; in ls1x_dma_prep_lli()
286 list_for_each(pos, &desc->lli_list) { in ls1x_dma_prep_lli()
310 ls1x_dma_free_desc(&desc->vd); in ls1x_dma_prep_slave_sg()
314 return vchan_tx_prep(to_virt_chan(dchan), &desc->vd, flags); in ls1x_dma_prep_slave_sg()
354 ls1x_dma_free_desc(&desc->vd); in ls1x_dma_prep_dma_cyclic()
358 return vchan_tx_prep(to_virt_chan(dchan), &desc->vd, flags); in ls1x_dma_prep_dma_cyclic()
366 chan->src_addr = config->src_addr; in ls1x_dma_slave_config()
367 chan->src_addr_width = config->src_addr_width; in ls1x_dma_slave_config()
368 chan->dst_addr = config->dst_addr; in ls1x_dma_slave_config()
369 chan->dst_addr_width = config->dst_addr_width; in ls1x_dma_slave_config()
379 guard(spinlock_irqsave)(&chan->vc.lock); in ls1x_dma_pause()
381 ret = ls1x_dma_query(chan, &chan->curr_lli->phys); in ls1x_dma_pause()
392 guard(spinlock_irqsave)(&chan->vc.lock); in ls1x_dma_resume()
394 return ls1x_dma_start(chan, &chan->curr_lli->phys); in ls1x_dma_resume()
405 scoped_guard(spinlock_irqsave, &chan->vc.lock) { in ls1x_dma_terminate_all()
406 vd = vchan_next_desc(&chan->vc); in ls1x_dma_terminate_all()
410 vchan_get_all_descriptors(&chan->vc, &head); in ls1x_dma_terminate_all()
413 vchan_dma_desc_free_list(&chan->vc, &head); in ls1x_dma_terminate_all()
436 scoped_guard(spinlock_irqsave, &chan->vc.lock) { in ls1x_dma_tx_status()
437 vd = vchan_find_desc(&chan->vc, cookie); in ls1x_dma_tx_status()
444 if (ls1x_dma_query(chan, &chan->curr_lli->phys)) in ls1x_dma_tx_status()
448 next_phys = chan->curr_lli->hw[LS1X_DMADESC_NEXT]; in ls1x_dma_tx_status()
449 list_for_each_entry(lli, &desc->lli_list, node) in ls1x_dma_tx_status()
450 if (lli->hw[LS1X_DMADESC_NEXT] == next_phys) in ls1x_dma_tx_status()
454 &lli->phys); in ls1x_dma_tx_status()
457 list_for_each_entry_from(lli, &desc->lli_list, node) in ls1x_dma_tx_status()
458 bytes += lli->hw[LS1X_DMADESC_LENGTH] * in ls1x_dma_tx_status()
459 chan->bus_width; in ls1x_dma_tx_status()
472 guard(spinlock_irqsave)(&chan->vc.lock); in ls1x_dma_issue_pending()
474 if (vchan_issue_pending(&chan->vc)) { in ls1x_dma_issue_pending()
475 struct virt_dma_desc *vd = vchan_next_desc(&chan->vc); in ls1x_dma_issue_pending()
481 lli = list_first_entry(&desc->lli_list, in ls1x_dma_issue_pending()
483 ls1x_dma_start(chan, &lli->phys); in ls1x_dma_issue_pending()
491 struct dma_chan *dchan = &chan->vc.chan; in ls1x_dma_irq_handler()
495 scoped_guard(spinlock, &chan->vc.lock) { in ls1x_dma_irq_handler()
496 vd = vchan_next_desc(&chan->vc); in ls1x_dma_irq_handler()
500 irq, dchan->chan_id); in ls1x_dma_irq_handler()
504 if (chan->is_cyclic) { in ls1x_dma_irq_handler()
507 list_del(&vd->node); in ls1x_dma_irq_handler()
512 dev_dbg(dev, "DMA IRQ %d on channel %d\n", irq, dchan->chan_id); in ls1x_dma_irq_handler()
527 for (id = 0; id < dma->nr_chans; id++) { in ls1x_dma_chan_probe()
528 struct ls1x_dma_chan *chan = &dma->chan[id]; in ls1x_dma_chan_probe()
532 chan->irq = platform_get_irq_byname(pdev, pdev_irqname); in ls1x_dma_chan_probe()
533 if (chan->irq < 0) in ls1x_dma_chan_probe()
534 return dev_err_probe(&pdev->dev, chan->irq, in ls1x_dma_chan_probe()
538 chan->reg_base = reg_base; in ls1x_dma_chan_probe()
539 chan->vc.desc_free = ls1x_dma_free_desc; in ls1x_dma_chan_probe()
540 vchan_init(&chan->vc, &dma->ddev); in ls1x_dma_chan_probe()
550 for (id = 0; id < dma->nr_chans; id++) { in ls1x_dma_chan_remove()
551 struct ls1x_dma_chan *chan = &dma->chan[id]; in ls1x_dma_chan_remove()
553 if (chan->vc.chan.device == &dma->ddev) { in ls1x_dma_chan_remove()
554 list_del(&chan->vc.chan.device_node); in ls1x_dma_chan_remove()
555 tasklet_kill(&chan->vc.task); in ls1x_dma_chan_remove()
562 struct device *dev = &pdev->dev; in ls1x_dma_probe()
569 return dev_err_probe(dev, -EINVAL, in ls1x_dma_probe()
575 return -ENOMEM; in ls1x_dma_probe()
576 dma->nr_chans = ret; in ls1x_dma_probe()
579 ddev = &dma->ddev; in ls1x_dma_probe()
580 ddev->dev = dev; in ls1x_dma_probe()
581 ddev->copy_align = DMAENGINE_ALIGN_4_BYTES; in ls1x_dma_probe()
582 ddev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | in ls1x_dma_probe()
585 ddev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | in ls1x_dma_probe()
588 ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); in ls1x_dma_probe()
589 ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; in ls1x_dma_probe()
590 ddev->device_alloc_chan_resources = ls1x_dma_alloc_chan_resources; in ls1x_dma_probe()
591 ddev->device_free_chan_resources = ls1x_dma_free_chan_resources; in ls1x_dma_probe()
592 ddev->device_prep_slave_sg = ls1x_dma_prep_slave_sg; in ls1x_dma_probe()
593 ddev->device_prep_dma_cyclic = ls1x_dma_prep_dma_cyclic; in ls1x_dma_probe()
594 ddev->device_config = ls1x_dma_slave_config; in ls1x_dma_probe()
595 ddev->device_pause = ls1x_dma_pause; in ls1x_dma_probe()
596 ddev->device_resume = ls1x_dma_resume; in ls1x_dma_probe()
597 ddev->device_terminate_all = ls1x_dma_terminate_all; in ls1x_dma_probe()
598 ddev->device_synchronize = ls1x_dma_synchronize; in ls1x_dma_probe()
599 ddev->device_tx_status = ls1x_dma_tx_status; in ls1x_dma_probe()
600 ddev->device_issue_pending = ls1x_dma_issue_pending; in ls1x_dma_probe()
601 dma_cap_set(DMA_SLAVE, ddev->cap_mask); in ls1x_dma_probe()
602 INIT_LIST_HEAD(&ddev->channels); in ls1x_dma_probe()
615 ret = of_dma_controller_register(dev->of_node, of_dma_xlate_by_chan_id, in ls1x_dma_probe()
637 of_dma_controller_free(pdev->dev.of_node); in ls1x_dma_remove()
642 { .compatible = "loongson,ls1b-apbdma" },
659 MODULE_DESCRIPTION("Loongson-1 APB DMA Controller driver");