18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Core driver for the High Speed UART DMA 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Intel Corporation 68c2ecf20Sopenharmony_ci * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Partially based on the bits found in drivers/tty/serial/mfd.c. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * DMA channel allocation: 138c2ecf20Sopenharmony_ci * 1. Even number chans are used for DMA Read (UART TX), odd chans for DMA 148c2ecf20Sopenharmony_ci * Write (UART RX). 158c2ecf20Sopenharmony_ci * 2. 0/1 channel are assigned to port 0, 2/3 chan to port 1, 4/5 chan to 168c2ecf20Sopenharmony_ci * port 3, and so on. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "hsu.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define HSU_DMA_BUSWIDTHS \ 298c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \ 308c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ 318c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ 328c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \ 338c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ 348c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) | \ 358c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_16_BYTES) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline void hsu_chan_disable(struct hsu_dma_chan *hsuc) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci hsu_chan_writel(hsuc, HSU_CH_CR, 0); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic inline void hsu_chan_enable(struct hsu_dma_chan *hsuc) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci u32 cr = HSU_CH_CR_CHA; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (hsuc->direction == DMA_MEM_TO_DEV) 478c2ecf20Sopenharmony_ci cr &= ~HSU_CH_CR_CHD; 488c2ecf20Sopenharmony_ci else if (hsuc->direction == DMA_DEV_TO_MEM) 498c2ecf20Sopenharmony_ci cr |= HSU_CH_CR_CHD; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci hsu_chan_writel(hsuc, HSU_CH_CR, cr); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void hsu_dma_chan_start(struct hsu_dma_chan *hsuc) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct dma_slave_config *config = &hsuc->config; 578c2ecf20Sopenharmony_ci struct hsu_dma_desc *desc = hsuc->desc; 588c2ecf20Sopenharmony_ci u32 bsr = 0, mtsr = 0; /* to shut the compiler up */ 598c2ecf20Sopenharmony_ci u32 dcr = HSU_CH_DCR_CHSOE | HSU_CH_DCR_CHEI; 608c2ecf20Sopenharmony_ci unsigned int i, count; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (hsuc->direction == DMA_MEM_TO_DEV) { 638c2ecf20Sopenharmony_ci bsr = config->dst_maxburst; 648c2ecf20Sopenharmony_ci mtsr = config->dst_addr_width; 658c2ecf20Sopenharmony_ci } else if (hsuc->direction == DMA_DEV_TO_MEM) { 668c2ecf20Sopenharmony_ci bsr = config->src_maxburst; 678c2ecf20Sopenharmony_ci mtsr = config->src_addr_width; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci hsu_chan_disable(hsuc); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci hsu_chan_writel(hsuc, HSU_CH_DCR, 0); 738c2ecf20Sopenharmony_ci hsu_chan_writel(hsuc, HSU_CH_BSR, bsr); 748c2ecf20Sopenharmony_ci hsu_chan_writel(hsuc, HSU_CH_MTSR, mtsr); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* Set descriptors */ 778c2ecf20Sopenharmony_ci count = desc->nents - desc->active; 788c2ecf20Sopenharmony_ci for (i = 0; i < count && i < HSU_DMA_CHAN_NR_DESC; i++) { 798c2ecf20Sopenharmony_ci hsu_chan_writel(hsuc, HSU_CH_DxSAR(i), desc->sg[i].addr); 808c2ecf20Sopenharmony_ci hsu_chan_writel(hsuc, HSU_CH_DxTSR(i), desc->sg[i].len); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Prepare value for DCR */ 838c2ecf20Sopenharmony_ci dcr |= HSU_CH_DCR_DESCA(i); 848c2ecf20Sopenharmony_ci dcr |= HSU_CH_DCR_CHTOI(i); /* timeout bit, see HSU Errata 1 */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci desc->active++; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci /* Only for the last descriptor in the chain */ 898c2ecf20Sopenharmony_ci dcr |= HSU_CH_DCR_CHSOD(count - 1); 908c2ecf20Sopenharmony_ci dcr |= HSU_CH_DCR_CHDI(count - 1); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci hsu_chan_writel(hsuc, HSU_CH_DCR, dcr); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci hsu_chan_enable(hsuc); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void hsu_dma_stop_channel(struct hsu_dma_chan *hsuc) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci hsu_chan_disable(hsuc); 1008c2ecf20Sopenharmony_ci hsu_chan_writel(hsuc, HSU_CH_DCR, 0); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void hsu_dma_start_channel(struct hsu_dma_chan *hsuc) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci hsu_dma_chan_start(hsuc); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void hsu_dma_start_transfer(struct hsu_dma_chan *hsuc) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct virt_dma_desc *vdesc; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Get the next descriptor */ 1138c2ecf20Sopenharmony_ci vdesc = vchan_next_desc(&hsuc->vchan); 1148c2ecf20Sopenharmony_ci if (!vdesc) { 1158c2ecf20Sopenharmony_ci hsuc->desc = NULL; 1168c2ecf20Sopenharmony_ci return; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci list_del(&vdesc->node); 1208c2ecf20Sopenharmony_ci hsuc->desc = to_hsu_dma_desc(vdesc); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Start the channel with a new descriptor */ 1238c2ecf20Sopenharmony_ci hsu_dma_start_channel(hsuc); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* 1278c2ecf20Sopenharmony_ci * hsu_dma_get_status() - get DMA channel status 1288c2ecf20Sopenharmony_ci * @chip: HSUART DMA chip 1298c2ecf20Sopenharmony_ci * @nr: DMA channel number 1308c2ecf20Sopenharmony_ci * @status: pointer for DMA Channel Status Register value 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * Description: 1338c2ecf20Sopenharmony_ci * The function reads and clears the DMA Channel Status Register, checks 1348c2ecf20Sopenharmony_ci * if it was a timeout interrupt and returns a corresponding value. 1358c2ecf20Sopenharmony_ci * 1368c2ecf20Sopenharmony_ci * Caller should provide a valid pointer for the DMA Channel Status 1378c2ecf20Sopenharmony_ci * Register value that will be returned in @status. 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * Return: 1408c2ecf20Sopenharmony_ci * 1 for DMA timeout status, 0 for other DMA status, or error code for 1418c2ecf20Sopenharmony_ci * invalid parameters or no interrupt pending. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ciint hsu_dma_get_status(struct hsu_dma_chip *chip, unsigned short nr, 1448c2ecf20Sopenharmony_ci u32 *status) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc; 1478c2ecf20Sopenharmony_ci unsigned long flags; 1488c2ecf20Sopenharmony_ci u32 sr; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Sanity check */ 1518c2ecf20Sopenharmony_ci if (nr >= chip->hsu->nr_channels) 1528c2ecf20Sopenharmony_ci return -EINVAL; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci hsuc = &chip->hsu->chan[nr]; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * No matter what situation, need read clear the IRQ status 1588c2ecf20Sopenharmony_ci * There is a bug, see Errata 5, HSD 2900918 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci spin_lock_irqsave(&hsuc->vchan.lock, flags); 1618c2ecf20Sopenharmony_ci sr = hsu_chan_readl(hsuc, HSU_CH_SR); 1628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hsuc->vchan.lock, flags); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* Check if any interrupt is pending */ 1658c2ecf20Sopenharmony_ci sr &= ~(HSU_CH_SR_DESCE_ANY | HSU_CH_SR_CDESC_ANY); 1668c2ecf20Sopenharmony_ci if (!sr) 1678c2ecf20Sopenharmony_ci return -EIO; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* Timeout IRQ, need wait some time, see Errata 2 */ 1708c2ecf20Sopenharmony_ci if (sr & HSU_CH_SR_DESCTO_ANY) 1718c2ecf20Sopenharmony_ci udelay(2); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * At this point, at least one of Descriptor Time Out, Channel Error 1758c2ecf20Sopenharmony_ci * or Descriptor Done bits must be set. Clear the Descriptor Time Out 1768c2ecf20Sopenharmony_ci * bits and if sr is still non-zero, it must be channel error or 1778c2ecf20Sopenharmony_ci * descriptor done which are higher priority than timeout and handled 1788c2ecf20Sopenharmony_ci * in hsu_dma_do_irq(). Else, it must be a timeout. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci sr &= ~HSU_CH_SR_DESCTO_ANY; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci *status = sr; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return sr ? 0 : 1; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hsu_dma_get_status); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* 1898c2ecf20Sopenharmony_ci * hsu_dma_do_irq() - DMA interrupt handler 1908c2ecf20Sopenharmony_ci * @chip: HSUART DMA chip 1918c2ecf20Sopenharmony_ci * @nr: DMA channel number 1928c2ecf20Sopenharmony_ci * @status: Channel Status Register value 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * Description: 1958c2ecf20Sopenharmony_ci * This function handles Channel Error and Descriptor Done interrupts. 1968c2ecf20Sopenharmony_ci * This function should be called after determining that the DMA interrupt 1978c2ecf20Sopenharmony_ci * is not a normal timeout interrupt, ie. hsu_dma_get_status() returned 0. 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * Return: 2008c2ecf20Sopenharmony_ci * 0 for invalid channel number, 1 otherwise. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ciint hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr, u32 status) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc; 2058c2ecf20Sopenharmony_ci struct hsu_dma_desc *desc; 2068c2ecf20Sopenharmony_ci unsigned long flags; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* Sanity check */ 2098c2ecf20Sopenharmony_ci if (nr >= chip->hsu->nr_channels) 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci hsuc = &chip->hsu->chan[nr]; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci spin_lock_irqsave(&hsuc->vchan.lock, flags); 2158c2ecf20Sopenharmony_ci desc = hsuc->desc; 2168c2ecf20Sopenharmony_ci if (desc) { 2178c2ecf20Sopenharmony_ci if (status & HSU_CH_SR_CHE) { 2188c2ecf20Sopenharmony_ci desc->status = DMA_ERROR; 2198c2ecf20Sopenharmony_ci } else if (desc->active < desc->nents) { 2208c2ecf20Sopenharmony_ci hsu_dma_start_channel(hsuc); 2218c2ecf20Sopenharmony_ci } else { 2228c2ecf20Sopenharmony_ci vchan_cookie_complete(&desc->vdesc); 2238c2ecf20Sopenharmony_ci desc->status = DMA_COMPLETE; 2248c2ecf20Sopenharmony_ci hsu_dma_start_transfer(hsuc); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hsuc->vchan.lock, flags); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return 1; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hsu_dma_do_irq); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic struct hsu_dma_desc *hsu_dma_alloc_desc(unsigned int nents) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct hsu_dma_desc *desc; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_NOWAIT); 2388c2ecf20Sopenharmony_ci if (!desc) 2398c2ecf20Sopenharmony_ci return NULL; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci desc->sg = kcalloc(nents, sizeof(*desc->sg), GFP_NOWAIT); 2428c2ecf20Sopenharmony_ci if (!desc->sg) { 2438c2ecf20Sopenharmony_ci kfree(desc); 2448c2ecf20Sopenharmony_ci return NULL; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return desc; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void hsu_dma_desc_free(struct virt_dma_desc *vdesc) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct hsu_dma_desc *desc = to_hsu_dma_desc(vdesc); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci kfree(desc->sg); 2558c2ecf20Sopenharmony_ci kfree(desc); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *hsu_dma_prep_slave_sg( 2598c2ecf20Sopenharmony_ci struct dma_chan *chan, struct scatterlist *sgl, 2608c2ecf20Sopenharmony_ci unsigned int sg_len, enum dma_transfer_direction direction, 2618c2ecf20Sopenharmony_ci unsigned long flags, void *context) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); 2648c2ecf20Sopenharmony_ci struct hsu_dma_desc *desc; 2658c2ecf20Sopenharmony_ci struct scatterlist *sg; 2668c2ecf20Sopenharmony_ci unsigned int i; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci desc = hsu_dma_alloc_desc(sg_len); 2698c2ecf20Sopenharmony_ci if (!desc) 2708c2ecf20Sopenharmony_ci return NULL; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 2738c2ecf20Sopenharmony_ci desc->sg[i].addr = sg_dma_address(sg); 2748c2ecf20Sopenharmony_ci desc->sg[i].len = sg_dma_len(sg); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci desc->length += sg_dma_len(sg); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci desc->nents = sg_len; 2808c2ecf20Sopenharmony_ci desc->direction = direction; 2818c2ecf20Sopenharmony_ci /* desc->active = 0 by kzalloc */ 2828c2ecf20Sopenharmony_ci desc->status = DMA_IN_PROGRESS; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return vchan_tx_prep(&hsuc->vchan, &desc->vdesc, flags); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic void hsu_dma_issue_pending(struct dma_chan *chan) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); 2908c2ecf20Sopenharmony_ci unsigned long flags; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci spin_lock_irqsave(&hsuc->vchan.lock, flags); 2938c2ecf20Sopenharmony_ci if (vchan_issue_pending(&hsuc->vchan) && !hsuc->desc) 2948c2ecf20Sopenharmony_ci hsu_dma_start_transfer(hsuc); 2958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hsuc->vchan.lock, flags); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic size_t hsu_dma_active_desc_size(struct hsu_dma_chan *hsuc) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct hsu_dma_desc *desc = hsuc->desc; 3018c2ecf20Sopenharmony_ci size_t bytes = 0; 3028c2ecf20Sopenharmony_ci int i; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci for (i = desc->active; i < desc->nents; i++) 3058c2ecf20Sopenharmony_ci bytes += desc->sg[i].len; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci i = HSU_DMA_CHAN_NR_DESC - 1; 3088c2ecf20Sopenharmony_ci do { 3098c2ecf20Sopenharmony_ci bytes += hsu_chan_readl(hsuc, HSU_CH_DxTSR(i)); 3108c2ecf20Sopenharmony_ci } while (--i >= 0); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return bytes; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic enum dma_status hsu_dma_tx_status(struct dma_chan *chan, 3168c2ecf20Sopenharmony_ci dma_cookie_t cookie, struct dma_tx_state *state) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); 3198c2ecf20Sopenharmony_ci struct virt_dma_desc *vdesc; 3208c2ecf20Sopenharmony_ci enum dma_status status; 3218c2ecf20Sopenharmony_ci size_t bytes; 3228c2ecf20Sopenharmony_ci unsigned long flags; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci status = dma_cookie_status(chan, cookie, state); 3258c2ecf20Sopenharmony_ci if (status == DMA_COMPLETE) 3268c2ecf20Sopenharmony_ci return status; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci spin_lock_irqsave(&hsuc->vchan.lock, flags); 3298c2ecf20Sopenharmony_ci vdesc = vchan_find_desc(&hsuc->vchan, cookie); 3308c2ecf20Sopenharmony_ci if (hsuc->desc && cookie == hsuc->desc->vdesc.tx.cookie) { 3318c2ecf20Sopenharmony_ci bytes = hsu_dma_active_desc_size(hsuc); 3328c2ecf20Sopenharmony_ci dma_set_residue(state, bytes); 3338c2ecf20Sopenharmony_ci status = hsuc->desc->status; 3348c2ecf20Sopenharmony_ci } else if (vdesc) { 3358c2ecf20Sopenharmony_ci bytes = to_hsu_dma_desc(vdesc)->length; 3368c2ecf20Sopenharmony_ci dma_set_residue(state, bytes); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hsuc->vchan.lock, flags); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return status; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int hsu_dma_slave_config(struct dma_chan *chan, 3448c2ecf20Sopenharmony_ci struct dma_slave_config *config) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci memcpy(&hsuc->config, config, sizeof(hsuc->config)); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic int hsu_dma_pause(struct dma_chan *chan) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); 3568c2ecf20Sopenharmony_ci unsigned long flags; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci spin_lock_irqsave(&hsuc->vchan.lock, flags); 3598c2ecf20Sopenharmony_ci if (hsuc->desc && hsuc->desc->status == DMA_IN_PROGRESS) { 3608c2ecf20Sopenharmony_ci hsu_chan_disable(hsuc); 3618c2ecf20Sopenharmony_ci hsuc->desc->status = DMA_PAUSED; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hsuc->vchan.lock, flags); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int hsu_dma_resume(struct dma_chan *chan) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); 3718c2ecf20Sopenharmony_ci unsigned long flags; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci spin_lock_irqsave(&hsuc->vchan.lock, flags); 3748c2ecf20Sopenharmony_ci if (hsuc->desc && hsuc->desc->status == DMA_PAUSED) { 3758c2ecf20Sopenharmony_ci hsuc->desc->status = DMA_IN_PROGRESS; 3768c2ecf20Sopenharmony_ci hsu_chan_enable(hsuc); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hsuc->vchan.lock, flags); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int hsu_dma_terminate_all(struct dma_chan *chan) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); 3868c2ecf20Sopenharmony_ci unsigned long flags; 3878c2ecf20Sopenharmony_ci LIST_HEAD(head); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci spin_lock_irqsave(&hsuc->vchan.lock, flags); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci hsu_dma_stop_channel(hsuc); 3928c2ecf20Sopenharmony_ci if (hsuc->desc) { 3938c2ecf20Sopenharmony_ci hsu_dma_desc_free(&hsuc->desc->vdesc); 3948c2ecf20Sopenharmony_ci hsuc->desc = NULL; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci vchan_get_all_descriptors(&hsuc->vchan, &head); 3988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hsuc->vchan.lock, flags); 3998c2ecf20Sopenharmony_ci vchan_dma_desc_free_list(&hsuc->vchan, &head); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic void hsu_dma_free_chan_resources(struct dma_chan *chan) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci vchan_free_chan_resources(to_virt_chan(chan)); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic void hsu_dma_synchronize(struct dma_chan *chan) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci vchan_synchronize(&hsuc->vchan); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ciint hsu_dma_probe(struct hsu_dma_chip *chip) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct hsu_dma *hsu; 4198c2ecf20Sopenharmony_ci void __iomem *addr = chip->regs + chip->offset; 4208c2ecf20Sopenharmony_ci unsigned short i; 4218c2ecf20Sopenharmony_ci int ret; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci hsu = devm_kzalloc(chip->dev, sizeof(*hsu), GFP_KERNEL); 4248c2ecf20Sopenharmony_ci if (!hsu) 4258c2ecf20Sopenharmony_ci return -ENOMEM; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci chip->hsu = hsu; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Calculate nr_channels from the IO space length */ 4308c2ecf20Sopenharmony_ci hsu->nr_channels = (chip->length - chip->offset) / HSU_DMA_CHAN_LENGTH; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci hsu->chan = devm_kcalloc(chip->dev, hsu->nr_channels, 4338c2ecf20Sopenharmony_ci sizeof(*hsu->chan), GFP_KERNEL); 4348c2ecf20Sopenharmony_ci if (!hsu->chan) 4358c2ecf20Sopenharmony_ci return -ENOMEM; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&hsu->dma.channels); 4388c2ecf20Sopenharmony_ci for (i = 0; i < hsu->nr_channels; i++) { 4398c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc = &hsu->chan[i]; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci hsuc->vchan.desc_free = hsu_dma_desc_free; 4428c2ecf20Sopenharmony_ci vchan_init(&hsuc->vchan, &hsu->dma); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci hsuc->direction = (i & 0x1) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; 4458c2ecf20Sopenharmony_ci hsuc->reg = addr + i * HSU_DMA_CHAN_LENGTH; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, hsu->dma.cap_mask); 4498c2ecf20Sopenharmony_ci dma_cap_set(DMA_PRIVATE, hsu->dma.cap_mask); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci hsu->dma.device_free_chan_resources = hsu_dma_free_chan_resources; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci hsu->dma.device_prep_slave_sg = hsu_dma_prep_slave_sg; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci hsu->dma.device_issue_pending = hsu_dma_issue_pending; 4568c2ecf20Sopenharmony_ci hsu->dma.device_tx_status = hsu_dma_tx_status; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci hsu->dma.device_config = hsu_dma_slave_config; 4598c2ecf20Sopenharmony_ci hsu->dma.device_pause = hsu_dma_pause; 4608c2ecf20Sopenharmony_ci hsu->dma.device_resume = hsu_dma_resume; 4618c2ecf20Sopenharmony_ci hsu->dma.device_terminate_all = hsu_dma_terminate_all; 4628c2ecf20Sopenharmony_ci hsu->dma.device_synchronize = hsu_dma_synchronize; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci hsu->dma.src_addr_widths = HSU_DMA_BUSWIDTHS; 4658c2ecf20Sopenharmony_ci hsu->dma.dst_addr_widths = HSU_DMA_BUSWIDTHS; 4668c2ecf20Sopenharmony_ci hsu->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 4678c2ecf20Sopenharmony_ci hsu->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci hsu->dma.dev = chip->dev; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci dma_set_max_seg_size(hsu->dma.dev, HSU_CH_DxTSR_MASK); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ret = dma_async_device_register(&hsu->dma); 4748c2ecf20Sopenharmony_ci if (ret) 4758c2ecf20Sopenharmony_ci return ret; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci dev_info(chip->dev, "Found HSU DMA, %d channels\n", hsu->nr_channels); 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hsu_dma_probe); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ciint hsu_dma_remove(struct hsu_dma_chip *chip) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct hsu_dma *hsu = chip->hsu; 4858c2ecf20Sopenharmony_ci unsigned short i; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci dma_async_device_unregister(&hsu->dma); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci for (i = 0; i < hsu->nr_channels; i++) { 4908c2ecf20Sopenharmony_ci struct hsu_dma_chan *hsuc = &hsu->chan[i]; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci tasklet_kill(&hsuc->vchan.task); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hsu_dma_remove); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("High Speed UART DMA core driver"); 5018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); 502