18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OMAP DMAengine support 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/cpu_pm.h> 68c2ecf20Sopenharmony_ci#include <linux/delay.h> 78c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 88c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 98c2ecf20Sopenharmony_ci#include <linux/dmapool.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/list.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/omap-dma.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci#include <linux/of_dma.h> 208c2ecf20Sopenharmony_ci#include <linux/of_device.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "../virt-dma.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define OMAP_SDMA_REQUESTS 127 258c2ecf20Sopenharmony_ci#define OMAP_SDMA_CHANNELS 32 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct omap_dma_config { 288c2ecf20Sopenharmony_ci int lch_end; 298c2ecf20Sopenharmony_ci unsigned int rw_priority:1; 308c2ecf20Sopenharmony_ci unsigned int needs_busy_check:1; 318c2ecf20Sopenharmony_ci unsigned int may_lose_context:1; 328c2ecf20Sopenharmony_ci unsigned int needs_lch_clear:1; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct omap_dma_context { 368c2ecf20Sopenharmony_ci u32 irqenable_l0; 378c2ecf20Sopenharmony_ci u32 irqenable_l1; 388c2ecf20Sopenharmony_ci u32 ocp_sysconfig; 398c2ecf20Sopenharmony_ci u32 gcr; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct omap_dmadev { 438c2ecf20Sopenharmony_ci struct dma_device ddev; 448c2ecf20Sopenharmony_ci spinlock_t lock; 458c2ecf20Sopenharmony_ci void __iomem *base; 468c2ecf20Sopenharmony_ci const struct omap_dma_reg *reg_map; 478c2ecf20Sopenharmony_ci struct omap_system_dma_plat_info *plat; 488c2ecf20Sopenharmony_ci const struct omap_dma_config *cfg; 498c2ecf20Sopenharmony_ci struct notifier_block nb; 508c2ecf20Sopenharmony_ci struct omap_dma_context context; 518c2ecf20Sopenharmony_ci int lch_count; 528c2ecf20Sopenharmony_ci DECLARE_BITMAP(lch_bitmap, OMAP_SDMA_CHANNELS); 538c2ecf20Sopenharmony_ci struct mutex lch_lock; /* for assigning logical channels */ 548c2ecf20Sopenharmony_ci bool legacy; 558c2ecf20Sopenharmony_ci bool ll123_supported; 568c2ecf20Sopenharmony_ci struct dma_pool *desc_pool; 578c2ecf20Sopenharmony_ci unsigned dma_requests; 588c2ecf20Sopenharmony_ci spinlock_t irq_lock; 598c2ecf20Sopenharmony_ci uint32_t irq_enable_mask; 608c2ecf20Sopenharmony_ci struct omap_chan **lch_map; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistruct omap_chan { 648c2ecf20Sopenharmony_ci struct virt_dma_chan vc; 658c2ecf20Sopenharmony_ci void __iomem *channel_base; 668c2ecf20Sopenharmony_ci const struct omap_dma_reg *reg_map; 678c2ecf20Sopenharmony_ci uint32_t ccr; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci struct dma_slave_config cfg; 708c2ecf20Sopenharmony_ci unsigned dma_sig; 718c2ecf20Sopenharmony_ci bool cyclic; 728c2ecf20Sopenharmony_ci bool paused; 738c2ecf20Sopenharmony_ci bool running; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci int dma_ch; 768c2ecf20Sopenharmony_ci struct omap_desc *desc; 778c2ecf20Sopenharmony_ci unsigned sgidx; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define DESC_NXT_SV_REFRESH (0x1 << 24) 818c2ecf20Sopenharmony_ci#define DESC_NXT_SV_REUSE (0x2 << 24) 828c2ecf20Sopenharmony_ci#define DESC_NXT_DV_REFRESH (0x1 << 26) 838c2ecf20Sopenharmony_ci#define DESC_NXT_DV_REUSE (0x2 << 26) 848c2ecf20Sopenharmony_ci#define DESC_NTYPE_TYPE2 (0x2 << 29) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* Type 2 descriptor with Source or Destination address update */ 878c2ecf20Sopenharmony_cistruct omap_type2_desc { 888c2ecf20Sopenharmony_ci uint32_t next_desc; 898c2ecf20Sopenharmony_ci uint32_t en; 908c2ecf20Sopenharmony_ci uint32_t addr; /* src or dst */ 918c2ecf20Sopenharmony_ci uint16_t fn; 928c2ecf20Sopenharmony_ci uint16_t cicr; 938c2ecf20Sopenharmony_ci int16_t cdei; 948c2ecf20Sopenharmony_ci int16_t csei; 958c2ecf20Sopenharmony_ci int32_t cdfi; 968c2ecf20Sopenharmony_ci int32_t csfi; 978c2ecf20Sopenharmony_ci} __packed; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct omap_sg { 1008c2ecf20Sopenharmony_ci dma_addr_t addr; 1018c2ecf20Sopenharmony_ci uint32_t en; /* number of elements (24-bit) */ 1028c2ecf20Sopenharmony_ci uint32_t fn; /* number of frames (16-bit) */ 1038c2ecf20Sopenharmony_ci int32_t fi; /* for double indexing */ 1048c2ecf20Sopenharmony_ci int16_t ei; /* for double indexing */ 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* Linked list */ 1078c2ecf20Sopenharmony_ci struct omap_type2_desc *t2_desc; 1088c2ecf20Sopenharmony_ci dma_addr_t t2_desc_paddr; 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistruct omap_desc { 1128c2ecf20Sopenharmony_ci struct virt_dma_desc vd; 1138c2ecf20Sopenharmony_ci bool using_ll; 1148c2ecf20Sopenharmony_ci enum dma_transfer_direction dir; 1158c2ecf20Sopenharmony_ci dma_addr_t dev_addr; 1168c2ecf20Sopenharmony_ci bool polled; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci int32_t fi; /* for OMAP_DMA_SYNC_PACKET / double indexing */ 1198c2ecf20Sopenharmony_ci int16_t ei; /* for double indexing */ 1208c2ecf20Sopenharmony_ci uint8_t es; /* CSDP_DATA_TYPE_xxx */ 1218c2ecf20Sopenharmony_ci uint32_t ccr; /* CCR value */ 1228c2ecf20Sopenharmony_ci uint16_t clnk_ctrl; /* CLNK_CTRL value */ 1238c2ecf20Sopenharmony_ci uint16_t cicr; /* CICR value */ 1248c2ecf20Sopenharmony_ci uint32_t csdp; /* CSDP value */ 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci unsigned sglen; 1278c2ecf20Sopenharmony_ci struct omap_sg sg[]; 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cienum { 1318c2ecf20Sopenharmony_ci CAPS_0_SUPPORT_LL123 = BIT(20), /* Linked List type1/2/3 */ 1328c2ecf20Sopenharmony_ci CAPS_0_SUPPORT_LL4 = BIT(21), /* Linked List type4 */ 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci CCR_FS = BIT(5), 1358c2ecf20Sopenharmony_ci CCR_READ_PRIORITY = BIT(6), 1368c2ecf20Sopenharmony_ci CCR_ENABLE = BIT(7), 1378c2ecf20Sopenharmony_ci CCR_AUTO_INIT = BIT(8), /* OMAP1 only */ 1388c2ecf20Sopenharmony_ci CCR_REPEAT = BIT(9), /* OMAP1 only */ 1398c2ecf20Sopenharmony_ci CCR_OMAP31_DISABLE = BIT(10), /* OMAP1 only */ 1408c2ecf20Sopenharmony_ci CCR_SUSPEND_SENSITIVE = BIT(8), /* OMAP2+ only */ 1418c2ecf20Sopenharmony_ci CCR_RD_ACTIVE = BIT(9), /* OMAP2+ only */ 1428c2ecf20Sopenharmony_ci CCR_WR_ACTIVE = BIT(10), /* OMAP2+ only */ 1438c2ecf20Sopenharmony_ci CCR_SRC_AMODE_CONSTANT = 0 << 12, 1448c2ecf20Sopenharmony_ci CCR_SRC_AMODE_POSTINC = 1 << 12, 1458c2ecf20Sopenharmony_ci CCR_SRC_AMODE_SGLIDX = 2 << 12, 1468c2ecf20Sopenharmony_ci CCR_SRC_AMODE_DBLIDX = 3 << 12, 1478c2ecf20Sopenharmony_ci CCR_DST_AMODE_CONSTANT = 0 << 14, 1488c2ecf20Sopenharmony_ci CCR_DST_AMODE_POSTINC = 1 << 14, 1498c2ecf20Sopenharmony_ci CCR_DST_AMODE_SGLIDX = 2 << 14, 1508c2ecf20Sopenharmony_ci CCR_DST_AMODE_DBLIDX = 3 << 14, 1518c2ecf20Sopenharmony_ci CCR_CONSTANT_FILL = BIT(16), 1528c2ecf20Sopenharmony_ci CCR_TRANSPARENT_COPY = BIT(17), 1538c2ecf20Sopenharmony_ci CCR_BS = BIT(18), 1548c2ecf20Sopenharmony_ci CCR_SUPERVISOR = BIT(22), 1558c2ecf20Sopenharmony_ci CCR_PREFETCH = BIT(23), 1568c2ecf20Sopenharmony_ci CCR_TRIGGER_SRC = BIT(24), 1578c2ecf20Sopenharmony_ci CCR_BUFFERING_DISABLE = BIT(25), 1588c2ecf20Sopenharmony_ci CCR_WRITE_PRIORITY = BIT(26), 1598c2ecf20Sopenharmony_ci CCR_SYNC_ELEMENT = 0, 1608c2ecf20Sopenharmony_ci CCR_SYNC_FRAME = CCR_FS, 1618c2ecf20Sopenharmony_ci CCR_SYNC_BLOCK = CCR_BS, 1628c2ecf20Sopenharmony_ci CCR_SYNC_PACKET = CCR_BS | CCR_FS, 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci CSDP_DATA_TYPE_8 = 0, 1658c2ecf20Sopenharmony_ci CSDP_DATA_TYPE_16 = 1, 1668c2ecf20Sopenharmony_ci CSDP_DATA_TYPE_32 = 2, 1678c2ecf20Sopenharmony_ci CSDP_SRC_PORT_EMIFF = 0 << 2, /* OMAP1 only */ 1688c2ecf20Sopenharmony_ci CSDP_SRC_PORT_EMIFS = 1 << 2, /* OMAP1 only */ 1698c2ecf20Sopenharmony_ci CSDP_SRC_PORT_OCP_T1 = 2 << 2, /* OMAP1 only */ 1708c2ecf20Sopenharmony_ci CSDP_SRC_PORT_TIPB = 3 << 2, /* OMAP1 only */ 1718c2ecf20Sopenharmony_ci CSDP_SRC_PORT_OCP_T2 = 4 << 2, /* OMAP1 only */ 1728c2ecf20Sopenharmony_ci CSDP_SRC_PORT_MPUI = 5 << 2, /* OMAP1 only */ 1738c2ecf20Sopenharmony_ci CSDP_SRC_PACKED = BIT(6), 1748c2ecf20Sopenharmony_ci CSDP_SRC_BURST_1 = 0 << 7, 1758c2ecf20Sopenharmony_ci CSDP_SRC_BURST_16 = 1 << 7, 1768c2ecf20Sopenharmony_ci CSDP_SRC_BURST_32 = 2 << 7, 1778c2ecf20Sopenharmony_ci CSDP_SRC_BURST_64 = 3 << 7, 1788c2ecf20Sopenharmony_ci CSDP_DST_PORT_EMIFF = 0 << 9, /* OMAP1 only */ 1798c2ecf20Sopenharmony_ci CSDP_DST_PORT_EMIFS = 1 << 9, /* OMAP1 only */ 1808c2ecf20Sopenharmony_ci CSDP_DST_PORT_OCP_T1 = 2 << 9, /* OMAP1 only */ 1818c2ecf20Sopenharmony_ci CSDP_DST_PORT_TIPB = 3 << 9, /* OMAP1 only */ 1828c2ecf20Sopenharmony_ci CSDP_DST_PORT_OCP_T2 = 4 << 9, /* OMAP1 only */ 1838c2ecf20Sopenharmony_ci CSDP_DST_PORT_MPUI = 5 << 9, /* OMAP1 only */ 1848c2ecf20Sopenharmony_ci CSDP_DST_PACKED = BIT(13), 1858c2ecf20Sopenharmony_ci CSDP_DST_BURST_1 = 0 << 14, 1868c2ecf20Sopenharmony_ci CSDP_DST_BURST_16 = 1 << 14, 1878c2ecf20Sopenharmony_ci CSDP_DST_BURST_32 = 2 << 14, 1888c2ecf20Sopenharmony_ci CSDP_DST_BURST_64 = 3 << 14, 1898c2ecf20Sopenharmony_ci CSDP_WRITE_NON_POSTED = 0 << 16, 1908c2ecf20Sopenharmony_ci CSDP_WRITE_POSTED = 1 << 16, 1918c2ecf20Sopenharmony_ci CSDP_WRITE_LAST_NON_POSTED = 2 << 16, 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci CICR_TOUT_IE = BIT(0), /* OMAP1 only */ 1948c2ecf20Sopenharmony_ci CICR_DROP_IE = BIT(1), 1958c2ecf20Sopenharmony_ci CICR_HALF_IE = BIT(2), 1968c2ecf20Sopenharmony_ci CICR_FRAME_IE = BIT(3), 1978c2ecf20Sopenharmony_ci CICR_LAST_IE = BIT(4), 1988c2ecf20Sopenharmony_ci CICR_BLOCK_IE = BIT(5), 1998c2ecf20Sopenharmony_ci CICR_PKT_IE = BIT(7), /* OMAP2+ only */ 2008c2ecf20Sopenharmony_ci CICR_TRANS_ERR_IE = BIT(8), /* OMAP2+ only */ 2018c2ecf20Sopenharmony_ci CICR_SUPERVISOR_ERR_IE = BIT(10), /* OMAP2+ only */ 2028c2ecf20Sopenharmony_ci CICR_MISALIGNED_ERR_IE = BIT(11), /* OMAP2+ only */ 2038c2ecf20Sopenharmony_ci CICR_DRAIN_IE = BIT(12), /* OMAP2+ only */ 2048c2ecf20Sopenharmony_ci CICR_SUPER_BLOCK_IE = BIT(14), /* OMAP2+ only */ 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci CLNK_CTRL_ENABLE_LNK = BIT(15), 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci CDP_DST_VALID_INC = 0 << 0, 2098c2ecf20Sopenharmony_ci CDP_DST_VALID_RELOAD = 1 << 0, 2108c2ecf20Sopenharmony_ci CDP_DST_VALID_REUSE = 2 << 0, 2118c2ecf20Sopenharmony_ci CDP_SRC_VALID_INC = 0 << 2, 2128c2ecf20Sopenharmony_ci CDP_SRC_VALID_RELOAD = 1 << 2, 2138c2ecf20Sopenharmony_ci CDP_SRC_VALID_REUSE = 2 << 2, 2148c2ecf20Sopenharmony_ci CDP_NTYPE_TYPE1 = 1 << 4, 2158c2ecf20Sopenharmony_ci CDP_NTYPE_TYPE2 = 2 << 4, 2168c2ecf20Sopenharmony_ci CDP_NTYPE_TYPE3 = 3 << 4, 2178c2ecf20Sopenharmony_ci CDP_TMODE_NORMAL = 0 << 8, 2188c2ecf20Sopenharmony_ci CDP_TMODE_LLIST = 1 << 8, 2198c2ecf20Sopenharmony_ci CDP_FAST = BIT(10), 2208c2ecf20Sopenharmony_ci}; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic const unsigned es_bytes[] = { 2238c2ecf20Sopenharmony_ci [CSDP_DATA_TYPE_8] = 1, 2248c2ecf20Sopenharmony_ci [CSDP_DATA_TYPE_16] = 2, 2258c2ecf20Sopenharmony_ci [CSDP_DATA_TYPE_32] = 4, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic bool omap_dma_filter_fn(struct dma_chan *chan, void *param); 2298c2ecf20Sopenharmony_cistatic struct of_dma_filter_info omap_dma_info = { 2308c2ecf20Sopenharmony_ci .filter_fn = omap_dma_filter_fn, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci return container_of(d, struct omap_dmadev, ddev); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci return container_of(c, struct omap_chan, vc.chan); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci return container_of(t, struct omap_desc, vd.tx); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic void omap_dma_desc_free(struct virt_dma_desc *vd) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct omap_desc *d = to_omap_dma_desc(&vd->tx); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (d->using_ll) { 2538c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(vd->tx.chan->device); 2548c2ecf20Sopenharmony_ci int i; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci for (i = 0; i < d->sglen; i++) { 2578c2ecf20Sopenharmony_ci if (d->sg[i].t2_desc) 2588c2ecf20Sopenharmony_ci dma_pool_free(od->desc_pool, d->sg[i].t2_desc, 2598c2ecf20Sopenharmony_ci d->sg[i].t2_desc_paddr); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci kfree(d); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void omap_dma_fill_type2_desc(struct omap_desc *d, int idx, 2678c2ecf20Sopenharmony_ci enum dma_transfer_direction dir, bool last) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct omap_sg *sg = &d->sg[idx]; 2708c2ecf20Sopenharmony_ci struct omap_type2_desc *t2_desc = sg->t2_desc; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (idx) 2738c2ecf20Sopenharmony_ci d->sg[idx - 1].t2_desc->next_desc = sg->t2_desc_paddr; 2748c2ecf20Sopenharmony_ci if (last) 2758c2ecf20Sopenharmony_ci t2_desc->next_desc = 0xfffffffc; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci t2_desc->en = sg->en; 2788c2ecf20Sopenharmony_ci t2_desc->addr = sg->addr; 2798c2ecf20Sopenharmony_ci t2_desc->fn = sg->fn & 0xffff; 2808c2ecf20Sopenharmony_ci t2_desc->cicr = d->cicr; 2818c2ecf20Sopenharmony_ci if (!last) 2828c2ecf20Sopenharmony_ci t2_desc->cicr &= ~CICR_BLOCK_IE; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci switch (dir) { 2858c2ecf20Sopenharmony_ci case DMA_DEV_TO_MEM: 2868c2ecf20Sopenharmony_ci t2_desc->cdei = sg->ei; 2878c2ecf20Sopenharmony_ci t2_desc->csei = d->ei; 2888c2ecf20Sopenharmony_ci t2_desc->cdfi = sg->fi; 2898c2ecf20Sopenharmony_ci t2_desc->csfi = d->fi; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci t2_desc->en |= DESC_NXT_DV_REFRESH; 2928c2ecf20Sopenharmony_ci t2_desc->en |= DESC_NXT_SV_REUSE; 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci case DMA_MEM_TO_DEV: 2958c2ecf20Sopenharmony_ci t2_desc->cdei = d->ei; 2968c2ecf20Sopenharmony_ci t2_desc->csei = sg->ei; 2978c2ecf20Sopenharmony_ci t2_desc->cdfi = d->fi; 2988c2ecf20Sopenharmony_ci t2_desc->csfi = sg->fi; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci t2_desc->en |= DESC_NXT_SV_REFRESH; 3018c2ecf20Sopenharmony_ci t2_desc->en |= DESC_NXT_DV_REUSE; 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci default: 3048c2ecf20Sopenharmony_ci return; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci t2_desc->en |= DESC_NTYPE_TYPE2; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void omap_dma_write(uint32_t val, unsigned type, void __iomem *addr) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci switch (type) { 3138c2ecf20Sopenharmony_ci case OMAP_DMA_REG_16BIT: 3148c2ecf20Sopenharmony_ci writew_relaxed(val, addr); 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci case OMAP_DMA_REG_2X16BIT: 3178c2ecf20Sopenharmony_ci writew_relaxed(val, addr); 3188c2ecf20Sopenharmony_ci writew_relaxed(val >> 16, addr + 2); 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci case OMAP_DMA_REG_32BIT: 3218c2ecf20Sopenharmony_ci writel_relaxed(val, addr); 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci default: 3248c2ecf20Sopenharmony_ci WARN_ON(1); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic unsigned omap_dma_read(unsigned type, void __iomem *addr) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci unsigned val; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci switch (type) { 3338c2ecf20Sopenharmony_ci case OMAP_DMA_REG_16BIT: 3348c2ecf20Sopenharmony_ci val = readw_relaxed(addr); 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci case OMAP_DMA_REG_2X16BIT: 3378c2ecf20Sopenharmony_ci val = readw_relaxed(addr); 3388c2ecf20Sopenharmony_ci val |= readw_relaxed(addr + 2) << 16; 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case OMAP_DMA_REG_32BIT: 3418c2ecf20Sopenharmony_ci val = readl_relaxed(addr); 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci default: 3448c2ecf20Sopenharmony_ci WARN_ON(1); 3458c2ecf20Sopenharmony_ci val = 0; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return val; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void omap_dma_glbl_write(struct omap_dmadev *od, unsigned reg, unsigned val) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci const struct omap_dma_reg *r = od->reg_map + reg; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci WARN_ON(r->stride); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci omap_dma_write(val, r->type, od->base + r->offset); 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic unsigned omap_dma_glbl_read(struct omap_dmadev *od, unsigned reg) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci const struct omap_dma_reg *r = od->reg_map + reg; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci WARN_ON(r->stride); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return omap_dma_read(r->type, od->base + r->offset); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void omap_dma_chan_write(struct omap_chan *c, unsigned reg, unsigned val) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci const struct omap_dma_reg *r = c->reg_map + reg; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci omap_dma_write(val, r->type, c->channel_base + r->offset); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic unsigned omap_dma_chan_read(struct omap_chan *c, unsigned reg) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci const struct omap_dma_reg *r = c->reg_map + reg; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return omap_dma_read(r->type, c->channel_base + r->offset); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic void omap_dma_clear_csr(struct omap_chan *c) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci if (dma_omap1()) 3868c2ecf20Sopenharmony_ci omap_dma_chan_read(c, CSR); 3878c2ecf20Sopenharmony_ci else 3888c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CSR, ~0); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic unsigned omap_dma_get_csr(struct omap_chan *c) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci unsigned val = omap_dma_chan_read(c, CSR); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (!dma_omap1()) 3968c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CSR, val); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return val; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic void omap_dma_clear_lch(struct omap_dmadev *od, int lch) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct omap_chan *c; 4048c2ecf20Sopenharmony_ci int i; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci c = od->lch_map[lch]; 4078c2ecf20Sopenharmony_ci if (!c) 4088c2ecf20Sopenharmony_ci return; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci for (i = CSDP; i <= od->cfg->lch_end; i++) 4118c2ecf20Sopenharmony_ci omap_dma_chan_write(c, i, 0); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void omap_dma_assign(struct omap_dmadev *od, struct omap_chan *c, 4158c2ecf20Sopenharmony_ci unsigned lch) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci c->channel_base = od->base + od->plat->channel_stride * lch; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci od->lch_map[lch] = c; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic void omap_dma_start(struct omap_chan *c, struct omap_desc *d) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device); 4258c2ecf20Sopenharmony_ci uint16_t cicr = d->cicr; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (__dma_omap15xx(od->plat->dma_attr)) 4288c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CPC, 0); 4298c2ecf20Sopenharmony_ci else 4308c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CDAC, 0); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci omap_dma_clear_csr(c); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (d->using_ll) { 4358c2ecf20Sopenharmony_ci uint32_t cdp = CDP_TMODE_LLIST | CDP_NTYPE_TYPE2 | CDP_FAST; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (d->dir == DMA_DEV_TO_MEM) 4388c2ecf20Sopenharmony_ci cdp |= (CDP_DST_VALID_RELOAD | CDP_SRC_VALID_REUSE); 4398c2ecf20Sopenharmony_ci else 4408c2ecf20Sopenharmony_ci cdp |= (CDP_DST_VALID_REUSE | CDP_SRC_VALID_RELOAD); 4418c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CDP, cdp); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CNDP, d->sg[0].t2_desc_paddr); 4448c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CCDN, 0); 4458c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CCFN, 0xffff); 4468c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CCEN, 0xffffff); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci cicr &= ~CICR_BLOCK_IE; 4498c2ecf20Sopenharmony_ci } else if (od->ll123_supported) { 4508c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CDP, 0); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* Enable interrupts */ 4548c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CICR, cicr); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Enable channel */ 4578c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CCR, d->ccr | CCR_ENABLE); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci c->running = true; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic void omap_dma_drain_chan(struct omap_chan *c) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci int i; 4658c2ecf20Sopenharmony_ci u32 val; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* Wait for sDMA FIFO to drain */ 4688c2ecf20Sopenharmony_ci for (i = 0; ; i++) { 4698c2ecf20Sopenharmony_ci val = omap_dma_chan_read(c, CCR); 4708c2ecf20Sopenharmony_ci if (!(val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE))) 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (i > 100) 4748c2ecf20Sopenharmony_ci break; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci udelay(5); 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE)) 4808c2ecf20Sopenharmony_ci dev_err(c->vc.chan.device->dev, 4818c2ecf20Sopenharmony_ci "DMA drain did not complete on lch %d\n", 4828c2ecf20Sopenharmony_ci c->dma_ch); 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int omap_dma_stop(struct omap_chan *c) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device); 4888c2ecf20Sopenharmony_ci uint32_t val; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* disable irq */ 4918c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CICR, 0); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci omap_dma_clear_csr(c); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci val = omap_dma_chan_read(c, CCR); 4968c2ecf20Sopenharmony_ci if (od->plat->errata & DMA_ERRATA_i541 && val & CCR_TRIGGER_SRC) { 4978c2ecf20Sopenharmony_ci uint32_t sysconfig; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG); 5008c2ecf20Sopenharmony_ci val = sysconfig & ~DMA_SYSCONFIG_MIDLEMODE_MASK; 5018c2ecf20Sopenharmony_ci val |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE); 5028c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, OCP_SYSCONFIG, val); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci val = omap_dma_chan_read(c, CCR); 5058c2ecf20Sopenharmony_ci val &= ~CCR_ENABLE; 5068c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CCR, val); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (!(c->ccr & CCR_BUFFERING_DISABLE)) 5098c2ecf20Sopenharmony_ci omap_dma_drain_chan(c); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, OCP_SYSCONFIG, sysconfig); 5128c2ecf20Sopenharmony_ci } else { 5138c2ecf20Sopenharmony_ci if (!(val & CCR_ENABLE)) 5148c2ecf20Sopenharmony_ci return -EINVAL; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci val &= ~CCR_ENABLE; 5178c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CCR, val); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (!(c->ccr & CCR_BUFFERING_DISABLE)) 5208c2ecf20Sopenharmony_ci omap_dma_drain_chan(c); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci mb(); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (!__dma_omap15xx(od->plat->dma_attr) && c->cyclic) { 5268c2ecf20Sopenharmony_ci val = omap_dma_chan_read(c, CLNK_CTRL); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (dma_omap1()) 5298c2ecf20Sopenharmony_ci val |= 1 << 14; /* set the STOP_LNK bit */ 5308c2ecf20Sopenharmony_ci else 5318c2ecf20Sopenharmony_ci val &= ~CLNK_CTRL_ENABLE_LNK; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CLNK_CTRL, val); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci c->running = false; 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct omap_sg *sg = d->sg + c->sgidx; 5428c2ecf20Sopenharmony_ci unsigned cxsa, cxei, cxfi; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM) { 5458c2ecf20Sopenharmony_ci cxsa = CDSA; 5468c2ecf20Sopenharmony_ci cxei = CDEI; 5478c2ecf20Sopenharmony_ci cxfi = CDFI; 5488c2ecf20Sopenharmony_ci } else { 5498c2ecf20Sopenharmony_ci cxsa = CSSA; 5508c2ecf20Sopenharmony_ci cxei = CSEI; 5518c2ecf20Sopenharmony_ci cxfi = CSFI; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci omap_dma_chan_write(c, cxsa, sg->addr); 5558c2ecf20Sopenharmony_ci omap_dma_chan_write(c, cxei, sg->ei); 5568c2ecf20Sopenharmony_ci omap_dma_chan_write(c, cxfi, sg->fi); 5578c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CEN, sg->en); 5588c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CFN, sg->fn); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci omap_dma_start(c, d); 5618c2ecf20Sopenharmony_ci c->sgidx++; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic void omap_dma_start_desc(struct omap_chan *c) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct virt_dma_desc *vd = vchan_next_desc(&c->vc); 5678c2ecf20Sopenharmony_ci struct omap_desc *d; 5688c2ecf20Sopenharmony_ci unsigned cxsa, cxei, cxfi; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (!vd) { 5718c2ecf20Sopenharmony_ci c->desc = NULL; 5728c2ecf20Sopenharmony_ci return; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci list_del(&vd->node); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci c->desc = d = to_omap_dma_desc(&vd->tx); 5788c2ecf20Sopenharmony_ci c->sgidx = 0; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* 5818c2ecf20Sopenharmony_ci * This provides the necessary barrier to ensure data held in 5828c2ecf20Sopenharmony_ci * DMA coherent memory is visible to the DMA engine prior to 5838c2ecf20Sopenharmony_ci * the transfer starting. 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_ci mb(); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CCR, d->ccr); 5888c2ecf20Sopenharmony_ci if (dma_omap1()) 5898c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CCR2, d->ccr >> 16); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM) { 5928c2ecf20Sopenharmony_ci cxsa = CSSA; 5938c2ecf20Sopenharmony_ci cxei = CSEI; 5948c2ecf20Sopenharmony_ci cxfi = CSFI; 5958c2ecf20Sopenharmony_ci } else { 5968c2ecf20Sopenharmony_ci cxsa = CDSA; 5978c2ecf20Sopenharmony_ci cxei = CDEI; 5988c2ecf20Sopenharmony_ci cxfi = CDFI; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci omap_dma_chan_write(c, cxsa, d->dev_addr); 6028c2ecf20Sopenharmony_ci omap_dma_chan_write(c, cxei, d->ei); 6038c2ecf20Sopenharmony_ci omap_dma_chan_write(c, cxfi, d->fi); 6048c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CSDP, d->csdp); 6058c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CLNK_CTRL, d->clnk_ctrl); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci omap_dma_start_sg(c, d); 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic void omap_dma_callback(int ch, u16 status, void *data) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct omap_chan *c = data; 6138c2ecf20Sopenharmony_ci struct omap_desc *d; 6148c2ecf20Sopenharmony_ci unsigned long flags; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 6178c2ecf20Sopenharmony_ci d = c->desc; 6188c2ecf20Sopenharmony_ci if (d) { 6198c2ecf20Sopenharmony_ci if (c->cyclic) { 6208c2ecf20Sopenharmony_ci vchan_cyclic_callback(&d->vd); 6218c2ecf20Sopenharmony_ci } else if (d->using_ll || c->sgidx == d->sglen) { 6228c2ecf20Sopenharmony_ci omap_dma_start_desc(c); 6238c2ecf20Sopenharmony_ci vchan_cookie_complete(&d->vd); 6248c2ecf20Sopenharmony_ci } else { 6258c2ecf20Sopenharmony_ci omap_dma_start_sg(c, d); 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic irqreturn_t omap_dma_irq(int irq, void *devid) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct omap_dmadev *od = devid; 6348c2ecf20Sopenharmony_ci unsigned status, channel; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci spin_lock(&od->irq_lock); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci status = omap_dma_glbl_read(od, IRQSTATUS_L1); 6398c2ecf20Sopenharmony_ci status &= od->irq_enable_mask; 6408c2ecf20Sopenharmony_ci if (status == 0) { 6418c2ecf20Sopenharmony_ci spin_unlock(&od->irq_lock); 6428c2ecf20Sopenharmony_ci return IRQ_NONE; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci while ((channel = ffs(status)) != 0) { 6468c2ecf20Sopenharmony_ci unsigned mask, csr; 6478c2ecf20Sopenharmony_ci struct omap_chan *c; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci channel -= 1; 6508c2ecf20Sopenharmony_ci mask = BIT(channel); 6518c2ecf20Sopenharmony_ci status &= ~mask; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci c = od->lch_map[channel]; 6548c2ecf20Sopenharmony_ci if (c == NULL) { 6558c2ecf20Sopenharmony_ci /* This should never happen */ 6568c2ecf20Sopenharmony_ci dev_err(od->ddev.dev, "invalid channel %u\n", channel); 6578c2ecf20Sopenharmony_ci continue; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci csr = omap_dma_get_csr(c); 6618c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, IRQSTATUS_L1, mask); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci omap_dma_callback(channel, csr, c); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci spin_unlock(&od->irq_lock); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic int omap_dma_get_lch(struct omap_dmadev *od, int *lch) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci int channel; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci mutex_lock(&od->lch_lock); 6768c2ecf20Sopenharmony_ci channel = find_first_zero_bit(od->lch_bitmap, od->lch_count); 6778c2ecf20Sopenharmony_ci if (channel >= od->lch_count) 6788c2ecf20Sopenharmony_ci goto out_busy; 6798c2ecf20Sopenharmony_ci set_bit(channel, od->lch_bitmap); 6808c2ecf20Sopenharmony_ci mutex_unlock(&od->lch_lock); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci omap_dma_clear_lch(od, channel); 6838c2ecf20Sopenharmony_ci *lch = channel; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci return 0; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ciout_busy: 6888c2ecf20Sopenharmony_ci mutex_unlock(&od->lch_lock); 6898c2ecf20Sopenharmony_ci *lch = -EINVAL; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return -EBUSY; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic void omap_dma_put_lch(struct omap_dmadev *od, int lch) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci omap_dma_clear_lch(od, lch); 6978c2ecf20Sopenharmony_ci mutex_lock(&od->lch_lock); 6988c2ecf20Sopenharmony_ci clear_bit(lch, od->lch_bitmap); 6998c2ecf20Sopenharmony_ci mutex_unlock(&od->lch_lock); 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic int omap_dma_alloc_chan_resources(struct dma_chan *chan) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 7058c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 7068c2ecf20Sopenharmony_ci struct device *dev = od->ddev.dev; 7078c2ecf20Sopenharmony_ci int ret; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (od->legacy) { 7108c2ecf20Sopenharmony_ci ret = omap_request_dma(c->dma_sig, "DMA engine", 7118c2ecf20Sopenharmony_ci omap_dma_callback, c, &c->dma_ch); 7128c2ecf20Sopenharmony_ci } else { 7138c2ecf20Sopenharmony_ci ret = omap_dma_get_lch(od, &c->dma_ch); 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci dev_dbg(dev, "allocating channel %u for %u\n", c->dma_ch, c->dma_sig); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if (ret >= 0) { 7198c2ecf20Sopenharmony_ci omap_dma_assign(od, c, c->dma_ch); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (!od->legacy) { 7228c2ecf20Sopenharmony_ci unsigned val; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci spin_lock_irq(&od->irq_lock); 7258c2ecf20Sopenharmony_ci val = BIT(c->dma_ch); 7268c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, IRQSTATUS_L1, val); 7278c2ecf20Sopenharmony_ci od->irq_enable_mask |= val; 7288c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci val = omap_dma_glbl_read(od, IRQENABLE_L0); 7318c2ecf20Sopenharmony_ci val &= ~BIT(c->dma_ch); 7328c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L0, val); 7338c2ecf20Sopenharmony_ci spin_unlock_irq(&od->irq_lock); 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (dma_omap1()) { 7388c2ecf20Sopenharmony_ci if (__dma_omap16xx(od->plat->dma_attr)) { 7398c2ecf20Sopenharmony_ci c->ccr = CCR_OMAP31_DISABLE; 7408c2ecf20Sopenharmony_ci /* Duplicate what plat-omap/dma.c does */ 7418c2ecf20Sopenharmony_ci c->ccr |= c->dma_ch + 1; 7428c2ecf20Sopenharmony_ci } else { 7438c2ecf20Sopenharmony_ci c->ccr = c->dma_sig & 0x1f; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci } else { 7468c2ecf20Sopenharmony_ci c->ccr = c->dma_sig & 0x1f; 7478c2ecf20Sopenharmony_ci c->ccr |= (c->dma_sig & ~0x1f) << 14; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING) 7508c2ecf20Sopenharmony_ci c->ccr |= CCR_BUFFERING_DISABLE; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci return ret; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic void omap_dma_free_chan_resources(struct dma_chan *chan) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 7588c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (!od->legacy) { 7618c2ecf20Sopenharmony_ci spin_lock_irq(&od->irq_lock); 7628c2ecf20Sopenharmony_ci od->irq_enable_mask &= ~BIT(c->dma_ch); 7638c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask); 7648c2ecf20Sopenharmony_ci spin_unlock_irq(&od->irq_lock); 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci c->channel_base = NULL; 7688c2ecf20Sopenharmony_ci od->lch_map[c->dma_ch] = NULL; 7698c2ecf20Sopenharmony_ci vchan_free_chan_resources(&c->vc); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (od->legacy) 7728c2ecf20Sopenharmony_ci omap_free_dma(c->dma_ch); 7738c2ecf20Sopenharmony_ci else 7748c2ecf20Sopenharmony_ci omap_dma_put_lch(od, c->dma_ch); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci dev_dbg(od->ddev.dev, "freeing channel %u used for %u\n", c->dma_ch, 7778c2ecf20Sopenharmony_ci c->dma_sig); 7788c2ecf20Sopenharmony_ci c->dma_sig = 0; 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic size_t omap_dma_sg_size(struct omap_sg *sg) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci return sg->en * sg->fn; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic size_t omap_dma_desc_size(struct omap_desc *d) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci unsigned i; 7898c2ecf20Sopenharmony_ci size_t size; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci for (size = i = 0; i < d->sglen; i++) 7928c2ecf20Sopenharmony_ci size += omap_dma_sg_size(&d->sg[i]); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci return size * es_bytes[d->es]; 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic size_t omap_dma_desc_size_pos(struct omap_desc *d, dma_addr_t addr) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci unsigned i; 8008c2ecf20Sopenharmony_ci size_t size, es_size = es_bytes[d->es]; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci for (size = i = 0; i < d->sglen; i++) { 8038c2ecf20Sopenharmony_ci size_t this_size = omap_dma_sg_size(&d->sg[i]) * es_size; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (size) 8068c2ecf20Sopenharmony_ci size += this_size; 8078c2ecf20Sopenharmony_ci else if (addr >= d->sg[i].addr && 8088c2ecf20Sopenharmony_ci addr < d->sg[i].addr + this_size) 8098c2ecf20Sopenharmony_ci size += d->sg[i].addr + this_size - addr; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci return size; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci/* 8158c2ecf20Sopenharmony_ci * OMAP 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is 8168c2ecf20Sopenharmony_ci * read before the DMA controller finished disabling the channel. 8178c2ecf20Sopenharmony_ci */ 8188c2ecf20Sopenharmony_cistatic uint32_t omap_dma_chan_read_3_3(struct omap_chan *c, unsigned reg) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device); 8218c2ecf20Sopenharmony_ci uint32_t val; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci val = omap_dma_chan_read(c, reg); 8248c2ecf20Sopenharmony_ci if (val == 0 && od->plat->errata & DMA_ERRATA_3_3) 8258c2ecf20Sopenharmony_ci val = omap_dma_chan_read(c, reg); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return val; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic dma_addr_t omap_dma_get_src_pos(struct omap_chan *c) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device); 8338c2ecf20Sopenharmony_ci dma_addr_t addr, cdac; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (__dma_omap15xx(od->plat->dma_attr)) { 8368c2ecf20Sopenharmony_ci addr = omap_dma_chan_read(c, CPC); 8378c2ecf20Sopenharmony_ci } else { 8388c2ecf20Sopenharmony_ci addr = omap_dma_chan_read_3_3(c, CSAC); 8398c2ecf20Sopenharmony_ci cdac = omap_dma_chan_read_3_3(c, CDAC); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* 8428c2ecf20Sopenharmony_ci * CDAC == 0 indicates that the DMA transfer on the channel has 8438c2ecf20Sopenharmony_ci * not been started (no data has been transferred so far). 8448c2ecf20Sopenharmony_ci * Return the programmed source start address in this case. 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci if (cdac == 0) 8478c2ecf20Sopenharmony_ci addr = omap_dma_chan_read(c, CSSA); 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (dma_omap1()) 8518c2ecf20Sopenharmony_ci addr |= omap_dma_chan_read(c, CSSA) & 0xffff0000; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci return addr; 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic dma_addr_t omap_dma_get_dst_pos(struct omap_chan *c) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device); 8598c2ecf20Sopenharmony_ci dma_addr_t addr; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (__dma_omap15xx(od->plat->dma_attr)) { 8628c2ecf20Sopenharmony_ci addr = omap_dma_chan_read(c, CPC); 8638c2ecf20Sopenharmony_ci } else { 8648c2ecf20Sopenharmony_ci addr = omap_dma_chan_read_3_3(c, CDAC); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci /* 8678c2ecf20Sopenharmony_ci * CDAC == 0 indicates that the DMA transfer on the channel 8688c2ecf20Sopenharmony_ci * has not been started (no data has been transferred so 8698c2ecf20Sopenharmony_ci * far). Return the programmed destination start address in 8708c2ecf20Sopenharmony_ci * this case. 8718c2ecf20Sopenharmony_ci */ 8728c2ecf20Sopenharmony_ci if (addr == 0) 8738c2ecf20Sopenharmony_ci addr = omap_dma_chan_read(c, CDSA); 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (dma_omap1()) 8778c2ecf20Sopenharmony_ci addr |= omap_dma_chan_read(c, CDSA) & 0xffff0000; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci return addr; 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic enum dma_status omap_dma_tx_status(struct dma_chan *chan, 8838c2ecf20Sopenharmony_ci dma_cookie_t cookie, struct dma_tx_state *txstate) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 8868c2ecf20Sopenharmony_ci enum dma_status ret; 8878c2ecf20Sopenharmony_ci unsigned long flags; 8888c2ecf20Sopenharmony_ci struct omap_desc *d = NULL; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 8918c2ecf20Sopenharmony_ci if (ret == DMA_COMPLETE) 8928c2ecf20Sopenharmony_ci return ret; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 8958c2ecf20Sopenharmony_ci if (c->desc && c->desc->vd.tx.cookie == cookie) 8968c2ecf20Sopenharmony_ci d = c->desc; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (!txstate) 8998c2ecf20Sopenharmony_ci goto out; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (d) { 9028c2ecf20Sopenharmony_ci dma_addr_t pos; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (d->dir == DMA_MEM_TO_DEV) 9058c2ecf20Sopenharmony_ci pos = omap_dma_get_src_pos(c); 9068c2ecf20Sopenharmony_ci else if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM) 9078c2ecf20Sopenharmony_ci pos = omap_dma_get_dst_pos(c); 9088c2ecf20Sopenharmony_ci else 9098c2ecf20Sopenharmony_ci pos = 0; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci txstate->residue = omap_dma_desc_size_pos(d, pos); 9128c2ecf20Sopenharmony_ci } else { 9138c2ecf20Sopenharmony_ci struct virt_dma_desc *vd = vchan_find_desc(&c->vc, cookie); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (vd) 9168c2ecf20Sopenharmony_ci txstate->residue = omap_dma_desc_size( 9178c2ecf20Sopenharmony_ci to_omap_dma_desc(&vd->tx)); 9188c2ecf20Sopenharmony_ci else 9198c2ecf20Sopenharmony_ci txstate->residue = 0; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ciout: 9238c2ecf20Sopenharmony_ci if (ret == DMA_IN_PROGRESS && c->paused) { 9248c2ecf20Sopenharmony_ci ret = DMA_PAUSED; 9258c2ecf20Sopenharmony_ci } else if (d && d->polled && c->running) { 9268c2ecf20Sopenharmony_ci uint32_t ccr = omap_dma_chan_read(c, CCR); 9278c2ecf20Sopenharmony_ci /* 9288c2ecf20Sopenharmony_ci * The channel is no longer active, set the return value 9298c2ecf20Sopenharmony_ci * accordingly and mark it as completed 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci if (!(ccr & CCR_ENABLE)) { 9328c2ecf20Sopenharmony_ci ret = DMA_COMPLETE; 9338c2ecf20Sopenharmony_ci omap_dma_start_desc(c); 9348c2ecf20Sopenharmony_ci vchan_cookie_complete(&d->vd); 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci return ret; 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic void omap_dma_issue_pending(struct dma_chan *chan) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 9468c2ecf20Sopenharmony_ci unsigned long flags; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 9498c2ecf20Sopenharmony_ci if (vchan_issue_pending(&c->vc) && !c->desc) 9508c2ecf20Sopenharmony_ci omap_dma_start_desc(c); 9518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_slave_sg( 9558c2ecf20Sopenharmony_ci struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen, 9568c2ecf20Sopenharmony_ci enum dma_transfer_direction dir, unsigned long tx_flags, void *context) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 9598c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 9608c2ecf20Sopenharmony_ci enum dma_slave_buswidth dev_width; 9618c2ecf20Sopenharmony_ci struct scatterlist *sgent; 9628c2ecf20Sopenharmony_ci struct omap_desc *d; 9638c2ecf20Sopenharmony_ci dma_addr_t dev_addr; 9648c2ecf20Sopenharmony_ci unsigned i, es, en, frame_bytes; 9658c2ecf20Sopenharmony_ci bool ll_failed = false; 9668c2ecf20Sopenharmony_ci u32 burst; 9678c2ecf20Sopenharmony_ci u32 port_window, port_window_bytes; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 9708c2ecf20Sopenharmony_ci dev_addr = c->cfg.src_addr; 9718c2ecf20Sopenharmony_ci dev_width = c->cfg.src_addr_width; 9728c2ecf20Sopenharmony_ci burst = c->cfg.src_maxburst; 9738c2ecf20Sopenharmony_ci port_window = c->cfg.src_port_window_size; 9748c2ecf20Sopenharmony_ci } else if (dir == DMA_MEM_TO_DEV) { 9758c2ecf20Sopenharmony_ci dev_addr = c->cfg.dst_addr; 9768c2ecf20Sopenharmony_ci dev_width = c->cfg.dst_addr_width; 9778c2ecf20Sopenharmony_ci burst = c->cfg.dst_maxburst; 9788c2ecf20Sopenharmony_ci port_window = c->cfg.dst_port_window_size; 9798c2ecf20Sopenharmony_ci } else { 9808c2ecf20Sopenharmony_ci dev_err(chan->device->dev, "%s: bad direction?\n", __func__); 9818c2ecf20Sopenharmony_ci return NULL; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci /* Bus width translates to the element size (ES) */ 9858c2ecf20Sopenharmony_ci switch (dev_width) { 9868c2ecf20Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_1_BYTE: 9878c2ecf20Sopenharmony_ci es = CSDP_DATA_TYPE_8; 9888c2ecf20Sopenharmony_ci break; 9898c2ecf20Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_2_BYTES: 9908c2ecf20Sopenharmony_ci es = CSDP_DATA_TYPE_16; 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_4_BYTES: 9938c2ecf20Sopenharmony_ci es = CSDP_DATA_TYPE_32; 9948c2ecf20Sopenharmony_ci break; 9958c2ecf20Sopenharmony_ci default: /* not reached */ 9968c2ecf20Sopenharmony_ci return NULL; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* Now allocate and setup the descriptor. */ 10008c2ecf20Sopenharmony_ci d = kzalloc(struct_size(d, sg, sglen), GFP_ATOMIC); 10018c2ecf20Sopenharmony_ci if (!d) 10028c2ecf20Sopenharmony_ci return NULL; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci d->dir = dir; 10058c2ecf20Sopenharmony_ci d->dev_addr = dev_addr; 10068c2ecf20Sopenharmony_ci d->es = es; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* When the port_window is used, one frame must cover the window */ 10098c2ecf20Sopenharmony_ci if (port_window) { 10108c2ecf20Sopenharmony_ci burst = port_window; 10118c2ecf20Sopenharmony_ci port_window_bytes = port_window * es_bytes[es]; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci d->ei = 1; 10148c2ecf20Sopenharmony_ci /* 10158c2ecf20Sopenharmony_ci * One frame covers the port_window and by configure 10168c2ecf20Sopenharmony_ci * the source frame index to be -1 * (port_window - 1) 10178c2ecf20Sopenharmony_ci * we instruct the sDMA that after a frame is processed 10188c2ecf20Sopenharmony_ci * it should move back to the start of the window. 10198c2ecf20Sopenharmony_ci */ 10208c2ecf20Sopenharmony_ci d->fi = -(port_window_bytes - 1); 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci d->ccr = c->ccr | CCR_SYNC_FRAME; 10248c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 10258c2ecf20Sopenharmony_ci d->csdp = CSDP_DST_BURST_64 | CSDP_DST_PACKED; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci d->ccr |= CCR_DST_AMODE_POSTINC; 10288c2ecf20Sopenharmony_ci if (port_window) { 10298c2ecf20Sopenharmony_ci d->ccr |= CCR_SRC_AMODE_DBLIDX; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (port_window_bytes >= 64) 10328c2ecf20Sopenharmony_ci d->csdp |= CSDP_SRC_BURST_64; 10338c2ecf20Sopenharmony_ci else if (port_window_bytes >= 32) 10348c2ecf20Sopenharmony_ci d->csdp |= CSDP_SRC_BURST_32; 10358c2ecf20Sopenharmony_ci else if (port_window_bytes >= 16) 10368c2ecf20Sopenharmony_ci d->csdp |= CSDP_SRC_BURST_16; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci } else { 10398c2ecf20Sopenharmony_ci d->ccr |= CCR_SRC_AMODE_CONSTANT; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci } else { 10428c2ecf20Sopenharmony_ci d->csdp = CSDP_SRC_BURST_64 | CSDP_SRC_PACKED; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci d->ccr |= CCR_SRC_AMODE_POSTINC; 10458c2ecf20Sopenharmony_ci if (port_window) { 10468c2ecf20Sopenharmony_ci d->ccr |= CCR_DST_AMODE_DBLIDX; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (port_window_bytes >= 64) 10498c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_BURST_64; 10508c2ecf20Sopenharmony_ci else if (port_window_bytes >= 32) 10518c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_BURST_32; 10528c2ecf20Sopenharmony_ci else if (port_window_bytes >= 16) 10538c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_BURST_16; 10548c2ecf20Sopenharmony_ci } else { 10558c2ecf20Sopenharmony_ci d->ccr |= CCR_DST_AMODE_CONSTANT; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci d->cicr = CICR_DROP_IE | CICR_BLOCK_IE; 10608c2ecf20Sopenharmony_ci d->csdp |= es; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci if (dma_omap1()) { 10638c2ecf20Sopenharmony_ci d->cicr |= CICR_TOUT_IE; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 10668c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_TIPB; 10678c2ecf20Sopenharmony_ci else 10688c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_PORT_TIPB | CSDP_SRC_PORT_EMIFF; 10698c2ecf20Sopenharmony_ci } else { 10708c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 10718c2ecf20Sopenharmony_ci d->ccr |= CCR_TRIGGER_SRC; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci if (port_window) 10768c2ecf20Sopenharmony_ci d->csdp |= CSDP_WRITE_LAST_NON_POSTED; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci if (od->plat->errata & DMA_ERRATA_PARALLEL_CHANNELS) 10798c2ecf20Sopenharmony_ci d->clnk_ctrl = c->dma_ch; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci /* 10828c2ecf20Sopenharmony_ci * Build our scatterlist entries: each contains the address, 10838c2ecf20Sopenharmony_ci * the number of elements (EN) in each frame, and the number of 10848c2ecf20Sopenharmony_ci * frames (FN). Number of bytes for this entry = ES * EN * FN. 10858c2ecf20Sopenharmony_ci * 10868c2ecf20Sopenharmony_ci * Burst size translates to number of elements with frame sync. 10878c2ecf20Sopenharmony_ci * Note: DMA engine defines burst to be the number of dev-width 10888c2ecf20Sopenharmony_ci * transfers. 10898c2ecf20Sopenharmony_ci */ 10908c2ecf20Sopenharmony_ci en = burst; 10918c2ecf20Sopenharmony_ci frame_bytes = es_bytes[es] * en; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (sglen >= 2) 10948c2ecf20Sopenharmony_ci d->using_ll = od->ll123_supported; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci for_each_sg(sgl, sgent, sglen, i) { 10978c2ecf20Sopenharmony_ci struct omap_sg *osg = &d->sg[i]; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci osg->addr = sg_dma_address(sgent); 11008c2ecf20Sopenharmony_ci osg->en = en; 11018c2ecf20Sopenharmony_ci osg->fn = sg_dma_len(sgent) / frame_bytes; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci if (d->using_ll) { 11048c2ecf20Sopenharmony_ci osg->t2_desc = dma_pool_alloc(od->desc_pool, GFP_ATOMIC, 11058c2ecf20Sopenharmony_ci &osg->t2_desc_paddr); 11068c2ecf20Sopenharmony_ci if (!osg->t2_desc) { 11078c2ecf20Sopenharmony_ci dev_err(chan->device->dev, 11088c2ecf20Sopenharmony_ci "t2_desc[%d] allocation failed\n", i); 11098c2ecf20Sopenharmony_ci ll_failed = true; 11108c2ecf20Sopenharmony_ci d->using_ll = false; 11118c2ecf20Sopenharmony_ci continue; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci omap_dma_fill_type2_desc(d, i, dir, (i == sglen - 1)); 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci d->sglen = sglen; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* Release the dma_pool entries if one allocation failed */ 11218c2ecf20Sopenharmony_ci if (ll_failed) { 11228c2ecf20Sopenharmony_ci for (i = 0; i < d->sglen; i++) { 11238c2ecf20Sopenharmony_ci struct omap_sg *osg = &d->sg[i]; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (osg->t2_desc) { 11268c2ecf20Sopenharmony_ci dma_pool_free(od->desc_pool, osg->t2_desc, 11278c2ecf20Sopenharmony_ci osg->t2_desc_paddr); 11288c2ecf20Sopenharmony_ci osg->t2_desc = NULL; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci return vchan_tx_prep(&c->vc, &d->vd, tx_flags); 11348c2ecf20Sopenharmony_ci} 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic( 11378c2ecf20Sopenharmony_ci struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, 11388c2ecf20Sopenharmony_ci size_t period_len, enum dma_transfer_direction dir, unsigned long flags) 11398c2ecf20Sopenharmony_ci{ 11408c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 11418c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 11428c2ecf20Sopenharmony_ci enum dma_slave_buswidth dev_width; 11438c2ecf20Sopenharmony_ci struct omap_desc *d; 11448c2ecf20Sopenharmony_ci dma_addr_t dev_addr; 11458c2ecf20Sopenharmony_ci unsigned es; 11468c2ecf20Sopenharmony_ci u32 burst; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 11498c2ecf20Sopenharmony_ci dev_addr = c->cfg.src_addr; 11508c2ecf20Sopenharmony_ci dev_width = c->cfg.src_addr_width; 11518c2ecf20Sopenharmony_ci burst = c->cfg.src_maxburst; 11528c2ecf20Sopenharmony_ci } else if (dir == DMA_MEM_TO_DEV) { 11538c2ecf20Sopenharmony_ci dev_addr = c->cfg.dst_addr; 11548c2ecf20Sopenharmony_ci dev_width = c->cfg.dst_addr_width; 11558c2ecf20Sopenharmony_ci burst = c->cfg.dst_maxburst; 11568c2ecf20Sopenharmony_ci } else { 11578c2ecf20Sopenharmony_ci dev_err(chan->device->dev, "%s: bad direction?\n", __func__); 11588c2ecf20Sopenharmony_ci return NULL; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* Bus width translates to the element size (ES) */ 11628c2ecf20Sopenharmony_ci switch (dev_width) { 11638c2ecf20Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_1_BYTE: 11648c2ecf20Sopenharmony_ci es = CSDP_DATA_TYPE_8; 11658c2ecf20Sopenharmony_ci break; 11668c2ecf20Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_2_BYTES: 11678c2ecf20Sopenharmony_ci es = CSDP_DATA_TYPE_16; 11688c2ecf20Sopenharmony_ci break; 11698c2ecf20Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_4_BYTES: 11708c2ecf20Sopenharmony_ci es = CSDP_DATA_TYPE_32; 11718c2ecf20Sopenharmony_ci break; 11728c2ecf20Sopenharmony_ci default: /* not reached */ 11738c2ecf20Sopenharmony_ci return NULL; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci /* Now allocate and setup the descriptor. */ 11778c2ecf20Sopenharmony_ci d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC); 11788c2ecf20Sopenharmony_ci if (!d) 11798c2ecf20Sopenharmony_ci return NULL; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci d->dir = dir; 11828c2ecf20Sopenharmony_ci d->dev_addr = dev_addr; 11838c2ecf20Sopenharmony_ci d->fi = burst; 11848c2ecf20Sopenharmony_ci d->es = es; 11858c2ecf20Sopenharmony_ci d->sg[0].addr = buf_addr; 11868c2ecf20Sopenharmony_ci d->sg[0].en = period_len / es_bytes[es]; 11878c2ecf20Sopenharmony_ci d->sg[0].fn = buf_len / period_len; 11888c2ecf20Sopenharmony_ci d->sglen = 1; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci d->ccr = c->ccr; 11918c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 11928c2ecf20Sopenharmony_ci d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT; 11938c2ecf20Sopenharmony_ci else 11948c2ecf20Sopenharmony_ci d->ccr |= CCR_DST_AMODE_CONSTANT | CCR_SRC_AMODE_POSTINC; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci d->cicr = CICR_DROP_IE; 11978c2ecf20Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 11988c2ecf20Sopenharmony_ci d->cicr |= CICR_FRAME_IE; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci d->csdp = es; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (dma_omap1()) { 12038c2ecf20Sopenharmony_ci d->cicr |= CICR_TOUT_IE; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 12068c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_MPUI; 12078c2ecf20Sopenharmony_ci else 12088c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_PORT_MPUI | CSDP_SRC_PORT_EMIFF; 12098c2ecf20Sopenharmony_ci } else { 12108c2ecf20Sopenharmony_ci if (burst) 12118c2ecf20Sopenharmony_ci d->ccr |= CCR_SYNC_PACKET; 12128c2ecf20Sopenharmony_ci else 12138c2ecf20Sopenharmony_ci d->ccr |= CCR_SYNC_ELEMENT; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 12168c2ecf20Sopenharmony_ci d->ccr |= CCR_TRIGGER_SRC; 12178c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_PACKED; 12188c2ecf20Sopenharmony_ci } else { 12198c2ecf20Sopenharmony_ci d->csdp |= CSDP_SRC_PACKED; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64; 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (__dma_omap15xx(od->plat->dma_attr)) 12288c2ecf20Sopenharmony_ci d->ccr |= CCR_AUTO_INIT | CCR_REPEAT; 12298c2ecf20Sopenharmony_ci else 12308c2ecf20Sopenharmony_ci d->clnk_ctrl = c->dma_ch | CLNK_CTRL_ENABLE_LNK; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci c->cyclic = true; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci return vchan_tx_prep(&c->vc, &d->vd, flags); 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_memcpy( 12388c2ecf20Sopenharmony_ci struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, 12398c2ecf20Sopenharmony_ci size_t len, unsigned long tx_flags) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 12428c2ecf20Sopenharmony_ci struct omap_desc *d; 12438c2ecf20Sopenharmony_ci uint8_t data_type; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC); 12468c2ecf20Sopenharmony_ci if (!d) 12478c2ecf20Sopenharmony_ci return NULL; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci data_type = __ffs((src | dest | len)); 12508c2ecf20Sopenharmony_ci if (data_type > CSDP_DATA_TYPE_32) 12518c2ecf20Sopenharmony_ci data_type = CSDP_DATA_TYPE_32; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci d->dir = DMA_MEM_TO_MEM; 12548c2ecf20Sopenharmony_ci d->dev_addr = src; 12558c2ecf20Sopenharmony_ci d->fi = 0; 12568c2ecf20Sopenharmony_ci d->es = data_type; 12578c2ecf20Sopenharmony_ci d->sg[0].en = len / BIT(data_type); 12588c2ecf20Sopenharmony_ci d->sg[0].fn = 1; 12598c2ecf20Sopenharmony_ci d->sg[0].addr = dest; 12608c2ecf20Sopenharmony_ci d->sglen = 1; 12618c2ecf20Sopenharmony_ci d->ccr = c->ccr; 12628c2ecf20Sopenharmony_ci d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_POSTINC; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci if (tx_flags & DMA_PREP_INTERRUPT) 12658c2ecf20Sopenharmony_ci d->cicr |= CICR_FRAME_IE; 12668c2ecf20Sopenharmony_ci else 12678c2ecf20Sopenharmony_ci d->polled = true; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci d->csdp = data_type; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci if (dma_omap1()) { 12728c2ecf20Sopenharmony_ci d->cicr |= CICR_TOUT_IE; 12738c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_EMIFF; 12748c2ecf20Sopenharmony_ci } else { 12758c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_PACKED | CSDP_SRC_PACKED; 12768c2ecf20Sopenharmony_ci d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE; 12778c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci return vchan_tx_prep(&c->vc, &d->vd, tx_flags); 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_interleaved( 12848c2ecf20Sopenharmony_ci struct dma_chan *chan, struct dma_interleaved_template *xt, 12858c2ecf20Sopenharmony_ci unsigned long flags) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 12888c2ecf20Sopenharmony_ci struct omap_desc *d; 12898c2ecf20Sopenharmony_ci struct omap_sg *sg; 12908c2ecf20Sopenharmony_ci uint8_t data_type; 12918c2ecf20Sopenharmony_ci size_t src_icg, dst_icg; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci /* Slave mode is not supported */ 12948c2ecf20Sopenharmony_ci if (is_slave_direction(xt->dir)) 12958c2ecf20Sopenharmony_ci return NULL; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci if (xt->frame_size != 1 || xt->numf == 0) 12988c2ecf20Sopenharmony_ci return NULL; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC); 13018c2ecf20Sopenharmony_ci if (!d) 13028c2ecf20Sopenharmony_ci return NULL; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci data_type = __ffs((xt->src_start | xt->dst_start | xt->sgl[0].size)); 13058c2ecf20Sopenharmony_ci if (data_type > CSDP_DATA_TYPE_32) 13068c2ecf20Sopenharmony_ci data_type = CSDP_DATA_TYPE_32; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci sg = &d->sg[0]; 13098c2ecf20Sopenharmony_ci d->dir = DMA_MEM_TO_MEM; 13108c2ecf20Sopenharmony_ci d->dev_addr = xt->src_start; 13118c2ecf20Sopenharmony_ci d->es = data_type; 13128c2ecf20Sopenharmony_ci sg->en = xt->sgl[0].size / BIT(data_type); 13138c2ecf20Sopenharmony_ci sg->fn = xt->numf; 13148c2ecf20Sopenharmony_ci sg->addr = xt->dst_start; 13158c2ecf20Sopenharmony_ci d->sglen = 1; 13168c2ecf20Sopenharmony_ci d->ccr = c->ccr; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); 13198c2ecf20Sopenharmony_ci dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); 13208c2ecf20Sopenharmony_ci if (src_icg) { 13218c2ecf20Sopenharmony_ci d->ccr |= CCR_SRC_AMODE_DBLIDX; 13228c2ecf20Sopenharmony_ci d->ei = 1; 13238c2ecf20Sopenharmony_ci d->fi = src_icg + 1; 13248c2ecf20Sopenharmony_ci } else if (xt->src_inc) { 13258c2ecf20Sopenharmony_ci d->ccr |= CCR_SRC_AMODE_POSTINC; 13268c2ecf20Sopenharmony_ci d->fi = 0; 13278c2ecf20Sopenharmony_ci } else { 13288c2ecf20Sopenharmony_ci dev_err(chan->device->dev, 13298c2ecf20Sopenharmony_ci "%s: SRC constant addressing is not supported\n", 13308c2ecf20Sopenharmony_ci __func__); 13318c2ecf20Sopenharmony_ci kfree(d); 13328c2ecf20Sopenharmony_ci return NULL; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci if (dst_icg) { 13368c2ecf20Sopenharmony_ci d->ccr |= CCR_DST_AMODE_DBLIDX; 13378c2ecf20Sopenharmony_ci sg->ei = 1; 13388c2ecf20Sopenharmony_ci sg->fi = dst_icg + 1; 13398c2ecf20Sopenharmony_ci } else if (xt->dst_inc) { 13408c2ecf20Sopenharmony_ci d->ccr |= CCR_DST_AMODE_POSTINC; 13418c2ecf20Sopenharmony_ci sg->fi = 0; 13428c2ecf20Sopenharmony_ci } else { 13438c2ecf20Sopenharmony_ci dev_err(chan->device->dev, 13448c2ecf20Sopenharmony_ci "%s: DST constant addressing is not supported\n", 13458c2ecf20Sopenharmony_ci __func__); 13468c2ecf20Sopenharmony_ci kfree(d); 13478c2ecf20Sopenharmony_ci return NULL; 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci d->cicr = CICR_DROP_IE | CICR_FRAME_IE; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci d->csdp = data_type; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci if (dma_omap1()) { 13558c2ecf20Sopenharmony_ci d->cicr |= CICR_TOUT_IE; 13568c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_EMIFF; 13578c2ecf20Sopenharmony_ci } else { 13588c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_PACKED | CSDP_SRC_PACKED; 13598c2ecf20Sopenharmony_ci d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE; 13608c2ecf20Sopenharmony_ci d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci return vchan_tx_prep(&c->vc, &d->vd, flags); 13648c2ecf20Sopenharmony_ci} 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_cistatic int omap_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) 13678c2ecf20Sopenharmony_ci{ 13688c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || 13718c2ecf20Sopenharmony_ci cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) 13728c2ecf20Sopenharmony_ci return -EINVAL; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci if (cfg->src_maxburst > chan->device->max_burst || 13758c2ecf20Sopenharmony_ci cfg->dst_maxburst > chan->device->max_burst) 13768c2ecf20Sopenharmony_ci return -EINVAL; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci memcpy(&c->cfg, cfg, sizeof(c->cfg)); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci return 0; 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic int omap_dma_terminate_all(struct dma_chan *chan) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 13868c2ecf20Sopenharmony_ci unsigned long flags; 13878c2ecf20Sopenharmony_ci LIST_HEAD(head); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci /* 13928c2ecf20Sopenharmony_ci * Stop DMA activity: we assume the callback will not be called 13938c2ecf20Sopenharmony_ci * after omap_dma_stop() returns (even if it does, it will see 13948c2ecf20Sopenharmony_ci * c->desc is NULL and exit.) 13958c2ecf20Sopenharmony_ci */ 13968c2ecf20Sopenharmony_ci if (c->desc) { 13978c2ecf20Sopenharmony_ci vchan_terminate_vdesc(&c->desc->vd); 13988c2ecf20Sopenharmony_ci c->desc = NULL; 13998c2ecf20Sopenharmony_ci /* Avoid stopping the dma twice */ 14008c2ecf20Sopenharmony_ci if (!c->paused) 14018c2ecf20Sopenharmony_ci omap_dma_stop(c); 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci c->cyclic = false; 14058c2ecf20Sopenharmony_ci c->paused = false; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci vchan_get_all_descriptors(&c->vc, &head); 14088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 14098c2ecf20Sopenharmony_ci vchan_dma_desc_free_list(&c->vc, &head); 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci return 0; 14128c2ecf20Sopenharmony_ci} 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_cistatic void omap_dma_synchronize(struct dma_chan *chan) 14158c2ecf20Sopenharmony_ci{ 14168c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci vchan_synchronize(&c->vc); 14198c2ecf20Sopenharmony_ci} 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_cistatic int omap_dma_pause(struct dma_chan *chan) 14228c2ecf20Sopenharmony_ci{ 14238c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 14248c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 14258c2ecf20Sopenharmony_ci unsigned long flags; 14268c2ecf20Sopenharmony_ci int ret = -EINVAL; 14278c2ecf20Sopenharmony_ci bool can_pause = false; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci spin_lock_irqsave(&od->irq_lock, flags); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (!c->desc) 14328c2ecf20Sopenharmony_ci goto out; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci if (c->cyclic) 14358c2ecf20Sopenharmony_ci can_pause = true; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci /* 14388c2ecf20Sopenharmony_ci * We do not allow DMA_MEM_TO_DEV transfers to be paused. 14398c2ecf20Sopenharmony_ci * From the AM572x TRM, 16.1.4.18 Disabling a Channel During Transfer: 14408c2ecf20Sopenharmony_ci * "When a channel is disabled during a transfer, the channel undergoes 14418c2ecf20Sopenharmony_ci * an abort, unless it is hardware-source-synchronized …". 14428c2ecf20Sopenharmony_ci * A source-synchronised channel is one where the fetching of data is 14438c2ecf20Sopenharmony_ci * under control of the device. In other words, a device-to-memory 14448c2ecf20Sopenharmony_ci * transfer. So, a destination-synchronised channel (which would be a 14458c2ecf20Sopenharmony_ci * memory-to-device transfer) undergoes an abort if the the CCR_ENABLE 14468c2ecf20Sopenharmony_ci * bit is cleared. 14478c2ecf20Sopenharmony_ci * From 16.1.4.20.4.6.2 Abort: "If an abort trigger occurs, the channel 14488c2ecf20Sopenharmony_ci * aborts immediately after completion of current read/write 14498c2ecf20Sopenharmony_ci * transactions and then the FIFO is cleaned up." The term "cleaned up" 14508c2ecf20Sopenharmony_ci * is not defined. TI recommends to check that RD_ACTIVE and WR_ACTIVE 14518c2ecf20Sopenharmony_ci * are both clear _before_ disabling the channel, otherwise data loss 14528c2ecf20Sopenharmony_ci * will occur. 14538c2ecf20Sopenharmony_ci * The problem is that if the channel is active, then device activity 14548c2ecf20Sopenharmony_ci * can result in DMA activity starting between reading those as both 14558c2ecf20Sopenharmony_ci * clear and the write to DMA_CCR to clear the enable bit hitting the 14568c2ecf20Sopenharmony_ci * hardware. If the DMA hardware can't drain the data in its FIFO to the 14578c2ecf20Sopenharmony_ci * destination, then data loss "might" occur (say if we write to an UART 14588c2ecf20Sopenharmony_ci * and the UART is not accepting any further data). 14598c2ecf20Sopenharmony_ci */ 14608c2ecf20Sopenharmony_ci else if (c->desc->dir == DMA_DEV_TO_MEM) 14618c2ecf20Sopenharmony_ci can_pause = true; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci if (can_pause && !c->paused) { 14648c2ecf20Sopenharmony_ci ret = omap_dma_stop(c); 14658c2ecf20Sopenharmony_ci if (!ret) 14668c2ecf20Sopenharmony_ci c->paused = true; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ciout: 14698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&od->irq_lock, flags); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci return ret; 14728c2ecf20Sopenharmony_ci} 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_cistatic int omap_dma_resume(struct dma_chan *chan) 14758c2ecf20Sopenharmony_ci{ 14768c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 14778c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 14788c2ecf20Sopenharmony_ci unsigned long flags; 14798c2ecf20Sopenharmony_ci int ret = -EINVAL; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci spin_lock_irqsave(&od->irq_lock, flags); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci if (c->paused && c->desc) { 14848c2ecf20Sopenharmony_ci mb(); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci /* Restore channel link register */ 14878c2ecf20Sopenharmony_ci omap_dma_chan_write(c, CLNK_CTRL, c->desc->clnk_ctrl); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci omap_dma_start(c, c->desc); 14908c2ecf20Sopenharmony_ci c->paused = false; 14918c2ecf20Sopenharmony_ci ret = 0; 14928c2ecf20Sopenharmony_ci } 14938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&od->irq_lock, flags); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci return ret; 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_cistatic int omap_dma_chan_init(struct omap_dmadev *od) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci struct omap_chan *c; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci c = kzalloc(sizeof(*c), GFP_KERNEL); 15038c2ecf20Sopenharmony_ci if (!c) 15048c2ecf20Sopenharmony_ci return -ENOMEM; 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci c->reg_map = od->reg_map; 15078c2ecf20Sopenharmony_ci c->vc.desc_free = omap_dma_desc_free; 15088c2ecf20Sopenharmony_ci vchan_init(&c->vc, &od->ddev); 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci return 0; 15118c2ecf20Sopenharmony_ci} 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_cistatic void omap_dma_free(struct omap_dmadev *od) 15148c2ecf20Sopenharmony_ci{ 15158c2ecf20Sopenharmony_ci while (!list_empty(&od->ddev.channels)) { 15168c2ecf20Sopenharmony_ci struct omap_chan *c = list_first_entry(&od->ddev.channels, 15178c2ecf20Sopenharmony_ci struct omap_chan, vc.chan.device_node); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci list_del(&c->vc.chan.device_node); 15208c2ecf20Sopenharmony_ci tasklet_kill(&c->vc.task); 15218c2ecf20Sopenharmony_ci kfree(c); 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci} 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci/* Currently used by omap2 & 3 to block deeper SoC idle states */ 15268c2ecf20Sopenharmony_cistatic bool omap_dma_busy(struct omap_dmadev *od) 15278c2ecf20Sopenharmony_ci{ 15288c2ecf20Sopenharmony_ci struct omap_chan *c; 15298c2ecf20Sopenharmony_ci int lch = -1; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci while (1) { 15328c2ecf20Sopenharmony_ci lch = find_next_bit(od->lch_bitmap, od->lch_count, lch + 1); 15338c2ecf20Sopenharmony_ci if (lch >= od->lch_count) 15348c2ecf20Sopenharmony_ci break; 15358c2ecf20Sopenharmony_ci c = od->lch_map[lch]; 15368c2ecf20Sopenharmony_ci if (!c) 15378c2ecf20Sopenharmony_ci continue; 15388c2ecf20Sopenharmony_ci if (omap_dma_chan_read(c, CCR) & CCR_ENABLE) 15398c2ecf20Sopenharmony_ci return true; 15408c2ecf20Sopenharmony_ci } 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci return false; 15438c2ecf20Sopenharmony_ci} 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci/* Currently only used for omap2. For omap1, also a check for lcd_dma is needed */ 15468c2ecf20Sopenharmony_cistatic int omap_dma_busy_notifier(struct notifier_block *nb, 15478c2ecf20Sopenharmony_ci unsigned long cmd, void *v) 15488c2ecf20Sopenharmony_ci{ 15498c2ecf20Sopenharmony_ci struct omap_dmadev *od; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci od = container_of(nb, struct omap_dmadev, nb); 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci switch (cmd) { 15548c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_ENTER: 15558c2ecf20Sopenharmony_ci if (omap_dma_busy(od)) 15568c2ecf20Sopenharmony_ci return NOTIFY_BAD; 15578c2ecf20Sopenharmony_ci break; 15588c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_ENTER_FAILED: 15598c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_EXIT: 15608c2ecf20Sopenharmony_ci break; 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci return NOTIFY_OK; 15648c2ecf20Sopenharmony_ci} 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci/* 15678c2ecf20Sopenharmony_ci * We are using IRQENABLE_L1, and legacy DMA code was using IRQENABLE_L0. 15688c2ecf20Sopenharmony_ci * As the DSP may be using IRQENABLE_L2 and L3, let's not touch those for 15698c2ecf20Sopenharmony_ci * now. Context save seems to be only currently needed on omap3. 15708c2ecf20Sopenharmony_ci */ 15718c2ecf20Sopenharmony_cistatic void omap_dma_context_save(struct omap_dmadev *od) 15728c2ecf20Sopenharmony_ci{ 15738c2ecf20Sopenharmony_ci od->context.irqenable_l0 = omap_dma_glbl_read(od, IRQENABLE_L0); 15748c2ecf20Sopenharmony_ci od->context.irqenable_l1 = omap_dma_glbl_read(od, IRQENABLE_L1); 15758c2ecf20Sopenharmony_ci od->context.ocp_sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG); 15768c2ecf20Sopenharmony_ci od->context.gcr = omap_dma_glbl_read(od, GCR); 15778c2ecf20Sopenharmony_ci} 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_cistatic void omap_dma_context_restore(struct omap_dmadev *od) 15808c2ecf20Sopenharmony_ci{ 15818c2ecf20Sopenharmony_ci int i; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, GCR, od->context.gcr); 15848c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, OCP_SYSCONFIG, od->context.ocp_sysconfig); 15858c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L0, od->context.irqenable_l0); 15868c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L1, od->context.irqenable_l1); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /* Clear IRQSTATUS_L0 as legacy DMA code is no longer doing it */ 15898c2ecf20Sopenharmony_ci if (od->plat->errata & DMA_ROMCODE_BUG) 15908c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, IRQSTATUS_L0, 0); 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci /* Clear dma channels */ 15938c2ecf20Sopenharmony_ci for (i = 0; i < od->lch_count; i++) 15948c2ecf20Sopenharmony_ci omap_dma_clear_lch(od, i); 15958c2ecf20Sopenharmony_ci} 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci/* Currently only used for omap3 */ 15988c2ecf20Sopenharmony_cistatic int omap_dma_context_notifier(struct notifier_block *nb, 15998c2ecf20Sopenharmony_ci unsigned long cmd, void *v) 16008c2ecf20Sopenharmony_ci{ 16018c2ecf20Sopenharmony_ci struct omap_dmadev *od; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci od = container_of(nb, struct omap_dmadev, nb); 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci switch (cmd) { 16068c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_ENTER: 16078c2ecf20Sopenharmony_ci if (omap_dma_busy(od)) 16088c2ecf20Sopenharmony_ci return NOTIFY_BAD; 16098c2ecf20Sopenharmony_ci omap_dma_context_save(od); 16108c2ecf20Sopenharmony_ci break; 16118c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_ENTER_FAILED: 16128c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_EXIT: 16138c2ecf20Sopenharmony_ci omap_dma_context_restore(od); 16148c2ecf20Sopenharmony_ci break; 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci return NOTIFY_OK; 16188c2ecf20Sopenharmony_ci} 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_cistatic void omap_dma_init_gcr(struct omap_dmadev *od, int arb_rate, 16218c2ecf20Sopenharmony_ci int max_fifo_depth, int tparams) 16228c2ecf20Sopenharmony_ci{ 16238c2ecf20Sopenharmony_ci u32 val; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci /* Set only for omap2430 and later */ 16268c2ecf20Sopenharmony_ci if (!od->cfg->rw_priority) 16278c2ecf20Sopenharmony_ci return; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci if (max_fifo_depth == 0) 16308c2ecf20Sopenharmony_ci max_fifo_depth = 1; 16318c2ecf20Sopenharmony_ci if (arb_rate == 0) 16328c2ecf20Sopenharmony_ci arb_rate = 1; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci val = 0xff & max_fifo_depth; 16358c2ecf20Sopenharmony_ci val |= (0x3 & tparams) << 12; 16368c2ecf20Sopenharmony_ci val |= (arb_rate & 0xff) << 16; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, GCR, val); 16398c2ecf20Sopenharmony_ci} 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci#define OMAP_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ 16428c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ 16438c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci/* 16468c2ecf20Sopenharmony_ci * No flags currently set for default configuration as omap1 is still 16478c2ecf20Sopenharmony_ci * using platform data. 16488c2ecf20Sopenharmony_ci */ 16498c2ecf20Sopenharmony_cistatic const struct omap_dma_config default_cfg; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_cistatic int omap_dma_probe(struct platform_device *pdev) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci const struct omap_dma_config *conf; 16548c2ecf20Sopenharmony_ci struct omap_dmadev *od; 16558c2ecf20Sopenharmony_ci struct resource *res; 16568c2ecf20Sopenharmony_ci int rc, i, irq; 16578c2ecf20Sopenharmony_ci u32 val; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); 16608c2ecf20Sopenharmony_ci if (!od) 16618c2ecf20Sopenharmony_ci return -ENOMEM; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 16648c2ecf20Sopenharmony_ci od->base = devm_ioremap_resource(&pdev->dev, res); 16658c2ecf20Sopenharmony_ci if (IS_ERR(od->base)) 16668c2ecf20Sopenharmony_ci return PTR_ERR(od->base); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci conf = of_device_get_match_data(&pdev->dev); 16698c2ecf20Sopenharmony_ci if (conf) { 16708c2ecf20Sopenharmony_ci od->cfg = conf; 16718c2ecf20Sopenharmony_ci od->plat = dev_get_platdata(&pdev->dev); 16728c2ecf20Sopenharmony_ci if (!od->plat) { 16738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "omap_system_dma_plat_info is missing"); 16748c2ecf20Sopenharmony_ci return -ENODEV; 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci } else { 16778c2ecf20Sopenharmony_ci od->cfg = &default_cfg; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci od->plat = omap_get_plat_info(); 16808c2ecf20Sopenharmony_ci if (!od->plat) 16818c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 16828c2ecf20Sopenharmony_ci } 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci od->reg_map = od->plat->reg_map; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); 16878c2ecf20Sopenharmony_ci dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); 16888c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, od->ddev.cap_mask); 16898c2ecf20Sopenharmony_ci dma_cap_set(DMA_INTERLEAVE, od->ddev.cap_mask); 16908c2ecf20Sopenharmony_ci od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources; 16918c2ecf20Sopenharmony_ci od->ddev.device_free_chan_resources = omap_dma_free_chan_resources; 16928c2ecf20Sopenharmony_ci od->ddev.device_tx_status = omap_dma_tx_status; 16938c2ecf20Sopenharmony_ci od->ddev.device_issue_pending = omap_dma_issue_pending; 16948c2ecf20Sopenharmony_ci od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg; 16958c2ecf20Sopenharmony_ci od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic; 16968c2ecf20Sopenharmony_ci od->ddev.device_prep_dma_memcpy = omap_dma_prep_dma_memcpy; 16978c2ecf20Sopenharmony_ci od->ddev.device_prep_interleaved_dma = omap_dma_prep_dma_interleaved; 16988c2ecf20Sopenharmony_ci od->ddev.device_config = omap_dma_slave_config; 16998c2ecf20Sopenharmony_ci od->ddev.device_pause = omap_dma_pause; 17008c2ecf20Sopenharmony_ci od->ddev.device_resume = omap_dma_resume; 17018c2ecf20Sopenharmony_ci od->ddev.device_terminate_all = omap_dma_terminate_all; 17028c2ecf20Sopenharmony_ci od->ddev.device_synchronize = omap_dma_synchronize; 17038c2ecf20Sopenharmony_ci od->ddev.src_addr_widths = OMAP_DMA_BUSWIDTHS; 17048c2ecf20Sopenharmony_ci od->ddev.dst_addr_widths = OMAP_DMA_BUSWIDTHS; 17058c2ecf20Sopenharmony_ci od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 17068c2ecf20Sopenharmony_ci if (__dma_omap15xx(od->plat->dma_attr)) 17078c2ecf20Sopenharmony_ci od->ddev.residue_granularity = 17088c2ecf20Sopenharmony_ci DMA_RESIDUE_GRANULARITY_DESCRIPTOR; 17098c2ecf20Sopenharmony_ci else 17108c2ecf20Sopenharmony_ci od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 17118c2ecf20Sopenharmony_ci od->ddev.max_burst = SZ_16M - 1; /* CCEN: 24bit unsigned */ 17128c2ecf20Sopenharmony_ci od->ddev.dev = &pdev->dev; 17138c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&od->ddev.channels); 17148c2ecf20Sopenharmony_ci mutex_init(&od->lch_lock); 17158c2ecf20Sopenharmony_ci spin_lock_init(&od->lock); 17168c2ecf20Sopenharmony_ci spin_lock_init(&od->irq_lock); 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci /* Number of DMA requests */ 17198c2ecf20Sopenharmony_ci od->dma_requests = OMAP_SDMA_REQUESTS; 17208c2ecf20Sopenharmony_ci if (pdev->dev.of_node && of_property_read_u32(pdev->dev.of_node, 17218c2ecf20Sopenharmony_ci "dma-requests", 17228c2ecf20Sopenharmony_ci &od->dma_requests)) { 17238c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 17248c2ecf20Sopenharmony_ci "Missing dma-requests property, using %u.\n", 17258c2ecf20Sopenharmony_ci OMAP_SDMA_REQUESTS); 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci /* Number of available logical channels */ 17298c2ecf20Sopenharmony_ci if (!pdev->dev.of_node) { 17308c2ecf20Sopenharmony_ci od->lch_count = od->plat->dma_attr->lch_count; 17318c2ecf20Sopenharmony_ci if (unlikely(!od->lch_count)) 17328c2ecf20Sopenharmony_ci od->lch_count = OMAP_SDMA_CHANNELS; 17338c2ecf20Sopenharmony_ci } else if (of_property_read_u32(pdev->dev.of_node, "dma-channels", 17348c2ecf20Sopenharmony_ci &od->lch_count)) { 17358c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 17368c2ecf20Sopenharmony_ci "Missing dma-channels property, using %u.\n", 17378c2ecf20Sopenharmony_ci OMAP_SDMA_CHANNELS); 17388c2ecf20Sopenharmony_ci od->lch_count = OMAP_SDMA_CHANNELS; 17398c2ecf20Sopenharmony_ci } 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci /* Mask of allowed logical channels */ 17428c2ecf20Sopenharmony_ci if (pdev->dev.of_node && !of_property_read_u32(pdev->dev.of_node, 17438c2ecf20Sopenharmony_ci "dma-channel-mask", 17448c2ecf20Sopenharmony_ci &val)) { 17458c2ecf20Sopenharmony_ci /* Tag channels not in mask as reserved */ 17468c2ecf20Sopenharmony_ci val = ~val; 17478c2ecf20Sopenharmony_ci bitmap_from_arr32(od->lch_bitmap, &val, od->lch_count); 17488c2ecf20Sopenharmony_ci } 17498c2ecf20Sopenharmony_ci if (od->plat->dma_attr->dev_caps & HS_CHANNELS_RESERVED) 17508c2ecf20Sopenharmony_ci bitmap_set(od->lch_bitmap, 0, 2); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci od->lch_map = devm_kcalloc(&pdev->dev, od->lch_count, 17538c2ecf20Sopenharmony_ci sizeof(*od->lch_map), 17548c2ecf20Sopenharmony_ci GFP_KERNEL); 17558c2ecf20Sopenharmony_ci if (!od->lch_map) 17568c2ecf20Sopenharmony_ci return -ENOMEM; 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci for (i = 0; i < od->dma_requests; i++) { 17598c2ecf20Sopenharmony_ci rc = omap_dma_chan_init(od); 17608c2ecf20Sopenharmony_ci if (rc) { 17618c2ecf20Sopenharmony_ci omap_dma_free(od); 17628c2ecf20Sopenharmony_ci return rc; 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 1); 17678c2ecf20Sopenharmony_ci if (irq <= 0) { 17688c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "failed to get L1 IRQ: %d\n", irq); 17698c2ecf20Sopenharmony_ci od->legacy = true; 17708c2ecf20Sopenharmony_ci } else { 17718c2ecf20Sopenharmony_ci /* Disable all interrupts */ 17728c2ecf20Sopenharmony_ci od->irq_enable_mask = 0; 17738c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L1, 0); 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci rc = devm_request_irq(&pdev->dev, irq, omap_dma_irq, 17768c2ecf20Sopenharmony_ci IRQF_SHARED, "omap-dma-engine", od); 17778c2ecf20Sopenharmony_ci if (rc) { 17788c2ecf20Sopenharmony_ci omap_dma_free(od); 17798c2ecf20Sopenharmony_ci return rc; 17808c2ecf20Sopenharmony_ci } 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci if (omap_dma_glbl_read(od, CAPS_0) & CAPS_0_SUPPORT_LL123) 17848c2ecf20Sopenharmony_ci od->ll123_supported = true; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci od->ddev.filter.map = od->plat->slave_map; 17878c2ecf20Sopenharmony_ci od->ddev.filter.mapcnt = od->plat->slavecnt; 17888c2ecf20Sopenharmony_ci od->ddev.filter.fn = omap_dma_filter_fn; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci if (od->ll123_supported) { 17918c2ecf20Sopenharmony_ci od->desc_pool = dma_pool_create(dev_name(&pdev->dev), 17928c2ecf20Sopenharmony_ci &pdev->dev, 17938c2ecf20Sopenharmony_ci sizeof(struct omap_type2_desc), 17948c2ecf20Sopenharmony_ci 4, 0); 17958c2ecf20Sopenharmony_ci if (!od->desc_pool) { 17968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 17978c2ecf20Sopenharmony_ci "unable to allocate descriptor pool\n"); 17988c2ecf20Sopenharmony_ci od->ll123_supported = false; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci rc = dma_async_device_register(&od->ddev); 18038c2ecf20Sopenharmony_ci if (rc) { 18048c2ecf20Sopenharmony_ci pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n", 18058c2ecf20Sopenharmony_ci rc); 18068c2ecf20Sopenharmony_ci omap_dma_free(od); 18078c2ecf20Sopenharmony_ci return rc; 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, od); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci if (pdev->dev.of_node) { 18138c2ecf20Sopenharmony_ci omap_dma_info.dma_cap = od->ddev.cap_mask; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci /* Device-tree DMA controller registration */ 18168c2ecf20Sopenharmony_ci rc = of_dma_controller_register(pdev->dev.of_node, 18178c2ecf20Sopenharmony_ci of_dma_simple_xlate, &omap_dma_info); 18188c2ecf20Sopenharmony_ci if (rc) { 18198c2ecf20Sopenharmony_ci pr_warn("OMAP-DMA: failed to register DMA controller\n"); 18208c2ecf20Sopenharmony_ci dma_async_device_unregister(&od->ddev); 18218c2ecf20Sopenharmony_ci omap_dma_free(od); 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci omap_dma_init_gcr(od, DMA_DEFAULT_ARB_RATE, DMA_DEFAULT_FIFO_DEPTH, 0); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (od->cfg->needs_busy_check) { 18288c2ecf20Sopenharmony_ci od->nb.notifier_call = omap_dma_busy_notifier; 18298c2ecf20Sopenharmony_ci cpu_pm_register_notifier(&od->nb); 18308c2ecf20Sopenharmony_ci } else if (od->cfg->may_lose_context) { 18318c2ecf20Sopenharmony_ci od->nb.notifier_call = omap_dma_context_notifier; 18328c2ecf20Sopenharmony_ci cpu_pm_register_notifier(&od->nb); 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "OMAP DMA engine driver%s\n", 18368c2ecf20Sopenharmony_ci od->ll123_supported ? " (LinkedList1/2/3 supported)" : ""); 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci return rc; 18398c2ecf20Sopenharmony_ci} 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_cistatic int omap_dma_remove(struct platform_device *pdev) 18428c2ecf20Sopenharmony_ci{ 18438c2ecf20Sopenharmony_ci struct omap_dmadev *od = platform_get_drvdata(pdev); 18448c2ecf20Sopenharmony_ci int irq; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (od->cfg->may_lose_context) 18478c2ecf20Sopenharmony_ci cpu_pm_unregister_notifier(&od->nb); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci if (pdev->dev.of_node) 18508c2ecf20Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 1); 18538c2ecf20Sopenharmony_ci devm_free_irq(&pdev->dev, irq, od); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci dma_async_device_unregister(&od->ddev); 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci if (!od->legacy) { 18588c2ecf20Sopenharmony_ci /* Disable all interrupts */ 18598c2ecf20Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L0, 0); 18608c2ecf20Sopenharmony_ci } 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci if (od->ll123_supported) 18638c2ecf20Sopenharmony_ci dma_pool_destroy(od->desc_pool); 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci omap_dma_free(od); 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci return 0; 18688c2ecf20Sopenharmony_ci} 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_cistatic const struct omap_dma_config omap2420_data = { 18718c2ecf20Sopenharmony_ci .lch_end = CCFN, 18728c2ecf20Sopenharmony_ci .rw_priority = true, 18738c2ecf20Sopenharmony_ci .needs_lch_clear = true, 18748c2ecf20Sopenharmony_ci .needs_busy_check = true, 18758c2ecf20Sopenharmony_ci}; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_cistatic const struct omap_dma_config omap2430_data = { 18788c2ecf20Sopenharmony_ci .lch_end = CCFN, 18798c2ecf20Sopenharmony_ci .rw_priority = true, 18808c2ecf20Sopenharmony_ci .needs_lch_clear = true, 18818c2ecf20Sopenharmony_ci}; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_cistatic const struct omap_dma_config omap3430_data = { 18848c2ecf20Sopenharmony_ci .lch_end = CCFN, 18858c2ecf20Sopenharmony_ci .rw_priority = true, 18868c2ecf20Sopenharmony_ci .needs_lch_clear = true, 18878c2ecf20Sopenharmony_ci .may_lose_context = true, 18888c2ecf20Sopenharmony_ci}; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_cistatic const struct omap_dma_config omap3630_data = { 18918c2ecf20Sopenharmony_ci .lch_end = CCDN, 18928c2ecf20Sopenharmony_ci .rw_priority = true, 18938c2ecf20Sopenharmony_ci .needs_lch_clear = true, 18948c2ecf20Sopenharmony_ci .may_lose_context = true, 18958c2ecf20Sopenharmony_ci}; 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_cistatic const struct omap_dma_config omap4_data = { 18988c2ecf20Sopenharmony_ci .lch_end = CCDN, 18998c2ecf20Sopenharmony_ci .rw_priority = true, 19008c2ecf20Sopenharmony_ci .needs_lch_clear = true, 19018c2ecf20Sopenharmony_ci}; 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_cistatic const struct of_device_id omap_dma_match[] = { 19048c2ecf20Sopenharmony_ci { .compatible = "ti,omap2420-sdma", .data = &omap2420_data, }, 19058c2ecf20Sopenharmony_ci { .compatible = "ti,omap2430-sdma", .data = &omap2430_data, }, 19068c2ecf20Sopenharmony_ci { .compatible = "ti,omap3430-sdma", .data = &omap3430_data, }, 19078c2ecf20Sopenharmony_ci { .compatible = "ti,omap3630-sdma", .data = &omap3630_data, }, 19088c2ecf20Sopenharmony_ci { .compatible = "ti,omap4430-sdma", .data = &omap4_data, }, 19098c2ecf20Sopenharmony_ci {}, 19108c2ecf20Sopenharmony_ci}; 19118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_dma_match); 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_cistatic struct platform_driver omap_dma_driver = { 19148c2ecf20Sopenharmony_ci .probe = omap_dma_probe, 19158c2ecf20Sopenharmony_ci .remove = omap_dma_remove, 19168c2ecf20Sopenharmony_ci .driver = { 19178c2ecf20Sopenharmony_ci .name = "omap-dma-engine", 19188c2ecf20Sopenharmony_ci .of_match_table = omap_dma_match, 19198c2ecf20Sopenharmony_ci }, 19208c2ecf20Sopenharmony_ci}; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_cistatic bool omap_dma_filter_fn(struct dma_chan *chan, void *param) 19238c2ecf20Sopenharmony_ci{ 19248c2ecf20Sopenharmony_ci if (chan->device->dev->driver == &omap_dma_driver.driver) { 19258c2ecf20Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 19268c2ecf20Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 19278c2ecf20Sopenharmony_ci unsigned req = *(unsigned *)param; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci if (req <= od->dma_requests) { 19308c2ecf20Sopenharmony_ci c->dma_sig = req; 19318c2ecf20Sopenharmony_ci return true; 19328c2ecf20Sopenharmony_ci } 19338c2ecf20Sopenharmony_ci } 19348c2ecf20Sopenharmony_ci return false; 19358c2ecf20Sopenharmony_ci} 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_cistatic int omap_dma_init(void) 19388c2ecf20Sopenharmony_ci{ 19398c2ecf20Sopenharmony_ci return platform_driver_register(&omap_dma_driver); 19408c2ecf20Sopenharmony_ci} 19418c2ecf20Sopenharmony_cisubsys_initcall(omap_dma_init); 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_cistatic void __exit omap_dma_exit(void) 19448c2ecf20Sopenharmony_ci{ 19458c2ecf20Sopenharmony_ci platform_driver_unregister(&omap_dma_driver); 19468c2ecf20Sopenharmony_ci} 19478c2ecf20Sopenharmony_cimodule_exit(omap_dma_exit); 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King"); 19508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1951