18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Texas Instruments CPDMA Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Texas Instruments 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/genalloc.h> 188c2ecf20Sopenharmony_ci#include "davinci_cpdma.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* DMA Registers */ 218c2ecf20Sopenharmony_ci#define CPDMA_TXIDVER 0x00 228c2ecf20Sopenharmony_ci#define CPDMA_TXCONTROL 0x04 238c2ecf20Sopenharmony_ci#define CPDMA_TXTEARDOWN 0x08 248c2ecf20Sopenharmony_ci#define CPDMA_RXIDVER 0x10 258c2ecf20Sopenharmony_ci#define CPDMA_RXCONTROL 0x14 268c2ecf20Sopenharmony_ci#define CPDMA_SOFTRESET 0x1c 278c2ecf20Sopenharmony_ci#define CPDMA_RXTEARDOWN 0x18 288c2ecf20Sopenharmony_ci#define CPDMA_TX_PRI0_RATE 0x30 298c2ecf20Sopenharmony_ci#define CPDMA_TXINTSTATRAW 0x80 308c2ecf20Sopenharmony_ci#define CPDMA_TXINTSTATMASKED 0x84 318c2ecf20Sopenharmony_ci#define CPDMA_TXINTMASKSET 0x88 328c2ecf20Sopenharmony_ci#define CPDMA_TXINTMASKCLEAR 0x8c 338c2ecf20Sopenharmony_ci#define CPDMA_MACINVECTOR 0x90 348c2ecf20Sopenharmony_ci#define CPDMA_MACEOIVECTOR 0x94 358c2ecf20Sopenharmony_ci#define CPDMA_RXINTSTATRAW 0xa0 368c2ecf20Sopenharmony_ci#define CPDMA_RXINTSTATMASKED 0xa4 378c2ecf20Sopenharmony_ci#define CPDMA_RXINTMASKSET 0xa8 388c2ecf20Sopenharmony_ci#define CPDMA_RXINTMASKCLEAR 0xac 398c2ecf20Sopenharmony_ci#define CPDMA_DMAINTSTATRAW 0xb0 408c2ecf20Sopenharmony_ci#define CPDMA_DMAINTSTATMASKED 0xb4 418c2ecf20Sopenharmony_ci#define CPDMA_DMAINTMASKSET 0xb8 428c2ecf20Sopenharmony_ci#define CPDMA_DMAINTMASKCLEAR 0xbc 438c2ecf20Sopenharmony_ci#define CPDMA_DMAINT_HOSTERR BIT(1) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* the following exist only if has_ext_regs is set */ 468c2ecf20Sopenharmony_ci#define CPDMA_DMACONTROL 0x20 478c2ecf20Sopenharmony_ci#define CPDMA_DMASTATUS 0x24 488c2ecf20Sopenharmony_ci#define CPDMA_RXBUFFOFS 0x28 498c2ecf20Sopenharmony_ci#define CPDMA_EM_CONTROL 0x2c 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Descriptor mode bits */ 528c2ecf20Sopenharmony_ci#define CPDMA_DESC_SOP BIT(31) 538c2ecf20Sopenharmony_ci#define CPDMA_DESC_EOP BIT(30) 548c2ecf20Sopenharmony_ci#define CPDMA_DESC_OWNER BIT(29) 558c2ecf20Sopenharmony_ci#define CPDMA_DESC_EOQ BIT(28) 568c2ecf20Sopenharmony_ci#define CPDMA_DESC_TD_COMPLETE BIT(27) 578c2ecf20Sopenharmony_ci#define CPDMA_DESC_PASS_CRC BIT(26) 588c2ecf20Sopenharmony_ci#define CPDMA_DESC_TO_PORT_EN BIT(20) 598c2ecf20Sopenharmony_ci#define CPDMA_TO_PORT_SHIFT 16 608c2ecf20Sopenharmony_ci#define CPDMA_DESC_PORT_MASK (BIT(18) | BIT(17) | BIT(16)) 618c2ecf20Sopenharmony_ci#define CPDMA_DESC_CRC_LEN 4 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define CPDMA_TEARDOWN_VALUE 0xfffffffc 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define CPDMA_MAX_RLIM_CNT 16384 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistruct cpdma_desc { 688c2ecf20Sopenharmony_ci /* hardware fields */ 698c2ecf20Sopenharmony_ci u32 hw_next; 708c2ecf20Sopenharmony_ci u32 hw_buffer; 718c2ecf20Sopenharmony_ci u32 hw_len; 728c2ecf20Sopenharmony_ci u32 hw_mode; 738c2ecf20Sopenharmony_ci /* software fields */ 748c2ecf20Sopenharmony_ci void *sw_token; 758c2ecf20Sopenharmony_ci u32 sw_buffer; 768c2ecf20Sopenharmony_ci u32 sw_len; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistruct cpdma_desc_pool { 808c2ecf20Sopenharmony_ci phys_addr_t phys; 818c2ecf20Sopenharmony_ci dma_addr_t hw_addr; 828c2ecf20Sopenharmony_ci void __iomem *iomap; /* ioremap map */ 838c2ecf20Sopenharmony_ci void *cpumap; /* dma_alloc map */ 848c2ecf20Sopenharmony_ci int desc_size, mem_size; 858c2ecf20Sopenharmony_ci int num_desc; 868c2ecf20Sopenharmony_ci struct device *dev; 878c2ecf20Sopenharmony_ci struct gen_pool *gen_pool; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cienum cpdma_state { 918c2ecf20Sopenharmony_ci CPDMA_STATE_IDLE, 928c2ecf20Sopenharmony_ci CPDMA_STATE_ACTIVE, 938c2ecf20Sopenharmony_ci CPDMA_STATE_TEARDOWN, 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistruct cpdma_ctlr { 978c2ecf20Sopenharmony_ci enum cpdma_state state; 988c2ecf20Sopenharmony_ci struct cpdma_params params; 998c2ecf20Sopenharmony_ci struct device *dev; 1008c2ecf20Sopenharmony_ci struct cpdma_desc_pool *pool; 1018c2ecf20Sopenharmony_ci spinlock_t lock; 1028c2ecf20Sopenharmony_ci struct cpdma_chan *channels[2 * CPDMA_MAX_CHANNELS]; 1038c2ecf20Sopenharmony_ci int chan_num; 1048c2ecf20Sopenharmony_ci int num_rx_desc; /* RX descriptors number */ 1058c2ecf20Sopenharmony_ci int num_tx_desc; /* TX descriptors number */ 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistruct cpdma_chan { 1098c2ecf20Sopenharmony_ci struct cpdma_desc __iomem *head, *tail; 1108c2ecf20Sopenharmony_ci void __iomem *hdp, *cp, *rxfree; 1118c2ecf20Sopenharmony_ci enum cpdma_state state; 1128c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr; 1138c2ecf20Sopenharmony_ci int chan_num; 1148c2ecf20Sopenharmony_ci spinlock_t lock; 1158c2ecf20Sopenharmony_ci int count; 1168c2ecf20Sopenharmony_ci u32 desc_num; 1178c2ecf20Sopenharmony_ci u32 mask; 1188c2ecf20Sopenharmony_ci cpdma_handler_fn handler; 1198c2ecf20Sopenharmony_ci enum dma_data_direction dir; 1208c2ecf20Sopenharmony_ci struct cpdma_chan_stats stats; 1218c2ecf20Sopenharmony_ci /* offsets into dmaregs */ 1228c2ecf20Sopenharmony_ci int int_set, int_clear, td; 1238c2ecf20Sopenharmony_ci int weight; 1248c2ecf20Sopenharmony_ci u32 rate_factor; 1258c2ecf20Sopenharmony_ci u32 rate; 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistruct cpdma_control_info { 1298c2ecf20Sopenharmony_ci u32 reg; 1308c2ecf20Sopenharmony_ci u32 shift, mask; 1318c2ecf20Sopenharmony_ci int access; 1328c2ecf20Sopenharmony_ci#define ACCESS_RO BIT(0) 1338c2ecf20Sopenharmony_ci#define ACCESS_WO BIT(1) 1348c2ecf20Sopenharmony_ci#define ACCESS_RW (ACCESS_RO | ACCESS_WO) 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistruct submit_info { 1388c2ecf20Sopenharmony_ci struct cpdma_chan *chan; 1398c2ecf20Sopenharmony_ci int directed; 1408c2ecf20Sopenharmony_ci void *token; 1418c2ecf20Sopenharmony_ci void *data_virt; 1428c2ecf20Sopenharmony_ci dma_addr_t data_dma; 1438c2ecf20Sopenharmony_ci int len; 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic struct cpdma_control_info controls[] = { 1478c2ecf20Sopenharmony_ci [CPDMA_TX_RLIM] = {CPDMA_DMACONTROL, 8, 0xffff, ACCESS_RW}, 1488c2ecf20Sopenharmony_ci [CPDMA_CMD_IDLE] = {CPDMA_DMACONTROL, 3, 1, ACCESS_WO}, 1498c2ecf20Sopenharmony_ci [CPDMA_COPY_ERROR_FRAMES] = {CPDMA_DMACONTROL, 4, 1, ACCESS_RW}, 1508c2ecf20Sopenharmony_ci [CPDMA_RX_OFF_LEN_UPDATE] = {CPDMA_DMACONTROL, 2, 1, ACCESS_RW}, 1518c2ecf20Sopenharmony_ci [CPDMA_RX_OWNERSHIP_FLIP] = {CPDMA_DMACONTROL, 1, 1, ACCESS_RW}, 1528c2ecf20Sopenharmony_ci [CPDMA_TX_PRIO_FIXED] = {CPDMA_DMACONTROL, 0, 1, ACCESS_RW}, 1538c2ecf20Sopenharmony_ci [CPDMA_STAT_IDLE] = {CPDMA_DMASTATUS, 31, 1, ACCESS_RO}, 1548c2ecf20Sopenharmony_ci [CPDMA_STAT_TX_ERR_CODE] = {CPDMA_DMASTATUS, 20, 0xf, ACCESS_RW}, 1558c2ecf20Sopenharmony_ci [CPDMA_STAT_TX_ERR_CHAN] = {CPDMA_DMASTATUS, 16, 0x7, ACCESS_RW}, 1568c2ecf20Sopenharmony_ci [CPDMA_STAT_RX_ERR_CODE] = {CPDMA_DMASTATUS, 12, 0xf, ACCESS_RW}, 1578c2ecf20Sopenharmony_ci [CPDMA_STAT_RX_ERR_CHAN] = {CPDMA_DMASTATUS, 8, 0x7, ACCESS_RW}, 1588c2ecf20Sopenharmony_ci [CPDMA_RX_BUFFER_OFFSET] = {CPDMA_RXBUFFOFS, 0, 0xffff, ACCESS_RW}, 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#define tx_chan_num(chan) (chan) 1628c2ecf20Sopenharmony_ci#define rx_chan_num(chan) ((chan) + CPDMA_MAX_CHANNELS) 1638c2ecf20Sopenharmony_ci#define is_rx_chan(chan) ((chan)->chan_num >= CPDMA_MAX_CHANNELS) 1648c2ecf20Sopenharmony_ci#define is_tx_chan(chan) (!is_rx_chan(chan)) 1658c2ecf20Sopenharmony_ci#define __chan_linear(chan_num) ((chan_num) & (CPDMA_MAX_CHANNELS - 1)) 1668c2ecf20Sopenharmony_ci#define chan_linear(chan) __chan_linear((chan)->chan_num) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* The following make access to common cpdma_ctlr params more readable */ 1698c2ecf20Sopenharmony_ci#define dmaregs params.dmaregs 1708c2ecf20Sopenharmony_ci#define num_chan params.num_chan 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* various accessors */ 1738c2ecf20Sopenharmony_ci#define dma_reg_read(ctlr, ofs) readl((ctlr)->dmaregs + (ofs)) 1748c2ecf20Sopenharmony_ci#define chan_read(chan, fld) readl((chan)->fld) 1758c2ecf20Sopenharmony_ci#define desc_read(desc, fld) readl(&(desc)->fld) 1768c2ecf20Sopenharmony_ci#define dma_reg_write(ctlr, ofs, v) writel(v, (ctlr)->dmaregs + (ofs)) 1778c2ecf20Sopenharmony_ci#define chan_write(chan, fld, v) writel(v, (chan)->fld) 1788c2ecf20Sopenharmony_ci#define desc_write(desc, fld, v) writel((u32)(v), &(desc)->fld) 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#define cpdma_desc_to_port(chan, mode, directed) \ 1818c2ecf20Sopenharmony_ci do { \ 1828c2ecf20Sopenharmony_ci if (!is_rx_chan(chan) && ((directed == 1) || \ 1838c2ecf20Sopenharmony_ci (directed == 2))) \ 1848c2ecf20Sopenharmony_ci mode |= (CPDMA_DESC_TO_PORT_EN | \ 1858c2ecf20Sopenharmony_ci (directed << CPDMA_TO_PORT_SHIFT)); \ 1868c2ecf20Sopenharmony_ci } while (0) 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#define CPDMA_DMA_EXT_MAP BIT(16) 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void cpdma_desc_pool_destroy(struct cpdma_ctlr *ctlr) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct cpdma_desc_pool *pool = ctlr->pool; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!pool) 1958c2ecf20Sopenharmony_ci return; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci WARN(gen_pool_size(pool->gen_pool) != gen_pool_avail(pool->gen_pool), 1988c2ecf20Sopenharmony_ci "cpdma_desc_pool size %zd != avail %zd", 1998c2ecf20Sopenharmony_ci gen_pool_size(pool->gen_pool), 2008c2ecf20Sopenharmony_ci gen_pool_avail(pool->gen_pool)); 2018c2ecf20Sopenharmony_ci if (pool->cpumap) 2028c2ecf20Sopenharmony_ci dma_free_coherent(ctlr->dev, pool->mem_size, pool->cpumap, 2038c2ecf20Sopenharmony_ci pool->phys); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * Utility constructs for a cpdma descriptor pool. Some devices (e.g. davinci 2088c2ecf20Sopenharmony_ci * emac) have dedicated on-chip memory for these descriptors. Some other 2098c2ecf20Sopenharmony_ci * devices (e.g. cpsw switches) use plain old memory. Descriptor pools 2108c2ecf20Sopenharmony_ci * abstract out these details 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_cistatic int cpdma_desc_pool_create(struct cpdma_ctlr *ctlr) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct cpdma_params *cpdma_params = &ctlr->params; 2158c2ecf20Sopenharmony_ci struct cpdma_desc_pool *pool; 2168c2ecf20Sopenharmony_ci int ret = -ENOMEM; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci pool = devm_kzalloc(ctlr->dev, sizeof(*pool), GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!pool) 2208c2ecf20Sopenharmony_ci goto gen_pool_create_fail; 2218c2ecf20Sopenharmony_ci ctlr->pool = pool; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci pool->mem_size = cpdma_params->desc_mem_size; 2248c2ecf20Sopenharmony_ci pool->desc_size = ALIGN(sizeof(struct cpdma_desc), 2258c2ecf20Sopenharmony_ci cpdma_params->desc_align); 2268c2ecf20Sopenharmony_ci pool->num_desc = pool->mem_size / pool->desc_size; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (cpdma_params->descs_pool_size) { 2298c2ecf20Sopenharmony_ci /* recalculate memory size required cpdma descriptor pool 2308c2ecf20Sopenharmony_ci * basing on number of descriptors specified by user and 2318c2ecf20Sopenharmony_ci * if memory size > CPPI internal RAM size (desc_mem_size) 2328c2ecf20Sopenharmony_ci * then switch to use DDR 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci pool->num_desc = cpdma_params->descs_pool_size; 2358c2ecf20Sopenharmony_ci pool->mem_size = pool->desc_size * pool->num_desc; 2368c2ecf20Sopenharmony_ci if (pool->mem_size > cpdma_params->desc_mem_size) 2378c2ecf20Sopenharmony_ci cpdma_params->desc_mem_phys = 0; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci pool->gen_pool = devm_gen_pool_create(ctlr->dev, ilog2(pool->desc_size), 2418c2ecf20Sopenharmony_ci -1, "cpdma"); 2428c2ecf20Sopenharmony_ci if (IS_ERR(pool->gen_pool)) { 2438c2ecf20Sopenharmony_ci ret = PTR_ERR(pool->gen_pool); 2448c2ecf20Sopenharmony_ci dev_err(ctlr->dev, "pool create failed %d\n", ret); 2458c2ecf20Sopenharmony_ci goto gen_pool_create_fail; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (cpdma_params->desc_mem_phys) { 2498c2ecf20Sopenharmony_ci pool->phys = cpdma_params->desc_mem_phys; 2508c2ecf20Sopenharmony_ci pool->iomap = devm_ioremap(ctlr->dev, pool->phys, 2518c2ecf20Sopenharmony_ci pool->mem_size); 2528c2ecf20Sopenharmony_ci pool->hw_addr = cpdma_params->desc_hw_addr; 2538c2ecf20Sopenharmony_ci } else { 2548c2ecf20Sopenharmony_ci pool->cpumap = dma_alloc_coherent(ctlr->dev, pool->mem_size, 2558c2ecf20Sopenharmony_ci &pool->hw_addr, GFP_KERNEL); 2568c2ecf20Sopenharmony_ci pool->iomap = (void __iomem __force *)pool->cpumap; 2578c2ecf20Sopenharmony_ci pool->phys = pool->hw_addr; /* assumes no IOMMU, don't use this value */ 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (!pool->iomap) 2618c2ecf20Sopenharmony_ci goto gen_pool_create_fail; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ret = gen_pool_add_virt(pool->gen_pool, (unsigned long)pool->iomap, 2648c2ecf20Sopenharmony_ci pool->phys, pool->mem_size, -1); 2658c2ecf20Sopenharmony_ci if (ret < 0) { 2668c2ecf20Sopenharmony_ci dev_err(ctlr->dev, "pool add failed %d\n", ret); 2678c2ecf20Sopenharmony_ci goto gen_pool_add_virt_fail; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cigen_pool_add_virt_fail: 2738c2ecf20Sopenharmony_ci cpdma_desc_pool_destroy(ctlr); 2748c2ecf20Sopenharmony_cigen_pool_create_fail: 2758c2ecf20Sopenharmony_ci ctlr->pool = NULL; 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic inline dma_addr_t desc_phys(struct cpdma_desc_pool *pool, 2808c2ecf20Sopenharmony_ci struct cpdma_desc __iomem *desc) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci if (!desc) 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci return pool->hw_addr + (__force long)desc - (__force long)pool->iomap; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic inline struct cpdma_desc __iomem * 2888c2ecf20Sopenharmony_cidesc_from_phys(struct cpdma_desc_pool *pool, dma_addr_t dma) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci return dma ? pool->iomap + dma - pool->hw_addr : NULL; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic struct cpdma_desc __iomem * 2948c2ecf20Sopenharmony_cicpdma_desc_alloc(struct cpdma_desc_pool *pool) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci return (struct cpdma_desc __iomem *) 2978c2ecf20Sopenharmony_ci gen_pool_alloc(pool->gen_pool, pool->desc_size); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic void cpdma_desc_free(struct cpdma_desc_pool *pool, 3018c2ecf20Sopenharmony_ci struct cpdma_desc __iomem *desc, int num_desc) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci gen_pool_free(pool->gen_pool, (unsigned long)desc, pool->desc_size); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int _cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct cpdma_control_info *info = &controls[control]; 3098c2ecf20Sopenharmony_ci u32 val; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (!ctlr->params.has_ext_regs) 3128c2ecf20Sopenharmony_ci return -ENOTSUPP; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (ctlr->state != CPDMA_STATE_ACTIVE) 3158c2ecf20Sopenharmony_ci return -EINVAL; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (control < 0 || control >= ARRAY_SIZE(controls)) 3188c2ecf20Sopenharmony_ci return -ENOENT; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if ((info->access & ACCESS_WO) != ACCESS_WO) 3218c2ecf20Sopenharmony_ci return -EPERM; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci val = dma_reg_read(ctlr, info->reg); 3248c2ecf20Sopenharmony_ci val &= ~(info->mask << info->shift); 3258c2ecf20Sopenharmony_ci val |= (value & info->mask) << info->shift; 3268c2ecf20Sopenharmony_ci dma_reg_write(ctlr, info->reg, val); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int _cpdma_control_get(struct cpdma_ctlr *ctlr, int control) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct cpdma_control_info *info = &controls[control]; 3348c2ecf20Sopenharmony_ci int ret; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (!ctlr->params.has_ext_regs) 3378c2ecf20Sopenharmony_ci return -ENOTSUPP; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (ctlr->state != CPDMA_STATE_ACTIVE) 3408c2ecf20Sopenharmony_ci return -EINVAL; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (control < 0 || control >= ARRAY_SIZE(controls)) 3438c2ecf20Sopenharmony_ci return -ENOENT; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if ((info->access & ACCESS_RO) != ACCESS_RO) 3468c2ecf20Sopenharmony_ci return -EPERM; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci ret = (dma_reg_read(ctlr, info->reg) >> info->shift) & info->mask; 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* cpdma_chan_set_chan_shaper - set shaper for a channel 3538c2ecf20Sopenharmony_ci * Has to be called under ctlr lock 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_cistatic int cpdma_chan_set_chan_shaper(struct cpdma_chan *chan) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = chan->ctlr; 3588c2ecf20Sopenharmony_ci u32 rate_reg; 3598c2ecf20Sopenharmony_ci u32 rmask; 3608c2ecf20Sopenharmony_ci int ret; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (!chan->rate) 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci rate_reg = CPDMA_TX_PRI0_RATE + 4 * chan->chan_num; 3668c2ecf20Sopenharmony_ci dma_reg_write(ctlr, rate_reg, chan->rate_factor); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci rmask = _cpdma_control_get(ctlr, CPDMA_TX_RLIM); 3698c2ecf20Sopenharmony_ci rmask |= chan->mask; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ret = _cpdma_control_set(ctlr, CPDMA_TX_RLIM, rmask); 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int cpdma_chan_on(struct cpdma_chan *chan) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = chan->ctlr; 3788c2ecf20Sopenharmony_ci struct cpdma_desc_pool *pool = ctlr->pool; 3798c2ecf20Sopenharmony_ci unsigned long flags; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 3828c2ecf20Sopenharmony_ci if (chan->state != CPDMA_STATE_IDLE) { 3838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 3848c2ecf20Sopenharmony_ci return -EBUSY; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci if (ctlr->state != CPDMA_STATE_ACTIVE) { 3878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 3888c2ecf20Sopenharmony_ci return -EINVAL; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci dma_reg_write(ctlr, chan->int_set, chan->mask); 3918c2ecf20Sopenharmony_ci chan->state = CPDMA_STATE_ACTIVE; 3928c2ecf20Sopenharmony_ci if (chan->head) { 3938c2ecf20Sopenharmony_ci chan_write(chan, hdp, desc_phys(pool, chan->head)); 3948c2ecf20Sopenharmony_ci if (chan->rxfree) 3958c2ecf20Sopenharmony_ci chan_write(chan, rxfree, chan->count); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/* cpdma_chan_fit_rate - set rate for a channel and check if it's possible. 4038c2ecf20Sopenharmony_ci * rmask - mask of rate limited channels 4048c2ecf20Sopenharmony_ci * Returns min rate in Kb/s 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_cistatic int cpdma_chan_fit_rate(struct cpdma_chan *ch, u32 rate, 4078c2ecf20Sopenharmony_ci u32 *rmask, int *prio_mode) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = ch->ctlr; 4108c2ecf20Sopenharmony_ci struct cpdma_chan *chan; 4118c2ecf20Sopenharmony_ci u32 old_rate = ch->rate; 4128c2ecf20Sopenharmony_ci u32 new_rmask = 0; 4138c2ecf20Sopenharmony_ci int rlim = 0; 4148c2ecf20Sopenharmony_ci int i; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci for (i = tx_chan_num(0); i < tx_chan_num(CPDMA_MAX_CHANNELS); i++) { 4178c2ecf20Sopenharmony_ci chan = ctlr->channels[i]; 4188c2ecf20Sopenharmony_ci if (!chan) 4198c2ecf20Sopenharmony_ci continue; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (chan == ch) 4228c2ecf20Sopenharmony_ci chan->rate = rate; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (chan->rate) { 4258c2ecf20Sopenharmony_ci rlim = 1; 4268c2ecf20Sopenharmony_ci new_rmask |= chan->mask; 4278c2ecf20Sopenharmony_ci continue; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (rlim) 4318c2ecf20Sopenharmony_ci goto err; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci *rmask = new_rmask; 4358c2ecf20Sopenharmony_ci *prio_mode = rlim; 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cierr: 4398c2ecf20Sopenharmony_ci ch->rate = old_rate; 4408c2ecf20Sopenharmony_ci dev_err(ctlr->dev, "Upper cpdma ch%d is not rate limited\n", 4418c2ecf20Sopenharmony_ci chan->chan_num); 4428c2ecf20Sopenharmony_ci return -EINVAL; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic u32 cpdma_chan_set_factors(struct cpdma_ctlr *ctlr, 4468c2ecf20Sopenharmony_ci struct cpdma_chan *ch) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci u32 delta = UINT_MAX, prev_delta = UINT_MAX, best_delta = UINT_MAX; 4498c2ecf20Sopenharmony_ci u32 best_send_cnt = 0, best_idle_cnt = 0; 4508c2ecf20Sopenharmony_ci u32 new_rate, best_rate = 0, rate_reg; 4518c2ecf20Sopenharmony_ci u64 send_cnt, idle_cnt; 4528c2ecf20Sopenharmony_ci u32 min_send_cnt, freq; 4538c2ecf20Sopenharmony_ci u64 divident, divisor; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (!ch->rate) { 4568c2ecf20Sopenharmony_ci ch->rate_factor = 0; 4578c2ecf20Sopenharmony_ci goto set_factor; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci freq = ctlr->params.bus_freq_mhz * 1000 * 32; 4618c2ecf20Sopenharmony_ci if (!freq) { 4628c2ecf20Sopenharmony_ci dev_err(ctlr->dev, "The bus frequency is not set\n"); 4638c2ecf20Sopenharmony_ci return -EINVAL; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci min_send_cnt = freq - ch->rate; 4678c2ecf20Sopenharmony_ci send_cnt = DIV_ROUND_UP(min_send_cnt, ch->rate); 4688c2ecf20Sopenharmony_ci while (send_cnt <= CPDMA_MAX_RLIM_CNT) { 4698c2ecf20Sopenharmony_ci divident = ch->rate * send_cnt; 4708c2ecf20Sopenharmony_ci divisor = min_send_cnt; 4718c2ecf20Sopenharmony_ci idle_cnt = DIV_ROUND_CLOSEST_ULL(divident, divisor); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci divident = freq * idle_cnt; 4748c2ecf20Sopenharmony_ci divisor = idle_cnt + send_cnt; 4758c2ecf20Sopenharmony_ci new_rate = DIV_ROUND_CLOSEST_ULL(divident, divisor); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci delta = new_rate >= ch->rate ? new_rate - ch->rate : delta; 4788c2ecf20Sopenharmony_ci if (delta < best_delta) { 4798c2ecf20Sopenharmony_ci best_delta = delta; 4808c2ecf20Sopenharmony_ci best_send_cnt = send_cnt; 4818c2ecf20Sopenharmony_ci best_idle_cnt = idle_cnt; 4828c2ecf20Sopenharmony_ci best_rate = new_rate; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (!delta) 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (prev_delta >= delta) { 4898c2ecf20Sopenharmony_ci prev_delta = delta; 4908c2ecf20Sopenharmony_ci send_cnt++; 4918c2ecf20Sopenharmony_ci continue; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci idle_cnt++; 4958c2ecf20Sopenharmony_ci divident = freq * idle_cnt; 4968c2ecf20Sopenharmony_ci send_cnt = DIV_ROUND_CLOSEST_ULL(divident, ch->rate); 4978c2ecf20Sopenharmony_ci send_cnt -= idle_cnt; 4988c2ecf20Sopenharmony_ci prev_delta = UINT_MAX; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci ch->rate = best_rate; 5028c2ecf20Sopenharmony_ci ch->rate_factor = best_send_cnt | (best_idle_cnt << 16); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ciset_factor: 5058c2ecf20Sopenharmony_ci rate_reg = CPDMA_TX_PRI0_RATE + 4 * ch->chan_num; 5068c2ecf20Sopenharmony_ci dma_reg_write(ctlr, rate_reg, ch->rate_factor); 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistruct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci ctlr = devm_kzalloc(params->dev, sizeof(*ctlr), GFP_KERNEL); 5158c2ecf20Sopenharmony_ci if (!ctlr) 5168c2ecf20Sopenharmony_ci return NULL; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci ctlr->state = CPDMA_STATE_IDLE; 5198c2ecf20Sopenharmony_ci ctlr->params = *params; 5208c2ecf20Sopenharmony_ci ctlr->dev = params->dev; 5218c2ecf20Sopenharmony_ci ctlr->chan_num = 0; 5228c2ecf20Sopenharmony_ci spin_lock_init(&ctlr->lock); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (cpdma_desc_pool_create(ctlr)) 5258c2ecf20Sopenharmony_ci return NULL; 5268c2ecf20Sopenharmony_ci /* split pool equally between RX/TX by default */ 5278c2ecf20Sopenharmony_ci ctlr->num_tx_desc = ctlr->pool->num_desc / 2; 5288c2ecf20Sopenharmony_ci ctlr->num_rx_desc = ctlr->pool->num_desc - ctlr->num_tx_desc; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (WARN_ON(ctlr->num_chan > CPDMA_MAX_CHANNELS)) 5318c2ecf20Sopenharmony_ci ctlr->num_chan = CPDMA_MAX_CHANNELS; 5328c2ecf20Sopenharmony_ci return ctlr; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ciint cpdma_ctlr_start(struct cpdma_ctlr *ctlr) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct cpdma_chan *chan; 5388c2ecf20Sopenharmony_ci unsigned long flags; 5398c2ecf20Sopenharmony_ci int i, prio_mode; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 5428c2ecf20Sopenharmony_ci if (ctlr->state != CPDMA_STATE_IDLE) { 5438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 5448c2ecf20Sopenharmony_ci return -EBUSY; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (ctlr->params.has_soft_reset) { 5488c2ecf20Sopenharmony_ci unsigned timeout = 10 * 100; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci dma_reg_write(ctlr, CPDMA_SOFTRESET, 1); 5518c2ecf20Sopenharmony_ci while (timeout) { 5528c2ecf20Sopenharmony_ci if (dma_reg_read(ctlr, CPDMA_SOFTRESET) == 0) 5538c2ecf20Sopenharmony_ci break; 5548c2ecf20Sopenharmony_ci udelay(10); 5558c2ecf20Sopenharmony_ci timeout--; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci WARN_ON(!timeout); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci for (i = 0; i < ctlr->num_chan; i++) { 5618c2ecf20Sopenharmony_ci writel(0, ctlr->params.txhdp + 4 * i); 5628c2ecf20Sopenharmony_ci writel(0, ctlr->params.rxhdp + 4 * i); 5638c2ecf20Sopenharmony_ci writel(0, ctlr->params.txcp + 4 * i); 5648c2ecf20Sopenharmony_ci writel(0, ctlr->params.rxcp + 4 * i); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci dma_reg_write(ctlr, CPDMA_RXINTMASKCLEAR, 0xffffffff); 5688c2ecf20Sopenharmony_ci dma_reg_write(ctlr, CPDMA_TXINTMASKCLEAR, 0xffffffff); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci dma_reg_write(ctlr, CPDMA_TXCONTROL, 1); 5718c2ecf20Sopenharmony_ci dma_reg_write(ctlr, CPDMA_RXCONTROL, 1); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci ctlr->state = CPDMA_STATE_ACTIVE; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci prio_mode = 0; 5768c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { 5778c2ecf20Sopenharmony_ci chan = ctlr->channels[i]; 5788c2ecf20Sopenharmony_ci if (chan) { 5798c2ecf20Sopenharmony_ci cpdma_chan_set_chan_shaper(chan); 5808c2ecf20Sopenharmony_ci cpdma_chan_on(chan); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* off prio mode if all tx channels are rate limited */ 5838c2ecf20Sopenharmony_ci if (is_tx_chan(chan) && !chan->rate) 5848c2ecf20Sopenharmony_ci prio_mode = 1; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci _cpdma_control_set(ctlr, CPDMA_TX_PRIO_FIXED, prio_mode); 5898c2ecf20Sopenharmony_ci _cpdma_control_set(ctlr, CPDMA_RX_BUFFER_OFFSET, 0); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ciint cpdma_ctlr_stop(struct cpdma_ctlr *ctlr) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci unsigned long flags; 5988c2ecf20Sopenharmony_ci int i; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 6018c2ecf20Sopenharmony_ci if (ctlr->state != CPDMA_STATE_ACTIVE) { 6028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 6038c2ecf20Sopenharmony_ci return -EINVAL; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci ctlr->state = CPDMA_STATE_TEARDOWN; 6078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { 6108c2ecf20Sopenharmony_ci if (ctlr->channels[i]) 6118c2ecf20Sopenharmony_ci cpdma_chan_stop(ctlr->channels[i]); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 6158c2ecf20Sopenharmony_ci dma_reg_write(ctlr, CPDMA_RXINTMASKCLEAR, 0xffffffff); 6168c2ecf20Sopenharmony_ci dma_reg_write(ctlr, CPDMA_TXINTMASKCLEAR, 0xffffffff); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci dma_reg_write(ctlr, CPDMA_TXCONTROL, 0); 6198c2ecf20Sopenharmony_ci dma_reg_write(ctlr, CPDMA_RXCONTROL, 0); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci ctlr->state = CPDMA_STATE_IDLE; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 6248c2ecf20Sopenharmony_ci return 0; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ciint cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci int ret = 0, i; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (!ctlr) 6328c2ecf20Sopenharmony_ci return -EINVAL; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (ctlr->state != CPDMA_STATE_IDLE) 6358c2ecf20Sopenharmony_ci cpdma_ctlr_stop(ctlr); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) 6388c2ecf20Sopenharmony_ci cpdma_chan_destroy(ctlr->channels[i]); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci cpdma_desc_pool_destroy(ctlr); 6418c2ecf20Sopenharmony_ci return ret; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ciint cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci unsigned long flags; 6478c2ecf20Sopenharmony_ci int i; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 6508c2ecf20Sopenharmony_ci if (ctlr->state != CPDMA_STATE_ACTIVE) { 6518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 6528c2ecf20Sopenharmony_ci return -EINVAL; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { 6568c2ecf20Sopenharmony_ci if (ctlr->channels[i]) 6578c2ecf20Sopenharmony_ci cpdma_chan_int_ctrl(ctlr->channels[i], enable); 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_civoid cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, value); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ciu32 cpdma_ctrl_rxchs_state(struct cpdma_ctlr *ctlr) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci return dma_reg_read(ctlr, CPDMA_RXINTSTATMASKED); 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ciu32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci return dma_reg_read(ctlr, CPDMA_TXINTSTATMASKED); 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic void cpdma_chan_set_descs(struct cpdma_ctlr *ctlr, 6808c2ecf20Sopenharmony_ci int rx, int desc_num, 6818c2ecf20Sopenharmony_ci int per_ch_desc) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci struct cpdma_chan *chan, *most_chan = NULL; 6848c2ecf20Sopenharmony_ci int desc_cnt = desc_num; 6858c2ecf20Sopenharmony_ci int most_dnum = 0; 6868c2ecf20Sopenharmony_ci int min, max, i; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (!desc_num) 6898c2ecf20Sopenharmony_ci return; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (rx) { 6928c2ecf20Sopenharmony_ci min = rx_chan_num(0); 6938c2ecf20Sopenharmony_ci max = rx_chan_num(CPDMA_MAX_CHANNELS); 6948c2ecf20Sopenharmony_ci } else { 6958c2ecf20Sopenharmony_ci min = tx_chan_num(0); 6968c2ecf20Sopenharmony_ci max = tx_chan_num(CPDMA_MAX_CHANNELS); 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci for (i = min; i < max; i++) { 7008c2ecf20Sopenharmony_ci chan = ctlr->channels[i]; 7018c2ecf20Sopenharmony_ci if (!chan) 7028c2ecf20Sopenharmony_ci continue; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (chan->weight) 7058c2ecf20Sopenharmony_ci chan->desc_num = (chan->weight * desc_num) / 100; 7068c2ecf20Sopenharmony_ci else 7078c2ecf20Sopenharmony_ci chan->desc_num = per_ch_desc; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci desc_cnt -= chan->desc_num; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (most_dnum < chan->desc_num) { 7128c2ecf20Sopenharmony_ci most_dnum = chan->desc_num; 7138c2ecf20Sopenharmony_ci most_chan = chan; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci /* use remains */ 7178c2ecf20Sopenharmony_ci if (most_chan) 7188c2ecf20Sopenharmony_ci most_chan->desc_num += desc_cnt; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/* 7228c2ecf20Sopenharmony_ci * cpdma_chan_split_pool - Splits ctrl pool between all channels. 7238c2ecf20Sopenharmony_ci * Has to be called under ctlr lock 7248c2ecf20Sopenharmony_ci */ 7258c2ecf20Sopenharmony_cistatic int cpdma_chan_split_pool(struct cpdma_ctlr *ctlr) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci int tx_per_ch_desc = 0, rx_per_ch_desc = 0; 7288c2ecf20Sopenharmony_ci int free_rx_num = 0, free_tx_num = 0; 7298c2ecf20Sopenharmony_ci int rx_weight = 0, tx_weight = 0; 7308c2ecf20Sopenharmony_ci int tx_desc_num, rx_desc_num; 7318c2ecf20Sopenharmony_ci struct cpdma_chan *chan; 7328c2ecf20Sopenharmony_ci int i; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (!ctlr->chan_num) 7358c2ecf20Sopenharmony_ci return 0; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { 7388c2ecf20Sopenharmony_ci chan = ctlr->channels[i]; 7398c2ecf20Sopenharmony_ci if (!chan) 7408c2ecf20Sopenharmony_ci continue; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (is_rx_chan(chan)) { 7438c2ecf20Sopenharmony_ci if (!chan->weight) 7448c2ecf20Sopenharmony_ci free_rx_num++; 7458c2ecf20Sopenharmony_ci rx_weight += chan->weight; 7468c2ecf20Sopenharmony_ci } else { 7478c2ecf20Sopenharmony_ci if (!chan->weight) 7488c2ecf20Sopenharmony_ci free_tx_num++; 7498c2ecf20Sopenharmony_ci tx_weight += chan->weight; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (rx_weight > 100 || tx_weight > 100) 7548c2ecf20Sopenharmony_ci return -EINVAL; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci tx_desc_num = ctlr->num_tx_desc; 7578c2ecf20Sopenharmony_ci rx_desc_num = ctlr->num_rx_desc; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (free_tx_num) { 7608c2ecf20Sopenharmony_ci tx_per_ch_desc = tx_desc_num - (tx_weight * tx_desc_num) / 100; 7618c2ecf20Sopenharmony_ci tx_per_ch_desc /= free_tx_num; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci if (free_rx_num) { 7648c2ecf20Sopenharmony_ci rx_per_ch_desc = rx_desc_num - (rx_weight * rx_desc_num) / 100; 7658c2ecf20Sopenharmony_ci rx_per_ch_desc /= free_rx_num; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci cpdma_chan_set_descs(ctlr, 0, tx_desc_num, tx_per_ch_desc); 7698c2ecf20Sopenharmony_ci cpdma_chan_set_descs(ctlr, 1, rx_desc_num, rx_per_ch_desc); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci return 0; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci/* cpdma_chan_set_weight - set weight of a channel in percentage. 7768c2ecf20Sopenharmony_ci * Tx and Rx channels have separate weights. That is 100% for RX 7778c2ecf20Sopenharmony_ci * and 100% for Tx. The weight is used to split cpdma resources 7788c2ecf20Sopenharmony_ci * in correct proportion required by the channels, including number 7798c2ecf20Sopenharmony_ci * of descriptors. The channel rate is not enough to know the 7808c2ecf20Sopenharmony_ci * weight of a channel as the maximum rate of an interface is needed. 7818c2ecf20Sopenharmony_ci * If weight = 0, then channel uses rest of descriptors leaved by 7828c2ecf20Sopenharmony_ci * weighted channels. 7838c2ecf20Sopenharmony_ci */ 7848c2ecf20Sopenharmony_ciint cpdma_chan_set_weight(struct cpdma_chan *ch, int weight) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = ch->ctlr; 7878c2ecf20Sopenharmony_ci unsigned long flags, ch_flags; 7888c2ecf20Sopenharmony_ci int ret; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 7918c2ecf20Sopenharmony_ci spin_lock_irqsave(&ch->lock, ch_flags); 7928c2ecf20Sopenharmony_ci if (ch->weight == weight) { 7938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, ch_flags); 7948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 7958c2ecf20Sopenharmony_ci return 0; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci ch->weight = weight; 7988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, ch_flags); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* re-split pool using new channel weight */ 8018c2ecf20Sopenharmony_ci ret = cpdma_chan_split_pool(ctlr); 8028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 8038c2ecf20Sopenharmony_ci return ret; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci/* cpdma_chan_get_min_rate - get minimum allowed rate for channel 8078c2ecf20Sopenharmony_ci * Should be called before cpdma_chan_set_rate. 8088c2ecf20Sopenharmony_ci * Returns min rate in Kb/s 8098c2ecf20Sopenharmony_ci */ 8108c2ecf20Sopenharmony_ciu32 cpdma_chan_get_min_rate(struct cpdma_ctlr *ctlr) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci unsigned int divident, divisor; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci divident = ctlr->params.bus_freq_mhz * 32 * 1000; 8158c2ecf20Sopenharmony_ci divisor = 1 + CPDMA_MAX_RLIM_CNT; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return DIV_ROUND_UP(divident, divisor); 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci/* cpdma_chan_set_rate - limits bandwidth for transmit channel. 8218c2ecf20Sopenharmony_ci * The bandwidth * limited channels have to be in order beginning from lowest. 8228c2ecf20Sopenharmony_ci * ch - transmit channel the bandwidth is configured for 8238c2ecf20Sopenharmony_ci * rate - bandwidth in Kb/s, if 0 - then off shaper 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_ciint cpdma_chan_set_rate(struct cpdma_chan *ch, u32 rate) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci unsigned long flags, ch_flags; 8288c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr; 8298c2ecf20Sopenharmony_ci int ret, prio_mode; 8308c2ecf20Sopenharmony_ci u32 rmask; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (!ch || !is_tx_chan(ch)) 8338c2ecf20Sopenharmony_ci return -EINVAL; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (ch->rate == rate) 8368c2ecf20Sopenharmony_ci return rate; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci ctlr = ch->ctlr; 8398c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 8408c2ecf20Sopenharmony_ci spin_lock_irqsave(&ch->lock, ch_flags); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci ret = cpdma_chan_fit_rate(ch, rate, &rmask, &prio_mode); 8438c2ecf20Sopenharmony_ci if (ret) 8448c2ecf20Sopenharmony_ci goto err; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci ret = cpdma_chan_set_factors(ctlr, ch); 8478c2ecf20Sopenharmony_ci if (ret) 8488c2ecf20Sopenharmony_ci goto err; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, ch_flags); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci /* on shapers */ 8538c2ecf20Sopenharmony_ci _cpdma_control_set(ctlr, CPDMA_TX_RLIM, rmask); 8548c2ecf20Sopenharmony_ci _cpdma_control_set(ctlr, CPDMA_TX_PRIO_FIXED, prio_mode); 8558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 8568c2ecf20Sopenharmony_ci return ret; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cierr: 8598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, ch_flags); 8608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 8618c2ecf20Sopenharmony_ci return ret; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ciu32 cpdma_chan_get_rate(struct cpdma_chan *ch) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci unsigned long flags; 8678c2ecf20Sopenharmony_ci u32 rate; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci spin_lock_irqsave(&ch->lock, flags); 8708c2ecf20Sopenharmony_ci rate = ch->rate; 8718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, flags); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci return rate; 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistruct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, 8778c2ecf20Sopenharmony_ci cpdma_handler_fn handler, int rx_type) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci int offset = chan_num * 4; 8808c2ecf20Sopenharmony_ci struct cpdma_chan *chan; 8818c2ecf20Sopenharmony_ci unsigned long flags; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci chan_num = rx_type ? rx_chan_num(chan_num) : tx_chan_num(chan_num); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci if (__chan_linear(chan_num) >= ctlr->num_chan) 8868c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci chan = devm_kzalloc(ctlr->dev, sizeof(*chan), GFP_KERNEL); 8898c2ecf20Sopenharmony_ci if (!chan) 8908c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 8938c2ecf20Sopenharmony_ci if (ctlr->channels[chan_num]) { 8948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 8958c2ecf20Sopenharmony_ci devm_kfree(ctlr->dev, chan); 8968c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci chan->ctlr = ctlr; 9008c2ecf20Sopenharmony_ci chan->state = CPDMA_STATE_IDLE; 9018c2ecf20Sopenharmony_ci chan->chan_num = chan_num; 9028c2ecf20Sopenharmony_ci chan->handler = handler; 9038c2ecf20Sopenharmony_ci chan->rate = 0; 9048c2ecf20Sopenharmony_ci chan->weight = 0; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (is_rx_chan(chan)) { 9078c2ecf20Sopenharmony_ci chan->hdp = ctlr->params.rxhdp + offset; 9088c2ecf20Sopenharmony_ci chan->cp = ctlr->params.rxcp + offset; 9098c2ecf20Sopenharmony_ci chan->rxfree = ctlr->params.rxfree + offset; 9108c2ecf20Sopenharmony_ci chan->int_set = CPDMA_RXINTMASKSET; 9118c2ecf20Sopenharmony_ci chan->int_clear = CPDMA_RXINTMASKCLEAR; 9128c2ecf20Sopenharmony_ci chan->td = CPDMA_RXTEARDOWN; 9138c2ecf20Sopenharmony_ci chan->dir = DMA_FROM_DEVICE; 9148c2ecf20Sopenharmony_ci } else { 9158c2ecf20Sopenharmony_ci chan->hdp = ctlr->params.txhdp + offset; 9168c2ecf20Sopenharmony_ci chan->cp = ctlr->params.txcp + offset; 9178c2ecf20Sopenharmony_ci chan->int_set = CPDMA_TXINTMASKSET; 9188c2ecf20Sopenharmony_ci chan->int_clear = CPDMA_TXINTMASKCLEAR; 9198c2ecf20Sopenharmony_ci chan->td = CPDMA_TXTEARDOWN; 9208c2ecf20Sopenharmony_ci chan->dir = DMA_TO_DEVICE; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci chan->mask = BIT(chan_linear(chan)); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci spin_lock_init(&chan->lock); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci ctlr->channels[chan_num] = chan; 9278c2ecf20Sopenharmony_ci ctlr->chan_num++; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci cpdma_chan_split_pool(ctlr); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 9328c2ecf20Sopenharmony_ci return chan; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ciint cpdma_chan_get_rx_buf_num(struct cpdma_chan *chan) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci unsigned long flags; 9388c2ecf20Sopenharmony_ci int desc_num; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 9418c2ecf20Sopenharmony_ci desc_num = chan->desc_num; 9428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci return desc_num; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ciint cpdma_chan_destroy(struct cpdma_chan *chan) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr; 9508c2ecf20Sopenharmony_ci unsigned long flags; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci if (!chan) 9538c2ecf20Sopenharmony_ci return -EINVAL; 9548c2ecf20Sopenharmony_ci ctlr = chan->ctlr; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 9578c2ecf20Sopenharmony_ci if (chan->state != CPDMA_STATE_IDLE) 9588c2ecf20Sopenharmony_ci cpdma_chan_stop(chan); 9598c2ecf20Sopenharmony_ci ctlr->channels[chan->chan_num] = NULL; 9608c2ecf20Sopenharmony_ci ctlr->chan_num--; 9618c2ecf20Sopenharmony_ci devm_kfree(ctlr->dev, chan); 9628c2ecf20Sopenharmony_ci cpdma_chan_split_pool(ctlr); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 9658c2ecf20Sopenharmony_ci return 0; 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ciint cpdma_chan_get_stats(struct cpdma_chan *chan, 9698c2ecf20Sopenharmony_ci struct cpdma_chan_stats *stats) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci unsigned long flags; 9728c2ecf20Sopenharmony_ci if (!chan) 9738c2ecf20Sopenharmony_ci return -EINVAL; 9748c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 9758c2ecf20Sopenharmony_ci memcpy(stats, &chan->stats, sizeof(*stats)); 9768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 9778c2ecf20Sopenharmony_ci return 0; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic void __cpdma_chan_submit(struct cpdma_chan *chan, 9818c2ecf20Sopenharmony_ci struct cpdma_desc __iomem *desc) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = chan->ctlr; 9848c2ecf20Sopenharmony_ci struct cpdma_desc __iomem *prev = chan->tail; 9858c2ecf20Sopenharmony_ci struct cpdma_desc_pool *pool = ctlr->pool; 9868c2ecf20Sopenharmony_ci dma_addr_t desc_dma; 9878c2ecf20Sopenharmony_ci u32 mode; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci desc_dma = desc_phys(pool, desc); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci /* simple case - idle channel */ 9928c2ecf20Sopenharmony_ci if (!chan->head) { 9938c2ecf20Sopenharmony_ci chan->stats.head_enqueue++; 9948c2ecf20Sopenharmony_ci chan->head = desc; 9958c2ecf20Sopenharmony_ci chan->tail = desc; 9968c2ecf20Sopenharmony_ci if (chan->state == CPDMA_STATE_ACTIVE) 9978c2ecf20Sopenharmony_ci chan_write(chan, hdp, desc_dma); 9988c2ecf20Sopenharmony_ci return; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* first chain the descriptor at the tail of the list */ 10028c2ecf20Sopenharmony_ci desc_write(prev, hw_next, desc_dma); 10038c2ecf20Sopenharmony_ci chan->tail = desc; 10048c2ecf20Sopenharmony_ci chan->stats.tail_enqueue++; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* next check if EOQ has been triggered already */ 10078c2ecf20Sopenharmony_ci mode = desc_read(prev, hw_mode); 10088c2ecf20Sopenharmony_ci if (((mode & (CPDMA_DESC_EOQ | CPDMA_DESC_OWNER)) == CPDMA_DESC_EOQ) && 10098c2ecf20Sopenharmony_ci (chan->state == CPDMA_STATE_ACTIVE)) { 10108c2ecf20Sopenharmony_ci desc_write(prev, hw_mode, mode & ~CPDMA_DESC_EOQ); 10118c2ecf20Sopenharmony_ci chan_write(chan, hdp, desc_dma); 10128c2ecf20Sopenharmony_ci chan->stats.misqueued++; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci} 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_cistatic int cpdma_chan_submit_si(struct submit_info *si) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci struct cpdma_chan *chan = si->chan; 10198c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = chan->ctlr; 10208c2ecf20Sopenharmony_ci int len = si->len; 10218c2ecf20Sopenharmony_ci struct cpdma_desc __iomem *desc; 10228c2ecf20Sopenharmony_ci dma_addr_t buffer; 10238c2ecf20Sopenharmony_ci u32 mode; 10248c2ecf20Sopenharmony_ci int ret; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (chan->count >= chan->desc_num) { 10278c2ecf20Sopenharmony_ci chan->stats.desc_alloc_fail++; 10288c2ecf20Sopenharmony_ci return -ENOMEM; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci desc = cpdma_desc_alloc(ctlr->pool); 10328c2ecf20Sopenharmony_ci if (!desc) { 10338c2ecf20Sopenharmony_ci chan->stats.desc_alloc_fail++; 10348c2ecf20Sopenharmony_ci return -ENOMEM; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci if (len < ctlr->params.min_packet_size) { 10388c2ecf20Sopenharmony_ci len = ctlr->params.min_packet_size; 10398c2ecf20Sopenharmony_ci chan->stats.runt_transmit_buff++; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP; 10438c2ecf20Sopenharmony_ci cpdma_desc_to_port(chan, mode, si->directed); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci if (si->data_dma) { 10468c2ecf20Sopenharmony_ci buffer = si->data_dma; 10478c2ecf20Sopenharmony_ci dma_sync_single_for_device(ctlr->dev, buffer, len, chan->dir); 10488c2ecf20Sopenharmony_ci } else { 10498c2ecf20Sopenharmony_ci buffer = dma_map_single(ctlr->dev, si->data_virt, len, chan->dir); 10508c2ecf20Sopenharmony_ci ret = dma_mapping_error(ctlr->dev, buffer); 10518c2ecf20Sopenharmony_ci if (ret) { 10528c2ecf20Sopenharmony_ci cpdma_desc_free(ctlr->pool, desc, 1); 10538c2ecf20Sopenharmony_ci return -EINVAL; 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* Relaxed IO accessors can be used here as there is read barrier 10588c2ecf20Sopenharmony_ci * at the end of write sequence. 10598c2ecf20Sopenharmony_ci */ 10608c2ecf20Sopenharmony_ci writel_relaxed(0, &desc->hw_next); 10618c2ecf20Sopenharmony_ci writel_relaxed(buffer, &desc->hw_buffer); 10628c2ecf20Sopenharmony_ci writel_relaxed(len, &desc->hw_len); 10638c2ecf20Sopenharmony_ci writel_relaxed(mode | len, &desc->hw_mode); 10648c2ecf20Sopenharmony_ci writel_relaxed((uintptr_t)si->token, &desc->sw_token); 10658c2ecf20Sopenharmony_ci writel_relaxed(buffer, &desc->sw_buffer); 10668c2ecf20Sopenharmony_ci writel_relaxed(si->data_dma ? len | CPDMA_DMA_EXT_MAP : len, 10678c2ecf20Sopenharmony_ci &desc->sw_len); 10688c2ecf20Sopenharmony_ci desc_read(desc, sw_len); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci __cpdma_chan_submit(chan, desc); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if (chan->state == CPDMA_STATE_ACTIVE && chan->rxfree) 10738c2ecf20Sopenharmony_ci chan_write(chan, rxfree, 1); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci chan->count++; 10768c2ecf20Sopenharmony_ci return 0; 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ciint cpdma_chan_idle_submit(struct cpdma_chan *chan, void *token, void *data, 10808c2ecf20Sopenharmony_ci int len, int directed) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci struct submit_info si; 10838c2ecf20Sopenharmony_ci unsigned long flags; 10848c2ecf20Sopenharmony_ci int ret; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci si.chan = chan; 10878c2ecf20Sopenharmony_ci si.token = token; 10888c2ecf20Sopenharmony_ci si.data_virt = data; 10898c2ecf20Sopenharmony_ci si.data_dma = 0; 10908c2ecf20Sopenharmony_ci si.len = len; 10918c2ecf20Sopenharmony_ci si.directed = directed; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 10948c2ecf20Sopenharmony_ci if (chan->state == CPDMA_STATE_TEARDOWN) { 10958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 10968c2ecf20Sopenharmony_ci return -EINVAL; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci ret = cpdma_chan_submit_si(&si); 11008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 11018c2ecf20Sopenharmony_ci return ret; 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ciint cpdma_chan_idle_submit_mapped(struct cpdma_chan *chan, void *token, 11058c2ecf20Sopenharmony_ci dma_addr_t data, int len, int directed) 11068c2ecf20Sopenharmony_ci{ 11078c2ecf20Sopenharmony_ci struct submit_info si; 11088c2ecf20Sopenharmony_ci unsigned long flags; 11098c2ecf20Sopenharmony_ci int ret; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci si.chan = chan; 11128c2ecf20Sopenharmony_ci si.token = token; 11138c2ecf20Sopenharmony_ci si.data_virt = NULL; 11148c2ecf20Sopenharmony_ci si.data_dma = data; 11158c2ecf20Sopenharmony_ci si.len = len; 11168c2ecf20Sopenharmony_ci si.directed = directed; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 11198c2ecf20Sopenharmony_ci if (chan->state == CPDMA_STATE_TEARDOWN) { 11208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 11218c2ecf20Sopenharmony_ci return -EINVAL; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci ret = cpdma_chan_submit_si(&si); 11258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 11268c2ecf20Sopenharmony_ci return ret; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ciint cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data, 11308c2ecf20Sopenharmony_ci int len, int directed) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct submit_info si; 11338c2ecf20Sopenharmony_ci unsigned long flags; 11348c2ecf20Sopenharmony_ci int ret; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci si.chan = chan; 11378c2ecf20Sopenharmony_ci si.token = token; 11388c2ecf20Sopenharmony_ci si.data_virt = data; 11398c2ecf20Sopenharmony_ci si.data_dma = 0; 11408c2ecf20Sopenharmony_ci si.len = len; 11418c2ecf20Sopenharmony_ci si.directed = directed; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 11448c2ecf20Sopenharmony_ci if (chan->state != CPDMA_STATE_ACTIVE) { 11458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 11468c2ecf20Sopenharmony_ci return -EINVAL; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci ret = cpdma_chan_submit_si(&si); 11508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 11518c2ecf20Sopenharmony_ci return ret; 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ciint cpdma_chan_submit_mapped(struct cpdma_chan *chan, void *token, 11558c2ecf20Sopenharmony_ci dma_addr_t data, int len, int directed) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci struct submit_info si; 11588c2ecf20Sopenharmony_ci unsigned long flags; 11598c2ecf20Sopenharmony_ci int ret; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci si.chan = chan; 11628c2ecf20Sopenharmony_ci si.token = token; 11638c2ecf20Sopenharmony_ci si.data_virt = NULL; 11648c2ecf20Sopenharmony_ci si.data_dma = data; 11658c2ecf20Sopenharmony_ci si.len = len; 11668c2ecf20Sopenharmony_ci si.directed = directed; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 11698c2ecf20Sopenharmony_ci if (chan->state != CPDMA_STATE_ACTIVE) { 11708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 11718c2ecf20Sopenharmony_ci return -EINVAL; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci ret = cpdma_chan_submit_si(&si); 11758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 11768c2ecf20Sopenharmony_ci return ret; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cibool cpdma_check_free_tx_desc(struct cpdma_chan *chan) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = chan->ctlr; 11828c2ecf20Sopenharmony_ci struct cpdma_desc_pool *pool = ctlr->pool; 11838c2ecf20Sopenharmony_ci bool free_tx_desc; 11848c2ecf20Sopenharmony_ci unsigned long flags; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 11878c2ecf20Sopenharmony_ci free_tx_desc = (chan->count < chan->desc_num) && 11888c2ecf20Sopenharmony_ci gen_pool_avail(pool->gen_pool); 11898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 11908c2ecf20Sopenharmony_ci return free_tx_desc; 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic void __cpdma_chan_free(struct cpdma_chan *chan, 11948c2ecf20Sopenharmony_ci struct cpdma_desc __iomem *desc, 11958c2ecf20Sopenharmony_ci int outlen, int status) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = chan->ctlr; 11988c2ecf20Sopenharmony_ci struct cpdma_desc_pool *pool = ctlr->pool; 11998c2ecf20Sopenharmony_ci dma_addr_t buff_dma; 12008c2ecf20Sopenharmony_ci int origlen; 12018c2ecf20Sopenharmony_ci uintptr_t token; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci token = desc_read(desc, sw_token); 12048c2ecf20Sopenharmony_ci origlen = desc_read(desc, sw_len); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci buff_dma = desc_read(desc, sw_buffer); 12078c2ecf20Sopenharmony_ci if (origlen & CPDMA_DMA_EXT_MAP) { 12088c2ecf20Sopenharmony_ci origlen &= ~CPDMA_DMA_EXT_MAP; 12098c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(ctlr->dev, buff_dma, origlen, 12108c2ecf20Sopenharmony_ci chan->dir); 12118c2ecf20Sopenharmony_ci } else { 12128c2ecf20Sopenharmony_ci dma_unmap_single(ctlr->dev, buff_dma, origlen, chan->dir); 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci cpdma_desc_free(pool, desc, 1); 12168c2ecf20Sopenharmony_ci (*chan->handler)((void *)token, outlen, status); 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic int __cpdma_chan_process(struct cpdma_chan *chan) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = chan->ctlr; 12228c2ecf20Sopenharmony_ci struct cpdma_desc __iomem *desc; 12238c2ecf20Sopenharmony_ci int status, outlen; 12248c2ecf20Sopenharmony_ci int cb_status = 0; 12258c2ecf20Sopenharmony_ci struct cpdma_desc_pool *pool = ctlr->pool; 12268c2ecf20Sopenharmony_ci dma_addr_t desc_dma; 12278c2ecf20Sopenharmony_ci unsigned long flags; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci desc = chan->head; 12328c2ecf20Sopenharmony_ci if (!desc) { 12338c2ecf20Sopenharmony_ci chan->stats.empty_dequeue++; 12348c2ecf20Sopenharmony_ci status = -ENOENT; 12358c2ecf20Sopenharmony_ci goto unlock_ret; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci desc_dma = desc_phys(pool, desc); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci status = desc_read(desc, hw_mode); 12408c2ecf20Sopenharmony_ci outlen = status & 0x7ff; 12418c2ecf20Sopenharmony_ci if (status & CPDMA_DESC_OWNER) { 12428c2ecf20Sopenharmony_ci chan->stats.busy_dequeue++; 12438c2ecf20Sopenharmony_ci status = -EBUSY; 12448c2ecf20Sopenharmony_ci goto unlock_ret; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (status & CPDMA_DESC_PASS_CRC) 12488c2ecf20Sopenharmony_ci outlen -= CPDMA_DESC_CRC_LEN; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci status = status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE | 12518c2ecf20Sopenharmony_ci CPDMA_DESC_PORT_MASK | CPDMA_RX_VLAN_ENCAP); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci chan->head = desc_from_phys(pool, desc_read(desc, hw_next)); 12548c2ecf20Sopenharmony_ci chan_write(chan, cp, desc_dma); 12558c2ecf20Sopenharmony_ci chan->count--; 12568c2ecf20Sopenharmony_ci chan->stats.good_dequeue++; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if ((status & CPDMA_DESC_EOQ) && chan->head) { 12598c2ecf20Sopenharmony_ci chan->stats.requeue++; 12608c2ecf20Sopenharmony_ci chan_write(chan, hdp, desc_phys(pool, chan->head)); 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 12648c2ecf20Sopenharmony_ci if (unlikely(status & CPDMA_DESC_TD_COMPLETE)) 12658c2ecf20Sopenharmony_ci cb_status = -ENOSYS; 12668c2ecf20Sopenharmony_ci else 12678c2ecf20Sopenharmony_ci cb_status = status; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci __cpdma_chan_free(chan, desc, outlen, cb_status); 12708c2ecf20Sopenharmony_ci return status; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ciunlock_ret: 12738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 12748c2ecf20Sopenharmony_ci return status; 12758c2ecf20Sopenharmony_ci} 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ciint cpdma_chan_process(struct cpdma_chan *chan, int quota) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci int used = 0, ret = 0; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (chan->state != CPDMA_STATE_ACTIVE) 12828c2ecf20Sopenharmony_ci return -EINVAL; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci while (used < quota) { 12858c2ecf20Sopenharmony_ci ret = __cpdma_chan_process(chan); 12868c2ecf20Sopenharmony_ci if (ret < 0) 12878c2ecf20Sopenharmony_ci break; 12888c2ecf20Sopenharmony_ci used++; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci return used; 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ciint cpdma_chan_start(struct cpdma_chan *chan) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = chan->ctlr; 12968c2ecf20Sopenharmony_ci unsigned long flags; 12978c2ecf20Sopenharmony_ci int ret; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 13008c2ecf20Sopenharmony_ci ret = cpdma_chan_set_chan_shaper(chan); 13018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 13028c2ecf20Sopenharmony_ci if (ret) 13038c2ecf20Sopenharmony_ci return ret; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci ret = cpdma_chan_on(chan); 13068c2ecf20Sopenharmony_ci if (ret) 13078c2ecf20Sopenharmony_ci return ret; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci return 0; 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ciint cpdma_chan_stop(struct cpdma_chan *chan) 13138c2ecf20Sopenharmony_ci{ 13148c2ecf20Sopenharmony_ci struct cpdma_ctlr *ctlr = chan->ctlr; 13158c2ecf20Sopenharmony_ci struct cpdma_desc_pool *pool = ctlr->pool; 13168c2ecf20Sopenharmony_ci unsigned long flags; 13178c2ecf20Sopenharmony_ci int ret; 13188c2ecf20Sopenharmony_ci unsigned timeout; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 13218c2ecf20Sopenharmony_ci if (chan->state == CPDMA_STATE_TEARDOWN) { 13228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 13238c2ecf20Sopenharmony_ci return -EINVAL; 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci chan->state = CPDMA_STATE_TEARDOWN; 13278c2ecf20Sopenharmony_ci dma_reg_write(ctlr, chan->int_clear, chan->mask); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* trigger teardown */ 13308c2ecf20Sopenharmony_ci dma_reg_write(ctlr, chan->td, chan_linear(chan)); 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci /* wait for teardown complete */ 13338c2ecf20Sopenharmony_ci timeout = 100 * 100; /* 100 ms */ 13348c2ecf20Sopenharmony_ci while (timeout) { 13358c2ecf20Sopenharmony_ci u32 cp = chan_read(chan, cp); 13368c2ecf20Sopenharmony_ci if ((cp & CPDMA_TEARDOWN_VALUE) == CPDMA_TEARDOWN_VALUE) 13378c2ecf20Sopenharmony_ci break; 13388c2ecf20Sopenharmony_ci udelay(10); 13398c2ecf20Sopenharmony_ci timeout--; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci WARN_ON(!timeout); 13428c2ecf20Sopenharmony_ci chan_write(chan, cp, CPDMA_TEARDOWN_VALUE); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci /* handle completed packets */ 13458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 13468c2ecf20Sopenharmony_ci do { 13478c2ecf20Sopenharmony_ci ret = __cpdma_chan_process(chan); 13488c2ecf20Sopenharmony_ci if (ret < 0) 13498c2ecf20Sopenharmony_ci break; 13508c2ecf20Sopenharmony_ci } while ((ret & CPDMA_DESC_TD_COMPLETE) == 0); 13518c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci /* remaining packets haven't been tx/rx'ed, clean them up */ 13548c2ecf20Sopenharmony_ci while (chan->head) { 13558c2ecf20Sopenharmony_ci struct cpdma_desc __iomem *desc = chan->head; 13568c2ecf20Sopenharmony_ci dma_addr_t next_dma; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci next_dma = desc_read(desc, hw_next); 13598c2ecf20Sopenharmony_ci chan->head = desc_from_phys(pool, next_dma); 13608c2ecf20Sopenharmony_ci chan->count--; 13618c2ecf20Sopenharmony_ci chan->stats.teardown_dequeue++; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci /* issue callback without locks held */ 13648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 13658c2ecf20Sopenharmony_ci __cpdma_chan_free(chan, desc, 0, -ENOSYS); 13668c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 13678c2ecf20Sopenharmony_ci } 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci chan->state = CPDMA_STATE_IDLE; 13708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 13718c2ecf20Sopenharmony_ci return 0; 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ciint cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable) 13758c2ecf20Sopenharmony_ci{ 13768c2ecf20Sopenharmony_ci unsigned long flags; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 13798c2ecf20Sopenharmony_ci if (chan->state != CPDMA_STATE_ACTIVE) { 13808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 13818c2ecf20Sopenharmony_ci return -EINVAL; 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci dma_reg_write(chan->ctlr, enable ? chan->int_set : chan->int_clear, 13858c2ecf20Sopenharmony_ci chan->mask); 13868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci return 0; 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ciint cpdma_control_get(struct cpdma_ctlr *ctlr, int control) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci unsigned long flags; 13948c2ecf20Sopenharmony_ci int ret; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 13978c2ecf20Sopenharmony_ci ret = _cpdma_control_get(ctlr, control); 13988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci return ret; 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ciint cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci unsigned long flags; 14068c2ecf20Sopenharmony_ci int ret; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 14098c2ecf20Sopenharmony_ci ret = _cpdma_control_set(ctlr, control, value); 14108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci return ret; 14138c2ecf20Sopenharmony_ci} 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ciint cpdma_get_num_rx_descs(struct cpdma_ctlr *ctlr) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci return ctlr->num_rx_desc; 14188c2ecf20Sopenharmony_ci} 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ciint cpdma_get_num_tx_descs(struct cpdma_ctlr *ctlr) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci return ctlr->num_tx_desc; 14238c2ecf20Sopenharmony_ci} 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ciint cpdma_set_num_rx_descs(struct cpdma_ctlr *ctlr, int num_rx_desc) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci unsigned long flags; 14288c2ecf20Sopenharmony_ci int temp, ret; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctlr->lock, flags); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci temp = ctlr->num_rx_desc; 14338c2ecf20Sopenharmony_ci ctlr->num_rx_desc = num_rx_desc; 14348c2ecf20Sopenharmony_ci ctlr->num_tx_desc = ctlr->pool->num_desc - ctlr->num_rx_desc; 14358c2ecf20Sopenharmony_ci ret = cpdma_chan_split_pool(ctlr); 14368c2ecf20Sopenharmony_ci if (ret) { 14378c2ecf20Sopenharmony_ci ctlr->num_rx_desc = temp; 14388c2ecf20Sopenharmony_ci ctlr->num_tx_desc = ctlr->pool->num_desc - ctlr->num_rx_desc; 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctlr->lock, flags); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci return ret; 14448c2ecf20Sopenharmony_ci} 1445