18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci/* 68c2ecf20Sopenharmony_ci * QCOM BAM DMA engine driver 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * QCOM BAM DMA blocks are distributed amongst a number of the on-chip 98c2ecf20Sopenharmony_ci * peripherals on the MSM 8x74. The configuration of the channels are dependent 108c2ecf20Sopenharmony_ci * on the way they are hard wired to that specific peripheral. The peripheral 118c2ecf20Sopenharmony_ci * device tree entries specify the configuration of each channel. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The DMA controller requires the use of external memory for storage of the 148c2ecf20Sopenharmony_ci * hardware descriptors for each channel. The descriptor FIFO is accessed as a 158c2ecf20Sopenharmony_ci * circular buffer and operations are managed according to the offset within the 168c2ecf20Sopenharmony_ci * FIFO. After pipe/channel reset, all of the pipe registers and internal state 178c2ecf20Sopenharmony_ci * are back to defaults. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * During DMA operations, we write descriptors to the FIFO, being careful to 208c2ecf20Sopenharmony_ci * handle wrapping and then write the last FIFO offset to that channel's 218c2ecf20Sopenharmony_ci * P_EVNT_REG register to kick off the transaction. The P_SW_OFSTS register 228c2ecf20Sopenharmony_ci * indicates the current FIFO offset that is being processed, so there is some 238c2ecf20Sopenharmony_ci * indication of where the hardware is currently working. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/io.h> 288c2ecf20Sopenharmony_ci#include <linux/init.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci#include <linux/module.h> 318c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 328c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 338c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 348c2ecf20Sopenharmony_ci#include <linux/device.h> 358c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 368c2ecf20Sopenharmony_ci#include <linux/of.h> 378c2ecf20Sopenharmony_ci#include <linux/of_address.h> 388c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 398c2ecf20Sopenharmony_ci#include <linux/of_dma.h> 408c2ecf20Sopenharmony_ci#include <linux/circ_buf.h> 418c2ecf20Sopenharmony_ci#include <linux/clk.h> 428c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 438c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include "../dmaengine.h" 468c2ecf20Sopenharmony_ci#include "../virt-dma.h" 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct bam_desc_hw { 498c2ecf20Sopenharmony_ci __le32 addr; /* Buffer physical address */ 508c2ecf20Sopenharmony_ci __le16 size; /* Buffer size in bytes */ 518c2ecf20Sopenharmony_ci __le16 flags; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define BAM_DMA_AUTOSUSPEND_DELAY 100 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define DESC_FLAG_INT BIT(15) 578c2ecf20Sopenharmony_ci#define DESC_FLAG_EOT BIT(14) 588c2ecf20Sopenharmony_ci#define DESC_FLAG_EOB BIT(13) 598c2ecf20Sopenharmony_ci#define DESC_FLAG_NWD BIT(12) 608c2ecf20Sopenharmony_ci#define DESC_FLAG_CMD BIT(11) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistruct bam_async_desc { 638c2ecf20Sopenharmony_ci struct virt_dma_desc vd; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci u32 num_desc; 668c2ecf20Sopenharmony_ci u32 xfer_len; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* transaction flags, EOT|EOB|NWD */ 698c2ecf20Sopenharmony_ci u16 flags; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci struct bam_desc_hw *curr_desc; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* list node for the desc in the bam_chan list of descriptors */ 748c2ecf20Sopenharmony_ci struct list_head desc_node; 758c2ecf20Sopenharmony_ci enum dma_transfer_direction dir; 768c2ecf20Sopenharmony_ci size_t length; 778c2ecf20Sopenharmony_ci struct bam_desc_hw desc[]; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cienum bam_reg { 818c2ecf20Sopenharmony_ci BAM_CTRL, 828c2ecf20Sopenharmony_ci BAM_REVISION, 838c2ecf20Sopenharmony_ci BAM_NUM_PIPES, 848c2ecf20Sopenharmony_ci BAM_DESC_CNT_TRSHLD, 858c2ecf20Sopenharmony_ci BAM_IRQ_SRCS, 868c2ecf20Sopenharmony_ci BAM_IRQ_SRCS_MSK, 878c2ecf20Sopenharmony_ci BAM_IRQ_SRCS_UNMASKED, 888c2ecf20Sopenharmony_ci BAM_IRQ_STTS, 898c2ecf20Sopenharmony_ci BAM_IRQ_CLR, 908c2ecf20Sopenharmony_ci BAM_IRQ_EN, 918c2ecf20Sopenharmony_ci BAM_CNFG_BITS, 928c2ecf20Sopenharmony_ci BAM_IRQ_SRCS_EE, 938c2ecf20Sopenharmony_ci BAM_IRQ_SRCS_MSK_EE, 948c2ecf20Sopenharmony_ci BAM_P_CTRL, 958c2ecf20Sopenharmony_ci BAM_P_RST, 968c2ecf20Sopenharmony_ci BAM_P_HALT, 978c2ecf20Sopenharmony_ci BAM_P_IRQ_STTS, 988c2ecf20Sopenharmony_ci BAM_P_IRQ_CLR, 998c2ecf20Sopenharmony_ci BAM_P_IRQ_EN, 1008c2ecf20Sopenharmony_ci BAM_P_EVNT_DEST_ADDR, 1018c2ecf20Sopenharmony_ci BAM_P_EVNT_REG, 1028c2ecf20Sopenharmony_ci BAM_P_SW_OFSTS, 1038c2ecf20Sopenharmony_ci BAM_P_DATA_FIFO_ADDR, 1048c2ecf20Sopenharmony_ci BAM_P_DESC_FIFO_ADDR, 1058c2ecf20Sopenharmony_ci BAM_P_EVNT_GEN_TRSHLD, 1068c2ecf20Sopenharmony_ci BAM_P_FIFO_SIZES, 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistruct reg_offset_data { 1108c2ecf20Sopenharmony_ci u32 base_offset; 1118c2ecf20Sopenharmony_ci unsigned int pipe_mult, evnt_mult, ee_mult; 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic const struct reg_offset_data bam_v1_3_reg_info[] = { 1158c2ecf20Sopenharmony_ci [BAM_CTRL] = { 0x0F80, 0x00, 0x00, 0x00 }, 1168c2ecf20Sopenharmony_ci [BAM_REVISION] = { 0x0F84, 0x00, 0x00, 0x00 }, 1178c2ecf20Sopenharmony_ci [BAM_NUM_PIPES] = { 0x0FBC, 0x00, 0x00, 0x00 }, 1188c2ecf20Sopenharmony_ci [BAM_DESC_CNT_TRSHLD] = { 0x0F88, 0x00, 0x00, 0x00 }, 1198c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS] = { 0x0F8C, 0x00, 0x00, 0x00 }, 1208c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_MSK] = { 0x0F90, 0x00, 0x00, 0x00 }, 1218c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_UNMASKED] = { 0x0FB0, 0x00, 0x00, 0x00 }, 1228c2ecf20Sopenharmony_ci [BAM_IRQ_STTS] = { 0x0F94, 0x00, 0x00, 0x00 }, 1238c2ecf20Sopenharmony_ci [BAM_IRQ_CLR] = { 0x0F98, 0x00, 0x00, 0x00 }, 1248c2ecf20Sopenharmony_ci [BAM_IRQ_EN] = { 0x0F9C, 0x00, 0x00, 0x00 }, 1258c2ecf20Sopenharmony_ci [BAM_CNFG_BITS] = { 0x0FFC, 0x00, 0x00, 0x00 }, 1268c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_EE] = { 0x1800, 0x00, 0x00, 0x80 }, 1278c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_MSK_EE] = { 0x1804, 0x00, 0x00, 0x80 }, 1288c2ecf20Sopenharmony_ci [BAM_P_CTRL] = { 0x0000, 0x80, 0x00, 0x00 }, 1298c2ecf20Sopenharmony_ci [BAM_P_RST] = { 0x0004, 0x80, 0x00, 0x00 }, 1308c2ecf20Sopenharmony_ci [BAM_P_HALT] = { 0x0008, 0x80, 0x00, 0x00 }, 1318c2ecf20Sopenharmony_ci [BAM_P_IRQ_STTS] = { 0x0010, 0x80, 0x00, 0x00 }, 1328c2ecf20Sopenharmony_ci [BAM_P_IRQ_CLR] = { 0x0014, 0x80, 0x00, 0x00 }, 1338c2ecf20Sopenharmony_ci [BAM_P_IRQ_EN] = { 0x0018, 0x80, 0x00, 0x00 }, 1348c2ecf20Sopenharmony_ci [BAM_P_EVNT_DEST_ADDR] = { 0x102C, 0x00, 0x40, 0x00 }, 1358c2ecf20Sopenharmony_ci [BAM_P_EVNT_REG] = { 0x1018, 0x00, 0x40, 0x00 }, 1368c2ecf20Sopenharmony_ci [BAM_P_SW_OFSTS] = { 0x1000, 0x00, 0x40, 0x00 }, 1378c2ecf20Sopenharmony_ci [BAM_P_DATA_FIFO_ADDR] = { 0x1024, 0x00, 0x40, 0x00 }, 1388c2ecf20Sopenharmony_ci [BAM_P_DESC_FIFO_ADDR] = { 0x101C, 0x00, 0x40, 0x00 }, 1398c2ecf20Sopenharmony_ci [BAM_P_EVNT_GEN_TRSHLD] = { 0x1028, 0x00, 0x40, 0x00 }, 1408c2ecf20Sopenharmony_ci [BAM_P_FIFO_SIZES] = { 0x1020, 0x00, 0x40, 0x00 }, 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const struct reg_offset_data bam_v1_4_reg_info[] = { 1448c2ecf20Sopenharmony_ci [BAM_CTRL] = { 0x0000, 0x00, 0x00, 0x00 }, 1458c2ecf20Sopenharmony_ci [BAM_REVISION] = { 0x0004, 0x00, 0x00, 0x00 }, 1468c2ecf20Sopenharmony_ci [BAM_NUM_PIPES] = { 0x003C, 0x00, 0x00, 0x00 }, 1478c2ecf20Sopenharmony_ci [BAM_DESC_CNT_TRSHLD] = { 0x0008, 0x00, 0x00, 0x00 }, 1488c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS] = { 0x000C, 0x00, 0x00, 0x00 }, 1498c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_MSK] = { 0x0010, 0x00, 0x00, 0x00 }, 1508c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_UNMASKED] = { 0x0030, 0x00, 0x00, 0x00 }, 1518c2ecf20Sopenharmony_ci [BAM_IRQ_STTS] = { 0x0014, 0x00, 0x00, 0x00 }, 1528c2ecf20Sopenharmony_ci [BAM_IRQ_CLR] = { 0x0018, 0x00, 0x00, 0x00 }, 1538c2ecf20Sopenharmony_ci [BAM_IRQ_EN] = { 0x001C, 0x00, 0x00, 0x00 }, 1548c2ecf20Sopenharmony_ci [BAM_CNFG_BITS] = { 0x007C, 0x00, 0x00, 0x00 }, 1558c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_EE] = { 0x0800, 0x00, 0x00, 0x80 }, 1568c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_MSK_EE] = { 0x0804, 0x00, 0x00, 0x80 }, 1578c2ecf20Sopenharmony_ci [BAM_P_CTRL] = { 0x1000, 0x1000, 0x00, 0x00 }, 1588c2ecf20Sopenharmony_ci [BAM_P_RST] = { 0x1004, 0x1000, 0x00, 0x00 }, 1598c2ecf20Sopenharmony_ci [BAM_P_HALT] = { 0x1008, 0x1000, 0x00, 0x00 }, 1608c2ecf20Sopenharmony_ci [BAM_P_IRQ_STTS] = { 0x1010, 0x1000, 0x00, 0x00 }, 1618c2ecf20Sopenharmony_ci [BAM_P_IRQ_CLR] = { 0x1014, 0x1000, 0x00, 0x00 }, 1628c2ecf20Sopenharmony_ci [BAM_P_IRQ_EN] = { 0x1018, 0x1000, 0x00, 0x00 }, 1638c2ecf20Sopenharmony_ci [BAM_P_EVNT_DEST_ADDR] = { 0x182C, 0x00, 0x1000, 0x00 }, 1648c2ecf20Sopenharmony_ci [BAM_P_EVNT_REG] = { 0x1818, 0x00, 0x1000, 0x00 }, 1658c2ecf20Sopenharmony_ci [BAM_P_SW_OFSTS] = { 0x1800, 0x00, 0x1000, 0x00 }, 1668c2ecf20Sopenharmony_ci [BAM_P_DATA_FIFO_ADDR] = { 0x1824, 0x00, 0x1000, 0x00 }, 1678c2ecf20Sopenharmony_ci [BAM_P_DESC_FIFO_ADDR] = { 0x181C, 0x00, 0x1000, 0x00 }, 1688c2ecf20Sopenharmony_ci [BAM_P_EVNT_GEN_TRSHLD] = { 0x1828, 0x00, 0x1000, 0x00 }, 1698c2ecf20Sopenharmony_ci [BAM_P_FIFO_SIZES] = { 0x1820, 0x00, 0x1000, 0x00 }, 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic const struct reg_offset_data bam_v1_7_reg_info[] = { 1738c2ecf20Sopenharmony_ci [BAM_CTRL] = { 0x00000, 0x00, 0x00, 0x00 }, 1748c2ecf20Sopenharmony_ci [BAM_REVISION] = { 0x01000, 0x00, 0x00, 0x00 }, 1758c2ecf20Sopenharmony_ci [BAM_NUM_PIPES] = { 0x01008, 0x00, 0x00, 0x00 }, 1768c2ecf20Sopenharmony_ci [BAM_DESC_CNT_TRSHLD] = { 0x00008, 0x00, 0x00, 0x00 }, 1778c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS] = { 0x03010, 0x00, 0x00, 0x00 }, 1788c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_MSK] = { 0x03014, 0x00, 0x00, 0x00 }, 1798c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_UNMASKED] = { 0x03018, 0x00, 0x00, 0x00 }, 1808c2ecf20Sopenharmony_ci [BAM_IRQ_STTS] = { 0x00014, 0x00, 0x00, 0x00 }, 1818c2ecf20Sopenharmony_ci [BAM_IRQ_CLR] = { 0x00018, 0x00, 0x00, 0x00 }, 1828c2ecf20Sopenharmony_ci [BAM_IRQ_EN] = { 0x0001C, 0x00, 0x00, 0x00 }, 1838c2ecf20Sopenharmony_ci [BAM_CNFG_BITS] = { 0x0007C, 0x00, 0x00, 0x00 }, 1848c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_EE] = { 0x03000, 0x00, 0x00, 0x1000 }, 1858c2ecf20Sopenharmony_ci [BAM_IRQ_SRCS_MSK_EE] = { 0x03004, 0x00, 0x00, 0x1000 }, 1868c2ecf20Sopenharmony_ci [BAM_P_CTRL] = { 0x13000, 0x1000, 0x00, 0x00 }, 1878c2ecf20Sopenharmony_ci [BAM_P_RST] = { 0x13004, 0x1000, 0x00, 0x00 }, 1888c2ecf20Sopenharmony_ci [BAM_P_HALT] = { 0x13008, 0x1000, 0x00, 0x00 }, 1898c2ecf20Sopenharmony_ci [BAM_P_IRQ_STTS] = { 0x13010, 0x1000, 0x00, 0x00 }, 1908c2ecf20Sopenharmony_ci [BAM_P_IRQ_CLR] = { 0x13014, 0x1000, 0x00, 0x00 }, 1918c2ecf20Sopenharmony_ci [BAM_P_IRQ_EN] = { 0x13018, 0x1000, 0x00, 0x00 }, 1928c2ecf20Sopenharmony_ci [BAM_P_EVNT_DEST_ADDR] = { 0x1382C, 0x00, 0x1000, 0x00 }, 1938c2ecf20Sopenharmony_ci [BAM_P_EVNT_REG] = { 0x13818, 0x00, 0x1000, 0x00 }, 1948c2ecf20Sopenharmony_ci [BAM_P_SW_OFSTS] = { 0x13800, 0x00, 0x1000, 0x00 }, 1958c2ecf20Sopenharmony_ci [BAM_P_DATA_FIFO_ADDR] = { 0x13824, 0x00, 0x1000, 0x00 }, 1968c2ecf20Sopenharmony_ci [BAM_P_DESC_FIFO_ADDR] = { 0x1381C, 0x00, 0x1000, 0x00 }, 1978c2ecf20Sopenharmony_ci [BAM_P_EVNT_GEN_TRSHLD] = { 0x13828, 0x00, 0x1000, 0x00 }, 1988c2ecf20Sopenharmony_ci [BAM_P_FIFO_SIZES] = { 0x13820, 0x00, 0x1000, 0x00 }, 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* BAM CTRL */ 2028c2ecf20Sopenharmony_ci#define BAM_SW_RST BIT(0) 2038c2ecf20Sopenharmony_ci#define BAM_EN BIT(1) 2048c2ecf20Sopenharmony_ci#define BAM_EN_ACCUM BIT(4) 2058c2ecf20Sopenharmony_ci#define BAM_TESTBUS_SEL_SHIFT 5 2068c2ecf20Sopenharmony_ci#define BAM_TESTBUS_SEL_MASK 0x3F 2078c2ecf20Sopenharmony_ci#define BAM_DESC_CACHE_SEL_SHIFT 13 2088c2ecf20Sopenharmony_ci#define BAM_DESC_CACHE_SEL_MASK 0x3 2098c2ecf20Sopenharmony_ci#define BAM_CACHED_DESC_STORE BIT(15) 2108c2ecf20Sopenharmony_ci#define IBC_DISABLE BIT(16) 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* BAM REVISION */ 2138c2ecf20Sopenharmony_ci#define REVISION_SHIFT 0 2148c2ecf20Sopenharmony_ci#define REVISION_MASK 0xFF 2158c2ecf20Sopenharmony_ci#define NUM_EES_SHIFT 8 2168c2ecf20Sopenharmony_ci#define NUM_EES_MASK 0xF 2178c2ecf20Sopenharmony_ci#define CE_BUFFER_SIZE BIT(13) 2188c2ecf20Sopenharmony_ci#define AXI_ACTIVE BIT(14) 2198c2ecf20Sopenharmony_ci#define USE_VMIDMT BIT(15) 2208c2ecf20Sopenharmony_ci#define SECURED BIT(16) 2218c2ecf20Sopenharmony_ci#define BAM_HAS_NO_BYPASS BIT(17) 2228c2ecf20Sopenharmony_ci#define HIGH_FREQUENCY_BAM BIT(18) 2238c2ecf20Sopenharmony_ci#define INACTIV_TMRS_EXST BIT(19) 2248c2ecf20Sopenharmony_ci#define NUM_INACTIV_TMRS BIT(20) 2258c2ecf20Sopenharmony_ci#define DESC_CACHE_DEPTH_SHIFT 21 2268c2ecf20Sopenharmony_ci#define DESC_CACHE_DEPTH_1 (0 << DESC_CACHE_DEPTH_SHIFT) 2278c2ecf20Sopenharmony_ci#define DESC_CACHE_DEPTH_2 (1 << DESC_CACHE_DEPTH_SHIFT) 2288c2ecf20Sopenharmony_ci#define DESC_CACHE_DEPTH_3 (2 << DESC_CACHE_DEPTH_SHIFT) 2298c2ecf20Sopenharmony_ci#define DESC_CACHE_DEPTH_4 (3 << DESC_CACHE_DEPTH_SHIFT) 2308c2ecf20Sopenharmony_ci#define CMD_DESC_EN BIT(23) 2318c2ecf20Sopenharmony_ci#define INACTIV_TMR_BASE_SHIFT 24 2328c2ecf20Sopenharmony_ci#define INACTIV_TMR_BASE_MASK 0xFF 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/* BAM NUM PIPES */ 2358c2ecf20Sopenharmony_ci#define BAM_NUM_PIPES_SHIFT 0 2368c2ecf20Sopenharmony_ci#define BAM_NUM_PIPES_MASK 0xFF 2378c2ecf20Sopenharmony_ci#define PERIPH_NON_PIPE_GRP_SHIFT 16 2388c2ecf20Sopenharmony_ci#define PERIPH_NON_PIP_GRP_MASK 0xFF 2398c2ecf20Sopenharmony_ci#define BAM_NON_PIPE_GRP_SHIFT 24 2408c2ecf20Sopenharmony_ci#define BAM_NON_PIPE_GRP_MASK 0xFF 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* BAM CNFG BITS */ 2438c2ecf20Sopenharmony_ci#define BAM_PIPE_CNFG BIT(2) 2448c2ecf20Sopenharmony_ci#define BAM_FULL_PIPE BIT(11) 2458c2ecf20Sopenharmony_ci#define BAM_NO_EXT_P_RST BIT(12) 2468c2ecf20Sopenharmony_ci#define BAM_IBC_DISABLE BIT(13) 2478c2ecf20Sopenharmony_ci#define BAM_SB_CLK_REQ BIT(14) 2488c2ecf20Sopenharmony_ci#define BAM_PSM_CSW_REQ BIT(15) 2498c2ecf20Sopenharmony_ci#define BAM_PSM_P_RES BIT(16) 2508c2ecf20Sopenharmony_ci#define BAM_AU_P_RES BIT(17) 2518c2ecf20Sopenharmony_ci#define BAM_SI_P_RES BIT(18) 2528c2ecf20Sopenharmony_ci#define BAM_WB_P_RES BIT(19) 2538c2ecf20Sopenharmony_ci#define BAM_WB_BLK_CSW BIT(20) 2548c2ecf20Sopenharmony_ci#define BAM_WB_CSW_ACK_IDL BIT(21) 2558c2ecf20Sopenharmony_ci#define BAM_WB_RETR_SVPNT BIT(22) 2568c2ecf20Sopenharmony_ci#define BAM_WB_DSC_AVL_P_RST BIT(23) 2578c2ecf20Sopenharmony_ci#define BAM_REG_P_EN BIT(24) 2588c2ecf20Sopenharmony_ci#define BAM_PSM_P_HD_DATA BIT(25) 2598c2ecf20Sopenharmony_ci#define BAM_AU_ACCUMED BIT(26) 2608c2ecf20Sopenharmony_ci#define BAM_CMD_ENABLE BIT(27) 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci#define BAM_CNFG_BITS_DEFAULT (BAM_PIPE_CNFG | \ 2638c2ecf20Sopenharmony_ci BAM_NO_EXT_P_RST | \ 2648c2ecf20Sopenharmony_ci BAM_IBC_DISABLE | \ 2658c2ecf20Sopenharmony_ci BAM_SB_CLK_REQ | \ 2668c2ecf20Sopenharmony_ci BAM_PSM_CSW_REQ | \ 2678c2ecf20Sopenharmony_ci BAM_PSM_P_RES | \ 2688c2ecf20Sopenharmony_ci BAM_AU_P_RES | \ 2698c2ecf20Sopenharmony_ci BAM_SI_P_RES | \ 2708c2ecf20Sopenharmony_ci BAM_WB_P_RES | \ 2718c2ecf20Sopenharmony_ci BAM_WB_BLK_CSW | \ 2728c2ecf20Sopenharmony_ci BAM_WB_CSW_ACK_IDL | \ 2738c2ecf20Sopenharmony_ci BAM_WB_RETR_SVPNT | \ 2748c2ecf20Sopenharmony_ci BAM_WB_DSC_AVL_P_RST | \ 2758c2ecf20Sopenharmony_ci BAM_REG_P_EN | \ 2768c2ecf20Sopenharmony_ci BAM_PSM_P_HD_DATA | \ 2778c2ecf20Sopenharmony_ci BAM_AU_ACCUMED | \ 2788c2ecf20Sopenharmony_ci BAM_CMD_ENABLE) 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* PIPE CTRL */ 2818c2ecf20Sopenharmony_ci#define P_EN BIT(1) 2828c2ecf20Sopenharmony_ci#define P_DIRECTION BIT(3) 2838c2ecf20Sopenharmony_ci#define P_SYS_STRM BIT(4) 2848c2ecf20Sopenharmony_ci#define P_SYS_MODE BIT(5) 2858c2ecf20Sopenharmony_ci#define P_AUTO_EOB BIT(6) 2868c2ecf20Sopenharmony_ci#define P_AUTO_EOB_SEL_SHIFT 7 2878c2ecf20Sopenharmony_ci#define P_AUTO_EOB_SEL_512 (0 << P_AUTO_EOB_SEL_SHIFT) 2888c2ecf20Sopenharmony_ci#define P_AUTO_EOB_SEL_256 (1 << P_AUTO_EOB_SEL_SHIFT) 2898c2ecf20Sopenharmony_ci#define P_AUTO_EOB_SEL_128 (2 << P_AUTO_EOB_SEL_SHIFT) 2908c2ecf20Sopenharmony_ci#define P_AUTO_EOB_SEL_64 (3 << P_AUTO_EOB_SEL_SHIFT) 2918c2ecf20Sopenharmony_ci#define P_PREFETCH_LIMIT_SHIFT 9 2928c2ecf20Sopenharmony_ci#define P_PREFETCH_LIMIT_32 (0 << P_PREFETCH_LIMIT_SHIFT) 2938c2ecf20Sopenharmony_ci#define P_PREFETCH_LIMIT_16 (1 << P_PREFETCH_LIMIT_SHIFT) 2948c2ecf20Sopenharmony_ci#define P_PREFETCH_LIMIT_4 (2 << P_PREFETCH_LIMIT_SHIFT) 2958c2ecf20Sopenharmony_ci#define P_WRITE_NWD BIT(11) 2968c2ecf20Sopenharmony_ci#define P_LOCK_GROUP_SHIFT 16 2978c2ecf20Sopenharmony_ci#define P_LOCK_GROUP_MASK 0x1F 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* BAM_DESC_CNT_TRSHLD */ 3008c2ecf20Sopenharmony_ci#define CNT_TRSHLD 0xffff 3018c2ecf20Sopenharmony_ci#define DEFAULT_CNT_THRSHLD 0x4 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/* BAM_IRQ_SRCS */ 3048c2ecf20Sopenharmony_ci#define BAM_IRQ BIT(31) 3058c2ecf20Sopenharmony_ci#define P_IRQ 0x7fffffff 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* BAM_IRQ_SRCS_MSK */ 3088c2ecf20Sopenharmony_ci#define BAM_IRQ_MSK BAM_IRQ 3098c2ecf20Sopenharmony_ci#define P_IRQ_MSK P_IRQ 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/* BAM_IRQ_STTS */ 3128c2ecf20Sopenharmony_ci#define BAM_TIMER_IRQ BIT(4) 3138c2ecf20Sopenharmony_ci#define BAM_EMPTY_IRQ BIT(3) 3148c2ecf20Sopenharmony_ci#define BAM_ERROR_IRQ BIT(2) 3158c2ecf20Sopenharmony_ci#define BAM_HRESP_ERR_IRQ BIT(1) 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/* BAM_IRQ_CLR */ 3188c2ecf20Sopenharmony_ci#define BAM_TIMER_CLR BIT(4) 3198c2ecf20Sopenharmony_ci#define BAM_EMPTY_CLR BIT(3) 3208c2ecf20Sopenharmony_ci#define BAM_ERROR_CLR BIT(2) 3218c2ecf20Sopenharmony_ci#define BAM_HRESP_ERR_CLR BIT(1) 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* BAM_IRQ_EN */ 3248c2ecf20Sopenharmony_ci#define BAM_TIMER_EN BIT(4) 3258c2ecf20Sopenharmony_ci#define BAM_EMPTY_EN BIT(3) 3268c2ecf20Sopenharmony_ci#define BAM_ERROR_EN BIT(2) 3278c2ecf20Sopenharmony_ci#define BAM_HRESP_ERR_EN BIT(1) 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/* BAM_P_IRQ_EN */ 3308c2ecf20Sopenharmony_ci#define P_PRCSD_DESC_EN BIT(0) 3318c2ecf20Sopenharmony_ci#define P_TIMER_EN BIT(1) 3328c2ecf20Sopenharmony_ci#define P_WAKE_EN BIT(2) 3338c2ecf20Sopenharmony_ci#define P_OUT_OF_DESC_EN BIT(3) 3348c2ecf20Sopenharmony_ci#define P_ERR_EN BIT(4) 3358c2ecf20Sopenharmony_ci#define P_TRNSFR_END_EN BIT(5) 3368c2ecf20Sopenharmony_ci#define P_DEFAULT_IRQS_EN (P_PRCSD_DESC_EN | P_ERR_EN | P_TRNSFR_END_EN) 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/* BAM_P_SW_OFSTS */ 3398c2ecf20Sopenharmony_ci#define P_SW_OFSTS_MASK 0xffff 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci#define BAM_DESC_FIFO_SIZE SZ_32K 3428c2ecf20Sopenharmony_ci#define MAX_DESCRIPTORS (BAM_DESC_FIFO_SIZE / sizeof(struct bam_desc_hw) - 1) 3438c2ecf20Sopenharmony_ci#define BAM_FIFO_SIZE (SZ_32K - 8) 3448c2ecf20Sopenharmony_ci#define IS_BUSY(chan) (CIRC_SPACE(bchan->tail, bchan->head,\ 3458c2ecf20Sopenharmony_ci MAX_DESCRIPTORS + 1) == 0) 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistruct bam_chan { 3488c2ecf20Sopenharmony_ci struct virt_dma_chan vc; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci struct bam_device *bdev; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* configuration from device tree */ 3538c2ecf20Sopenharmony_ci u32 id; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* runtime configuration */ 3568c2ecf20Sopenharmony_ci struct dma_slave_config slave; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* fifo storage */ 3598c2ecf20Sopenharmony_ci struct bam_desc_hw *fifo_virt; 3608c2ecf20Sopenharmony_ci dma_addr_t fifo_phys; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* fifo markers */ 3638c2ecf20Sopenharmony_ci unsigned short head; /* start of active descriptor entries */ 3648c2ecf20Sopenharmony_ci unsigned short tail; /* end of active descriptor entries */ 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci unsigned int initialized; /* is the channel hw initialized? */ 3678c2ecf20Sopenharmony_ci unsigned int paused; /* is the channel paused? */ 3688c2ecf20Sopenharmony_ci unsigned int reconfigure; /* new slave config? */ 3698c2ecf20Sopenharmony_ci /* list of descriptors currently processed */ 3708c2ecf20Sopenharmony_ci struct list_head desc_list; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci struct list_head node; 3738c2ecf20Sopenharmony_ci}; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic inline struct bam_chan *to_bam_chan(struct dma_chan *common) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci return container_of(common, struct bam_chan, vc.chan); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistruct bam_device { 3818c2ecf20Sopenharmony_ci void __iomem *regs; 3828c2ecf20Sopenharmony_ci struct device *dev; 3838c2ecf20Sopenharmony_ci struct dma_device common; 3848c2ecf20Sopenharmony_ci struct bam_chan *channels; 3858c2ecf20Sopenharmony_ci u32 num_channels; 3868c2ecf20Sopenharmony_ci u32 num_ees; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* execution environment ID, from DT */ 3898c2ecf20Sopenharmony_ci u32 ee; 3908c2ecf20Sopenharmony_ci bool controlled_remotely; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci const struct reg_offset_data *layout; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci struct clk *bamclk; 3958c2ecf20Sopenharmony_ci int irq; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* dma start transaction tasklet */ 3988c2ecf20Sopenharmony_ci struct tasklet_struct task; 3998c2ecf20Sopenharmony_ci}; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci/** 4028c2ecf20Sopenharmony_ci * bam_addr - returns BAM register address 4038c2ecf20Sopenharmony_ci * @bdev: bam device 4048c2ecf20Sopenharmony_ci * @pipe: pipe instance (ignored when register doesn't have multiple instances) 4058c2ecf20Sopenharmony_ci * @reg: register enum 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_cistatic inline void __iomem *bam_addr(struct bam_device *bdev, u32 pipe, 4088c2ecf20Sopenharmony_ci enum bam_reg reg) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci const struct reg_offset_data r = bdev->layout[reg]; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return bdev->regs + r.base_offset + 4138c2ecf20Sopenharmony_ci r.pipe_mult * pipe + 4148c2ecf20Sopenharmony_ci r.evnt_mult * pipe + 4158c2ecf20Sopenharmony_ci r.ee_mult * bdev->ee; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/** 4198c2ecf20Sopenharmony_ci * bam_reset_channel - Reset individual BAM DMA channel 4208c2ecf20Sopenharmony_ci * @bchan: bam channel 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * This function resets a specific BAM channel 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_cistatic void bam_reset_channel(struct bam_chan *bchan) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct bam_device *bdev = bchan->bdev; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci lockdep_assert_held(&bchan->vc.lock); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* reset channel */ 4318c2ecf20Sopenharmony_ci writel_relaxed(1, bam_addr(bdev, bchan->id, BAM_P_RST)); 4328c2ecf20Sopenharmony_ci writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_RST)); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* don't allow cpu to reorder BAM register accesses done after this */ 4358c2ecf20Sopenharmony_ci wmb(); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* make sure hw is initialized when channel is used the first time */ 4388c2ecf20Sopenharmony_ci bchan->initialized = 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/** 4428c2ecf20Sopenharmony_ci * bam_chan_init_hw - Initialize channel hardware 4438c2ecf20Sopenharmony_ci * @bchan: bam channel 4448c2ecf20Sopenharmony_ci * @dir: DMA transfer direction 4458c2ecf20Sopenharmony_ci * 4468c2ecf20Sopenharmony_ci * This function resets and initializes the BAM channel 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_cistatic void bam_chan_init_hw(struct bam_chan *bchan, 4498c2ecf20Sopenharmony_ci enum dma_transfer_direction dir) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct bam_device *bdev = bchan->bdev; 4528c2ecf20Sopenharmony_ci u32 val; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* Reset the channel to clear internal state of the FIFO */ 4558c2ecf20Sopenharmony_ci bam_reset_channel(bchan); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * write out 8 byte aligned address. We have enough space for this 4598c2ecf20Sopenharmony_ci * because we allocated 1 more descriptor (8 bytes) than we can use 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci writel_relaxed(ALIGN(bchan->fifo_phys, sizeof(struct bam_desc_hw)), 4628c2ecf20Sopenharmony_ci bam_addr(bdev, bchan->id, BAM_P_DESC_FIFO_ADDR)); 4638c2ecf20Sopenharmony_ci writel_relaxed(BAM_FIFO_SIZE, 4648c2ecf20Sopenharmony_ci bam_addr(bdev, bchan->id, BAM_P_FIFO_SIZES)); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* enable the per pipe interrupts, enable EOT, ERR, and INT irqs */ 4678c2ecf20Sopenharmony_ci writel_relaxed(P_DEFAULT_IRQS_EN, 4688c2ecf20Sopenharmony_ci bam_addr(bdev, bchan->id, BAM_P_IRQ_EN)); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* unmask the specific pipe and EE combo */ 4718c2ecf20Sopenharmony_ci val = readl_relaxed(bam_addr(bdev, 0, BAM_IRQ_SRCS_MSK_EE)); 4728c2ecf20Sopenharmony_ci val |= BIT(bchan->id); 4738c2ecf20Sopenharmony_ci writel_relaxed(val, bam_addr(bdev, 0, BAM_IRQ_SRCS_MSK_EE)); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* don't allow cpu to reorder the channel enable done below */ 4768c2ecf20Sopenharmony_ci wmb(); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* set fixed direction and mode, then enable channel */ 4798c2ecf20Sopenharmony_ci val = P_EN | P_SYS_MODE; 4808c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 4818c2ecf20Sopenharmony_ci val |= P_DIRECTION; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci writel_relaxed(val, bam_addr(bdev, bchan->id, BAM_P_CTRL)); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci bchan->initialized = 1; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* init FIFO pointers */ 4888c2ecf20Sopenharmony_ci bchan->head = 0; 4898c2ecf20Sopenharmony_ci bchan->tail = 0; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/** 4938c2ecf20Sopenharmony_ci * bam_alloc_chan - Allocate channel resources for DMA channel. 4948c2ecf20Sopenharmony_ci * @chan: specified channel 4958c2ecf20Sopenharmony_ci * 4968c2ecf20Sopenharmony_ci * This function allocates the FIFO descriptor memory 4978c2ecf20Sopenharmony_ci */ 4988c2ecf20Sopenharmony_cistatic int bam_alloc_chan(struct dma_chan *chan) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct bam_chan *bchan = to_bam_chan(chan); 5018c2ecf20Sopenharmony_ci struct bam_device *bdev = bchan->bdev; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (bchan->fifo_virt) 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* allocate FIFO descriptor space, but only if necessary */ 5078c2ecf20Sopenharmony_ci bchan->fifo_virt = dma_alloc_wc(bdev->dev, BAM_DESC_FIFO_SIZE, 5088c2ecf20Sopenharmony_ci &bchan->fifo_phys, GFP_KERNEL); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (!bchan->fifo_virt) { 5118c2ecf20Sopenharmony_ci dev_err(bdev->dev, "Failed to allocate desc fifo\n"); 5128c2ecf20Sopenharmony_ci return -ENOMEM; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int bam_pm_runtime_get_sync(struct device *dev) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci if (pm_runtime_enabled(dev)) 5218c2ecf20Sopenharmony_ci return pm_runtime_get_sync(dev); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci/** 5278c2ecf20Sopenharmony_ci * bam_free_chan - Frees dma resources associated with specific channel 5288c2ecf20Sopenharmony_ci * @chan: specified channel 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * Free the allocated fifo descriptor memory and channel resources 5318c2ecf20Sopenharmony_ci * 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_cistatic void bam_free_chan(struct dma_chan *chan) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct bam_chan *bchan = to_bam_chan(chan); 5368c2ecf20Sopenharmony_ci struct bam_device *bdev = bchan->bdev; 5378c2ecf20Sopenharmony_ci u32 val; 5388c2ecf20Sopenharmony_ci unsigned long flags; 5398c2ecf20Sopenharmony_ci int ret; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci ret = bam_pm_runtime_get_sync(bdev->dev); 5428c2ecf20Sopenharmony_ci if (ret < 0) 5438c2ecf20Sopenharmony_ci return; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci vchan_free_chan_resources(to_virt_chan(chan)); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (!list_empty(&bchan->desc_list)) { 5488c2ecf20Sopenharmony_ci dev_err(bchan->bdev->dev, "Cannot free busy channel\n"); 5498c2ecf20Sopenharmony_ci goto err; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci spin_lock_irqsave(&bchan->vc.lock, flags); 5538c2ecf20Sopenharmony_ci bam_reset_channel(bchan); 5548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bchan->vc.lock, flags); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci dma_free_wc(bdev->dev, BAM_DESC_FIFO_SIZE, bchan->fifo_virt, 5578c2ecf20Sopenharmony_ci bchan->fifo_phys); 5588c2ecf20Sopenharmony_ci bchan->fifo_virt = NULL; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* mask irq for pipe/channel */ 5618c2ecf20Sopenharmony_ci val = readl_relaxed(bam_addr(bdev, 0, BAM_IRQ_SRCS_MSK_EE)); 5628c2ecf20Sopenharmony_ci val &= ~BIT(bchan->id); 5638c2ecf20Sopenharmony_ci writel_relaxed(val, bam_addr(bdev, 0, BAM_IRQ_SRCS_MSK_EE)); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* disable irq */ 5668c2ecf20Sopenharmony_ci writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_IRQ_EN)); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cierr: 5698c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(bdev->dev); 5708c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(bdev->dev); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/** 5748c2ecf20Sopenharmony_ci * bam_slave_config - set slave configuration for channel 5758c2ecf20Sopenharmony_ci * @chan: dma channel 5768c2ecf20Sopenharmony_ci * @cfg: slave configuration 5778c2ecf20Sopenharmony_ci * 5788c2ecf20Sopenharmony_ci * Sets slave configuration for channel 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci */ 5818c2ecf20Sopenharmony_cistatic int bam_slave_config(struct dma_chan *chan, 5828c2ecf20Sopenharmony_ci struct dma_slave_config *cfg) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct bam_chan *bchan = to_bam_chan(chan); 5858c2ecf20Sopenharmony_ci unsigned long flag; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci spin_lock_irqsave(&bchan->vc.lock, flag); 5888c2ecf20Sopenharmony_ci memcpy(&bchan->slave, cfg, sizeof(*cfg)); 5898c2ecf20Sopenharmony_ci bchan->reconfigure = 1; 5908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bchan->vc.lock, flag); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci/** 5968c2ecf20Sopenharmony_ci * bam_prep_slave_sg - Prep slave sg transaction 5978c2ecf20Sopenharmony_ci * 5988c2ecf20Sopenharmony_ci * @chan: dma channel 5998c2ecf20Sopenharmony_ci * @sgl: scatter gather list 6008c2ecf20Sopenharmony_ci * @sg_len: length of sg 6018c2ecf20Sopenharmony_ci * @direction: DMA transfer direction 6028c2ecf20Sopenharmony_ci * @flags: DMA flags 6038c2ecf20Sopenharmony_ci * @context: transfer context (unused) 6048c2ecf20Sopenharmony_ci */ 6058c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan, 6068c2ecf20Sopenharmony_ci struct scatterlist *sgl, unsigned int sg_len, 6078c2ecf20Sopenharmony_ci enum dma_transfer_direction direction, unsigned long flags, 6088c2ecf20Sopenharmony_ci void *context) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct bam_chan *bchan = to_bam_chan(chan); 6118c2ecf20Sopenharmony_ci struct bam_device *bdev = bchan->bdev; 6128c2ecf20Sopenharmony_ci struct bam_async_desc *async_desc; 6138c2ecf20Sopenharmony_ci struct scatterlist *sg; 6148c2ecf20Sopenharmony_ci u32 i; 6158c2ecf20Sopenharmony_ci struct bam_desc_hw *desc; 6168c2ecf20Sopenharmony_ci unsigned int num_alloc = 0; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (!is_slave_direction(direction)) { 6208c2ecf20Sopenharmony_ci dev_err(bdev->dev, "invalid dma direction\n"); 6218c2ecf20Sopenharmony_ci return NULL; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* calculate number of required entries */ 6258c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) 6268c2ecf20Sopenharmony_ci num_alloc += DIV_ROUND_UP(sg_dma_len(sg), BAM_FIFO_SIZE); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* allocate enough room to accomodate the number of entries */ 6298c2ecf20Sopenharmony_ci async_desc = kzalloc(struct_size(async_desc, desc, num_alloc), 6308c2ecf20Sopenharmony_ci GFP_NOWAIT); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (!async_desc) 6338c2ecf20Sopenharmony_ci goto err_out; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (flags & DMA_PREP_FENCE) 6368c2ecf20Sopenharmony_ci async_desc->flags |= DESC_FLAG_NWD; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 6398c2ecf20Sopenharmony_ci async_desc->flags |= DESC_FLAG_EOT; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci async_desc->num_desc = num_alloc; 6428c2ecf20Sopenharmony_ci async_desc->curr_desc = async_desc->desc; 6438c2ecf20Sopenharmony_ci async_desc->dir = direction; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* fill in temporary descriptors */ 6468c2ecf20Sopenharmony_ci desc = async_desc->desc; 6478c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 6488c2ecf20Sopenharmony_ci unsigned int remainder = sg_dma_len(sg); 6498c2ecf20Sopenharmony_ci unsigned int curr_offset = 0; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci do { 6528c2ecf20Sopenharmony_ci if (flags & DMA_PREP_CMD) 6538c2ecf20Sopenharmony_ci desc->flags |= cpu_to_le16(DESC_FLAG_CMD); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci desc->addr = cpu_to_le32(sg_dma_address(sg) + 6568c2ecf20Sopenharmony_ci curr_offset); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (remainder > BAM_FIFO_SIZE) { 6598c2ecf20Sopenharmony_ci desc->size = cpu_to_le16(BAM_FIFO_SIZE); 6608c2ecf20Sopenharmony_ci remainder -= BAM_FIFO_SIZE; 6618c2ecf20Sopenharmony_ci curr_offset += BAM_FIFO_SIZE; 6628c2ecf20Sopenharmony_ci } else { 6638c2ecf20Sopenharmony_ci desc->size = cpu_to_le16(remainder); 6648c2ecf20Sopenharmony_ci remainder = 0; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci async_desc->length += le16_to_cpu(desc->size); 6688c2ecf20Sopenharmony_ci desc++; 6698c2ecf20Sopenharmony_ci } while (remainder > 0); 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cierr_out: 6758c2ecf20Sopenharmony_ci kfree(async_desc); 6768c2ecf20Sopenharmony_ci return NULL; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci/** 6808c2ecf20Sopenharmony_ci * bam_dma_terminate_all - terminate all transactions on a channel 6818c2ecf20Sopenharmony_ci * @chan: bam dma channel 6828c2ecf20Sopenharmony_ci * 6838c2ecf20Sopenharmony_ci * Dequeues and frees all transactions 6848c2ecf20Sopenharmony_ci * No callbacks are done 6858c2ecf20Sopenharmony_ci * 6868c2ecf20Sopenharmony_ci */ 6878c2ecf20Sopenharmony_cistatic int bam_dma_terminate_all(struct dma_chan *chan) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct bam_chan *bchan = to_bam_chan(chan); 6908c2ecf20Sopenharmony_ci struct bam_async_desc *async_desc, *tmp; 6918c2ecf20Sopenharmony_ci unsigned long flag; 6928c2ecf20Sopenharmony_ci LIST_HEAD(head); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* remove all transactions, including active transaction */ 6958c2ecf20Sopenharmony_ci spin_lock_irqsave(&bchan->vc.lock, flag); 6968c2ecf20Sopenharmony_ci /* 6978c2ecf20Sopenharmony_ci * If we have transactions queued, then some might be committed to the 6988c2ecf20Sopenharmony_ci * hardware in the desc fifo. The only way to reset the desc fifo is 6998c2ecf20Sopenharmony_ci * to do a hardware reset (either by pipe or the entire block). 7008c2ecf20Sopenharmony_ci * bam_chan_init_hw() will trigger a pipe reset, and also reinit the 7018c2ecf20Sopenharmony_ci * pipe. If the pipe is left disabled (default state after pipe reset) 7028c2ecf20Sopenharmony_ci * and is accessed by a connected hardware engine, a fatal error in 7038c2ecf20Sopenharmony_ci * the BAM will occur. There is a small window where this could happen 7048c2ecf20Sopenharmony_ci * with bam_chan_init_hw(), but it is assumed that the caller has 7058c2ecf20Sopenharmony_ci * stopped activity on any attached hardware engine. Make sure to do 7068c2ecf20Sopenharmony_ci * this first so that the BAM hardware doesn't cause memory corruption 7078c2ecf20Sopenharmony_ci * by accessing freed resources. 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci if (!list_empty(&bchan->desc_list)) { 7108c2ecf20Sopenharmony_ci async_desc = list_first_entry(&bchan->desc_list, 7118c2ecf20Sopenharmony_ci struct bam_async_desc, desc_node); 7128c2ecf20Sopenharmony_ci bam_chan_init_hw(bchan, async_desc->dir); 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci list_for_each_entry_safe(async_desc, tmp, 7168c2ecf20Sopenharmony_ci &bchan->desc_list, desc_node) { 7178c2ecf20Sopenharmony_ci list_add(&async_desc->vd.node, &bchan->vc.desc_issued); 7188c2ecf20Sopenharmony_ci list_del(&async_desc->desc_node); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci vchan_get_all_descriptors(&bchan->vc, &head); 7228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bchan->vc.lock, flag); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci vchan_dma_desc_free_list(&bchan->vc, &head); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci/** 7308c2ecf20Sopenharmony_ci * bam_pause - Pause DMA channel 7318c2ecf20Sopenharmony_ci * @chan: dma channel 7328c2ecf20Sopenharmony_ci * 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_cistatic int bam_pause(struct dma_chan *chan) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct bam_chan *bchan = to_bam_chan(chan); 7378c2ecf20Sopenharmony_ci struct bam_device *bdev = bchan->bdev; 7388c2ecf20Sopenharmony_ci unsigned long flag; 7398c2ecf20Sopenharmony_ci int ret; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci ret = bam_pm_runtime_get_sync(bdev->dev); 7428c2ecf20Sopenharmony_ci if (ret < 0) 7438c2ecf20Sopenharmony_ci return ret; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci spin_lock_irqsave(&bchan->vc.lock, flag); 7468c2ecf20Sopenharmony_ci writel_relaxed(1, bam_addr(bdev, bchan->id, BAM_P_HALT)); 7478c2ecf20Sopenharmony_ci bchan->paused = 1; 7488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bchan->vc.lock, flag); 7498c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(bdev->dev); 7508c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(bdev->dev); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci return 0; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/** 7568c2ecf20Sopenharmony_ci * bam_resume - Resume DMA channel operations 7578c2ecf20Sopenharmony_ci * @chan: dma channel 7588c2ecf20Sopenharmony_ci * 7598c2ecf20Sopenharmony_ci */ 7608c2ecf20Sopenharmony_cistatic int bam_resume(struct dma_chan *chan) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci struct bam_chan *bchan = to_bam_chan(chan); 7638c2ecf20Sopenharmony_ci struct bam_device *bdev = bchan->bdev; 7648c2ecf20Sopenharmony_ci unsigned long flag; 7658c2ecf20Sopenharmony_ci int ret; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci ret = bam_pm_runtime_get_sync(bdev->dev); 7688c2ecf20Sopenharmony_ci if (ret < 0) 7698c2ecf20Sopenharmony_ci return ret; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci spin_lock_irqsave(&bchan->vc.lock, flag); 7728c2ecf20Sopenharmony_ci writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_HALT)); 7738c2ecf20Sopenharmony_ci bchan->paused = 0; 7748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bchan->vc.lock, flag); 7758c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(bdev->dev); 7768c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(bdev->dev); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci return 0; 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci/** 7828c2ecf20Sopenharmony_ci * process_channel_irqs - processes the channel interrupts 7838c2ecf20Sopenharmony_ci * @bdev: bam controller 7848c2ecf20Sopenharmony_ci * 7858c2ecf20Sopenharmony_ci * This function processes the channel interrupts 7868c2ecf20Sopenharmony_ci * 7878c2ecf20Sopenharmony_ci */ 7888c2ecf20Sopenharmony_cistatic u32 process_channel_irqs(struct bam_device *bdev) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci u32 i, srcs, pipe_stts, offset, avail; 7918c2ecf20Sopenharmony_ci unsigned long flags; 7928c2ecf20Sopenharmony_ci struct bam_async_desc *async_desc, *tmp; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci srcs = readl_relaxed(bam_addr(bdev, 0, BAM_IRQ_SRCS_EE)); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* return early if no pipe/channel interrupts are present */ 7978c2ecf20Sopenharmony_ci if (!(srcs & P_IRQ)) 7988c2ecf20Sopenharmony_ci return srcs; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci for (i = 0; i < bdev->num_channels; i++) { 8018c2ecf20Sopenharmony_ci struct bam_chan *bchan = &bdev->channels[i]; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci if (!(srcs & BIT(i))) 8048c2ecf20Sopenharmony_ci continue; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* clear pipe irq */ 8078c2ecf20Sopenharmony_ci pipe_stts = readl_relaxed(bam_addr(bdev, i, BAM_P_IRQ_STTS)); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci writel_relaxed(pipe_stts, bam_addr(bdev, i, BAM_P_IRQ_CLR)); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci spin_lock_irqsave(&bchan->vc.lock, flags); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci offset = readl_relaxed(bam_addr(bdev, i, BAM_P_SW_OFSTS)) & 8148c2ecf20Sopenharmony_ci P_SW_OFSTS_MASK; 8158c2ecf20Sopenharmony_ci offset /= sizeof(struct bam_desc_hw); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* Number of bytes available to read */ 8188c2ecf20Sopenharmony_ci avail = CIRC_CNT(offset, bchan->head, MAX_DESCRIPTORS + 1); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (offset < bchan->head) 8218c2ecf20Sopenharmony_ci avail--; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci list_for_each_entry_safe(async_desc, tmp, 8248c2ecf20Sopenharmony_ci &bchan->desc_list, desc_node) { 8258c2ecf20Sopenharmony_ci /* Not enough data to read */ 8268c2ecf20Sopenharmony_ci if (avail < async_desc->xfer_len) 8278c2ecf20Sopenharmony_ci break; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* manage FIFO */ 8308c2ecf20Sopenharmony_ci bchan->head += async_desc->xfer_len; 8318c2ecf20Sopenharmony_ci bchan->head %= MAX_DESCRIPTORS; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci async_desc->num_desc -= async_desc->xfer_len; 8348c2ecf20Sopenharmony_ci async_desc->curr_desc += async_desc->xfer_len; 8358c2ecf20Sopenharmony_ci avail -= async_desc->xfer_len; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci /* 8388c2ecf20Sopenharmony_ci * if complete, process cookie. Otherwise 8398c2ecf20Sopenharmony_ci * push back to front of desc_issued so that 8408c2ecf20Sopenharmony_ci * it gets restarted by the tasklet 8418c2ecf20Sopenharmony_ci */ 8428c2ecf20Sopenharmony_ci if (!async_desc->num_desc) { 8438c2ecf20Sopenharmony_ci vchan_cookie_complete(&async_desc->vd); 8448c2ecf20Sopenharmony_ci } else { 8458c2ecf20Sopenharmony_ci list_add(&async_desc->vd.node, 8468c2ecf20Sopenharmony_ci &bchan->vc.desc_issued); 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci list_del(&async_desc->desc_node); 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bchan->vc.lock, flags); 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci return srcs; 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci/** 8588c2ecf20Sopenharmony_ci * bam_dma_irq - irq handler for bam controller 8598c2ecf20Sopenharmony_ci * @irq: IRQ of interrupt 8608c2ecf20Sopenharmony_ci * @data: callback data 8618c2ecf20Sopenharmony_ci * 8628c2ecf20Sopenharmony_ci * IRQ handler for the bam controller 8638c2ecf20Sopenharmony_ci */ 8648c2ecf20Sopenharmony_cistatic irqreturn_t bam_dma_irq(int irq, void *data) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct bam_device *bdev = data; 8678c2ecf20Sopenharmony_ci u32 clr_mask = 0, srcs = 0; 8688c2ecf20Sopenharmony_ci int ret; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci srcs |= process_channel_irqs(bdev); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* kick off tasklet to start next dma transfer */ 8738c2ecf20Sopenharmony_ci if (srcs & P_IRQ) 8748c2ecf20Sopenharmony_ci tasklet_schedule(&bdev->task); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci ret = bam_pm_runtime_get_sync(bdev->dev); 8778c2ecf20Sopenharmony_ci if (ret < 0) 8788c2ecf20Sopenharmony_ci return ret; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (srcs & BAM_IRQ) { 8818c2ecf20Sopenharmony_ci clr_mask = readl_relaxed(bam_addr(bdev, 0, BAM_IRQ_STTS)); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* 8848c2ecf20Sopenharmony_ci * don't allow reorder of the various accesses to the BAM 8858c2ecf20Sopenharmony_ci * registers 8868c2ecf20Sopenharmony_ci */ 8878c2ecf20Sopenharmony_ci mb(); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci writel_relaxed(clr_mask, bam_addr(bdev, 0, BAM_IRQ_CLR)); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(bdev->dev); 8938c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(bdev->dev); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci/** 8998c2ecf20Sopenharmony_ci * bam_tx_status - returns status of transaction 9008c2ecf20Sopenharmony_ci * @chan: dma channel 9018c2ecf20Sopenharmony_ci * @cookie: transaction cookie 9028c2ecf20Sopenharmony_ci * @txstate: DMA transaction state 9038c2ecf20Sopenharmony_ci * 9048c2ecf20Sopenharmony_ci * Return status of dma transaction 9058c2ecf20Sopenharmony_ci */ 9068c2ecf20Sopenharmony_cistatic enum dma_status bam_tx_status(struct dma_chan *chan, dma_cookie_t cookie, 9078c2ecf20Sopenharmony_ci struct dma_tx_state *txstate) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct bam_chan *bchan = to_bam_chan(chan); 9108c2ecf20Sopenharmony_ci struct bam_async_desc *async_desc; 9118c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 9128c2ecf20Sopenharmony_ci int ret; 9138c2ecf20Sopenharmony_ci size_t residue = 0; 9148c2ecf20Sopenharmony_ci unsigned int i; 9158c2ecf20Sopenharmony_ci unsigned long flags; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 9188c2ecf20Sopenharmony_ci if (ret == DMA_COMPLETE) 9198c2ecf20Sopenharmony_ci return ret; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (!txstate) 9228c2ecf20Sopenharmony_ci return bchan->paused ? DMA_PAUSED : ret; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci spin_lock_irqsave(&bchan->vc.lock, flags); 9258c2ecf20Sopenharmony_ci vd = vchan_find_desc(&bchan->vc, cookie); 9268c2ecf20Sopenharmony_ci if (vd) { 9278c2ecf20Sopenharmony_ci residue = container_of(vd, struct bam_async_desc, vd)->length; 9288c2ecf20Sopenharmony_ci } else { 9298c2ecf20Sopenharmony_ci list_for_each_entry(async_desc, &bchan->desc_list, desc_node) { 9308c2ecf20Sopenharmony_ci if (async_desc->vd.tx.cookie != cookie) 9318c2ecf20Sopenharmony_ci continue; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci for (i = 0; i < async_desc->num_desc; i++) 9348c2ecf20Sopenharmony_ci residue += le16_to_cpu( 9358c2ecf20Sopenharmony_ci async_desc->curr_desc[i].size); 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bchan->vc.lock, flags); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci dma_set_residue(txstate, residue); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (ret == DMA_IN_PROGRESS && bchan->paused) 9448c2ecf20Sopenharmony_ci ret = DMA_PAUSED; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return ret; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci/** 9508c2ecf20Sopenharmony_ci * bam_apply_new_config 9518c2ecf20Sopenharmony_ci * @bchan: bam dma channel 9528c2ecf20Sopenharmony_ci * @dir: DMA direction 9538c2ecf20Sopenharmony_ci */ 9548c2ecf20Sopenharmony_cistatic void bam_apply_new_config(struct bam_chan *bchan, 9558c2ecf20Sopenharmony_ci enum dma_transfer_direction dir) 9568c2ecf20Sopenharmony_ci{ 9578c2ecf20Sopenharmony_ci struct bam_device *bdev = bchan->bdev; 9588c2ecf20Sopenharmony_ci u32 maxburst; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (!bdev->controlled_remotely) { 9618c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 9628c2ecf20Sopenharmony_ci maxburst = bchan->slave.src_maxburst; 9638c2ecf20Sopenharmony_ci else 9648c2ecf20Sopenharmony_ci maxburst = bchan->slave.dst_maxburst; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci writel_relaxed(maxburst, 9678c2ecf20Sopenharmony_ci bam_addr(bdev, 0, BAM_DESC_CNT_TRSHLD)); 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci bchan->reconfigure = 0; 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci/** 9748c2ecf20Sopenharmony_ci * bam_start_dma - start next transaction 9758c2ecf20Sopenharmony_ci * @bchan: bam dma channel 9768c2ecf20Sopenharmony_ci */ 9778c2ecf20Sopenharmony_cistatic void bam_start_dma(struct bam_chan *bchan) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci struct virt_dma_desc *vd = vchan_next_desc(&bchan->vc); 9808c2ecf20Sopenharmony_ci struct bam_device *bdev = bchan->bdev; 9818c2ecf20Sopenharmony_ci struct bam_async_desc *async_desc = NULL; 9828c2ecf20Sopenharmony_ci struct bam_desc_hw *desc; 9838c2ecf20Sopenharmony_ci struct bam_desc_hw *fifo = PTR_ALIGN(bchan->fifo_virt, 9848c2ecf20Sopenharmony_ci sizeof(struct bam_desc_hw)); 9858c2ecf20Sopenharmony_ci int ret; 9868c2ecf20Sopenharmony_ci unsigned int avail; 9878c2ecf20Sopenharmony_ci struct dmaengine_desc_callback cb; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci lockdep_assert_held(&bchan->vc.lock); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci if (!vd) 9928c2ecf20Sopenharmony_ci return; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci ret = bam_pm_runtime_get_sync(bdev->dev); 9958c2ecf20Sopenharmony_ci if (ret < 0) 9968c2ecf20Sopenharmony_ci return; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci while (vd && !IS_BUSY(bchan)) { 9998c2ecf20Sopenharmony_ci list_del(&vd->node); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci async_desc = container_of(vd, struct bam_async_desc, vd); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* on first use, initialize the channel hardware */ 10048c2ecf20Sopenharmony_ci if (!bchan->initialized) 10058c2ecf20Sopenharmony_ci bam_chan_init_hw(bchan, async_desc->dir); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* apply new slave config changes, if necessary */ 10088c2ecf20Sopenharmony_ci if (bchan->reconfigure) 10098c2ecf20Sopenharmony_ci bam_apply_new_config(bchan, async_desc->dir); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci desc = async_desc->curr_desc; 10128c2ecf20Sopenharmony_ci avail = CIRC_SPACE(bchan->tail, bchan->head, 10138c2ecf20Sopenharmony_ci MAX_DESCRIPTORS + 1); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (async_desc->num_desc > avail) 10168c2ecf20Sopenharmony_ci async_desc->xfer_len = avail; 10178c2ecf20Sopenharmony_ci else 10188c2ecf20Sopenharmony_ci async_desc->xfer_len = async_desc->num_desc; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* set any special flags on the last descriptor */ 10218c2ecf20Sopenharmony_ci if (async_desc->num_desc == async_desc->xfer_len) 10228c2ecf20Sopenharmony_ci desc[async_desc->xfer_len - 1].flags |= 10238c2ecf20Sopenharmony_ci cpu_to_le16(async_desc->flags); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci vd = vchan_next_desc(&bchan->vc); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci dmaengine_desc_get_callback(&async_desc->vd.tx, &cb); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* 10308c2ecf20Sopenharmony_ci * An interrupt is generated at this desc, if 10318c2ecf20Sopenharmony_ci * - FIFO is FULL. 10328c2ecf20Sopenharmony_ci * - No more descriptors to add. 10338c2ecf20Sopenharmony_ci * - If a callback completion was requested for this DESC, 10348c2ecf20Sopenharmony_ci * In this case, BAM will deliver the completion callback 10358c2ecf20Sopenharmony_ci * for this desc and continue processing the next desc. 10368c2ecf20Sopenharmony_ci */ 10378c2ecf20Sopenharmony_ci if (((avail <= async_desc->xfer_len) || !vd || 10388c2ecf20Sopenharmony_ci dmaengine_desc_callback_valid(&cb)) && 10398c2ecf20Sopenharmony_ci !(async_desc->flags & DESC_FLAG_EOT)) 10408c2ecf20Sopenharmony_ci desc[async_desc->xfer_len - 1].flags |= 10418c2ecf20Sopenharmony_ci cpu_to_le16(DESC_FLAG_INT); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (bchan->tail + async_desc->xfer_len > MAX_DESCRIPTORS) { 10448c2ecf20Sopenharmony_ci u32 partial = MAX_DESCRIPTORS - bchan->tail; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci memcpy(&fifo[bchan->tail], desc, 10478c2ecf20Sopenharmony_ci partial * sizeof(struct bam_desc_hw)); 10488c2ecf20Sopenharmony_ci memcpy(fifo, &desc[partial], 10498c2ecf20Sopenharmony_ci (async_desc->xfer_len - partial) * 10508c2ecf20Sopenharmony_ci sizeof(struct bam_desc_hw)); 10518c2ecf20Sopenharmony_ci } else { 10528c2ecf20Sopenharmony_ci memcpy(&fifo[bchan->tail], desc, 10538c2ecf20Sopenharmony_ci async_desc->xfer_len * 10548c2ecf20Sopenharmony_ci sizeof(struct bam_desc_hw)); 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci bchan->tail += async_desc->xfer_len; 10588c2ecf20Sopenharmony_ci bchan->tail %= MAX_DESCRIPTORS; 10598c2ecf20Sopenharmony_ci list_add_tail(&async_desc->desc_node, &bchan->desc_list); 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* ensure descriptor writes and dma start not reordered */ 10638c2ecf20Sopenharmony_ci wmb(); 10648c2ecf20Sopenharmony_ci writel_relaxed(bchan->tail * sizeof(struct bam_desc_hw), 10658c2ecf20Sopenharmony_ci bam_addr(bdev, bchan->id, BAM_P_EVNT_REG)); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(bdev->dev); 10688c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(bdev->dev); 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci/** 10728c2ecf20Sopenharmony_ci * dma_tasklet - DMA IRQ tasklet 10738c2ecf20Sopenharmony_ci * @t: tasklet argument (bam controller structure) 10748c2ecf20Sopenharmony_ci * 10758c2ecf20Sopenharmony_ci * Sets up next DMA operation and then processes all completed transactions 10768c2ecf20Sopenharmony_ci */ 10778c2ecf20Sopenharmony_cistatic void dma_tasklet(struct tasklet_struct *t) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci struct bam_device *bdev = from_tasklet(bdev, t, task); 10808c2ecf20Sopenharmony_ci struct bam_chan *bchan; 10818c2ecf20Sopenharmony_ci unsigned long flags; 10828c2ecf20Sopenharmony_ci unsigned int i; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci /* go through the channels and kick off transactions */ 10858c2ecf20Sopenharmony_ci for (i = 0; i < bdev->num_channels; i++) { 10868c2ecf20Sopenharmony_ci bchan = &bdev->channels[i]; 10878c2ecf20Sopenharmony_ci spin_lock_irqsave(&bchan->vc.lock, flags); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (!list_empty(&bchan->vc.desc_issued) && !IS_BUSY(bchan)) 10908c2ecf20Sopenharmony_ci bam_start_dma(bchan); 10918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bchan->vc.lock, flags); 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci/** 10978c2ecf20Sopenharmony_ci * bam_issue_pending - starts pending transactions 10988c2ecf20Sopenharmony_ci * @chan: dma channel 10998c2ecf20Sopenharmony_ci * 11008c2ecf20Sopenharmony_ci * Calls tasklet directly which in turn starts any pending transactions 11018c2ecf20Sopenharmony_ci */ 11028c2ecf20Sopenharmony_cistatic void bam_issue_pending(struct dma_chan *chan) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci struct bam_chan *bchan = to_bam_chan(chan); 11058c2ecf20Sopenharmony_ci unsigned long flags; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci spin_lock_irqsave(&bchan->vc.lock, flags); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* if work pending and idle, start a transaction */ 11108c2ecf20Sopenharmony_ci if (vchan_issue_pending(&bchan->vc) && !IS_BUSY(bchan)) 11118c2ecf20Sopenharmony_ci bam_start_dma(bchan); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bchan->vc.lock, flags); 11148c2ecf20Sopenharmony_ci} 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci/** 11178c2ecf20Sopenharmony_ci * bam_dma_free_desc - free descriptor memory 11188c2ecf20Sopenharmony_ci * @vd: virtual descriptor 11198c2ecf20Sopenharmony_ci * 11208c2ecf20Sopenharmony_ci */ 11218c2ecf20Sopenharmony_cistatic void bam_dma_free_desc(struct virt_dma_desc *vd) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci struct bam_async_desc *async_desc = container_of(vd, 11248c2ecf20Sopenharmony_ci struct bam_async_desc, vd); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci kfree(async_desc); 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic struct dma_chan *bam_dma_xlate(struct of_phandle_args *dma_spec, 11308c2ecf20Sopenharmony_ci struct of_dma *of) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct bam_device *bdev = container_of(of->of_dma_data, 11338c2ecf20Sopenharmony_ci struct bam_device, common); 11348c2ecf20Sopenharmony_ci unsigned int request; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (dma_spec->args_count != 1) 11378c2ecf20Sopenharmony_ci return NULL; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci request = dma_spec->args[0]; 11408c2ecf20Sopenharmony_ci if (request >= bdev->num_channels) 11418c2ecf20Sopenharmony_ci return NULL; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci return dma_get_slave_channel(&(bdev->channels[request].vc.chan)); 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci/** 11478c2ecf20Sopenharmony_ci * bam_init 11488c2ecf20Sopenharmony_ci * @bdev: bam device 11498c2ecf20Sopenharmony_ci * 11508c2ecf20Sopenharmony_ci * Initialization helper for global bam registers 11518c2ecf20Sopenharmony_ci */ 11528c2ecf20Sopenharmony_cistatic int bam_init(struct bam_device *bdev) 11538c2ecf20Sopenharmony_ci{ 11548c2ecf20Sopenharmony_ci u32 val; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci /* read revision and configuration information */ 11578c2ecf20Sopenharmony_ci if (!bdev->num_ees) { 11588c2ecf20Sopenharmony_ci val = readl_relaxed(bam_addr(bdev, 0, BAM_REVISION)); 11598c2ecf20Sopenharmony_ci bdev->num_ees = (val >> NUM_EES_SHIFT) & NUM_EES_MASK; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* check that configured EE is within range */ 11638c2ecf20Sopenharmony_ci if (bdev->ee >= bdev->num_ees) 11648c2ecf20Sopenharmony_ci return -EINVAL; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci if (!bdev->num_channels) { 11678c2ecf20Sopenharmony_ci val = readl_relaxed(bam_addr(bdev, 0, BAM_NUM_PIPES)); 11688c2ecf20Sopenharmony_ci bdev->num_channels = val & BAM_NUM_PIPES_MASK; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (bdev->controlled_remotely) 11728c2ecf20Sopenharmony_ci return 0; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* s/w reset bam */ 11758c2ecf20Sopenharmony_ci /* after reset all pipes are disabled and idle */ 11768c2ecf20Sopenharmony_ci val = readl_relaxed(bam_addr(bdev, 0, BAM_CTRL)); 11778c2ecf20Sopenharmony_ci val |= BAM_SW_RST; 11788c2ecf20Sopenharmony_ci writel_relaxed(val, bam_addr(bdev, 0, BAM_CTRL)); 11798c2ecf20Sopenharmony_ci val &= ~BAM_SW_RST; 11808c2ecf20Sopenharmony_ci writel_relaxed(val, bam_addr(bdev, 0, BAM_CTRL)); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci /* make sure previous stores are visible before enabling BAM */ 11838c2ecf20Sopenharmony_ci wmb(); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci /* enable bam */ 11868c2ecf20Sopenharmony_ci val |= BAM_EN; 11878c2ecf20Sopenharmony_ci writel_relaxed(val, bam_addr(bdev, 0, BAM_CTRL)); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci /* set descriptor threshhold, start with 4 bytes */ 11908c2ecf20Sopenharmony_ci writel_relaxed(DEFAULT_CNT_THRSHLD, 11918c2ecf20Sopenharmony_ci bam_addr(bdev, 0, BAM_DESC_CNT_TRSHLD)); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* Enable default set of h/w workarounds, ie all except BAM_FULL_PIPE */ 11948c2ecf20Sopenharmony_ci writel_relaxed(BAM_CNFG_BITS_DEFAULT, bam_addr(bdev, 0, BAM_CNFG_BITS)); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* enable irqs for errors */ 11978c2ecf20Sopenharmony_ci writel_relaxed(BAM_ERROR_EN | BAM_HRESP_ERR_EN, 11988c2ecf20Sopenharmony_ci bam_addr(bdev, 0, BAM_IRQ_EN)); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* unmask global bam interrupt */ 12018c2ecf20Sopenharmony_ci writel_relaxed(BAM_IRQ_MSK, bam_addr(bdev, 0, BAM_IRQ_SRCS_MSK_EE)); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci return 0; 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic void bam_channel_init(struct bam_device *bdev, struct bam_chan *bchan, 12078c2ecf20Sopenharmony_ci u32 index) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci bchan->id = index; 12108c2ecf20Sopenharmony_ci bchan->bdev = bdev; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci vchan_init(&bchan->vc, &bdev->common); 12138c2ecf20Sopenharmony_ci bchan->vc.desc_free = bam_dma_free_desc; 12148c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bchan->desc_list); 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic const struct of_device_id bam_of_match[] = { 12188c2ecf20Sopenharmony_ci { .compatible = "qcom,bam-v1.3.0", .data = &bam_v1_3_reg_info }, 12198c2ecf20Sopenharmony_ci { .compatible = "qcom,bam-v1.4.0", .data = &bam_v1_4_reg_info }, 12208c2ecf20Sopenharmony_ci { .compatible = "qcom,bam-v1.7.0", .data = &bam_v1_7_reg_info }, 12218c2ecf20Sopenharmony_ci {} 12228c2ecf20Sopenharmony_ci}; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bam_of_match); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic int bam_dma_probe(struct platform_device *pdev) 12278c2ecf20Sopenharmony_ci{ 12288c2ecf20Sopenharmony_ci struct bam_device *bdev; 12298c2ecf20Sopenharmony_ci const struct of_device_id *match; 12308c2ecf20Sopenharmony_ci struct resource *iores; 12318c2ecf20Sopenharmony_ci int ret, i; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL); 12348c2ecf20Sopenharmony_ci if (!bdev) 12358c2ecf20Sopenharmony_ci return -ENOMEM; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci bdev->dev = &pdev->dev; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci match = of_match_node(bam_of_match, pdev->dev.of_node); 12408c2ecf20Sopenharmony_ci if (!match) { 12418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unsupported BAM module\n"); 12428c2ecf20Sopenharmony_ci return -ENODEV; 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci bdev->layout = match->data; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); 12488c2ecf20Sopenharmony_ci bdev->regs = devm_ioremap_resource(&pdev->dev, iores); 12498c2ecf20Sopenharmony_ci if (IS_ERR(bdev->regs)) 12508c2ecf20Sopenharmony_ci return PTR_ERR(bdev->regs); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci bdev->irq = platform_get_irq(pdev, 0); 12538c2ecf20Sopenharmony_ci if (bdev->irq < 0) 12548c2ecf20Sopenharmony_ci return bdev->irq; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &bdev->ee); 12578c2ecf20Sopenharmony_ci if (ret) { 12588c2ecf20Sopenharmony_ci dev_err(bdev->dev, "Execution environment unspecified\n"); 12598c2ecf20Sopenharmony_ci return ret; 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci bdev->controlled_remotely = of_property_read_bool(pdev->dev.of_node, 12638c2ecf20Sopenharmony_ci "qcom,controlled-remotely"); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (bdev->controlled_remotely) { 12668c2ecf20Sopenharmony_ci ret = of_property_read_u32(pdev->dev.of_node, "num-channels", 12678c2ecf20Sopenharmony_ci &bdev->num_channels); 12688c2ecf20Sopenharmony_ci if (ret) 12698c2ecf20Sopenharmony_ci dev_err(bdev->dev, "num-channels unspecified in dt\n"); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci ret = of_property_read_u32(pdev->dev.of_node, "qcom,num-ees", 12728c2ecf20Sopenharmony_ci &bdev->num_ees); 12738c2ecf20Sopenharmony_ci if (ret) 12748c2ecf20Sopenharmony_ci dev_err(bdev->dev, "num-ees unspecified in dt\n"); 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk"); 12788c2ecf20Sopenharmony_ci if (IS_ERR(bdev->bamclk)) { 12798c2ecf20Sopenharmony_ci if (!bdev->controlled_remotely) 12808c2ecf20Sopenharmony_ci return PTR_ERR(bdev->bamclk); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci bdev->bamclk = NULL; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci ret = clk_prepare_enable(bdev->bamclk); 12868c2ecf20Sopenharmony_ci if (ret) { 12878c2ecf20Sopenharmony_ci dev_err(bdev->dev, "failed to prepare/enable clock\n"); 12888c2ecf20Sopenharmony_ci return ret; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci ret = bam_init(bdev); 12928c2ecf20Sopenharmony_ci if (ret) 12938c2ecf20Sopenharmony_ci goto err_disable_clk; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci tasklet_setup(&bdev->task, dma_tasklet); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci bdev->channels = devm_kcalloc(bdev->dev, bdev->num_channels, 12988c2ecf20Sopenharmony_ci sizeof(*bdev->channels), GFP_KERNEL); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci if (!bdev->channels) { 13018c2ecf20Sopenharmony_ci ret = -ENOMEM; 13028c2ecf20Sopenharmony_ci goto err_tasklet_kill; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci /* allocate and initialize channels */ 13068c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bdev->common.channels); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci for (i = 0; i < bdev->num_channels; i++) 13098c2ecf20Sopenharmony_ci bam_channel_init(bdev, &bdev->channels[i], i); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci ret = devm_request_irq(bdev->dev, bdev->irq, bam_dma_irq, 13128c2ecf20Sopenharmony_ci IRQF_TRIGGER_HIGH, "bam_dma", bdev); 13138c2ecf20Sopenharmony_ci if (ret) 13148c2ecf20Sopenharmony_ci goto err_bam_channel_exit; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci /* set max dma segment size */ 13178c2ecf20Sopenharmony_ci bdev->common.dev = bdev->dev; 13188c2ecf20Sopenharmony_ci ret = dma_set_max_seg_size(bdev->common.dev, BAM_FIFO_SIZE); 13198c2ecf20Sopenharmony_ci if (ret) { 13208c2ecf20Sopenharmony_ci dev_err(bdev->dev, "cannot set maximum segment size\n"); 13218c2ecf20Sopenharmony_ci goto err_bam_channel_exit; 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, bdev); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci /* set capabilities */ 13278c2ecf20Sopenharmony_ci dma_cap_zero(bdev->common.cap_mask); 13288c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, bdev->common.cap_mask); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci /* initialize dmaengine apis */ 13318c2ecf20Sopenharmony_ci bdev->common.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 13328c2ecf20Sopenharmony_ci bdev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; 13338c2ecf20Sopenharmony_ci bdev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; 13348c2ecf20Sopenharmony_ci bdev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; 13358c2ecf20Sopenharmony_ci bdev->common.device_alloc_chan_resources = bam_alloc_chan; 13368c2ecf20Sopenharmony_ci bdev->common.device_free_chan_resources = bam_free_chan; 13378c2ecf20Sopenharmony_ci bdev->common.device_prep_slave_sg = bam_prep_slave_sg; 13388c2ecf20Sopenharmony_ci bdev->common.device_config = bam_slave_config; 13398c2ecf20Sopenharmony_ci bdev->common.device_pause = bam_pause; 13408c2ecf20Sopenharmony_ci bdev->common.device_resume = bam_resume; 13418c2ecf20Sopenharmony_ci bdev->common.device_terminate_all = bam_dma_terminate_all; 13428c2ecf20Sopenharmony_ci bdev->common.device_issue_pending = bam_issue_pending; 13438c2ecf20Sopenharmony_ci bdev->common.device_tx_status = bam_tx_status; 13448c2ecf20Sopenharmony_ci bdev->common.dev = bdev->dev; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci ret = dma_async_device_register(&bdev->common); 13478c2ecf20Sopenharmony_ci if (ret) { 13488c2ecf20Sopenharmony_ci dev_err(bdev->dev, "failed to register dma async device\n"); 13498c2ecf20Sopenharmony_ci goto err_bam_channel_exit; 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci ret = of_dma_controller_register(pdev->dev.of_node, bam_dma_xlate, 13538c2ecf20Sopenharmony_ci &bdev->common); 13548c2ecf20Sopenharmony_ci if (ret) 13558c2ecf20Sopenharmony_ci goto err_unregister_dma; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci if (bdev->controlled_remotely) { 13588c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 13598c2ecf20Sopenharmony_ci return 0; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci pm_runtime_irq_safe(&pdev->dev); 13638c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, BAM_DMA_AUTOSUSPEND_DELAY); 13648c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 13658c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 13668c2ecf20Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 13678c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci return 0; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_cierr_unregister_dma: 13728c2ecf20Sopenharmony_ci dma_async_device_unregister(&bdev->common); 13738c2ecf20Sopenharmony_cierr_bam_channel_exit: 13748c2ecf20Sopenharmony_ci for (i = 0; i < bdev->num_channels; i++) 13758c2ecf20Sopenharmony_ci tasklet_kill(&bdev->channels[i].vc.task); 13768c2ecf20Sopenharmony_cierr_tasklet_kill: 13778c2ecf20Sopenharmony_ci tasklet_kill(&bdev->task); 13788c2ecf20Sopenharmony_cierr_disable_clk: 13798c2ecf20Sopenharmony_ci clk_disable_unprepare(bdev->bamclk); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci return ret; 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cistatic int bam_dma_remove(struct platform_device *pdev) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci struct bam_device *bdev = platform_get_drvdata(pdev); 13878c2ecf20Sopenharmony_ci u32 i; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci pm_runtime_force_suspend(&pdev->dev); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 13928c2ecf20Sopenharmony_ci dma_async_device_unregister(&bdev->common); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci /* mask all interrupts for this execution environment */ 13958c2ecf20Sopenharmony_ci writel_relaxed(0, bam_addr(bdev, 0, BAM_IRQ_SRCS_MSK_EE)); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci devm_free_irq(bdev->dev, bdev->irq, bdev); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci for (i = 0; i < bdev->num_channels; i++) { 14008c2ecf20Sopenharmony_ci bam_dma_terminate_all(&bdev->channels[i].vc.chan); 14018c2ecf20Sopenharmony_ci tasklet_kill(&bdev->channels[i].vc.task); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci if (!bdev->channels[i].fifo_virt) 14048c2ecf20Sopenharmony_ci continue; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci dma_free_wc(bdev->dev, BAM_DESC_FIFO_SIZE, 14078c2ecf20Sopenharmony_ci bdev->channels[i].fifo_virt, 14088c2ecf20Sopenharmony_ci bdev->channels[i].fifo_phys); 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci tasklet_kill(&bdev->task); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci clk_disable_unprepare(bdev->bamclk); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci return 0; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic int __maybe_unused bam_dma_runtime_suspend(struct device *dev) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci struct bam_device *bdev = dev_get_drvdata(dev); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci clk_disable(bdev->bamclk); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci return 0; 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_cistatic int __maybe_unused bam_dma_runtime_resume(struct device *dev) 14288c2ecf20Sopenharmony_ci{ 14298c2ecf20Sopenharmony_ci struct bam_device *bdev = dev_get_drvdata(dev); 14308c2ecf20Sopenharmony_ci int ret; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci ret = clk_enable(bdev->bamclk); 14338c2ecf20Sopenharmony_ci if (ret < 0) { 14348c2ecf20Sopenharmony_ci dev_err(dev, "clk_enable failed: %d\n", ret); 14358c2ecf20Sopenharmony_ci return ret; 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci return 0; 14398c2ecf20Sopenharmony_ci} 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_cistatic int __maybe_unused bam_dma_suspend(struct device *dev) 14428c2ecf20Sopenharmony_ci{ 14438c2ecf20Sopenharmony_ci struct bam_device *bdev = dev_get_drvdata(dev); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci if (!bdev->controlled_remotely) 14468c2ecf20Sopenharmony_ci pm_runtime_force_suspend(dev); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci clk_unprepare(bdev->bamclk); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_cistatic int __maybe_unused bam_dma_resume(struct device *dev) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci struct bam_device *bdev = dev_get_drvdata(dev); 14568c2ecf20Sopenharmony_ci int ret; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci ret = clk_prepare(bdev->bamclk); 14598c2ecf20Sopenharmony_ci if (ret) 14608c2ecf20Sopenharmony_ci return ret; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci if (!bdev->controlled_remotely) 14638c2ecf20Sopenharmony_ci pm_runtime_force_resume(dev); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci return 0; 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_cistatic const struct dev_pm_ops bam_dma_pm_ops = { 14698c2ecf20Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(bam_dma_suspend, bam_dma_resume) 14708c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(bam_dma_runtime_suspend, bam_dma_runtime_resume, 14718c2ecf20Sopenharmony_ci NULL) 14728c2ecf20Sopenharmony_ci}; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_cistatic struct platform_driver bam_dma_driver = { 14758c2ecf20Sopenharmony_ci .probe = bam_dma_probe, 14768c2ecf20Sopenharmony_ci .remove = bam_dma_remove, 14778c2ecf20Sopenharmony_ci .driver = { 14788c2ecf20Sopenharmony_ci .name = "bam-dma-engine", 14798c2ecf20Sopenharmony_ci .pm = &bam_dma_pm_ops, 14808c2ecf20Sopenharmony_ci .of_match_table = bam_of_match, 14818c2ecf20Sopenharmony_ci }, 14828c2ecf20Sopenharmony_ci}; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_cimodule_platform_driver(bam_dma_driver); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); 14878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QCOM BAM DMA engine driver"); 14888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1489