18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Omnitek Scatter-Gather DMA Controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. 68c2ecf20Sopenharmony_ci * All rights reserved. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/string.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/pci_regs.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "cobalt-driver.h" 158c2ecf20Sopenharmony_ci#include "cobalt-omnitek.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* descriptor */ 188c2ecf20Sopenharmony_ci#define END_OF_CHAIN (1 << 1) 198c2ecf20Sopenharmony_ci#define INTERRUPT_ENABLE (1 << 2) 208c2ecf20Sopenharmony_ci#define WRITE_TO_PCI (1 << 3) 218c2ecf20Sopenharmony_ci#define READ_FROM_PCI (0 << 3) 228c2ecf20Sopenharmony_ci#define DESCRIPTOR_FLAG_MSK (END_OF_CHAIN | INTERRUPT_ENABLE | WRITE_TO_PCI) 238c2ecf20Sopenharmony_ci#define NEXT_ADRS_MSK 0xffffffe0 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* control/status register */ 268c2ecf20Sopenharmony_ci#define ENABLE (1 << 0) 278c2ecf20Sopenharmony_ci#define START (1 << 1) 288c2ecf20Sopenharmony_ci#define ABORT (1 << 2) 298c2ecf20Sopenharmony_ci#define DONE (1 << 4) 308c2ecf20Sopenharmony_ci#define SG_INTERRUPT (1 << 5) 318c2ecf20Sopenharmony_ci#define EVENT_INTERRUPT (1 << 6) 328c2ecf20Sopenharmony_ci#define SCATTER_GATHER_MODE (1 << 8) 338c2ecf20Sopenharmony_ci#define DISABLE_VIDEO_RESYNC (1 << 9) 348c2ecf20Sopenharmony_ci#define EVENT_INTERRUPT_ENABLE (1 << 10) 358c2ecf20Sopenharmony_ci#define DIRECTIONAL_MSK (3 << 16) 368c2ecf20Sopenharmony_ci#define INPUT_ONLY (0 << 16) 378c2ecf20Sopenharmony_ci#define OUTPUT_ONLY (1 << 16) 388c2ecf20Sopenharmony_ci#define BIDIRECTIONAL (2 << 16) 398c2ecf20Sopenharmony_ci#define DMA_TYPE_MEMORY (0 << 18) 408c2ecf20Sopenharmony_ci#define DMA_TYPE_FIFO (1 << 18) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define BASE (cobalt->bar0) 438c2ecf20Sopenharmony_ci#define CAPABILITY_HEADER (BASE) 448c2ecf20Sopenharmony_ci#define CAPABILITY_REGISTER (BASE + 0x04) 458c2ecf20Sopenharmony_ci#define PCI_64BIT (1 << 8) 468c2ecf20Sopenharmony_ci#define LOCAL_64BIT (1 << 9) 478c2ecf20Sopenharmony_ci#define INTERRUPT_STATUS (BASE + 0x08) 488c2ecf20Sopenharmony_ci#define PCI(c) (BASE + 0x40 + ((c) * 0x40)) 498c2ecf20Sopenharmony_ci#define SIZE(c) (BASE + 0x58 + ((c) * 0x40)) 508c2ecf20Sopenharmony_ci#define DESCRIPTOR(c) (BASE + 0x50 + ((c) * 0x40)) 518c2ecf20Sopenharmony_ci#define CS_REG(c) (BASE + 0x60 + ((c) * 0x40)) 528c2ecf20Sopenharmony_ci#define BYTES_TRANSFERRED(c) (BASE + 0x64 + ((c) * 0x40)) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic char *get_dma_direction(u32 status) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci switch (status & DIRECTIONAL_MSK) { 588c2ecf20Sopenharmony_ci case INPUT_ONLY: return "Input"; 598c2ecf20Sopenharmony_ci case OUTPUT_ONLY: return "Output"; 608c2ecf20Sopenharmony_ci case BIDIRECTIONAL: return "Bidirectional"; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci return ""; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void show_dma_capability(struct cobalt *cobalt) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci u32 header = ioread32(CAPABILITY_HEADER); 688c2ecf20Sopenharmony_ci u32 capa = ioread32(CAPABILITY_REGISTER); 698c2ecf20Sopenharmony_ci u32 i; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci cobalt_info("Omnitek DMA capability: ID 0x%02x Version 0x%02x Next 0x%x Size 0x%x\n", 728c2ecf20Sopenharmony_ci header & 0xff, (header >> 8) & 0xff, 738c2ecf20Sopenharmony_ci (header >> 16) & 0xffff, (capa >> 24) & 0xff); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci switch ((capa >> 8) & 0x3) { 768c2ecf20Sopenharmony_ci case 0: 778c2ecf20Sopenharmony_ci cobalt_info("Omnitek DMA: 32 bits PCIe and Local\n"); 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci case 1: 808c2ecf20Sopenharmony_ci cobalt_info("Omnitek DMA: 64 bits PCIe, 32 bits Local\n"); 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci case 3: 838c2ecf20Sopenharmony_ci cobalt_info("Omnitek DMA: 64 bits PCIe and Local\n"); 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci for (i = 0; i < (capa & 0xf); i++) { 888c2ecf20Sopenharmony_ci u32 status = ioread32(CS_REG(i)); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci cobalt_info("Omnitek DMA channel #%d: %s %s\n", i, 918c2ecf20Sopenharmony_ci status & DMA_TYPE_FIFO ? "FIFO" : "MEMORY", 928c2ecf20Sopenharmony_ci get_dma_direction(status)); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_civoid omni_sg_dma_start(struct cobalt_stream *s, struct sg_dma_desc_info *desc) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci iowrite32((u32)((u64)desc->bus >> 32), DESCRIPTOR(s->dma_channel) + 4); 1018c2ecf20Sopenharmony_ci iowrite32((u32)desc->bus & NEXT_ADRS_MSK, DESCRIPTOR(s->dma_channel)); 1028c2ecf20Sopenharmony_ci iowrite32(ENABLE | SCATTER_GATHER_MODE | START, CS_REG(s->dma_channel)); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cibool is_dma_done(struct cobalt_stream *s) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (ioread32(CS_REG(s->dma_channel)) & DONE) 1108c2ecf20Sopenharmony_ci return true; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return false; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_civoid omni_sg_dma_abort_channel(struct cobalt_stream *s) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (!is_dma_done(s)) 1208c2ecf20Sopenharmony_ci iowrite32(ABORT, CS_REG(s->dma_channel)); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ciint omni_sg_dma_init(struct cobalt *cobalt) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci u32 capa = ioread32(CAPABILITY_REGISTER); 1268c2ecf20Sopenharmony_ci int i; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci cobalt->first_fifo_channel = 0; 1298c2ecf20Sopenharmony_ci cobalt->dma_channels = capa & 0xf; 1308c2ecf20Sopenharmony_ci if (capa & PCI_64BIT) 1318c2ecf20Sopenharmony_ci cobalt->pci_32_bit = false; 1328c2ecf20Sopenharmony_ci else 1338c2ecf20Sopenharmony_ci cobalt->pci_32_bit = true; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci for (i = 0; i < cobalt->dma_channels; i++) { 1368c2ecf20Sopenharmony_ci u32 status = ioread32(CS_REG(i)); 1378c2ecf20Sopenharmony_ci u32 ctrl = ioread32(CS_REG(i)); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!(ctrl & DONE)) 1408c2ecf20Sopenharmony_ci iowrite32(ABORT, CS_REG(i)); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (!(status & DMA_TYPE_FIFO)) 1438c2ecf20Sopenharmony_ci cobalt->first_fifo_channel++; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci show_dma_capability(cobalt); 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ciint descriptor_list_create(struct cobalt *cobalt, 1508c2ecf20Sopenharmony_ci struct scatterlist *scatter_list, bool to_pci, unsigned sglen, 1518c2ecf20Sopenharmony_ci unsigned size, unsigned width, unsigned stride, 1528c2ecf20Sopenharmony_ci struct sg_dma_desc_info *desc) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct sg_dma_descriptor *d = (struct sg_dma_descriptor *)desc->virt; 1558c2ecf20Sopenharmony_ci dma_addr_t next = desc->bus; 1568c2ecf20Sopenharmony_ci unsigned offset = 0; 1578c2ecf20Sopenharmony_ci unsigned copy_bytes = width; 1588c2ecf20Sopenharmony_ci unsigned copied = 0; 1598c2ecf20Sopenharmony_ci bool first = true; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* Must be 4-byte aligned */ 1628c2ecf20Sopenharmony_ci WARN_ON(sg_dma_address(scatter_list) & 3); 1638c2ecf20Sopenharmony_ci WARN_ON(size & 3); 1648c2ecf20Sopenharmony_ci WARN_ON(next & 3); 1658c2ecf20Sopenharmony_ci WARN_ON(stride & 3); 1668c2ecf20Sopenharmony_ci WARN_ON(stride < width); 1678c2ecf20Sopenharmony_ci if (width >= stride) 1688c2ecf20Sopenharmony_ci copy_bytes = stride = size; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci while (size) { 1718c2ecf20Sopenharmony_ci dma_addr_t addr = sg_dma_address(scatter_list) + offset; 1728c2ecf20Sopenharmony_ci unsigned bytes; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (addr == 0) 1758c2ecf20Sopenharmony_ci return -EFAULT; 1768c2ecf20Sopenharmony_ci if (cobalt->pci_32_bit) { 1778c2ecf20Sopenharmony_ci WARN_ON((u64)addr >> 32); 1788c2ecf20Sopenharmony_ci if ((u64)addr >> 32) 1798c2ecf20Sopenharmony_ci return -EFAULT; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* PCIe address */ 1838c2ecf20Sopenharmony_ci d->pci_l = addr & 0xffffffff; 1848c2ecf20Sopenharmony_ci /* If dma_addr_t is 32 bits, then addr >> 32 is actually the 1858c2ecf20Sopenharmony_ci equivalent of addr >> 0 in gcc. So must cast to u64. */ 1868c2ecf20Sopenharmony_ci d->pci_h = (u64)addr >> 32; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Sync to start of streaming frame */ 1898c2ecf20Sopenharmony_ci d->local = 0; 1908c2ecf20Sopenharmony_ci d->reserved0 = 0; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* Transfer bytes */ 1938c2ecf20Sopenharmony_ci bytes = min(sg_dma_len(scatter_list) - offset, 1948c2ecf20Sopenharmony_ci copy_bytes - copied); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (first) { 1978c2ecf20Sopenharmony_ci if (to_pci) 1988c2ecf20Sopenharmony_ci d->local = 0x11111111; 1998c2ecf20Sopenharmony_ci first = false; 2008c2ecf20Sopenharmony_ci if (sglen == 1) { 2018c2ecf20Sopenharmony_ci /* Make sure there are always at least two 2028c2ecf20Sopenharmony_ci * descriptors */ 2038c2ecf20Sopenharmony_ci d->bytes = (bytes / 2) & ~3; 2048c2ecf20Sopenharmony_ci d->reserved1 = 0; 2058c2ecf20Sopenharmony_ci size -= d->bytes; 2068c2ecf20Sopenharmony_ci copied += d->bytes; 2078c2ecf20Sopenharmony_ci offset += d->bytes; 2088c2ecf20Sopenharmony_ci addr += d->bytes; 2098c2ecf20Sopenharmony_ci next += sizeof(struct sg_dma_descriptor); 2108c2ecf20Sopenharmony_ci d->next_h = (u32)((u64)next >> 32); 2118c2ecf20Sopenharmony_ci d->next_l = (u32)next | 2128c2ecf20Sopenharmony_ci (to_pci ? WRITE_TO_PCI : 0); 2138c2ecf20Sopenharmony_ci bytes -= d->bytes; 2148c2ecf20Sopenharmony_ci d++; 2158c2ecf20Sopenharmony_ci /* PCIe address */ 2168c2ecf20Sopenharmony_ci d->pci_l = addr & 0xffffffff; 2178c2ecf20Sopenharmony_ci /* If dma_addr_t is 32 bits, then addr >> 32 2188c2ecf20Sopenharmony_ci * is actually the equivalent of addr >> 0 in 2198c2ecf20Sopenharmony_ci * gcc. So must cast to u64. */ 2208c2ecf20Sopenharmony_ci d->pci_h = (u64)addr >> 32; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Sync to start of streaming frame */ 2238c2ecf20Sopenharmony_ci d->local = 0; 2248c2ecf20Sopenharmony_ci d->reserved0 = 0; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci d->bytes = bytes; 2298c2ecf20Sopenharmony_ci d->reserved1 = 0; 2308c2ecf20Sopenharmony_ci size -= bytes; 2318c2ecf20Sopenharmony_ci copied += bytes; 2328c2ecf20Sopenharmony_ci offset += bytes; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (copied == copy_bytes) { 2358c2ecf20Sopenharmony_ci while (copied < stride) { 2368c2ecf20Sopenharmony_ci bytes = min(sg_dma_len(scatter_list) - offset, 2378c2ecf20Sopenharmony_ci stride - copied); 2388c2ecf20Sopenharmony_ci copied += bytes; 2398c2ecf20Sopenharmony_ci offset += bytes; 2408c2ecf20Sopenharmony_ci size -= bytes; 2418c2ecf20Sopenharmony_ci if (sg_dma_len(scatter_list) == offset) { 2428c2ecf20Sopenharmony_ci offset = 0; 2438c2ecf20Sopenharmony_ci scatter_list = sg_next(scatter_list); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci copied = 0; 2478c2ecf20Sopenharmony_ci } else { 2488c2ecf20Sopenharmony_ci offset = 0; 2498c2ecf20Sopenharmony_ci scatter_list = sg_next(scatter_list); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Next descriptor + control bits */ 2538c2ecf20Sopenharmony_ci next += sizeof(struct sg_dma_descriptor); 2548c2ecf20Sopenharmony_ci if (size == 0) { 2558c2ecf20Sopenharmony_ci /* Loopback to the first descriptor */ 2568c2ecf20Sopenharmony_ci d->next_h = (u32)((u64)desc->bus >> 32); 2578c2ecf20Sopenharmony_ci d->next_l = (u32)desc->bus | 2588c2ecf20Sopenharmony_ci (to_pci ? WRITE_TO_PCI : 0) | INTERRUPT_ENABLE; 2598c2ecf20Sopenharmony_ci if (!to_pci) 2608c2ecf20Sopenharmony_ci d->local = 0x22222222; 2618c2ecf20Sopenharmony_ci desc->last_desc_virt = d; 2628c2ecf20Sopenharmony_ci } else { 2638c2ecf20Sopenharmony_ci d->next_h = (u32)((u64)next >> 32); 2648c2ecf20Sopenharmony_ci d->next_l = (u32)next | (to_pci ? WRITE_TO_PCI : 0); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci d++; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_civoid descriptor_list_chain(struct sg_dma_desc_info *this, 2728c2ecf20Sopenharmony_ci struct sg_dma_desc_info *next) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct sg_dma_descriptor *d = this->last_desc_virt; 2758c2ecf20Sopenharmony_ci u32 direction = d->next_l & WRITE_TO_PCI; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (next == NULL) { 2788c2ecf20Sopenharmony_ci d->next_h = 0; 2798c2ecf20Sopenharmony_ci d->next_l = direction | INTERRUPT_ENABLE | END_OF_CHAIN; 2808c2ecf20Sopenharmony_ci } else { 2818c2ecf20Sopenharmony_ci d->next_h = (u32)((u64)next->bus >> 32); 2828c2ecf20Sopenharmony_ci d->next_l = (u32)next->bus | direction | INTERRUPT_ENABLE; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_civoid *descriptor_list_allocate(struct sg_dma_desc_info *desc, size_t bytes) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci desc->size = bytes; 2898c2ecf20Sopenharmony_ci desc->virt = dma_alloc_coherent(desc->dev, bytes, 2908c2ecf20Sopenharmony_ci &desc->bus, GFP_KERNEL); 2918c2ecf20Sopenharmony_ci return desc->virt; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_civoid descriptor_list_free(struct sg_dma_desc_info *desc) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci if (desc->virt) 2978c2ecf20Sopenharmony_ci dma_free_coherent(desc->dev, desc->size, 2988c2ecf20Sopenharmony_ci desc->virt, desc->bus); 2998c2ecf20Sopenharmony_ci desc->virt = NULL; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_civoid descriptor_list_interrupt_enable(struct sg_dma_desc_info *desc) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct sg_dma_descriptor *d = desc->last_desc_virt; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci d->next_l |= INTERRUPT_ENABLE; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_civoid descriptor_list_interrupt_disable(struct sg_dma_desc_info *desc) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct sg_dma_descriptor *d = desc->last_desc_virt; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci d->next_l &= ~INTERRUPT_ENABLE; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_civoid descriptor_list_loopback(struct sg_dma_desc_info *desc) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct sg_dma_descriptor *d = desc->last_desc_virt; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci d->next_h = (u32)((u64)desc->bus >> 32); 3218c2ecf20Sopenharmony_ci d->next_l = (u32)desc->bus | (d->next_l & DESCRIPTOR_FLAG_MSK); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_civoid descriptor_list_end_of_chain(struct sg_dma_desc_info *desc) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct sg_dma_descriptor *d = desc->last_desc_virt; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci d->next_l |= END_OF_CHAIN; 3298c2ecf20Sopenharmony_ci} 330