| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Driver for the Atmel Extensible DMA Controller (aka XDMAC on AT91 systems) |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2014 Atmel Corporation |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Author: Ludovic Desroches <ludovic.desroches@atmel.com> |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 9 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 10 | | - * the Free Software Foundation. |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is distributed in the hope that it will be useful, but WITHOUT |
|---|
| 13 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|---|
| 14 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|---|
| 15 | | - * more details. |
|---|
| 16 | | - * |
|---|
| 17 | | - * You should have received a copy of the GNU General Public License along with |
|---|
| 18 | | - * this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 19 | 8 | */ |
|---|
| 20 | 9 | |
|---|
| 21 | 10 | #include <asm/barrier.h> |
|---|
| .. | .. |
|---|
| 224 | 213 | struct clk *clk; |
|---|
| 225 | 214 | u32 save_gim; |
|---|
| 226 | 215 | struct dma_pool *at_xdmac_desc_pool; |
|---|
| 227 | | - struct at_xdmac_chan chan[0]; |
|---|
| 216 | + struct at_xdmac_chan chan[]; |
|---|
| 228 | 217 | }; |
|---|
| 229 | 218 | |
|---|
| 230 | 219 | |
|---|
| .. | .. |
|---|
| 309 | 298 | return csize; |
|---|
| 310 | 299 | }; |
|---|
| 311 | 300 | |
|---|
| 301 | +static inline bool at_xdmac_chan_is_peripheral_xfer(u32 cfg) |
|---|
| 302 | +{ |
|---|
| 303 | + return cfg & AT_XDMAC_CC_TYPE_PER_TRAN; |
|---|
| 304 | +} |
|---|
| 305 | + |
|---|
| 312 | 306 | static inline u8 at_xdmac_get_dwidth(u32 cfg) |
|---|
| 313 | 307 | { |
|---|
| 314 | 308 | return (cfg & AT_XDMAC_CC_DWIDTH_MASK) >> AT_XDMAC_CC_DWIDTH_OFFSET; |
|---|
| .. | .. |
|---|
| 388 | 382 | at_xdmac_chan_read(atchan, AT_XDMAC_CUBC)); |
|---|
| 389 | 383 | |
|---|
| 390 | 384 | at_xdmac_chan_write(atchan, AT_XDMAC_CID, 0xffffffff); |
|---|
| 391 | | - reg = AT_XDMAC_CIE_RBEIE | AT_XDMAC_CIE_WBEIE | AT_XDMAC_CIE_ROIE; |
|---|
| 385 | + reg = AT_XDMAC_CIE_RBEIE | AT_XDMAC_CIE_WBEIE; |
|---|
| 386 | + /* |
|---|
| 387 | + * Request Overflow Error is only for peripheral synchronized transfers |
|---|
| 388 | + */ |
|---|
| 389 | + if (at_xdmac_chan_is_peripheral_xfer(first->lld.mbr_cfg)) |
|---|
| 390 | + reg |= AT_XDMAC_CIE_ROIE; |
|---|
| 391 | + |
|---|
| 392 | 392 | /* |
|---|
| 393 | 393 | * There is no end of list when doing cyclic dma, we need to get |
|---|
| 394 | 394 | * an interrupt after each periods. |
|---|
| .. | .. |
|---|
| 1390 | 1390 | { |
|---|
| 1391 | 1391 | struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); |
|---|
| 1392 | 1392 | struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); |
|---|
| 1393 | | - struct at_xdmac_desc *desc, *_desc; |
|---|
| 1393 | + struct at_xdmac_desc *desc, *_desc, *iter; |
|---|
| 1394 | 1394 | struct list_head *descs_list; |
|---|
| 1395 | 1395 | enum dma_status ret; |
|---|
| 1396 | 1396 | int residue, retry; |
|---|
| .. | .. |
|---|
| 1505 | 1505 | * microblock. |
|---|
| 1506 | 1506 | */ |
|---|
| 1507 | 1507 | descs_list = &desc->descs_list; |
|---|
| 1508 | | - list_for_each_entry_safe(desc, _desc, descs_list, desc_node) { |
|---|
| 1509 | | - dwidth = at_xdmac_get_dwidth(desc->lld.mbr_cfg); |
|---|
| 1510 | | - residue -= (desc->lld.mbr_ubc & 0xffffff) << dwidth; |
|---|
| 1511 | | - if ((desc->lld.mbr_nda & 0xfffffffc) == cur_nda) |
|---|
| 1508 | + list_for_each_entry_safe(iter, _desc, descs_list, desc_node) { |
|---|
| 1509 | + dwidth = at_xdmac_get_dwidth(iter->lld.mbr_cfg); |
|---|
| 1510 | + residue -= (iter->lld.mbr_ubc & 0xffffff) << dwidth; |
|---|
| 1511 | + if ((iter->lld.mbr_nda & 0xfffffffc) == cur_nda) { |
|---|
| 1512 | + desc = iter; |
|---|
| 1512 | 1513 | break; |
|---|
| 1514 | + } |
|---|
| 1513 | 1515 | } |
|---|
| 1514 | 1516 | residue += cur_ubc << dwidth; |
|---|
| 1515 | 1517 | |
|---|
| .. | .. |
|---|
| 1541 | 1543 | static void at_xdmac_advance_work(struct at_xdmac_chan *atchan) |
|---|
| 1542 | 1544 | { |
|---|
| 1543 | 1545 | struct at_xdmac_desc *desc; |
|---|
| 1544 | | - unsigned long flags; |
|---|
| 1545 | | - |
|---|
| 1546 | | - spin_lock_irqsave(&atchan->lock, flags); |
|---|
| 1547 | 1546 | |
|---|
| 1548 | 1547 | /* |
|---|
| 1549 | 1548 | * If channel is enabled, do nothing, advance_work will be triggered |
|---|
| .. | .. |
|---|
| 1557 | 1556 | if (!desc->active_xfer) |
|---|
| 1558 | 1557 | at_xdmac_start_xfer(atchan, desc); |
|---|
| 1559 | 1558 | } |
|---|
| 1560 | | - |
|---|
| 1561 | | - spin_unlock_irqrestore(&atchan->lock, flags); |
|---|
| 1562 | 1559 | } |
|---|
| 1563 | 1560 | |
|---|
| 1564 | 1561 | static void at_xdmac_handle_cyclic(struct at_xdmac_chan *atchan) |
|---|
| .. | .. |
|---|
| 1566 | 1563 | struct at_xdmac_desc *desc; |
|---|
| 1567 | 1564 | struct dma_async_tx_descriptor *txd; |
|---|
| 1568 | 1565 | |
|---|
| 1569 | | - desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, xfer_node); |
|---|
| 1566 | + spin_lock_irq(&atchan->lock); |
|---|
| 1567 | + if (list_empty(&atchan->xfers_list)) { |
|---|
| 1568 | + spin_unlock_irq(&atchan->lock); |
|---|
| 1569 | + return; |
|---|
| 1570 | + } |
|---|
| 1571 | + desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, |
|---|
| 1572 | + xfer_node); |
|---|
| 1573 | + spin_unlock_irq(&atchan->lock); |
|---|
| 1570 | 1574 | txd = &desc->tx_dma_desc; |
|---|
| 1571 | | - |
|---|
| 1572 | 1575 | if (txd->flags & DMA_PREP_INTERRUPT) |
|---|
| 1573 | 1576 | dmaengine_desc_get_callback_invoke(txd, NULL); |
|---|
| 1574 | 1577 | } |
|---|
| 1575 | 1578 | |
|---|
| 1576 | | -static void at_xdmac_tasklet(unsigned long data) |
|---|
| 1579 | +static void at_xdmac_handle_error(struct at_xdmac_chan *atchan) |
|---|
| 1577 | 1580 | { |
|---|
| 1578 | | - struct at_xdmac_chan *atchan = (struct at_xdmac_chan *)data; |
|---|
| 1581 | + struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); |
|---|
| 1582 | + struct at_xdmac_desc *bad_desc; |
|---|
| 1583 | + |
|---|
| 1584 | + /* |
|---|
| 1585 | + * The descriptor currently at the head of the active list is |
|---|
| 1586 | + * broken. Since we don't have any way to report errors, we'll |
|---|
| 1587 | + * just have to scream loudly and try to continue with other |
|---|
| 1588 | + * descriptors queued (if any). |
|---|
| 1589 | + */ |
|---|
| 1590 | + if (atchan->irq_status & AT_XDMAC_CIS_RBEIS) |
|---|
| 1591 | + dev_err(chan2dev(&atchan->chan), "read bus error!!!"); |
|---|
| 1592 | + if (atchan->irq_status & AT_XDMAC_CIS_WBEIS) |
|---|
| 1593 | + dev_err(chan2dev(&atchan->chan), "write bus error!!!"); |
|---|
| 1594 | + if (atchan->irq_status & AT_XDMAC_CIS_ROIS) |
|---|
| 1595 | + dev_err(chan2dev(&atchan->chan), "request overflow error!!!"); |
|---|
| 1596 | + |
|---|
| 1597 | + spin_lock_irq(&atchan->lock); |
|---|
| 1598 | + |
|---|
| 1599 | + /* Channel must be disabled first as it's not done automatically */ |
|---|
| 1600 | + at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask); |
|---|
| 1601 | + while (at_xdmac_read(atxdmac, AT_XDMAC_GS) & atchan->mask) |
|---|
| 1602 | + cpu_relax(); |
|---|
| 1603 | + |
|---|
| 1604 | + bad_desc = list_first_entry(&atchan->xfers_list, |
|---|
| 1605 | + struct at_xdmac_desc, |
|---|
| 1606 | + xfer_node); |
|---|
| 1607 | + |
|---|
| 1608 | + spin_unlock_irq(&atchan->lock); |
|---|
| 1609 | + |
|---|
| 1610 | + /* Print bad descriptor's details if needed */ |
|---|
| 1611 | + dev_dbg(chan2dev(&atchan->chan), |
|---|
| 1612 | + "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n", |
|---|
| 1613 | + __func__, &bad_desc->lld.mbr_sa, &bad_desc->lld.mbr_da, |
|---|
| 1614 | + bad_desc->lld.mbr_ubc); |
|---|
| 1615 | + |
|---|
| 1616 | + /* Then continue with usual descriptor management */ |
|---|
| 1617 | +} |
|---|
| 1618 | + |
|---|
| 1619 | +static void at_xdmac_tasklet(struct tasklet_struct *t) |
|---|
| 1620 | +{ |
|---|
| 1621 | + struct at_xdmac_chan *atchan = from_tasklet(atchan, t, tasklet); |
|---|
| 1579 | 1622 | struct at_xdmac_desc *desc; |
|---|
| 1580 | 1623 | u32 error_mask; |
|---|
| 1581 | 1624 | |
|---|
| .. | .. |
|---|
| 1592 | 1635 | || (atchan->irq_status & error_mask)) { |
|---|
| 1593 | 1636 | struct dma_async_tx_descriptor *txd; |
|---|
| 1594 | 1637 | |
|---|
| 1595 | | - if (atchan->irq_status & AT_XDMAC_CIS_RBEIS) |
|---|
| 1596 | | - dev_err(chan2dev(&atchan->chan), "read bus error!!!"); |
|---|
| 1597 | | - if (atchan->irq_status & AT_XDMAC_CIS_WBEIS) |
|---|
| 1598 | | - dev_err(chan2dev(&atchan->chan), "write bus error!!!"); |
|---|
| 1599 | | - if (atchan->irq_status & AT_XDMAC_CIS_ROIS) |
|---|
| 1600 | | - dev_err(chan2dev(&atchan->chan), "request overflow error!!!"); |
|---|
| 1638 | + if (atchan->irq_status & error_mask) |
|---|
| 1639 | + at_xdmac_handle_error(atchan); |
|---|
| 1601 | 1640 | |
|---|
| 1602 | | - spin_lock_bh(&atchan->lock); |
|---|
| 1641 | + spin_lock_irq(&atchan->lock); |
|---|
| 1603 | 1642 | desc = list_first_entry(&atchan->xfers_list, |
|---|
| 1604 | 1643 | struct at_xdmac_desc, |
|---|
| 1605 | 1644 | xfer_node); |
|---|
| 1606 | 1645 | dev_vdbg(chan2dev(&atchan->chan), "%s: desc 0x%p\n", __func__, desc); |
|---|
| 1607 | 1646 | if (!desc->active_xfer) { |
|---|
| 1608 | 1647 | dev_err(chan2dev(&atchan->chan), "Xfer not active: exiting"); |
|---|
| 1609 | | - spin_unlock(&atchan->lock); |
|---|
| 1648 | + spin_unlock_irq(&atchan->lock); |
|---|
| 1610 | 1649 | return; |
|---|
| 1611 | 1650 | } |
|---|
| 1612 | 1651 | |
|---|
| 1613 | 1652 | txd = &desc->tx_dma_desc; |
|---|
| 1614 | 1653 | |
|---|
| 1615 | 1654 | at_xdmac_remove_xfer(atchan, desc); |
|---|
| 1616 | | - spin_unlock_bh(&atchan->lock); |
|---|
| 1655 | + spin_unlock_irq(&atchan->lock); |
|---|
| 1617 | 1656 | |
|---|
| 1618 | | - if (!at_xdmac_chan_is_cyclic(atchan)) { |
|---|
| 1619 | | - dma_cookie_complete(txd); |
|---|
| 1620 | | - if (txd->flags & DMA_PREP_INTERRUPT) |
|---|
| 1621 | | - dmaengine_desc_get_callback_invoke(txd, NULL); |
|---|
| 1622 | | - } |
|---|
| 1657 | + dma_cookie_complete(txd); |
|---|
| 1658 | + if (txd->flags & DMA_PREP_INTERRUPT) |
|---|
| 1659 | + dmaengine_desc_get_callback_invoke(txd, NULL); |
|---|
| 1623 | 1660 | |
|---|
| 1624 | 1661 | dma_run_dependencies(txd); |
|---|
| 1625 | 1662 | |
|---|
| 1663 | + spin_lock_irq(&atchan->lock); |
|---|
| 1626 | 1664 | at_xdmac_advance_work(atchan); |
|---|
| 1665 | + spin_unlock_irq(&atchan->lock); |
|---|
| 1627 | 1666 | } |
|---|
| 1628 | 1667 | } |
|---|
| 1629 | 1668 | |
|---|
| .. | .. |
|---|
| 1684 | 1723 | static void at_xdmac_issue_pending(struct dma_chan *chan) |
|---|
| 1685 | 1724 | { |
|---|
| 1686 | 1725 | struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); |
|---|
| 1726 | + unsigned long flags; |
|---|
| 1687 | 1727 | |
|---|
| 1688 | 1728 | dev_dbg(chan2dev(&atchan->chan), "%s\n", __func__); |
|---|
| 1689 | 1729 | |
|---|
| 1690 | | - if (!at_xdmac_chan_is_cyclic(atchan)) |
|---|
| 1691 | | - at_xdmac_advance_work(atchan); |
|---|
| 1730 | + spin_lock_irqsave(&atchan->lock, flags); |
|---|
| 1731 | + at_xdmac_advance_work(atchan); |
|---|
| 1732 | + spin_unlock_irqrestore(&atchan->lock, flags); |
|---|
| 1692 | 1733 | |
|---|
| 1693 | 1734 | return; |
|---|
| 1694 | 1735 | } |
|---|
| .. | .. |
|---|
| 1781 | 1822 | struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); |
|---|
| 1782 | 1823 | struct at_xdmac_desc *desc; |
|---|
| 1783 | 1824 | int i; |
|---|
| 1784 | | - unsigned long flags; |
|---|
| 1785 | | - |
|---|
| 1786 | | - spin_lock_irqsave(&atchan->lock, flags); |
|---|
| 1787 | 1825 | |
|---|
| 1788 | 1826 | if (at_xdmac_chan_is_enabled(atchan)) { |
|---|
| 1789 | 1827 | dev_err(chan2dev(chan), |
|---|
| 1790 | 1828 | "can't allocate channel resources (channel enabled)\n"); |
|---|
| 1791 | | - i = -EIO; |
|---|
| 1792 | | - goto spin_unlock; |
|---|
| 1829 | + return -EIO; |
|---|
| 1793 | 1830 | } |
|---|
| 1794 | 1831 | |
|---|
| 1795 | 1832 | if (!list_empty(&atchan->free_descs_list)) { |
|---|
| 1796 | 1833 | dev_err(chan2dev(chan), |
|---|
| 1797 | 1834 | "can't allocate channel resources (channel not free from a previous use)\n"); |
|---|
| 1798 | | - i = -EIO; |
|---|
| 1799 | | - goto spin_unlock; |
|---|
| 1835 | + return -EIO; |
|---|
| 1800 | 1836 | } |
|---|
| 1801 | 1837 | |
|---|
| 1802 | 1838 | for (i = 0; i < init_nr_desc_per_channel; i++) { |
|---|
| 1803 | | - desc = at_xdmac_alloc_desc(chan, GFP_ATOMIC); |
|---|
| 1839 | + desc = at_xdmac_alloc_desc(chan, GFP_KERNEL); |
|---|
| 1804 | 1840 | if (!desc) { |
|---|
| 1841 | + if (i == 0) { |
|---|
| 1842 | + dev_warn(chan2dev(chan), |
|---|
| 1843 | + "can't allocate any descriptors\n"); |
|---|
| 1844 | + return -EIO; |
|---|
| 1845 | + } |
|---|
| 1805 | 1846 | dev_warn(chan2dev(chan), |
|---|
| 1806 | 1847 | "only %d descriptors have been allocated\n", i); |
|---|
| 1807 | 1848 | break; |
|---|
| .. | .. |
|---|
| 1813 | 1854 | |
|---|
| 1814 | 1855 | dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); |
|---|
| 1815 | 1856 | |
|---|
| 1816 | | -spin_unlock: |
|---|
| 1817 | | - spin_unlock_irqrestore(&atchan->lock, flags); |
|---|
| 1818 | 1857 | return i; |
|---|
| 1819 | 1858 | } |
|---|
| 1820 | 1859 | |
|---|
| .. | .. |
|---|
| 1916 | 1955 | |
|---|
| 1917 | 1956 | static int at_xdmac_probe(struct platform_device *pdev) |
|---|
| 1918 | 1957 | { |
|---|
| 1919 | | - struct resource *res; |
|---|
| 1920 | 1958 | struct at_xdmac *atxdmac; |
|---|
| 1921 | 1959 | int irq, size, nr_channels, i, ret; |
|---|
| 1922 | 1960 | void __iomem *base; |
|---|
| 1923 | 1961 | u32 reg; |
|---|
| 1924 | 1962 | |
|---|
| 1925 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 1926 | | - if (!res) |
|---|
| 1927 | | - return -EINVAL; |
|---|
| 1928 | | - |
|---|
| 1929 | 1963 | irq = platform_get_irq(pdev, 0); |
|---|
| 1930 | 1964 | if (irq < 0) |
|---|
| 1931 | 1965 | return irq; |
|---|
| 1932 | 1966 | |
|---|
| 1933 | | - base = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 1967 | + base = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 1934 | 1968 | if (IS_ERR(base)) |
|---|
| 1935 | 1969 | return PTR_ERR(base); |
|---|
| 1936 | 1970 | |
|---|
| .. | .. |
|---|
| 2035 | 2069 | spin_lock_init(&atchan->lock); |
|---|
| 2036 | 2070 | INIT_LIST_HEAD(&atchan->xfers_list); |
|---|
| 2037 | 2071 | INIT_LIST_HEAD(&atchan->free_descs_list); |
|---|
| 2038 | | - tasklet_init(&atchan->tasklet, at_xdmac_tasklet, |
|---|
| 2039 | | - (unsigned long)atchan); |
|---|
| 2072 | + tasklet_setup(&atchan->tasklet, at_xdmac_tasklet); |
|---|
| 2040 | 2073 | |
|---|
| 2041 | 2074 | /* Clear pending interrupts. */ |
|---|
| 2042 | 2075 | while (at_xdmac_chan_read(atchan, AT_XDMAC_CIS)) |
|---|