diff options
author | Per Forlin <per.forlin@stericsson.com> | 2010-12-20 12:31:38 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2011-01-04 20:20:43 -0500 |
commit | d49278e3351b34870cbffffc5067348a318e7b06 (patch) | |
tree | a8569e9a24286a0bd885f96e27342e4de2fdecbb /drivers/dma/ste_dma40.c | |
parent | e8a7e48bb248a1196484d3f8afa53bded2b24e71 (diff) |
dmaengine: dma40: Add support to split up large elements
The maximum transfer size of the stedma40 is (64k-1) x data-width.
If the transfer size of one element exceeds this limit
the job is split up and sent as linked transfer.
Signed-off-by: Per Forlin <per.forlin@linaro.org>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/dma/ste_dma40.c')
-rw-r--r-- | drivers/dma/ste_dma40.c | 191 |
1 files changed, 152 insertions, 39 deletions
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index fab68a553205..6e1d46a65d0e 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) ST-Ericsson SA 2007-2010 | 2 | * Copyright (C) Ericsson AB 2007-2008 |
3 | * Copyright (C) ST-Ericsson SA 2008-2010 | ||
3 | * Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson | 4 | * Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson |
4 | * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson | 5 | * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson |
5 | * License terms: GNU General Public License (GPL) version 2 | 6 | * License terms: GNU General Public License (GPL) version 2 |
@@ -554,8 +555,66 @@ static struct d40_desc *d40_last_queued(struct d40_chan *d40c) | |||
554 | return d; | 555 | return d; |
555 | } | 556 | } |
556 | 557 | ||
557 | /* Support functions for logical channels */ | 558 | static int d40_psize_2_burst_size(bool is_log, int psize) |
559 | { | ||
560 | if (is_log) { | ||
561 | if (psize == STEDMA40_PSIZE_LOG_1) | ||
562 | return 1; | ||
563 | } else { | ||
564 | if (psize == STEDMA40_PSIZE_PHY_1) | ||
565 | return 1; | ||
566 | } | ||
567 | |||
568 | return 2 << psize; | ||
569 | } | ||
570 | |||
571 | /* | ||
572 | * The dma only supports transmitting packages up to | ||
573 | * STEDMA40_MAX_SEG_SIZE << data_width. Calculate the total number of | ||
574 | * dma elements required to send the entire sg list | ||
575 | */ | ||
576 | static int d40_size_2_dmalen(int size, u32 data_width1, u32 data_width2) | ||
577 | { | ||
578 | int dmalen; | ||
579 | u32 max_w = max(data_width1, data_width2); | ||
580 | u32 min_w = min(data_width1, data_width2); | ||
581 | u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w); | ||
582 | |||
583 | if (seg_max > STEDMA40_MAX_SEG_SIZE) | ||
584 | seg_max -= (1 << max_w); | ||
585 | |||
586 | if (!IS_ALIGNED(size, 1 << max_w)) | ||
587 | return -EINVAL; | ||
588 | |||
589 | if (size <= seg_max) | ||
590 | dmalen = 1; | ||
591 | else { | ||
592 | dmalen = size / seg_max; | ||
593 | if (dmalen * seg_max < size) | ||
594 | dmalen++; | ||
595 | } | ||
596 | return dmalen; | ||
597 | } | ||
598 | |||
599 | static int d40_sg_2_dmalen(struct scatterlist *sgl, int sg_len, | ||
600 | u32 data_width1, u32 data_width2) | ||
601 | { | ||
602 | struct scatterlist *sg; | ||
603 | int i; | ||
604 | int len = 0; | ||
605 | int ret; | ||
606 | |||
607 | for_each_sg(sgl, sg, sg_len, i) { | ||
608 | ret = d40_size_2_dmalen(sg_dma_len(sg), | ||
609 | data_width1, data_width2); | ||
610 | if (ret < 0) | ||
611 | return ret; | ||
612 | len += ret; | ||
613 | } | ||
614 | return len; | ||
615 | } | ||
558 | 616 | ||
617 | /* Support functions for logical channels */ | ||
559 | 618 | ||
560 | static int d40_channel_execute_command(struct d40_chan *d40c, | 619 | static int d40_channel_execute_command(struct d40_chan *d40c, |
561 | enum d40_command command) | 620 | enum d40_command command) |
@@ -1241,6 +1300,21 @@ static int d40_validate_conf(struct d40_chan *d40c, | |||
1241 | res = -EINVAL; | 1300 | res = -EINVAL; |
1242 | } | 1301 | } |
1243 | 1302 | ||
1303 | if (d40_psize_2_burst_size(is_log, conf->src_info.psize) * | ||
1304 | (1 << conf->src_info.data_width) != | ||
1305 | d40_psize_2_burst_size(is_log, conf->dst_info.psize) * | ||
1306 | (1 << conf->dst_info.data_width)) { | ||
1307 | /* | ||
1308 | * The DMAC hardware only supports | ||
1309 | * src (burst x width) == dst (burst x width) | ||
1310 | */ | ||
1311 | |||
1312 | dev_err(&d40c->chan.dev->device, | ||
1313 | "[%s] src (burst x width) != dst (burst x width)\n", | ||
1314 | __func__); | ||
1315 | res = -EINVAL; | ||
1316 | } | ||
1317 | |||
1244 | return res; | 1318 | return res; |
1245 | } | 1319 | } |
1246 | 1320 | ||
@@ -1638,13 +1712,21 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan, | |||
1638 | if (d40d == NULL) | 1712 | if (d40d == NULL) |
1639 | goto err; | 1713 | goto err; |
1640 | 1714 | ||
1641 | d40d->lli_len = sgl_len; | 1715 | d40d->lli_len = d40_sg_2_dmalen(sgl_dst, sgl_len, |
1716 | d40c->dma_cfg.src_info.data_width, | ||
1717 | d40c->dma_cfg.dst_info.data_width); | ||
1718 | if (d40d->lli_len < 0) { | ||
1719 | dev_err(&d40c->chan.dev->device, | ||
1720 | "[%s] Unaligned size\n", __func__); | ||
1721 | goto err; | ||
1722 | } | ||
1723 | |||
1642 | d40d->lli_current = 0; | 1724 | d40d->lli_current = 0; |
1643 | d40d->txd.flags = dma_flags; | 1725 | d40d->txd.flags = dma_flags; |
1644 | 1726 | ||
1645 | if (d40c->log_num != D40_PHY_CHAN) { | 1727 | if (d40c->log_num != D40_PHY_CHAN) { |
1646 | 1728 | ||
1647 | if (d40_pool_lli_alloc(d40d, sgl_len, true) < 0) { | 1729 | if (d40_pool_lli_alloc(d40d, d40d->lli_len, true) < 0) { |
1648 | dev_err(&d40c->chan.dev->device, | 1730 | dev_err(&d40c->chan.dev->device, |
1649 | "[%s] Out of memory\n", __func__); | 1731 | "[%s] Out of memory\n", __func__); |
1650 | goto err; | 1732 | goto err; |
@@ -1654,15 +1736,17 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan, | |||
1654 | sgl_len, | 1736 | sgl_len, |
1655 | d40d->lli_log.src, | 1737 | d40d->lli_log.src, |
1656 | d40c->log_def.lcsp1, | 1738 | d40c->log_def.lcsp1, |
1657 | d40c->dma_cfg.src_info.data_width); | 1739 | d40c->dma_cfg.src_info.data_width, |
1740 | d40c->dma_cfg.dst_info.data_width); | ||
1658 | 1741 | ||
1659 | (void) d40_log_sg_to_lli(sgl_dst, | 1742 | (void) d40_log_sg_to_lli(sgl_dst, |
1660 | sgl_len, | 1743 | sgl_len, |
1661 | d40d->lli_log.dst, | 1744 | d40d->lli_log.dst, |
1662 | d40c->log_def.lcsp3, | 1745 | d40c->log_def.lcsp3, |
1663 | d40c->dma_cfg.dst_info.data_width); | 1746 | d40c->dma_cfg.dst_info.data_width, |
1747 | d40c->dma_cfg.src_info.data_width); | ||
1664 | } else { | 1748 | } else { |
1665 | if (d40_pool_lli_alloc(d40d, sgl_len, false) < 0) { | 1749 | if (d40_pool_lli_alloc(d40d, d40d->lli_len, false) < 0) { |
1666 | dev_err(&d40c->chan.dev->device, | 1750 | dev_err(&d40c->chan.dev->device, |
1667 | "[%s] Out of memory\n", __func__); | 1751 | "[%s] Out of memory\n", __func__); |
1668 | goto err; | 1752 | goto err; |
@@ -1675,6 +1759,7 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan, | |||
1675 | virt_to_phys(d40d->lli_phy.src), | 1759 | virt_to_phys(d40d->lli_phy.src), |
1676 | d40c->src_def_cfg, | 1760 | d40c->src_def_cfg, |
1677 | d40c->dma_cfg.src_info.data_width, | 1761 | d40c->dma_cfg.src_info.data_width, |
1762 | d40c->dma_cfg.dst_info.data_width, | ||
1678 | d40c->dma_cfg.src_info.psize); | 1763 | d40c->dma_cfg.src_info.psize); |
1679 | 1764 | ||
1680 | if (res < 0) | 1765 | if (res < 0) |
@@ -1687,6 +1772,7 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan, | |||
1687 | virt_to_phys(d40d->lli_phy.dst), | 1772 | virt_to_phys(d40d->lli_phy.dst), |
1688 | d40c->dst_def_cfg, | 1773 | d40c->dst_def_cfg, |
1689 | d40c->dma_cfg.dst_info.data_width, | 1774 | d40c->dma_cfg.dst_info.data_width, |
1775 | d40c->dma_cfg.src_info.data_width, | ||
1690 | d40c->dma_cfg.dst_info.psize); | 1776 | d40c->dma_cfg.dst_info.psize); |
1691 | 1777 | ||
1692 | if (res < 0) | 1778 | if (res < 0) |
@@ -1826,7 +1912,6 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan, | |||
1826 | struct d40_chan *d40c = container_of(chan, struct d40_chan, | 1912 | struct d40_chan *d40c = container_of(chan, struct d40_chan, |
1827 | chan); | 1913 | chan); |
1828 | unsigned long flags; | 1914 | unsigned long flags; |
1829 | int err = 0; | ||
1830 | 1915 | ||
1831 | if (d40c->phy_chan == NULL) { | 1916 | if (d40c->phy_chan == NULL) { |
1832 | dev_err(&d40c->chan.dev->device, | 1917 | dev_err(&d40c->chan.dev->device, |
@@ -1844,6 +1929,15 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan, | |||
1844 | } | 1929 | } |
1845 | 1930 | ||
1846 | d40d->txd.flags = dma_flags; | 1931 | d40d->txd.flags = dma_flags; |
1932 | d40d->lli_len = d40_size_2_dmalen(size, | ||
1933 | d40c->dma_cfg.src_info.data_width, | ||
1934 | d40c->dma_cfg.dst_info.data_width); | ||
1935 | if (d40d->lli_len < 0) { | ||
1936 | dev_err(&d40c->chan.dev->device, | ||
1937 | "[%s] Unaligned size\n", __func__); | ||
1938 | goto err; | ||
1939 | } | ||
1940 | |||
1847 | 1941 | ||
1848 | dma_async_tx_descriptor_init(&d40d->txd, chan); | 1942 | dma_async_tx_descriptor_init(&d40d->txd, chan); |
1849 | 1943 | ||
@@ -1851,37 +1945,40 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan, | |||
1851 | 1945 | ||
1852 | if (d40c->log_num != D40_PHY_CHAN) { | 1946 | if (d40c->log_num != D40_PHY_CHAN) { |
1853 | 1947 | ||
1854 | if (d40_pool_lli_alloc(d40d, 1, true) < 0) { | 1948 | if (d40_pool_lli_alloc(d40d, d40d->lli_len, true) < 0) { |
1855 | dev_err(&d40c->chan.dev->device, | 1949 | dev_err(&d40c->chan.dev->device, |
1856 | "[%s] Out of memory\n", __func__); | 1950 | "[%s] Out of memory\n", __func__); |
1857 | goto err; | 1951 | goto err; |
1858 | } | 1952 | } |
1859 | d40d->lli_len = 1; | ||
1860 | d40d->lli_current = 0; | 1953 | d40d->lli_current = 0; |
1861 | 1954 | ||
1862 | d40_log_fill_lli(d40d->lli_log.src, | 1955 | if (d40_log_buf_to_lli(d40d->lli_log.src, |
1863 | src, | 1956 | src, |
1864 | size, | 1957 | size, |
1865 | d40c->log_def.lcsp1, | 1958 | d40c->log_def.lcsp1, |
1866 | d40c->dma_cfg.src_info.data_width, | 1959 | d40c->dma_cfg.src_info.data_width, |
1867 | true); | 1960 | d40c->dma_cfg.dst_info.data_width, |
1961 | true) == NULL) | ||
1962 | goto err; | ||
1868 | 1963 | ||
1869 | d40_log_fill_lli(d40d->lli_log.dst, | 1964 | if (d40_log_buf_to_lli(d40d->lli_log.dst, |
1870 | dst, | 1965 | dst, |
1871 | size, | 1966 | size, |
1872 | d40c->log_def.lcsp3, | 1967 | d40c->log_def.lcsp3, |
1873 | d40c->dma_cfg.dst_info.data_width, | 1968 | d40c->dma_cfg.dst_info.data_width, |
1874 | true); | 1969 | d40c->dma_cfg.src_info.data_width, |
1970 | true) == NULL) | ||
1971 | goto err; | ||
1875 | 1972 | ||
1876 | } else { | 1973 | } else { |
1877 | 1974 | ||
1878 | if (d40_pool_lli_alloc(d40d, 1, false) < 0) { | 1975 | if (d40_pool_lli_alloc(d40d, d40d->lli_len, false) < 0) { |
1879 | dev_err(&d40c->chan.dev->device, | 1976 | dev_err(&d40c->chan.dev->device, |
1880 | "[%s] Out of memory\n", __func__); | 1977 | "[%s] Out of memory\n", __func__); |
1881 | goto err; | 1978 | goto err; |
1882 | } | 1979 | } |
1883 | 1980 | ||
1884 | err = d40_phy_fill_lli(d40d->lli_phy.src, | 1981 | if (d40_phy_buf_to_lli(d40d->lli_phy.src, |
1885 | src, | 1982 | src, |
1886 | size, | 1983 | size, |
1887 | d40c->dma_cfg.src_info.psize, | 1984 | d40c->dma_cfg.src_info.psize, |
@@ -1889,11 +1986,11 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan, | |||
1889 | d40c->src_def_cfg, | 1986 | d40c->src_def_cfg, |
1890 | true, | 1987 | true, |
1891 | d40c->dma_cfg.src_info.data_width, | 1988 | d40c->dma_cfg.src_info.data_width, |
1892 | false); | 1989 | d40c->dma_cfg.dst_info.data_width, |
1893 | if (err) | 1990 | false) == NULL) |
1894 | goto err_fill_lli; | 1991 | goto err; |
1895 | 1992 | ||
1896 | err = d40_phy_fill_lli(d40d->lli_phy.dst, | 1993 | if (d40_phy_buf_to_lli(d40d->lli_phy.dst, |
1897 | dst, | 1994 | dst, |
1898 | size, | 1995 | size, |
1899 | d40c->dma_cfg.dst_info.psize, | 1996 | d40c->dma_cfg.dst_info.psize, |
@@ -1901,10 +1998,9 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan, | |||
1901 | d40c->dst_def_cfg, | 1998 | d40c->dst_def_cfg, |
1902 | true, | 1999 | true, |
1903 | d40c->dma_cfg.dst_info.data_width, | 2000 | d40c->dma_cfg.dst_info.data_width, |
1904 | false); | 2001 | d40c->dma_cfg.src_info.data_width, |
1905 | 2002 | false) == NULL) | |
1906 | if (err) | 2003 | goto err; |
1907 | goto err_fill_lli; | ||
1908 | 2004 | ||
1909 | (void) dma_map_single(d40c->base->dev, d40d->lli_phy.src, | 2005 | (void) dma_map_single(d40c->base->dev, d40d->lli_phy.src, |
1910 | d40d->lli_pool.size, DMA_TO_DEVICE); | 2006 | d40d->lli_pool.size, DMA_TO_DEVICE); |
@@ -1913,9 +2009,6 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan, | |||
1913 | spin_unlock_irqrestore(&d40c->lock, flags); | 2009 | spin_unlock_irqrestore(&d40c->lock, flags); |
1914 | return &d40d->txd; | 2010 | return &d40d->txd; |
1915 | 2011 | ||
1916 | err_fill_lli: | ||
1917 | dev_err(&d40c->chan.dev->device, | ||
1918 | "[%s] Failed filling in PHY LLI\n", __func__); | ||
1919 | err: | 2012 | err: |
1920 | if (d40d) | 2013 | if (d40d) |
1921 | d40_desc_free(d40c, d40d); | 2014 | d40_desc_free(d40c, d40d); |
@@ -1945,13 +2038,21 @@ static int d40_prep_slave_sg_log(struct d40_desc *d40d, | |||
1945 | dma_addr_t dev_addr = 0; | 2038 | dma_addr_t dev_addr = 0; |
1946 | int total_size; | 2039 | int total_size; |
1947 | 2040 | ||
1948 | if (d40_pool_lli_alloc(d40d, sg_len, true) < 0) { | 2041 | d40d->lli_len = d40_sg_2_dmalen(sgl, sg_len, |
2042 | d40c->dma_cfg.src_info.data_width, | ||
2043 | d40c->dma_cfg.dst_info.data_width); | ||
2044 | if (d40d->lli_len < 0) { | ||
2045 | dev_err(&d40c->chan.dev->device, | ||
2046 | "[%s] Unaligned size\n", __func__); | ||
2047 | return -EINVAL; | ||
2048 | } | ||
2049 | |||
2050 | if (d40_pool_lli_alloc(d40d, d40d->lli_len, true) < 0) { | ||
1949 | dev_err(&d40c->chan.dev->device, | 2051 | dev_err(&d40c->chan.dev->device, |
1950 | "[%s] Out of memory\n", __func__); | 2052 | "[%s] Out of memory\n", __func__); |
1951 | return -ENOMEM; | 2053 | return -ENOMEM; |
1952 | } | 2054 | } |
1953 | 2055 | ||
1954 | d40d->lli_len = sg_len; | ||
1955 | d40d->lli_current = 0; | 2056 | d40d->lli_current = 0; |
1956 | 2057 | ||
1957 | if (direction == DMA_FROM_DEVICE) | 2058 | if (direction == DMA_FROM_DEVICE) |
@@ -1993,13 +2094,21 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d, | |||
1993 | dma_addr_t dst_dev_addr; | 2094 | dma_addr_t dst_dev_addr; |
1994 | int res; | 2095 | int res; |
1995 | 2096 | ||
1996 | if (d40_pool_lli_alloc(d40d, sgl_len, false) < 0) { | 2097 | d40d->lli_len = d40_sg_2_dmalen(sgl, sgl_len, |
2098 | d40c->dma_cfg.src_info.data_width, | ||
2099 | d40c->dma_cfg.dst_info.data_width); | ||
2100 | if (d40d->lli_len < 0) { | ||
2101 | dev_err(&d40c->chan.dev->device, | ||
2102 | "[%s] Unaligned size\n", __func__); | ||
2103 | return -EINVAL; | ||
2104 | } | ||
2105 | |||
2106 | if (d40_pool_lli_alloc(d40d, d40d->lli_len, false) < 0) { | ||
1997 | dev_err(&d40c->chan.dev->device, | 2107 | dev_err(&d40c->chan.dev->device, |
1998 | "[%s] Out of memory\n", __func__); | 2108 | "[%s] Out of memory\n", __func__); |
1999 | return -ENOMEM; | 2109 | return -ENOMEM; |
2000 | } | 2110 | } |
2001 | 2111 | ||
2002 | d40d->lli_len = sgl_len; | ||
2003 | d40d->lli_current = 0; | 2112 | d40d->lli_current = 0; |
2004 | 2113 | ||
2005 | if (direction == DMA_FROM_DEVICE) { | 2114 | if (direction == DMA_FROM_DEVICE) { |
@@ -2024,6 +2133,7 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d, | |||
2024 | virt_to_phys(d40d->lli_phy.src), | 2133 | virt_to_phys(d40d->lli_phy.src), |
2025 | d40c->src_def_cfg, | 2134 | d40c->src_def_cfg, |
2026 | d40c->dma_cfg.src_info.data_width, | 2135 | d40c->dma_cfg.src_info.data_width, |
2136 | d40c->dma_cfg.dst_info.data_width, | ||
2027 | d40c->dma_cfg.src_info.psize); | 2137 | d40c->dma_cfg.src_info.psize); |
2028 | if (res < 0) | 2138 | if (res < 0) |
2029 | return res; | 2139 | return res; |
@@ -2035,6 +2145,7 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d, | |||
2035 | virt_to_phys(d40d->lli_phy.dst), | 2145 | virt_to_phys(d40d->lli_phy.dst), |
2036 | d40c->dst_def_cfg, | 2146 | d40c->dst_def_cfg, |
2037 | d40c->dma_cfg.dst_info.data_width, | 2147 | d40c->dma_cfg.dst_info.data_width, |
2148 | d40c->dma_cfg.src_info.data_width, | ||
2038 | d40c->dma_cfg.dst_info.psize); | 2149 | d40c->dma_cfg.dst_info.psize); |
2039 | if (res < 0) | 2150 | if (res < 0) |
2040 | return res; | 2151 | return res; |
@@ -2244,6 +2355,8 @@ static void d40_set_runtime_config(struct dma_chan *chan, | |||
2244 | psize = STEDMA40_PSIZE_PHY_8; | 2355 | psize = STEDMA40_PSIZE_PHY_8; |
2245 | else if (config_maxburst >= 4) | 2356 | else if (config_maxburst >= 4) |
2246 | psize = STEDMA40_PSIZE_PHY_4; | 2357 | psize = STEDMA40_PSIZE_PHY_4; |
2358 | else if (config_maxburst >= 2) | ||
2359 | psize = STEDMA40_PSIZE_PHY_2; | ||
2247 | else | 2360 | else |
2248 | psize = STEDMA40_PSIZE_PHY_1; | 2361 | psize = STEDMA40_PSIZE_PHY_1; |
2249 | } | 2362 | } |