15c9f8c2dSJonathan McDowell // SPDX-License-Identifier: GPL-2.0-only 25c9f8c2dSJonathan McDowell /* 35c9f8c2dSJonathan McDowell * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. 45c9f8c2dSJonathan McDowell */ 55c9f8c2dSJonathan McDowell 65c9f8c2dSJonathan McDowell #include <linux/clk.h> 75c9f8c2dSJonathan McDowell #include <linux/delay.h> 85c9f8c2dSJonathan McDowell #include <linux/device.h> 95c9f8c2dSJonathan McDowell #include <linux/dmaengine.h> 105c9f8c2dSJonathan McDowell #include <linux/dma-mapping.h> 115c9f8c2dSJonathan McDowell #include <linux/init.h> 125c9f8c2dSJonathan McDowell #include <linux/interrupt.h> 135c9f8c2dSJonathan McDowell #include <linux/io.h> 145c9f8c2dSJonathan McDowell #include <linux/kernel.h> 155c9f8c2dSJonathan McDowell #include <linux/module.h> 165c9f8c2dSJonathan McDowell #include <linux/of.h> 175c9f8c2dSJonathan McDowell #include <linux/of_address.h> 185c9f8c2dSJonathan McDowell #include <linux/of_irq.h> 195c9f8c2dSJonathan McDowell #include <linux/of_dma.h> 205c9f8c2dSJonathan McDowell #include <linux/platform_device.h> 215c9f8c2dSJonathan McDowell #include <linux/reset.h> 225c9f8c2dSJonathan McDowell #include <linux/scatterlist.h> 235c9f8c2dSJonathan McDowell #include <linux/slab.h> 245c9f8c2dSJonathan McDowell 255c9f8c2dSJonathan McDowell #include "../dmaengine.h" 265c9f8c2dSJonathan McDowell #include "../virt-dma.h" 275c9f8c2dSJonathan McDowell 285c9f8c2dSJonathan McDowell /* ADM registers - calculated from channel number and security domain */ 295c9f8c2dSJonathan McDowell #define ADM_CHAN_MULTI 0x4 305c9f8c2dSJonathan McDowell #define ADM_CI_MULTI 0x4 315c9f8c2dSJonathan McDowell #define ADM_CRCI_MULTI 0x4 325c9f8c2dSJonathan McDowell #define ADM_EE_MULTI 0x800 335c9f8c2dSJonathan McDowell #define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * (chan)) 345c9f8c2dSJonathan McDowell #define ADM_EE_OFFS(ee) (ADM_EE_MULTI * (ee)) 355c9f8c2dSJonathan McDowell #define ADM_CHAN_EE_OFFS(chan, ee) (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee)) 365c9f8c2dSJonathan McDowell #define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * (chan)) 375c9f8c2dSJonathan McDowell #define ADM_CI_OFFS(ci) (ADM_CHAN_OFF(ci)) 385c9f8c2dSJonathan McDowell #define ADM_CH_CMD_PTR(chan, ee) (ADM_CHAN_EE_OFFS(chan, ee)) 395c9f8c2dSJonathan McDowell #define ADM_CH_RSLT(chan, ee) (0x40 + ADM_CHAN_EE_OFFS(chan, ee)) 405c9f8c2dSJonathan McDowell #define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee)) 415c9f8c2dSJonathan McDowell #define ADM_CH_STATUS_SD(chan, ee) (0x200 + ADM_CHAN_EE_OFFS(chan, ee)) 425c9f8c2dSJonathan McDowell #define ADM_CH_CONF(chan) (0x240 + ADM_CHAN_OFFS(chan)) 435c9f8c2dSJonathan McDowell #define ADM_CH_RSLT_CONF(chan, ee) (0x300 + ADM_CHAN_EE_OFFS(chan, ee)) 445c9f8c2dSJonathan McDowell #define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee)) 455c9f8c2dSJonathan McDowell #define ADM_CI_CONF(ci) (0x390 + (ci) * ADM_CI_MULTI) 465c9f8c2dSJonathan McDowell #define ADM_GP_CTL 0x3d8 475c9f8c2dSJonathan McDowell #define ADM_CRCI_CTL(crci, ee) (0x400 + (crci) * ADM_CRCI_MULTI + \ 485c9f8c2dSJonathan McDowell ADM_EE_OFFS(ee)) 495c9f8c2dSJonathan McDowell 505c9f8c2dSJonathan McDowell /* channel status */ 515c9f8c2dSJonathan McDowell #define ADM_CH_STATUS_VALID BIT(1) 525c9f8c2dSJonathan McDowell 535c9f8c2dSJonathan McDowell /* channel result */ 545c9f8c2dSJonathan McDowell #define ADM_CH_RSLT_VALID BIT(31) 555c9f8c2dSJonathan McDowell #define ADM_CH_RSLT_ERR BIT(3) 565c9f8c2dSJonathan McDowell #define ADM_CH_RSLT_FLUSH BIT(2) 575c9f8c2dSJonathan McDowell #define ADM_CH_RSLT_TPD BIT(1) 585c9f8c2dSJonathan McDowell 595c9f8c2dSJonathan McDowell /* channel conf */ 605c9f8c2dSJonathan McDowell #define ADM_CH_CONF_SHADOW_EN BIT(12) 615c9f8c2dSJonathan McDowell #define ADM_CH_CONF_MPU_DISABLE BIT(11) 625c9f8c2dSJonathan McDowell #define ADM_CH_CONF_PERM_MPU_CONF BIT(9) 635c9f8c2dSJonathan McDowell #define ADM_CH_CONF_FORCE_RSLT_EN BIT(7) 645c9f8c2dSJonathan McDowell #define ADM_CH_CONF_SEC_DOMAIN(ee) ((((ee) & 0x3) << 4) | (((ee) & 0x4) << 11)) 655c9f8c2dSJonathan McDowell 665c9f8c2dSJonathan McDowell /* channel result conf */ 675c9f8c2dSJonathan McDowell #define ADM_CH_RSLT_CONF_FLUSH_EN BIT(1) 685c9f8c2dSJonathan McDowell #define ADM_CH_RSLT_CONF_IRQ_EN BIT(0) 695c9f8c2dSJonathan McDowell 705c9f8c2dSJonathan McDowell /* CRCI CTL */ 715c9f8c2dSJonathan McDowell #define ADM_CRCI_CTL_MUX_SEL BIT(18) 725c9f8c2dSJonathan McDowell #define ADM_CRCI_CTL_RST BIT(17) 735c9f8c2dSJonathan McDowell 745c9f8c2dSJonathan McDowell /* CI configuration */ 755c9f8c2dSJonathan McDowell #define ADM_CI_RANGE_END(x) ((x) << 24) 765c9f8c2dSJonathan McDowell #define ADM_CI_RANGE_START(x) ((x) << 16) 775c9f8c2dSJonathan McDowell #define ADM_CI_BURST_4_WORDS BIT(2) 785c9f8c2dSJonathan McDowell #define ADM_CI_BURST_8_WORDS BIT(3) 795c9f8c2dSJonathan McDowell 805c9f8c2dSJonathan McDowell /* GP CTL */ 815c9f8c2dSJonathan McDowell #define ADM_GP_CTL_LP_EN BIT(12) 825c9f8c2dSJonathan McDowell #define ADM_GP_CTL_LP_CNT(x) ((x) << 8) 835c9f8c2dSJonathan McDowell 845c9f8c2dSJonathan McDowell /* Command pointer list entry */ 855c9f8c2dSJonathan McDowell #define ADM_CPLE_LP BIT(31) 865c9f8c2dSJonathan McDowell #define ADM_CPLE_CMD_PTR_LIST BIT(29) 875c9f8c2dSJonathan McDowell 885c9f8c2dSJonathan McDowell /* Command list entry */ 895c9f8c2dSJonathan McDowell #define ADM_CMD_LC BIT(31) 905c9f8c2dSJonathan McDowell #define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7) 915c9f8c2dSJonathan McDowell #define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3) 925c9f8c2dSJonathan McDowell 935c9f8c2dSJonathan McDowell #define ADM_CMD_TYPE_SINGLE 0x0 945c9f8c2dSJonathan McDowell #define ADM_CMD_TYPE_BOX 0x3 955c9f8c2dSJonathan McDowell 965c9f8c2dSJonathan McDowell #define ADM_CRCI_MUX_SEL BIT(4) 975c9f8c2dSJonathan McDowell #define ADM_DESC_ALIGN 8 985c9f8c2dSJonathan McDowell #define ADM_MAX_XFER (SZ_64K - 1) 995c9f8c2dSJonathan McDowell #define ADM_MAX_ROWS (SZ_64K - 1) 1005c9f8c2dSJonathan McDowell #define ADM_MAX_CHANNELS 16 1015c9f8c2dSJonathan McDowell 1025c9f8c2dSJonathan McDowell struct adm_desc_hw_box { 1035c9f8c2dSJonathan McDowell u32 cmd; 1045c9f8c2dSJonathan McDowell u32 src_addr; 1055c9f8c2dSJonathan McDowell u32 dst_addr; 1065c9f8c2dSJonathan McDowell u32 row_len; 1075c9f8c2dSJonathan McDowell u32 num_rows; 1085c9f8c2dSJonathan McDowell u32 row_offset; 1095c9f8c2dSJonathan McDowell }; 1105c9f8c2dSJonathan McDowell 1115c9f8c2dSJonathan McDowell struct adm_desc_hw_single { 1125c9f8c2dSJonathan McDowell u32 cmd; 1135c9f8c2dSJonathan McDowell u32 src_addr; 1145c9f8c2dSJonathan McDowell u32 dst_addr; 1155c9f8c2dSJonathan McDowell u32 len; 1165c9f8c2dSJonathan McDowell }; 1175c9f8c2dSJonathan McDowell 1185c9f8c2dSJonathan McDowell struct adm_async_desc { 1195c9f8c2dSJonathan McDowell struct virt_dma_desc vd; 1205c9f8c2dSJonathan McDowell struct adm_device *adev; 1215c9f8c2dSJonathan McDowell 1225c9f8c2dSJonathan McDowell size_t length; 1235c9f8c2dSJonathan McDowell enum dma_transfer_direction dir; 1245c9f8c2dSJonathan McDowell dma_addr_t dma_addr; 1255c9f8c2dSJonathan McDowell size_t dma_len; 1265c9f8c2dSJonathan McDowell 1275c9f8c2dSJonathan McDowell void *cpl; 1285c9f8c2dSJonathan McDowell dma_addr_t cp_addr; 1295c9f8c2dSJonathan McDowell u32 crci; 1305c9f8c2dSJonathan McDowell u32 mux; 1315c9f8c2dSJonathan McDowell u32 blk_size; 1325c9f8c2dSJonathan McDowell }; 1335c9f8c2dSJonathan McDowell 1345c9f8c2dSJonathan McDowell struct adm_chan { 1355c9f8c2dSJonathan McDowell struct virt_dma_chan vc; 1365c9f8c2dSJonathan McDowell struct adm_device *adev; 1375c9f8c2dSJonathan McDowell 1385c9f8c2dSJonathan McDowell /* parsed from DT */ 1395c9f8c2dSJonathan McDowell u32 id; /* channel id */ 1405c9f8c2dSJonathan McDowell 1415c9f8c2dSJonathan McDowell struct adm_async_desc *curr_txd; 1425c9f8c2dSJonathan McDowell struct dma_slave_config slave; 1435c9f8c2dSJonathan McDowell struct list_head node; 1445c9f8c2dSJonathan McDowell 1455c9f8c2dSJonathan McDowell int error; 1465c9f8c2dSJonathan McDowell int initialized; 1475c9f8c2dSJonathan McDowell }; 1485c9f8c2dSJonathan McDowell 1495c9f8c2dSJonathan McDowell static inline struct adm_chan *to_adm_chan(struct dma_chan *common) 1505c9f8c2dSJonathan McDowell { 1515c9f8c2dSJonathan McDowell return container_of(common, struct adm_chan, vc.chan); 1525c9f8c2dSJonathan McDowell } 1535c9f8c2dSJonathan McDowell 1545c9f8c2dSJonathan McDowell struct adm_device { 1555c9f8c2dSJonathan McDowell void __iomem *regs; 1565c9f8c2dSJonathan McDowell struct device *dev; 1575c9f8c2dSJonathan McDowell struct dma_device common; 1585c9f8c2dSJonathan McDowell struct device_dma_parameters dma_parms; 1595c9f8c2dSJonathan McDowell struct adm_chan *channels; 1605c9f8c2dSJonathan McDowell 1615c9f8c2dSJonathan McDowell u32 ee; 1625c9f8c2dSJonathan McDowell 1635c9f8c2dSJonathan McDowell struct clk *core_clk; 1645c9f8c2dSJonathan McDowell struct clk *iface_clk; 1655c9f8c2dSJonathan McDowell 1665c9f8c2dSJonathan McDowell struct reset_control *clk_reset; 1675c9f8c2dSJonathan McDowell struct reset_control *c0_reset; 1685c9f8c2dSJonathan McDowell struct reset_control *c1_reset; 1695c9f8c2dSJonathan McDowell struct reset_control *c2_reset; 1705c9f8c2dSJonathan McDowell int irq; 1715c9f8c2dSJonathan McDowell }; 1725c9f8c2dSJonathan McDowell 1735c9f8c2dSJonathan McDowell /** 1745c9f8c2dSJonathan McDowell * adm_free_chan - Frees dma resources associated with the specific channel 1755c9f8c2dSJonathan McDowell * 176*4facce4cSJonathan McDowell * @chan: dma channel 1775c9f8c2dSJonathan McDowell * 178*4facce4cSJonathan McDowell * Free all allocated descriptors associated with this channel 1795c9f8c2dSJonathan McDowell */ 1805c9f8c2dSJonathan McDowell static void adm_free_chan(struct dma_chan *chan) 1815c9f8c2dSJonathan McDowell { 1825c9f8c2dSJonathan McDowell /* free all queued descriptors */ 1835c9f8c2dSJonathan McDowell vchan_free_chan_resources(to_virt_chan(chan)); 1845c9f8c2dSJonathan McDowell } 1855c9f8c2dSJonathan McDowell 1865c9f8c2dSJonathan McDowell /** 1875c9f8c2dSJonathan McDowell * adm_get_blksize - Get block size from burst value 1885c9f8c2dSJonathan McDowell * 189*4facce4cSJonathan McDowell * @burst: Burst size of transaction 1905c9f8c2dSJonathan McDowell */ 1915c9f8c2dSJonathan McDowell static int adm_get_blksize(unsigned int burst) 1925c9f8c2dSJonathan McDowell { 1935c9f8c2dSJonathan McDowell int ret; 1945c9f8c2dSJonathan McDowell 1955c9f8c2dSJonathan McDowell switch (burst) { 1965c9f8c2dSJonathan McDowell case 16: 1975c9f8c2dSJonathan McDowell case 32: 1985c9f8c2dSJonathan McDowell case 64: 1995c9f8c2dSJonathan McDowell case 128: 2005c9f8c2dSJonathan McDowell ret = ffs(burst >> 4) - 1; 2015c9f8c2dSJonathan McDowell break; 2025c9f8c2dSJonathan McDowell case 192: 2035c9f8c2dSJonathan McDowell ret = 4; 2045c9f8c2dSJonathan McDowell break; 2055c9f8c2dSJonathan McDowell case 256: 2065c9f8c2dSJonathan McDowell ret = 5; 2075c9f8c2dSJonathan McDowell break; 2085c9f8c2dSJonathan McDowell default: 2095c9f8c2dSJonathan McDowell ret = -EINVAL; 2105c9f8c2dSJonathan McDowell break; 2115c9f8c2dSJonathan McDowell } 2125c9f8c2dSJonathan McDowell 2135c9f8c2dSJonathan McDowell return ret; 2145c9f8c2dSJonathan McDowell } 2155c9f8c2dSJonathan McDowell 2165c9f8c2dSJonathan McDowell /** 2175c9f8c2dSJonathan McDowell * adm_process_fc_descriptors - Process descriptors for flow controlled xfers 2185c9f8c2dSJonathan McDowell * 2195c9f8c2dSJonathan McDowell * @achan: ADM channel 2205c9f8c2dSJonathan McDowell * @desc: Descriptor memory pointer 2215c9f8c2dSJonathan McDowell * @sg: Scatterlist entry 2225c9f8c2dSJonathan McDowell * @crci: CRCI value 2235c9f8c2dSJonathan McDowell * @burst: Burst size of transaction 2245c9f8c2dSJonathan McDowell * @direction: DMA transfer direction 2255c9f8c2dSJonathan McDowell */ 2265c9f8c2dSJonathan McDowell static void *adm_process_fc_descriptors(struct adm_chan *achan, void *desc, 2275c9f8c2dSJonathan McDowell struct scatterlist *sg, u32 crci, 2285c9f8c2dSJonathan McDowell u32 burst, 2295c9f8c2dSJonathan McDowell enum dma_transfer_direction direction) 2305c9f8c2dSJonathan McDowell { 2315c9f8c2dSJonathan McDowell struct adm_desc_hw_box *box_desc = NULL; 2325c9f8c2dSJonathan McDowell struct adm_desc_hw_single *single_desc; 2335c9f8c2dSJonathan McDowell u32 remainder = sg_dma_len(sg); 2345c9f8c2dSJonathan McDowell u32 rows, row_offset, crci_cmd; 2355c9f8c2dSJonathan McDowell u32 mem_addr = sg_dma_address(sg); 2365c9f8c2dSJonathan McDowell u32 *incr_addr = &mem_addr; 2375c9f8c2dSJonathan McDowell u32 *src, *dst; 2385c9f8c2dSJonathan McDowell 2395c9f8c2dSJonathan McDowell if (direction == DMA_DEV_TO_MEM) { 2405c9f8c2dSJonathan McDowell crci_cmd = ADM_CMD_SRC_CRCI(crci); 2415c9f8c2dSJonathan McDowell row_offset = burst; 2425c9f8c2dSJonathan McDowell src = &achan->slave.src_addr; 2435c9f8c2dSJonathan McDowell dst = &mem_addr; 2445c9f8c2dSJonathan McDowell } else { 2455c9f8c2dSJonathan McDowell crci_cmd = ADM_CMD_DST_CRCI(crci); 2465c9f8c2dSJonathan McDowell row_offset = burst << 16; 2475c9f8c2dSJonathan McDowell src = &mem_addr; 2485c9f8c2dSJonathan McDowell dst = &achan->slave.dst_addr; 2495c9f8c2dSJonathan McDowell } 2505c9f8c2dSJonathan McDowell 2515c9f8c2dSJonathan McDowell while (remainder >= burst) { 2525c9f8c2dSJonathan McDowell box_desc = desc; 2535c9f8c2dSJonathan McDowell box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd; 2545c9f8c2dSJonathan McDowell box_desc->row_offset = row_offset; 2555c9f8c2dSJonathan McDowell box_desc->src_addr = *src; 2565c9f8c2dSJonathan McDowell box_desc->dst_addr = *dst; 2575c9f8c2dSJonathan McDowell 2585c9f8c2dSJonathan McDowell rows = remainder / burst; 2595c9f8c2dSJonathan McDowell rows = min_t(u32, rows, ADM_MAX_ROWS); 2605c9f8c2dSJonathan McDowell box_desc->num_rows = rows << 16 | rows; 2615c9f8c2dSJonathan McDowell box_desc->row_len = burst << 16 | burst; 2625c9f8c2dSJonathan McDowell 2635c9f8c2dSJonathan McDowell *incr_addr += burst * rows; 2645c9f8c2dSJonathan McDowell remainder -= burst * rows; 2655c9f8c2dSJonathan McDowell desc += sizeof(*box_desc); 2665c9f8c2dSJonathan McDowell } 2675c9f8c2dSJonathan McDowell 2685c9f8c2dSJonathan McDowell /* if leftover bytes, do one single descriptor */ 2695c9f8c2dSJonathan McDowell if (remainder) { 2705c9f8c2dSJonathan McDowell single_desc = desc; 2715c9f8c2dSJonathan McDowell single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd; 2725c9f8c2dSJonathan McDowell single_desc->len = remainder; 2735c9f8c2dSJonathan McDowell single_desc->src_addr = *src; 2745c9f8c2dSJonathan McDowell single_desc->dst_addr = *dst; 2755c9f8c2dSJonathan McDowell desc += sizeof(*single_desc); 2765c9f8c2dSJonathan McDowell 2775c9f8c2dSJonathan McDowell if (sg_is_last(sg)) 2785c9f8c2dSJonathan McDowell single_desc->cmd |= ADM_CMD_LC; 2795c9f8c2dSJonathan McDowell } else { 2805c9f8c2dSJonathan McDowell if (box_desc && sg_is_last(sg)) 2815c9f8c2dSJonathan McDowell box_desc->cmd |= ADM_CMD_LC; 2825c9f8c2dSJonathan McDowell } 2835c9f8c2dSJonathan McDowell 2845c9f8c2dSJonathan McDowell return desc; 2855c9f8c2dSJonathan McDowell } 2865c9f8c2dSJonathan McDowell 2875c9f8c2dSJonathan McDowell /** 2885c9f8c2dSJonathan McDowell * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers 2895c9f8c2dSJonathan McDowell * 2905c9f8c2dSJonathan McDowell * @achan: ADM channel 2915c9f8c2dSJonathan McDowell * @desc: Descriptor memory pointer 2925c9f8c2dSJonathan McDowell * @sg: Scatterlist entry 2935c9f8c2dSJonathan McDowell * @direction: DMA transfer direction 2945c9f8c2dSJonathan McDowell */ 2955c9f8c2dSJonathan McDowell static void *adm_process_non_fc_descriptors(struct adm_chan *achan, void *desc, 2965c9f8c2dSJonathan McDowell struct scatterlist *sg, 2975c9f8c2dSJonathan McDowell enum dma_transfer_direction direction) 2985c9f8c2dSJonathan McDowell { 2995c9f8c2dSJonathan McDowell struct adm_desc_hw_single *single_desc; 3005c9f8c2dSJonathan McDowell u32 remainder = sg_dma_len(sg); 3015c9f8c2dSJonathan McDowell u32 mem_addr = sg_dma_address(sg); 3025c9f8c2dSJonathan McDowell u32 *incr_addr = &mem_addr; 3035c9f8c2dSJonathan McDowell u32 *src, *dst; 3045c9f8c2dSJonathan McDowell 3055c9f8c2dSJonathan McDowell if (direction == DMA_DEV_TO_MEM) { 3065c9f8c2dSJonathan McDowell src = &achan->slave.src_addr; 3075c9f8c2dSJonathan McDowell dst = &mem_addr; 3085c9f8c2dSJonathan McDowell } else { 3095c9f8c2dSJonathan McDowell src = &mem_addr; 3105c9f8c2dSJonathan McDowell dst = &achan->slave.dst_addr; 3115c9f8c2dSJonathan McDowell } 3125c9f8c2dSJonathan McDowell 3135c9f8c2dSJonathan McDowell do { 3145c9f8c2dSJonathan McDowell single_desc = desc; 3155c9f8c2dSJonathan McDowell single_desc->cmd = ADM_CMD_TYPE_SINGLE; 3165c9f8c2dSJonathan McDowell single_desc->src_addr = *src; 3175c9f8c2dSJonathan McDowell single_desc->dst_addr = *dst; 3185c9f8c2dSJonathan McDowell single_desc->len = (remainder > ADM_MAX_XFER) ? 3195c9f8c2dSJonathan McDowell ADM_MAX_XFER : remainder; 3205c9f8c2dSJonathan McDowell 3215c9f8c2dSJonathan McDowell remainder -= single_desc->len; 3225c9f8c2dSJonathan McDowell *incr_addr += single_desc->len; 3235c9f8c2dSJonathan McDowell desc += sizeof(*single_desc); 3245c9f8c2dSJonathan McDowell } while (remainder); 3255c9f8c2dSJonathan McDowell 3265c9f8c2dSJonathan McDowell /* set last command if this is the end of the whole transaction */ 3275c9f8c2dSJonathan McDowell if (sg_is_last(sg)) 3285c9f8c2dSJonathan McDowell single_desc->cmd |= ADM_CMD_LC; 3295c9f8c2dSJonathan McDowell 3305c9f8c2dSJonathan McDowell return desc; 3315c9f8c2dSJonathan McDowell } 3325c9f8c2dSJonathan McDowell 3335c9f8c2dSJonathan McDowell /** 3345c9f8c2dSJonathan McDowell * adm_prep_slave_sg - Prep slave sg transaction 3355c9f8c2dSJonathan McDowell * 3365c9f8c2dSJonathan McDowell * @chan: dma channel 3375c9f8c2dSJonathan McDowell * @sgl: scatter gather list 3385c9f8c2dSJonathan McDowell * @sg_len: length of sg 3395c9f8c2dSJonathan McDowell * @direction: DMA transfer direction 3405c9f8c2dSJonathan McDowell * @flags: DMA flags 3415c9f8c2dSJonathan McDowell * @context: transfer context (unused) 3425c9f8c2dSJonathan McDowell */ 3435c9f8c2dSJonathan McDowell static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, 3445c9f8c2dSJonathan McDowell struct scatterlist *sgl, 3455c9f8c2dSJonathan McDowell unsigned int sg_len, 3465c9f8c2dSJonathan McDowell enum dma_transfer_direction direction, 3475c9f8c2dSJonathan McDowell unsigned long flags, 3485c9f8c2dSJonathan McDowell void *context) 3495c9f8c2dSJonathan McDowell { 3505c9f8c2dSJonathan McDowell struct adm_chan *achan = to_adm_chan(chan); 3515c9f8c2dSJonathan McDowell struct adm_device *adev = achan->adev; 3525c9f8c2dSJonathan McDowell struct adm_async_desc *async_desc; 3535c9f8c2dSJonathan McDowell struct scatterlist *sg; 3545c9f8c2dSJonathan McDowell dma_addr_t cple_addr; 3555c9f8c2dSJonathan McDowell u32 i, burst; 3565c9f8c2dSJonathan McDowell u32 single_count = 0, box_count = 0, crci = 0; 3575c9f8c2dSJonathan McDowell void *desc; 3585c9f8c2dSJonathan McDowell u32 *cple; 3595c9f8c2dSJonathan McDowell int blk_size = 0; 3605c9f8c2dSJonathan McDowell 3615c9f8c2dSJonathan McDowell if (!is_slave_direction(direction)) { 3625c9f8c2dSJonathan McDowell dev_err(adev->dev, "invalid dma direction\n"); 3635c9f8c2dSJonathan McDowell return NULL; 3645c9f8c2dSJonathan McDowell } 3655c9f8c2dSJonathan McDowell 3665c9f8c2dSJonathan McDowell /* 3675c9f8c2dSJonathan McDowell * get burst value from slave configuration 3685c9f8c2dSJonathan McDowell */ 3695c9f8c2dSJonathan McDowell burst = (direction == DMA_MEM_TO_DEV) ? 3705c9f8c2dSJonathan McDowell achan->slave.dst_maxburst : 3715c9f8c2dSJonathan McDowell achan->slave.src_maxburst; 3725c9f8c2dSJonathan McDowell 3735c9f8c2dSJonathan McDowell /* if using flow control, validate burst and crci values */ 3745c9f8c2dSJonathan McDowell if (achan->slave.device_fc) { 3755c9f8c2dSJonathan McDowell blk_size = adm_get_blksize(burst); 3765c9f8c2dSJonathan McDowell if (blk_size < 0) { 3775c9f8c2dSJonathan McDowell dev_err(adev->dev, "invalid burst value: %d\n", 3785c9f8c2dSJonathan McDowell burst); 3795c9f8c2dSJonathan McDowell return ERR_PTR(-EINVAL); 3805c9f8c2dSJonathan McDowell } 3815c9f8c2dSJonathan McDowell 3825c9f8c2dSJonathan McDowell crci = achan->slave.slave_id & 0xf; 3835c9f8c2dSJonathan McDowell if (!crci || achan->slave.slave_id > 0x1f) { 3845c9f8c2dSJonathan McDowell dev_err(adev->dev, "invalid crci value\n"); 3855c9f8c2dSJonathan McDowell return ERR_PTR(-EINVAL); 3865c9f8c2dSJonathan McDowell } 3875c9f8c2dSJonathan McDowell } 3885c9f8c2dSJonathan McDowell 3895c9f8c2dSJonathan McDowell /* iterate through sgs and compute allocation size of structures */ 3905c9f8c2dSJonathan McDowell for_each_sg(sgl, sg, sg_len, i) { 3915c9f8c2dSJonathan McDowell if (achan->slave.device_fc) { 3925c9f8c2dSJonathan McDowell box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst, 3935c9f8c2dSJonathan McDowell ADM_MAX_ROWS); 3945c9f8c2dSJonathan McDowell if (sg_dma_len(sg) % burst) 3955c9f8c2dSJonathan McDowell single_count++; 3965c9f8c2dSJonathan McDowell } else { 3975c9f8c2dSJonathan McDowell single_count += DIV_ROUND_UP(sg_dma_len(sg), 3985c9f8c2dSJonathan McDowell ADM_MAX_XFER); 3995c9f8c2dSJonathan McDowell } 4005c9f8c2dSJonathan McDowell } 4015c9f8c2dSJonathan McDowell 4025c9f8c2dSJonathan McDowell async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT); 4035c9f8c2dSJonathan McDowell if (!async_desc) 4045c9f8c2dSJonathan McDowell return ERR_PTR(-ENOMEM); 4055c9f8c2dSJonathan McDowell 4065c9f8c2dSJonathan McDowell if (crci) 4075c9f8c2dSJonathan McDowell async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ? 4085c9f8c2dSJonathan McDowell ADM_CRCI_CTL_MUX_SEL : 0; 4095c9f8c2dSJonathan McDowell async_desc->crci = crci; 4105c9f8c2dSJonathan McDowell async_desc->blk_size = blk_size; 4115c9f8c2dSJonathan McDowell async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) + 4125c9f8c2dSJonathan McDowell box_count * sizeof(struct adm_desc_hw_box) + 4135c9f8c2dSJonathan McDowell sizeof(*cple) + 2 * ADM_DESC_ALIGN; 4145c9f8c2dSJonathan McDowell 4155c9f8c2dSJonathan McDowell async_desc->cpl = kzalloc(async_desc->dma_len, GFP_NOWAIT); 4165c9f8c2dSJonathan McDowell if (!async_desc->cpl) 4175c9f8c2dSJonathan McDowell goto free; 4185c9f8c2dSJonathan McDowell 4195c9f8c2dSJonathan McDowell async_desc->adev = adev; 4205c9f8c2dSJonathan McDowell 4215c9f8c2dSJonathan McDowell /* both command list entry and descriptors must be 8 byte aligned */ 4225c9f8c2dSJonathan McDowell cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN); 4235c9f8c2dSJonathan McDowell desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN); 4245c9f8c2dSJonathan McDowell 4255c9f8c2dSJonathan McDowell for_each_sg(sgl, sg, sg_len, i) { 4265c9f8c2dSJonathan McDowell async_desc->length += sg_dma_len(sg); 4275c9f8c2dSJonathan McDowell 4285c9f8c2dSJonathan McDowell if (achan->slave.device_fc) 4295c9f8c2dSJonathan McDowell desc = adm_process_fc_descriptors(achan, desc, sg, crci, 4305c9f8c2dSJonathan McDowell burst, direction); 4315c9f8c2dSJonathan McDowell else 4325c9f8c2dSJonathan McDowell desc = adm_process_non_fc_descriptors(achan, desc, sg, 4335c9f8c2dSJonathan McDowell direction); 4345c9f8c2dSJonathan McDowell } 4355c9f8c2dSJonathan McDowell 4365c9f8c2dSJonathan McDowell async_desc->dma_addr = dma_map_single(adev->dev, async_desc->cpl, 4375c9f8c2dSJonathan McDowell async_desc->dma_len, 4385c9f8c2dSJonathan McDowell DMA_TO_DEVICE); 4395c9f8c2dSJonathan McDowell if (dma_mapping_error(adev->dev, async_desc->dma_addr)) 4405c9f8c2dSJonathan McDowell goto free; 4415c9f8c2dSJonathan McDowell 4425c9f8c2dSJonathan McDowell cple_addr = async_desc->dma_addr + ((void *)cple - async_desc->cpl); 4435c9f8c2dSJonathan McDowell 4445c9f8c2dSJonathan McDowell /* init cmd list */ 4455c9f8c2dSJonathan McDowell dma_sync_single_for_cpu(adev->dev, cple_addr, sizeof(*cple), 4465c9f8c2dSJonathan McDowell DMA_TO_DEVICE); 4475c9f8c2dSJonathan McDowell *cple = ADM_CPLE_LP; 4485c9f8c2dSJonathan McDowell *cple |= (async_desc->dma_addr + ADM_DESC_ALIGN) >> 3; 4495c9f8c2dSJonathan McDowell dma_sync_single_for_device(adev->dev, cple_addr, sizeof(*cple), 4505c9f8c2dSJonathan McDowell DMA_TO_DEVICE); 4515c9f8c2dSJonathan McDowell 4525c9f8c2dSJonathan McDowell return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); 4535c9f8c2dSJonathan McDowell 4545c9f8c2dSJonathan McDowell free: 4555c9f8c2dSJonathan McDowell kfree(async_desc); 4565c9f8c2dSJonathan McDowell return ERR_PTR(-ENOMEM); 4575c9f8c2dSJonathan McDowell } 4585c9f8c2dSJonathan McDowell 4595c9f8c2dSJonathan McDowell /** 4605c9f8c2dSJonathan McDowell * adm_terminate_all - terminate all transactions on a channel 461*4facce4cSJonathan McDowell * @chan: dma channel 4625c9f8c2dSJonathan McDowell * 4635c9f8c2dSJonathan McDowell * Dequeues and frees all transactions, aborts current transaction 4645c9f8c2dSJonathan McDowell * No callbacks are done 4655c9f8c2dSJonathan McDowell * 4665c9f8c2dSJonathan McDowell */ 4675c9f8c2dSJonathan McDowell static int adm_terminate_all(struct dma_chan *chan) 4685c9f8c2dSJonathan McDowell { 4695c9f8c2dSJonathan McDowell struct adm_chan *achan = to_adm_chan(chan); 4705c9f8c2dSJonathan McDowell struct adm_device *adev = achan->adev; 4715c9f8c2dSJonathan McDowell unsigned long flags; 4725c9f8c2dSJonathan McDowell LIST_HEAD(head); 4735c9f8c2dSJonathan McDowell 4745c9f8c2dSJonathan McDowell spin_lock_irqsave(&achan->vc.lock, flags); 4755c9f8c2dSJonathan McDowell vchan_get_all_descriptors(&achan->vc, &head); 4765c9f8c2dSJonathan McDowell 4775c9f8c2dSJonathan McDowell /* send flush command to terminate current transaction */ 4785c9f8c2dSJonathan McDowell writel_relaxed(0x0, 4795c9f8c2dSJonathan McDowell adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee)); 4805c9f8c2dSJonathan McDowell 4815c9f8c2dSJonathan McDowell spin_unlock_irqrestore(&achan->vc.lock, flags); 4825c9f8c2dSJonathan McDowell 4835c9f8c2dSJonathan McDowell vchan_dma_desc_free_list(&achan->vc, &head); 4845c9f8c2dSJonathan McDowell 4855c9f8c2dSJonathan McDowell return 0; 4865c9f8c2dSJonathan McDowell } 4875c9f8c2dSJonathan McDowell 4885c9f8c2dSJonathan McDowell static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) 4895c9f8c2dSJonathan McDowell { 4905c9f8c2dSJonathan McDowell struct adm_chan *achan = to_adm_chan(chan); 4915c9f8c2dSJonathan McDowell unsigned long flag; 4925c9f8c2dSJonathan McDowell 4935c9f8c2dSJonathan McDowell spin_lock_irqsave(&achan->vc.lock, flag); 4945c9f8c2dSJonathan McDowell memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config)); 4955c9f8c2dSJonathan McDowell spin_unlock_irqrestore(&achan->vc.lock, flag); 4965c9f8c2dSJonathan McDowell 4975c9f8c2dSJonathan McDowell return 0; 4985c9f8c2dSJonathan McDowell } 4995c9f8c2dSJonathan McDowell 5005c9f8c2dSJonathan McDowell /** 5015c9f8c2dSJonathan McDowell * adm_start_dma - start next transaction 502*4facce4cSJonathan McDowell * @achan: ADM dma channel 5035c9f8c2dSJonathan McDowell */ 5045c9f8c2dSJonathan McDowell static void adm_start_dma(struct adm_chan *achan) 5055c9f8c2dSJonathan McDowell { 5065c9f8c2dSJonathan McDowell struct virt_dma_desc *vd = vchan_next_desc(&achan->vc); 5075c9f8c2dSJonathan McDowell struct adm_device *adev = achan->adev; 5085c9f8c2dSJonathan McDowell struct adm_async_desc *async_desc; 5095c9f8c2dSJonathan McDowell 5105c9f8c2dSJonathan McDowell lockdep_assert_held(&achan->vc.lock); 5115c9f8c2dSJonathan McDowell 5125c9f8c2dSJonathan McDowell if (!vd) 5135c9f8c2dSJonathan McDowell return; 5145c9f8c2dSJonathan McDowell 5155c9f8c2dSJonathan McDowell list_del(&vd->node); 5165c9f8c2dSJonathan McDowell 5175c9f8c2dSJonathan McDowell /* write next command list out to the CMD FIFO */ 5185c9f8c2dSJonathan McDowell async_desc = container_of(vd, struct adm_async_desc, vd); 5195c9f8c2dSJonathan McDowell achan->curr_txd = async_desc; 5205c9f8c2dSJonathan McDowell 5215c9f8c2dSJonathan McDowell /* reset channel error */ 5225c9f8c2dSJonathan McDowell achan->error = 0; 5235c9f8c2dSJonathan McDowell 5245c9f8c2dSJonathan McDowell if (!achan->initialized) { 5255c9f8c2dSJonathan McDowell /* enable interrupts */ 5265c9f8c2dSJonathan McDowell writel(ADM_CH_CONF_SHADOW_EN | 5275c9f8c2dSJonathan McDowell ADM_CH_CONF_PERM_MPU_CONF | 5285c9f8c2dSJonathan McDowell ADM_CH_CONF_MPU_DISABLE | 5295c9f8c2dSJonathan McDowell ADM_CH_CONF_SEC_DOMAIN(adev->ee), 5305c9f8c2dSJonathan McDowell adev->regs + ADM_CH_CONF(achan->id)); 5315c9f8c2dSJonathan McDowell 5325c9f8c2dSJonathan McDowell writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN, 5335c9f8c2dSJonathan McDowell adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); 5345c9f8c2dSJonathan McDowell 5355c9f8c2dSJonathan McDowell achan->initialized = 1; 5365c9f8c2dSJonathan McDowell } 5375c9f8c2dSJonathan McDowell 5385c9f8c2dSJonathan McDowell /* set the crci block size if this transaction requires CRCI */ 5395c9f8c2dSJonathan McDowell if (async_desc->crci) { 5405c9f8c2dSJonathan McDowell writel(async_desc->mux | async_desc->blk_size, 5415c9f8c2dSJonathan McDowell adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee)); 5425c9f8c2dSJonathan McDowell } 5435c9f8c2dSJonathan McDowell 5445c9f8c2dSJonathan McDowell /* make sure IRQ enable doesn't get reordered */ 5455c9f8c2dSJonathan McDowell wmb(); 5465c9f8c2dSJonathan McDowell 5475c9f8c2dSJonathan McDowell /* write next command list out to the CMD FIFO */ 5485c9f8c2dSJonathan McDowell writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3, 5495c9f8c2dSJonathan McDowell adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee)); 5505c9f8c2dSJonathan McDowell } 5515c9f8c2dSJonathan McDowell 5525c9f8c2dSJonathan McDowell /** 5535c9f8c2dSJonathan McDowell * adm_dma_irq - irq handler for ADM controller 5545c9f8c2dSJonathan McDowell * @irq: IRQ of interrupt 5555c9f8c2dSJonathan McDowell * @data: callback data 5565c9f8c2dSJonathan McDowell * 5575c9f8c2dSJonathan McDowell * IRQ handler for the bam controller 5585c9f8c2dSJonathan McDowell */ 5595c9f8c2dSJonathan McDowell static irqreturn_t adm_dma_irq(int irq, void *data) 5605c9f8c2dSJonathan McDowell { 5615c9f8c2dSJonathan McDowell struct adm_device *adev = data; 5625c9f8c2dSJonathan McDowell u32 srcs, i; 5635c9f8c2dSJonathan McDowell struct adm_async_desc *async_desc; 5645c9f8c2dSJonathan McDowell unsigned long flags; 5655c9f8c2dSJonathan McDowell 5665c9f8c2dSJonathan McDowell srcs = readl_relaxed(adev->regs + 5675c9f8c2dSJonathan McDowell ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee)); 5685c9f8c2dSJonathan McDowell 5695c9f8c2dSJonathan McDowell for (i = 0; i < ADM_MAX_CHANNELS; i++) { 5705c9f8c2dSJonathan McDowell struct adm_chan *achan = &adev->channels[i]; 5715c9f8c2dSJonathan McDowell u32 status, result; 5725c9f8c2dSJonathan McDowell 5735c9f8c2dSJonathan McDowell if (srcs & BIT(i)) { 5745c9f8c2dSJonathan McDowell status = readl_relaxed(adev->regs + 5755c9f8c2dSJonathan McDowell ADM_CH_STATUS_SD(i, adev->ee)); 5765c9f8c2dSJonathan McDowell 5775c9f8c2dSJonathan McDowell /* if no result present, skip */ 5785c9f8c2dSJonathan McDowell if (!(status & ADM_CH_STATUS_VALID)) 5795c9f8c2dSJonathan McDowell continue; 5805c9f8c2dSJonathan McDowell 5815c9f8c2dSJonathan McDowell result = readl_relaxed(adev->regs + 5825c9f8c2dSJonathan McDowell ADM_CH_RSLT(i, adev->ee)); 5835c9f8c2dSJonathan McDowell 5845c9f8c2dSJonathan McDowell /* no valid results, skip */ 5855c9f8c2dSJonathan McDowell if (!(result & ADM_CH_RSLT_VALID)) 5865c9f8c2dSJonathan McDowell continue; 5875c9f8c2dSJonathan McDowell 5885c9f8c2dSJonathan McDowell /* flag error if transaction was flushed or failed */ 5895c9f8c2dSJonathan McDowell if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH)) 5905c9f8c2dSJonathan McDowell achan->error = 1; 5915c9f8c2dSJonathan McDowell 5925c9f8c2dSJonathan McDowell spin_lock_irqsave(&achan->vc.lock, flags); 5935c9f8c2dSJonathan McDowell async_desc = achan->curr_txd; 5945c9f8c2dSJonathan McDowell 5955c9f8c2dSJonathan McDowell achan->curr_txd = NULL; 5965c9f8c2dSJonathan McDowell 5975c9f8c2dSJonathan McDowell if (async_desc) { 5985c9f8c2dSJonathan McDowell vchan_cookie_complete(&async_desc->vd); 5995c9f8c2dSJonathan McDowell 6005c9f8c2dSJonathan McDowell /* kick off next DMA */ 6015c9f8c2dSJonathan McDowell adm_start_dma(achan); 6025c9f8c2dSJonathan McDowell } 6035c9f8c2dSJonathan McDowell 6045c9f8c2dSJonathan McDowell spin_unlock_irqrestore(&achan->vc.lock, flags); 6055c9f8c2dSJonathan McDowell } 6065c9f8c2dSJonathan McDowell } 6075c9f8c2dSJonathan McDowell 6085c9f8c2dSJonathan McDowell return IRQ_HANDLED; 6095c9f8c2dSJonathan McDowell } 6105c9f8c2dSJonathan McDowell 6115c9f8c2dSJonathan McDowell /** 6125c9f8c2dSJonathan McDowell * adm_tx_status - returns status of transaction 6135c9f8c2dSJonathan McDowell * @chan: dma channel 6145c9f8c2dSJonathan McDowell * @cookie: transaction cookie 6155c9f8c2dSJonathan McDowell * @txstate: DMA transaction state 6165c9f8c2dSJonathan McDowell * 6175c9f8c2dSJonathan McDowell * Return status of dma transaction 6185c9f8c2dSJonathan McDowell */ 6195c9f8c2dSJonathan McDowell static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie, 6205c9f8c2dSJonathan McDowell struct dma_tx_state *txstate) 6215c9f8c2dSJonathan McDowell { 6225c9f8c2dSJonathan McDowell struct adm_chan *achan = to_adm_chan(chan); 6235c9f8c2dSJonathan McDowell struct virt_dma_desc *vd; 6245c9f8c2dSJonathan McDowell enum dma_status ret; 6255c9f8c2dSJonathan McDowell unsigned long flags; 6265c9f8c2dSJonathan McDowell size_t residue = 0; 6275c9f8c2dSJonathan McDowell 6285c9f8c2dSJonathan McDowell ret = dma_cookie_status(chan, cookie, txstate); 6295c9f8c2dSJonathan McDowell if (ret == DMA_COMPLETE || !txstate) 6305c9f8c2dSJonathan McDowell return ret; 6315c9f8c2dSJonathan McDowell 6325c9f8c2dSJonathan McDowell spin_lock_irqsave(&achan->vc.lock, flags); 6335c9f8c2dSJonathan McDowell 6345c9f8c2dSJonathan McDowell vd = vchan_find_desc(&achan->vc, cookie); 6355c9f8c2dSJonathan McDowell if (vd) 6365c9f8c2dSJonathan McDowell residue = container_of(vd, struct adm_async_desc, vd)->length; 6375c9f8c2dSJonathan McDowell 6385c9f8c2dSJonathan McDowell spin_unlock_irqrestore(&achan->vc.lock, flags); 6395c9f8c2dSJonathan McDowell 6405c9f8c2dSJonathan McDowell /* 6415c9f8c2dSJonathan McDowell * residue is either the full length if it is in the issued list, or 0 6425c9f8c2dSJonathan McDowell * if it is in progress. We have no reliable way of determining 6435c9f8c2dSJonathan McDowell * anything inbetween 6445c9f8c2dSJonathan McDowell */ 6455c9f8c2dSJonathan McDowell dma_set_residue(txstate, residue); 6465c9f8c2dSJonathan McDowell 6475c9f8c2dSJonathan McDowell if (achan->error) 6485c9f8c2dSJonathan McDowell return DMA_ERROR; 6495c9f8c2dSJonathan McDowell 6505c9f8c2dSJonathan McDowell return ret; 6515c9f8c2dSJonathan McDowell } 6525c9f8c2dSJonathan McDowell 6535c9f8c2dSJonathan McDowell /** 6545c9f8c2dSJonathan McDowell * adm_issue_pending - starts pending transactions 6555c9f8c2dSJonathan McDowell * @chan: dma channel 6565c9f8c2dSJonathan McDowell * 6575c9f8c2dSJonathan McDowell * Issues all pending transactions and starts DMA 6585c9f8c2dSJonathan McDowell */ 6595c9f8c2dSJonathan McDowell static void adm_issue_pending(struct dma_chan *chan) 6605c9f8c2dSJonathan McDowell { 6615c9f8c2dSJonathan McDowell struct adm_chan *achan = to_adm_chan(chan); 6625c9f8c2dSJonathan McDowell unsigned long flags; 6635c9f8c2dSJonathan McDowell 6645c9f8c2dSJonathan McDowell spin_lock_irqsave(&achan->vc.lock, flags); 6655c9f8c2dSJonathan McDowell 6665c9f8c2dSJonathan McDowell if (vchan_issue_pending(&achan->vc) && !achan->curr_txd) 6675c9f8c2dSJonathan McDowell adm_start_dma(achan); 6685c9f8c2dSJonathan McDowell spin_unlock_irqrestore(&achan->vc.lock, flags); 6695c9f8c2dSJonathan McDowell } 6705c9f8c2dSJonathan McDowell 6715c9f8c2dSJonathan McDowell /** 6725c9f8c2dSJonathan McDowell * adm_dma_free_desc - free descriptor memory 6735c9f8c2dSJonathan McDowell * @vd: virtual descriptor 6745c9f8c2dSJonathan McDowell * 6755c9f8c2dSJonathan McDowell */ 6765c9f8c2dSJonathan McDowell static void adm_dma_free_desc(struct virt_dma_desc *vd) 6775c9f8c2dSJonathan McDowell { 6785c9f8c2dSJonathan McDowell struct adm_async_desc *async_desc = container_of(vd, 6795c9f8c2dSJonathan McDowell struct adm_async_desc, vd); 6805c9f8c2dSJonathan McDowell 6815c9f8c2dSJonathan McDowell dma_unmap_single(async_desc->adev->dev, async_desc->dma_addr, 6825c9f8c2dSJonathan McDowell async_desc->dma_len, DMA_TO_DEVICE); 6835c9f8c2dSJonathan McDowell kfree(async_desc->cpl); 6845c9f8c2dSJonathan McDowell kfree(async_desc); 6855c9f8c2dSJonathan McDowell } 6865c9f8c2dSJonathan McDowell 6875c9f8c2dSJonathan McDowell static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, 6885c9f8c2dSJonathan McDowell u32 index) 6895c9f8c2dSJonathan McDowell { 6905c9f8c2dSJonathan McDowell achan->id = index; 6915c9f8c2dSJonathan McDowell achan->adev = adev; 6925c9f8c2dSJonathan McDowell 6935c9f8c2dSJonathan McDowell vchan_init(&achan->vc, &adev->common); 6945c9f8c2dSJonathan McDowell achan->vc.desc_free = adm_dma_free_desc; 6955c9f8c2dSJonathan McDowell } 6965c9f8c2dSJonathan McDowell 6975c9f8c2dSJonathan McDowell static int adm_dma_probe(struct platform_device *pdev) 6985c9f8c2dSJonathan McDowell { 6995c9f8c2dSJonathan McDowell struct adm_device *adev; 7005c9f8c2dSJonathan McDowell int ret; 7015c9f8c2dSJonathan McDowell u32 i; 7025c9f8c2dSJonathan McDowell 7035c9f8c2dSJonathan McDowell adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL); 7045c9f8c2dSJonathan McDowell if (!adev) 7055c9f8c2dSJonathan McDowell return -ENOMEM; 7065c9f8c2dSJonathan McDowell 7075c9f8c2dSJonathan McDowell adev->dev = &pdev->dev; 7085c9f8c2dSJonathan McDowell 7095c9f8c2dSJonathan McDowell adev->regs = devm_platform_ioremap_resource(pdev, 0); 7105c9f8c2dSJonathan McDowell if (IS_ERR(adev->regs)) 7115c9f8c2dSJonathan McDowell return PTR_ERR(adev->regs); 7125c9f8c2dSJonathan McDowell 7135c9f8c2dSJonathan McDowell adev->irq = platform_get_irq(pdev, 0); 7145c9f8c2dSJonathan McDowell if (adev->irq < 0) 7155c9f8c2dSJonathan McDowell return adev->irq; 7165c9f8c2dSJonathan McDowell 7175c9f8c2dSJonathan McDowell ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee); 7185c9f8c2dSJonathan McDowell if (ret) { 7195c9f8c2dSJonathan McDowell dev_err(adev->dev, "Execution environment unspecified\n"); 7205c9f8c2dSJonathan McDowell return ret; 7215c9f8c2dSJonathan McDowell } 7225c9f8c2dSJonathan McDowell 7235c9f8c2dSJonathan McDowell adev->core_clk = devm_clk_get(adev->dev, "core"); 7245c9f8c2dSJonathan McDowell if (IS_ERR(adev->core_clk)) 7255c9f8c2dSJonathan McDowell return PTR_ERR(adev->core_clk); 7265c9f8c2dSJonathan McDowell 7275c9f8c2dSJonathan McDowell adev->iface_clk = devm_clk_get(adev->dev, "iface"); 7285c9f8c2dSJonathan McDowell if (IS_ERR(adev->iface_clk)) 7295c9f8c2dSJonathan McDowell return PTR_ERR(adev->iface_clk); 7305c9f8c2dSJonathan McDowell 7315c9f8c2dSJonathan McDowell adev->clk_reset = devm_reset_control_get_exclusive(&pdev->dev, "clk"); 7325c9f8c2dSJonathan McDowell if (IS_ERR(adev->clk_reset)) { 7335c9f8c2dSJonathan McDowell dev_err(adev->dev, "failed to get ADM0 reset\n"); 7345c9f8c2dSJonathan McDowell return PTR_ERR(adev->clk_reset); 7355c9f8c2dSJonathan McDowell } 7365c9f8c2dSJonathan McDowell 7375c9f8c2dSJonathan McDowell adev->c0_reset = devm_reset_control_get_exclusive(&pdev->dev, "c0"); 7385c9f8c2dSJonathan McDowell if (IS_ERR(adev->c0_reset)) { 7395c9f8c2dSJonathan McDowell dev_err(adev->dev, "failed to get ADM0 C0 reset\n"); 7405c9f8c2dSJonathan McDowell return PTR_ERR(adev->c0_reset); 7415c9f8c2dSJonathan McDowell } 7425c9f8c2dSJonathan McDowell 7435c9f8c2dSJonathan McDowell adev->c1_reset = devm_reset_control_get_exclusive(&pdev->dev, "c1"); 7445c9f8c2dSJonathan McDowell if (IS_ERR(adev->c1_reset)) { 7455c9f8c2dSJonathan McDowell dev_err(adev->dev, "failed to get ADM0 C1 reset\n"); 7465c9f8c2dSJonathan McDowell return PTR_ERR(adev->c1_reset); 7475c9f8c2dSJonathan McDowell } 7485c9f8c2dSJonathan McDowell 7495c9f8c2dSJonathan McDowell adev->c2_reset = devm_reset_control_get_exclusive(&pdev->dev, "c2"); 7505c9f8c2dSJonathan McDowell if (IS_ERR(adev->c2_reset)) { 7515c9f8c2dSJonathan McDowell dev_err(adev->dev, "failed to get ADM0 C2 reset\n"); 7525c9f8c2dSJonathan McDowell return PTR_ERR(adev->c2_reset); 7535c9f8c2dSJonathan McDowell } 7545c9f8c2dSJonathan McDowell 7555c9f8c2dSJonathan McDowell ret = clk_prepare_enable(adev->core_clk); 7565c9f8c2dSJonathan McDowell if (ret) { 7575c9f8c2dSJonathan McDowell dev_err(adev->dev, "failed to prepare/enable core clock\n"); 7585c9f8c2dSJonathan McDowell return ret; 7595c9f8c2dSJonathan McDowell } 7605c9f8c2dSJonathan McDowell 7615c9f8c2dSJonathan McDowell ret = clk_prepare_enable(adev->iface_clk); 7625c9f8c2dSJonathan McDowell if (ret) { 7635c9f8c2dSJonathan McDowell dev_err(adev->dev, "failed to prepare/enable iface clock\n"); 7645c9f8c2dSJonathan McDowell goto err_disable_core_clk; 7655c9f8c2dSJonathan McDowell } 7665c9f8c2dSJonathan McDowell 7675c9f8c2dSJonathan McDowell reset_control_assert(adev->clk_reset); 7685c9f8c2dSJonathan McDowell reset_control_assert(adev->c0_reset); 7695c9f8c2dSJonathan McDowell reset_control_assert(adev->c1_reset); 7705c9f8c2dSJonathan McDowell reset_control_assert(adev->c2_reset); 7715c9f8c2dSJonathan McDowell 7725c9f8c2dSJonathan McDowell udelay(2); 7735c9f8c2dSJonathan McDowell 7745c9f8c2dSJonathan McDowell reset_control_deassert(adev->clk_reset); 7755c9f8c2dSJonathan McDowell reset_control_deassert(adev->c0_reset); 7765c9f8c2dSJonathan McDowell reset_control_deassert(adev->c1_reset); 7775c9f8c2dSJonathan McDowell reset_control_deassert(adev->c2_reset); 7785c9f8c2dSJonathan McDowell 7795c9f8c2dSJonathan McDowell adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS, 7805c9f8c2dSJonathan McDowell sizeof(*adev->channels), GFP_KERNEL); 7815c9f8c2dSJonathan McDowell 7825c9f8c2dSJonathan McDowell if (!adev->channels) { 7835c9f8c2dSJonathan McDowell ret = -ENOMEM; 7845c9f8c2dSJonathan McDowell goto err_disable_clks; 7855c9f8c2dSJonathan McDowell } 7865c9f8c2dSJonathan McDowell 7875c9f8c2dSJonathan McDowell /* allocate and initialize channels */ 7885c9f8c2dSJonathan McDowell INIT_LIST_HEAD(&adev->common.channels); 7895c9f8c2dSJonathan McDowell 7905c9f8c2dSJonathan McDowell for (i = 0; i < ADM_MAX_CHANNELS; i++) 7915c9f8c2dSJonathan McDowell adm_channel_init(adev, &adev->channels[i], i); 7925c9f8c2dSJonathan McDowell 7935c9f8c2dSJonathan McDowell /* reset CRCIs */ 7945c9f8c2dSJonathan McDowell for (i = 0; i < 16; i++) 7955c9f8c2dSJonathan McDowell writel(ADM_CRCI_CTL_RST, adev->regs + 7965c9f8c2dSJonathan McDowell ADM_CRCI_CTL(i, adev->ee)); 7975c9f8c2dSJonathan McDowell 7985c9f8c2dSJonathan McDowell /* configure client interfaces */ 7995c9f8c2dSJonathan McDowell writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) | 8005c9f8c2dSJonathan McDowell ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0)); 8015c9f8c2dSJonathan McDowell writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) | 8025c9f8c2dSJonathan McDowell ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1)); 8035c9f8c2dSJonathan McDowell writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) | 8045c9f8c2dSJonathan McDowell ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2)); 8055c9f8c2dSJonathan McDowell writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf), 8065c9f8c2dSJonathan McDowell adev->regs + ADM_GP_CTL); 8075c9f8c2dSJonathan McDowell 8085c9f8c2dSJonathan McDowell ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq, 8095c9f8c2dSJonathan McDowell 0, "adm_dma", adev); 8105c9f8c2dSJonathan McDowell if (ret) 8115c9f8c2dSJonathan McDowell goto err_disable_clks; 8125c9f8c2dSJonathan McDowell 8135c9f8c2dSJonathan McDowell platform_set_drvdata(pdev, adev); 8145c9f8c2dSJonathan McDowell 8155c9f8c2dSJonathan McDowell adev->common.dev = adev->dev; 8165c9f8c2dSJonathan McDowell adev->common.dev->dma_parms = &adev->dma_parms; 8175c9f8c2dSJonathan McDowell 8185c9f8c2dSJonathan McDowell /* set capabilities */ 8195c9f8c2dSJonathan McDowell dma_cap_zero(adev->common.cap_mask); 8205c9f8c2dSJonathan McDowell dma_cap_set(DMA_SLAVE, adev->common.cap_mask); 8215c9f8c2dSJonathan McDowell dma_cap_set(DMA_PRIVATE, adev->common.cap_mask); 8225c9f8c2dSJonathan McDowell 8235c9f8c2dSJonathan McDowell /* initialize dmaengine apis */ 8245c9f8c2dSJonathan McDowell adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV); 8255c9f8c2dSJonathan McDowell adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; 8265c9f8c2dSJonathan McDowell adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; 8275c9f8c2dSJonathan McDowell adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; 8285c9f8c2dSJonathan McDowell adev->common.device_free_chan_resources = adm_free_chan; 8295c9f8c2dSJonathan McDowell adev->common.device_prep_slave_sg = adm_prep_slave_sg; 8305c9f8c2dSJonathan McDowell adev->common.device_issue_pending = adm_issue_pending; 8315c9f8c2dSJonathan McDowell adev->common.device_tx_status = adm_tx_status; 8325c9f8c2dSJonathan McDowell adev->common.device_terminate_all = adm_terminate_all; 8335c9f8c2dSJonathan McDowell adev->common.device_config = adm_slave_config; 8345c9f8c2dSJonathan McDowell 8355c9f8c2dSJonathan McDowell ret = dma_async_device_register(&adev->common); 8365c9f8c2dSJonathan McDowell if (ret) { 8375c9f8c2dSJonathan McDowell dev_err(adev->dev, "failed to register dma async device\n"); 8385c9f8c2dSJonathan McDowell goto err_disable_clks; 8395c9f8c2dSJonathan McDowell } 8405c9f8c2dSJonathan McDowell 8415c9f8c2dSJonathan McDowell ret = of_dma_controller_register(pdev->dev.of_node, 8425c9f8c2dSJonathan McDowell of_dma_xlate_by_chan_id, 8435c9f8c2dSJonathan McDowell &adev->common); 8445c9f8c2dSJonathan McDowell if (ret) 8455c9f8c2dSJonathan McDowell goto err_unregister_dma; 8465c9f8c2dSJonathan McDowell 8475c9f8c2dSJonathan McDowell return 0; 8485c9f8c2dSJonathan McDowell 8495c9f8c2dSJonathan McDowell err_unregister_dma: 8505c9f8c2dSJonathan McDowell dma_async_device_unregister(&adev->common); 8515c9f8c2dSJonathan McDowell err_disable_clks: 8525c9f8c2dSJonathan McDowell clk_disable_unprepare(adev->iface_clk); 8535c9f8c2dSJonathan McDowell err_disable_core_clk: 8545c9f8c2dSJonathan McDowell clk_disable_unprepare(adev->core_clk); 8555c9f8c2dSJonathan McDowell 8565c9f8c2dSJonathan McDowell return ret; 8575c9f8c2dSJonathan McDowell } 8585c9f8c2dSJonathan McDowell 8595c9f8c2dSJonathan McDowell static int adm_dma_remove(struct platform_device *pdev) 8605c9f8c2dSJonathan McDowell { 8615c9f8c2dSJonathan McDowell struct adm_device *adev = platform_get_drvdata(pdev); 8625c9f8c2dSJonathan McDowell struct adm_chan *achan; 8635c9f8c2dSJonathan McDowell u32 i; 8645c9f8c2dSJonathan McDowell 8655c9f8c2dSJonathan McDowell of_dma_controller_free(pdev->dev.of_node); 8665c9f8c2dSJonathan McDowell dma_async_device_unregister(&adev->common); 8675c9f8c2dSJonathan McDowell 8685c9f8c2dSJonathan McDowell for (i = 0; i < ADM_MAX_CHANNELS; i++) { 8695c9f8c2dSJonathan McDowell achan = &adev->channels[i]; 8705c9f8c2dSJonathan McDowell 8715c9f8c2dSJonathan McDowell /* mask IRQs for this channel/EE pair */ 8725c9f8c2dSJonathan McDowell writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); 8735c9f8c2dSJonathan McDowell 8745c9f8c2dSJonathan McDowell tasklet_kill(&adev->channels[i].vc.task); 8755c9f8c2dSJonathan McDowell adm_terminate_all(&adev->channels[i].vc.chan); 8765c9f8c2dSJonathan McDowell } 8775c9f8c2dSJonathan McDowell 8785c9f8c2dSJonathan McDowell devm_free_irq(adev->dev, adev->irq, adev); 8795c9f8c2dSJonathan McDowell 8805c9f8c2dSJonathan McDowell clk_disable_unprepare(adev->core_clk); 8815c9f8c2dSJonathan McDowell clk_disable_unprepare(adev->iface_clk); 8825c9f8c2dSJonathan McDowell 8835c9f8c2dSJonathan McDowell return 0; 8845c9f8c2dSJonathan McDowell } 8855c9f8c2dSJonathan McDowell 8865c9f8c2dSJonathan McDowell static const struct of_device_id adm_of_match[] = { 8875c9f8c2dSJonathan McDowell { .compatible = "qcom,adm", }, 8885c9f8c2dSJonathan McDowell {} 8895c9f8c2dSJonathan McDowell }; 8905c9f8c2dSJonathan McDowell MODULE_DEVICE_TABLE(of, adm_of_match); 8915c9f8c2dSJonathan McDowell 8925c9f8c2dSJonathan McDowell static struct platform_driver adm_dma_driver = { 8935c9f8c2dSJonathan McDowell .probe = adm_dma_probe, 8945c9f8c2dSJonathan McDowell .remove = adm_dma_remove, 8955c9f8c2dSJonathan McDowell .driver = { 8965c9f8c2dSJonathan McDowell .name = "adm-dma-engine", 8975c9f8c2dSJonathan McDowell .of_match_table = adm_of_match, 8985c9f8c2dSJonathan McDowell }, 8995c9f8c2dSJonathan McDowell }; 9005c9f8c2dSJonathan McDowell 9015c9f8c2dSJonathan McDowell module_platform_driver(adm_dma_driver); 9025c9f8c2dSJonathan McDowell 9035c9f8c2dSJonathan McDowell MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); 9045c9f8c2dSJonathan McDowell MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); 9055c9f8c2dSJonathan McDowell MODULE_LICENSE("GPL v2"); 906