18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Renesas USB DMA Controller Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Renesas Electronics Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * based on rcar-dmac.c 88c2ecf20Sopenharmony_ci * Copyright (C) 2014 Renesas Electronics Inc. 98c2ecf20Sopenharmony_ci * Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 148c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/list.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_dma.h> 208c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "../dmaengine.h" 278c2ecf20Sopenharmony_ci#include "../virt-dma.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * struct usb_dmac_sg - Descriptor for a hardware transfer 318c2ecf20Sopenharmony_ci * @mem_addr: memory address 328c2ecf20Sopenharmony_ci * @size: transfer size in bytes 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cistruct usb_dmac_sg { 358c2ecf20Sopenharmony_ci dma_addr_t mem_addr; 368c2ecf20Sopenharmony_ci u32 size; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * struct usb_dmac_desc - USB DMA Transfer Descriptor 418c2ecf20Sopenharmony_ci * @vd: base virtual channel DMA transaction descriptor 428c2ecf20Sopenharmony_ci * @direction: direction of the DMA transfer 438c2ecf20Sopenharmony_ci * @sg_allocated_len: length of allocated sg 448c2ecf20Sopenharmony_ci * @sg_len: length of sg 458c2ecf20Sopenharmony_ci * @sg_index: index of sg 468c2ecf20Sopenharmony_ci * @residue: residue after the DMAC completed a transfer 478c2ecf20Sopenharmony_ci * @node: node for desc_got and desc_freed 488c2ecf20Sopenharmony_ci * @done_cookie: cookie after the DMAC completed a transfer 498c2ecf20Sopenharmony_ci * @sg: information for the transfer 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistruct usb_dmac_desc { 528c2ecf20Sopenharmony_ci struct virt_dma_desc vd; 538c2ecf20Sopenharmony_ci enum dma_transfer_direction direction; 548c2ecf20Sopenharmony_ci unsigned int sg_allocated_len; 558c2ecf20Sopenharmony_ci unsigned int sg_len; 568c2ecf20Sopenharmony_ci unsigned int sg_index; 578c2ecf20Sopenharmony_ci u32 residue; 588c2ecf20Sopenharmony_ci struct list_head node; 598c2ecf20Sopenharmony_ci dma_cookie_t done_cookie; 608c2ecf20Sopenharmony_ci struct usb_dmac_sg sg[]; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define to_usb_dmac_desc(vd) container_of(vd, struct usb_dmac_desc, vd) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * struct usb_dmac_chan - USB DMA Controller Channel 678c2ecf20Sopenharmony_ci * @vc: base virtual DMA channel object 688c2ecf20Sopenharmony_ci * @iomem: channel I/O memory base 698c2ecf20Sopenharmony_ci * @index: index of this channel in the controller 708c2ecf20Sopenharmony_ci * @irq: irq number of this channel 718c2ecf20Sopenharmony_ci * @desc: the current descriptor 728c2ecf20Sopenharmony_ci * @descs_allocated: number of descriptors allocated 738c2ecf20Sopenharmony_ci * @desc_got: got descriptors 748c2ecf20Sopenharmony_ci * @desc_freed: freed descriptors after the DMAC completed a transfer 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_cistruct usb_dmac_chan { 778c2ecf20Sopenharmony_ci struct virt_dma_chan vc; 788c2ecf20Sopenharmony_ci void __iomem *iomem; 798c2ecf20Sopenharmony_ci unsigned int index; 808c2ecf20Sopenharmony_ci int irq; 818c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc; 828c2ecf20Sopenharmony_ci int descs_allocated; 838c2ecf20Sopenharmony_ci struct list_head desc_got; 848c2ecf20Sopenharmony_ci struct list_head desc_freed; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define to_usb_dmac_chan(c) container_of(c, struct usb_dmac_chan, vc.chan) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * struct usb_dmac - USB DMA Controller 918c2ecf20Sopenharmony_ci * @engine: base DMA engine object 928c2ecf20Sopenharmony_ci * @dev: the hardware device 938c2ecf20Sopenharmony_ci * @iomem: remapped I/O memory base 948c2ecf20Sopenharmony_ci * @n_channels: number of available channels 958c2ecf20Sopenharmony_ci * @channels: array of DMAC channels 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistruct usb_dmac { 988c2ecf20Sopenharmony_ci struct dma_device engine; 998c2ecf20Sopenharmony_ci struct device *dev; 1008c2ecf20Sopenharmony_ci void __iomem *iomem; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci unsigned int n_channels; 1038c2ecf20Sopenharmony_ci struct usb_dmac_chan *channels; 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define to_usb_dmac(d) container_of(d, struct usb_dmac, engine) 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1098c2ecf20Sopenharmony_ci * Registers 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define USB_DMAC_CHAN_OFFSET(i) (0x20 + 0x20 * (i)) 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define USB_DMASWR 0x0008 1158c2ecf20Sopenharmony_ci#define USB_DMASWR_SWR (1 << 0) 1168c2ecf20Sopenharmony_ci#define USB_DMAOR 0x0060 1178c2ecf20Sopenharmony_ci#define USB_DMAOR_AE (1 << 1) 1188c2ecf20Sopenharmony_ci#define USB_DMAOR_DME (1 << 0) 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define USB_DMASAR 0x0000 1218c2ecf20Sopenharmony_ci#define USB_DMADAR 0x0004 1228c2ecf20Sopenharmony_ci#define USB_DMATCR 0x0008 1238c2ecf20Sopenharmony_ci#define USB_DMATCR_MASK 0x00ffffff 1248c2ecf20Sopenharmony_ci#define USB_DMACHCR 0x0014 1258c2ecf20Sopenharmony_ci#define USB_DMACHCR_FTE (1 << 24) 1268c2ecf20Sopenharmony_ci#define USB_DMACHCR_NULLE (1 << 16) 1278c2ecf20Sopenharmony_ci#define USB_DMACHCR_NULL (1 << 12) 1288c2ecf20Sopenharmony_ci#define USB_DMACHCR_TS_8B ((0 << 7) | (0 << 6)) 1298c2ecf20Sopenharmony_ci#define USB_DMACHCR_TS_16B ((0 << 7) | (1 << 6)) 1308c2ecf20Sopenharmony_ci#define USB_DMACHCR_TS_32B ((1 << 7) | (0 << 6)) 1318c2ecf20Sopenharmony_ci#define USB_DMACHCR_IE (1 << 5) 1328c2ecf20Sopenharmony_ci#define USB_DMACHCR_SP (1 << 2) 1338c2ecf20Sopenharmony_ci#define USB_DMACHCR_TE (1 << 1) 1348c2ecf20Sopenharmony_ci#define USB_DMACHCR_DE (1 << 0) 1358c2ecf20Sopenharmony_ci#define USB_DMATEND 0x0018 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* Hardcode the xfer_shift to 5 (32bytes) */ 1388c2ecf20Sopenharmony_ci#define USB_DMAC_XFER_SHIFT 5 1398c2ecf20Sopenharmony_ci#define USB_DMAC_XFER_SIZE (1 << USB_DMAC_XFER_SHIFT) 1408c2ecf20Sopenharmony_ci#define USB_DMAC_CHCR_TS USB_DMACHCR_TS_32B 1418c2ecf20Sopenharmony_ci#define USB_DMAC_SLAVE_BUSWIDTH DMA_SLAVE_BUSWIDTH_32_BYTES 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* for descriptors */ 1448c2ecf20Sopenharmony_ci#define USB_DMAC_INITIAL_NR_DESC 16 1458c2ecf20Sopenharmony_ci#define USB_DMAC_INITIAL_NR_SG 8 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1488c2ecf20Sopenharmony_ci * Device access 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void usb_dmac_write(struct usb_dmac *dmac, u32 reg, u32 data) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci writel(data, dmac->iomem + reg); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic u32 usb_dmac_read(struct usb_dmac *dmac, u32 reg) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci return readl(dmac->iomem + reg); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic u32 usb_dmac_chan_read(struct usb_dmac_chan *chan, u32 reg) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci return readl(chan->iomem + reg); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void usb_dmac_chan_write(struct usb_dmac_chan *chan, u32 reg, u32 data) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci writel(data, chan->iomem + reg); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1728c2ecf20Sopenharmony_ci * Initialization and configuration 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic bool usb_dmac_chan_is_busy(struct usb_dmac_chan *chan) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci u32 chcr = usb_dmac_chan_read(chan, USB_DMACHCR); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return (chcr & (USB_DMACHCR_DE | USB_DMACHCR_TE)) == USB_DMACHCR_DE; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic u32 usb_dmac_calc_tend(u32 size) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci /* 1858c2ecf20Sopenharmony_ci * Please refer to the Figure "Example of Final Transaction Valid 1868c2ecf20Sopenharmony_ci * Data Transfer Enable (EDTEN) Setting" in the data sheet. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ci return 0xffffffff << (32 - (size % USB_DMAC_XFER_SIZE ? : 1898c2ecf20Sopenharmony_ci USB_DMAC_XFER_SIZE)); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* This function is already held by vc.lock */ 1938c2ecf20Sopenharmony_cistatic void usb_dmac_chan_start_sg(struct usb_dmac_chan *chan, 1948c2ecf20Sopenharmony_ci unsigned int index) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc = chan->desc; 1978c2ecf20Sopenharmony_ci struct usb_dmac_sg *sg = desc->sg + index; 1988c2ecf20Sopenharmony_ci dma_addr_t src_addr = 0, dst_addr = 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci WARN_ON_ONCE(usb_dmac_chan_is_busy(chan)); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (desc->direction == DMA_DEV_TO_MEM) 2038c2ecf20Sopenharmony_ci dst_addr = sg->mem_addr; 2048c2ecf20Sopenharmony_ci else 2058c2ecf20Sopenharmony_ci src_addr = sg->mem_addr; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci dev_dbg(chan->vc.chan.device->dev, 2088c2ecf20Sopenharmony_ci "chan%u: queue sg %p: %u@%pad -> %pad\n", 2098c2ecf20Sopenharmony_ci chan->index, sg, sg->size, &src_addr, &dst_addr); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci usb_dmac_chan_write(chan, USB_DMASAR, src_addr & 0xffffffff); 2128c2ecf20Sopenharmony_ci usb_dmac_chan_write(chan, USB_DMADAR, dst_addr & 0xffffffff); 2138c2ecf20Sopenharmony_ci usb_dmac_chan_write(chan, USB_DMATCR, 2148c2ecf20Sopenharmony_ci DIV_ROUND_UP(sg->size, USB_DMAC_XFER_SIZE)); 2158c2ecf20Sopenharmony_ci usb_dmac_chan_write(chan, USB_DMATEND, usb_dmac_calc_tend(sg->size)); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci usb_dmac_chan_write(chan, USB_DMACHCR, USB_DMAC_CHCR_TS | 2188c2ecf20Sopenharmony_ci USB_DMACHCR_NULLE | USB_DMACHCR_IE | USB_DMACHCR_DE); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* This function is already held by vc.lock */ 2228c2ecf20Sopenharmony_cistatic void usb_dmac_chan_start_desc(struct usb_dmac_chan *chan) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci vd = vchan_next_desc(&chan->vc); 2278c2ecf20Sopenharmony_ci if (!vd) { 2288c2ecf20Sopenharmony_ci chan->desc = NULL; 2298c2ecf20Sopenharmony_ci return; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* 2338c2ecf20Sopenharmony_ci * Remove this request from vc->desc_issued. Otherwise, this driver 2348c2ecf20Sopenharmony_ci * will get the previous value from vchan_next_desc() after a transfer 2358c2ecf20Sopenharmony_ci * was completed. 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_ci list_del(&vd->node); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci chan->desc = to_usb_dmac_desc(vd); 2408c2ecf20Sopenharmony_ci chan->desc->sg_index = 0; 2418c2ecf20Sopenharmony_ci usb_dmac_chan_start_sg(chan, 0); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int usb_dmac_init(struct usb_dmac *dmac) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci u16 dmaor; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Clear all channels and enable the DMAC globally. */ 2498c2ecf20Sopenharmony_ci usb_dmac_write(dmac, USB_DMAOR, USB_DMAOR_DME); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dmaor = usb_dmac_read(dmac, USB_DMAOR); 2528c2ecf20Sopenharmony_ci if ((dmaor & (USB_DMAOR_AE | USB_DMAOR_DME)) != USB_DMAOR_DME) { 2538c2ecf20Sopenharmony_ci dev_warn(dmac->dev, "DMAOR initialization failed.\n"); 2548c2ecf20Sopenharmony_ci return -EIO; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 2618c2ecf20Sopenharmony_ci * Descriptors allocation and free 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_cistatic int usb_dmac_desc_alloc(struct usb_dmac_chan *chan, unsigned int sg_len, 2648c2ecf20Sopenharmony_ci gfp_t gfp) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc; 2678c2ecf20Sopenharmony_ci unsigned long flags; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci desc = kzalloc(struct_size(desc, sg, sg_len), gfp); 2708c2ecf20Sopenharmony_ci if (!desc) 2718c2ecf20Sopenharmony_ci return -ENOMEM; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci desc->sg_allocated_len = sg_len; 2748c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&desc->node); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 2778c2ecf20Sopenharmony_ci list_add_tail(&desc->node, &chan->desc_freed); 2788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic void usb_dmac_desc_free(struct usb_dmac_chan *chan) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc, *_desc; 2868c2ecf20Sopenharmony_ci LIST_HEAD(list); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci list_splice_init(&chan->desc_freed, &list); 2898c2ecf20Sopenharmony_ci list_splice_init(&chan->desc_got, &list); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &list, node) { 2928c2ecf20Sopenharmony_ci list_del(&desc->node); 2938c2ecf20Sopenharmony_ci kfree(desc); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci chan->descs_allocated = 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic struct usb_dmac_desc *usb_dmac_desc_get(struct usb_dmac_chan *chan, 2998c2ecf20Sopenharmony_ci unsigned int sg_len, gfp_t gfp) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc = NULL; 3028c2ecf20Sopenharmony_ci unsigned long flags; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Get a freed descritpor */ 3058c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 3068c2ecf20Sopenharmony_ci list_for_each_entry(desc, &chan->desc_freed, node) { 3078c2ecf20Sopenharmony_ci if (sg_len <= desc->sg_allocated_len) { 3088c2ecf20Sopenharmony_ci list_move_tail(&desc->node, &chan->desc_got); 3098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 3108c2ecf20Sopenharmony_ci return desc; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Allocate a new descriptor */ 3168c2ecf20Sopenharmony_ci if (!usb_dmac_desc_alloc(chan, sg_len, gfp)) { 3178c2ecf20Sopenharmony_ci /* If allocated the desc, it was added to tail of the list */ 3188c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 3198c2ecf20Sopenharmony_ci desc = list_last_entry(&chan->desc_freed, struct usb_dmac_desc, 3208c2ecf20Sopenharmony_ci node); 3218c2ecf20Sopenharmony_ci list_move_tail(&desc->node, &chan->desc_got); 3228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 3238c2ecf20Sopenharmony_ci return desc; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return NULL; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void usb_dmac_desc_put(struct usb_dmac_chan *chan, 3308c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci unsigned long flags; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 3358c2ecf20Sopenharmony_ci list_move_tail(&desc->node, &chan->desc_freed); 3368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 3408c2ecf20Sopenharmony_ci * Stop and reset 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic void usb_dmac_soft_reset(struct usb_dmac_chan *uchan) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct dma_chan *chan = &uchan->vc.chan; 3468c2ecf20Sopenharmony_ci struct usb_dmac *dmac = to_usb_dmac(chan->device); 3478c2ecf20Sopenharmony_ci int i; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Don't issue soft reset if any one of channels is busy */ 3508c2ecf20Sopenharmony_ci for (i = 0; i < dmac->n_channels; ++i) { 3518c2ecf20Sopenharmony_ci if (usb_dmac_chan_is_busy(uchan)) 3528c2ecf20Sopenharmony_ci return; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci usb_dmac_write(dmac, USB_DMAOR, 0); 3568c2ecf20Sopenharmony_ci usb_dmac_write(dmac, USB_DMASWR, USB_DMASWR_SWR); 3578c2ecf20Sopenharmony_ci udelay(100); 3588c2ecf20Sopenharmony_ci usb_dmac_write(dmac, USB_DMASWR, 0); 3598c2ecf20Sopenharmony_ci usb_dmac_write(dmac, USB_DMAOR, 1); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic void usb_dmac_chan_halt(struct usb_dmac_chan *chan) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci u32 chcr = usb_dmac_chan_read(chan, USB_DMACHCR); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci chcr &= ~(USB_DMACHCR_IE | USB_DMACHCR_TE | USB_DMACHCR_DE); 3678c2ecf20Sopenharmony_ci usb_dmac_chan_write(chan, USB_DMACHCR, chcr); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci usb_dmac_soft_reset(chan); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic void usb_dmac_stop(struct usb_dmac *dmac) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci usb_dmac_write(dmac, USB_DMAOR, 0); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 3788c2ecf20Sopenharmony_ci * DMA engine operations 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int usb_dmac_alloc_chan_resources(struct dma_chan *chan) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); 3848c2ecf20Sopenharmony_ci int ret; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci while (uchan->descs_allocated < USB_DMAC_INITIAL_NR_DESC) { 3878c2ecf20Sopenharmony_ci ret = usb_dmac_desc_alloc(uchan, USB_DMAC_INITIAL_NR_SG, 3888c2ecf20Sopenharmony_ci GFP_KERNEL); 3898c2ecf20Sopenharmony_ci if (ret < 0) { 3908c2ecf20Sopenharmony_ci usb_dmac_desc_free(uchan); 3918c2ecf20Sopenharmony_ci return ret; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci uchan->descs_allocated++; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return pm_runtime_get_sync(chan->device->dev); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic void usb_dmac_free_chan_resources(struct dma_chan *chan) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); 4028c2ecf20Sopenharmony_ci unsigned long flags; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* Protect against ISR */ 4058c2ecf20Sopenharmony_ci spin_lock_irqsave(&uchan->vc.lock, flags); 4068c2ecf20Sopenharmony_ci usb_dmac_chan_halt(uchan); 4078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&uchan->vc.lock, flags); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci usb_dmac_desc_free(uchan); 4108c2ecf20Sopenharmony_ci vchan_free_chan_resources(&uchan->vc); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci pm_runtime_put(chan->device->dev); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor * 4168c2ecf20Sopenharmony_ciusb_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, 4178c2ecf20Sopenharmony_ci unsigned int sg_len, enum dma_transfer_direction dir, 4188c2ecf20Sopenharmony_ci unsigned long dma_flags, void *context) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); 4218c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc; 4228c2ecf20Sopenharmony_ci struct scatterlist *sg; 4238c2ecf20Sopenharmony_ci int i; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (!sg_len) { 4268c2ecf20Sopenharmony_ci dev_warn(chan->device->dev, 4278c2ecf20Sopenharmony_ci "%s: bad parameter: len=%d\n", __func__, sg_len); 4288c2ecf20Sopenharmony_ci return NULL; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci desc = usb_dmac_desc_get(uchan, sg_len, GFP_NOWAIT); 4328c2ecf20Sopenharmony_ci if (!desc) 4338c2ecf20Sopenharmony_ci return NULL; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci desc->direction = dir; 4368c2ecf20Sopenharmony_ci desc->sg_len = sg_len; 4378c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 4388c2ecf20Sopenharmony_ci desc->sg[i].mem_addr = sg_dma_address(sg); 4398c2ecf20Sopenharmony_ci desc->sg[i].size = sg_dma_len(sg); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return vchan_tx_prep(&uchan->vc, &desc->vd, dma_flags); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int usb_dmac_chan_terminate_all(struct dma_chan *chan) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); 4488c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc, *_desc; 4498c2ecf20Sopenharmony_ci unsigned long flags; 4508c2ecf20Sopenharmony_ci LIST_HEAD(head); 4518c2ecf20Sopenharmony_ci LIST_HEAD(list); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci spin_lock_irqsave(&uchan->vc.lock, flags); 4548c2ecf20Sopenharmony_ci usb_dmac_chan_halt(uchan); 4558c2ecf20Sopenharmony_ci vchan_get_all_descriptors(&uchan->vc, &head); 4568c2ecf20Sopenharmony_ci if (uchan->desc) 4578c2ecf20Sopenharmony_ci uchan->desc = NULL; 4588c2ecf20Sopenharmony_ci list_splice_init(&uchan->desc_got, &list); 4598c2ecf20Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &list, node) 4608c2ecf20Sopenharmony_ci list_move_tail(&desc->node, &uchan->desc_freed); 4618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&uchan->vc.lock, flags); 4628c2ecf20Sopenharmony_ci vchan_dma_desc_free_list(&uchan->vc, &head); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic unsigned int usb_dmac_get_current_residue(struct usb_dmac_chan *chan, 4688c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc, 4698c2ecf20Sopenharmony_ci int sg_index) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct usb_dmac_sg *sg = desc->sg + sg_index; 4728c2ecf20Sopenharmony_ci u32 mem_addr = sg->mem_addr & 0xffffffff; 4738c2ecf20Sopenharmony_ci unsigned int residue = sg->size; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* 4768c2ecf20Sopenharmony_ci * We cannot use USB_DMATCR to calculate residue because USB_DMATCR 4778c2ecf20Sopenharmony_ci * has unsuited value to calculate. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci if (desc->direction == DMA_DEV_TO_MEM) 4808c2ecf20Sopenharmony_ci residue -= usb_dmac_chan_read(chan, USB_DMADAR) - mem_addr; 4818c2ecf20Sopenharmony_ci else 4828c2ecf20Sopenharmony_ci residue -= usb_dmac_chan_read(chan, USB_DMASAR) - mem_addr; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return residue; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic u32 usb_dmac_chan_get_residue_if_complete(struct usb_dmac_chan *chan, 4888c2ecf20Sopenharmony_ci dma_cookie_t cookie) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc; 4918c2ecf20Sopenharmony_ci u32 residue = 0; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci list_for_each_entry_reverse(desc, &chan->desc_freed, node) { 4948c2ecf20Sopenharmony_ci if (desc->done_cookie == cookie) { 4958c2ecf20Sopenharmony_ci residue = desc->residue; 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return residue; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic u32 usb_dmac_chan_get_residue(struct usb_dmac_chan *chan, 5048c2ecf20Sopenharmony_ci dma_cookie_t cookie) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci u32 residue = 0; 5078c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 5088c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc = chan->desc; 5098c2ecf20Sopenharmony_ci int i; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (!desc) { 5128c2ecf20Sopenharmony_ci vd = vchan_find_desc(&chan->vc, cookie); 5138c2ecf20Sopenharmony_ci if (!vd) 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci desc = to_usb_dmac_desc(vd); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Compute the size of all usb_dmac_sg still to be transferred */ 5198c2ecf20Sopenharmony_ci for (i = desc->sg_index + 1; i < desc->sg_len; i++) 5208c2ecf20Sopenharmony_ci residue += desc->sg[i].size; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* Add the residue for the current sg */ 5238c2ecf20Sopenharmony_ci residue += usb_dmac_get_current_residue(chan, desc, desc->sg_index); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return residue; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic enum dma_status usb_dmac_tx_status(struct dma_chan *chan, 5298c2ecf20Sopenharmony_ci dma_cookie_t cookie, 5308c2ecf20Sopenharmony_ci struct dma_tx_state *txstate) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); 5338c2ecf20Sopenharmony_ci enum dma_status status; 5348c2ecf20Sopenharmony_ci unsigned int residue = 0; 5358c2ecf20Sopenharmony_ci unsigned long flags; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci status = dma_cookie_status(chan, cookie, txstate); 5388c2ecf20Sopenharmony_ci /* a client driver will get residue after DMA_COMPLETE */ 5398c2ecf20Sopenharmony_ci if (!txstate) 5408c2ecf20Sopenharmony_ci return status; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci spin_lock_irqsave(&uchan->vc.lock, flags); 5438c2ecf20Sopenharmony_ci if (status == DMA_COMPLETE) 5448c2ecf20Sopenharmony_ci residue = usb_dmac_chan_get_residue_if_complete(uchan, cookie); 5458c2ecf20Sopenharmony_ci else 5468c2ecf20Sopenharmony_ci residue = usb_dmac_chan_get_residue(uchan, cookie); 5478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&uchan->vc.lock, flags); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci dma_set_residue(txstate, residue); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return status; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic void usb_dmac_issue_pending(struct dma_chan *chan) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); 5578c2ecf20Sopenharmony_ci unsigned long flags; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci spin_lock_irqsave(&uchan->vc.lock, flags); 5608c2ecf20Sopenharmony_ci if (vchan_issue_pending(&uchan->vc) && !uchan->desc) 5618c2ecf20Sopenharmony_ci usb_dmac_chan_start_desc(uchan); 5628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&uchan->vc.lock, flags); 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic void usb_dmac_virt_desc_free(struct virt_dma_desc *vd) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc = to_usb_dmac_desc(vd); 5688c2ecf20Sopenharmony_ci struct usb_dmac_chan *chan = to_usb_dmac_chan(vd->tx.chan); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci usb_dmac_desc_put(chan, desc); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 5748c2ecf20Sopenharmony_ci * IRQ handling 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic void usb_dmac_isr_transfer_end(struct usb_dmac_chan *chan) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct usb_dmac_desc *desc = chan->desc; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci BUG_ON(!desc); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (++desc->sg_index < desc->sg_len) { 5848c2ecf20Sopenharmony_ci usb_dmac_chan_start_sg(chan, desc->sg_index); 5858c2ecf20Sopenharmony_ci } else { 5868c2ecf20Sopenharmony_ci desc->residue = usb_dmac_get_current_residue(chan, desc, 5878c2ecf20Sopenharmony_ci desc->sg_index - 1); 5888c2ecf20Sopenharmony_ci desc->done_cookie = desc->vd.tx.cookie; 5898c2ecf20Sopenharmony_ci desc->vd.tx_result.result = DMA_TRANS_NOERROR; 5908c2ecf20Sopenharmony_ci desc->vd.tx_result.residue = desc->residue; 5918c2ecf20Sopenharmony_ci vchan_cookie_complete(&desc->vd); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* Restart the next transfer if this driver has a next desc */ 5948c2ecf20Sopenharmony_ci usb_dmac_chan_start_desc(chan); 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic irqreturn_t usb_dmac_isr_channel(int irq, void *dev) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct usb_dmac_chan *chan = dev; 6018c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 6028c2ecf20Sopenharmony_ci u32 mask = 0; 6038c2ecf20Sopenharmony_ci u32 chcr; 6048c2ecf20Sopenharmony_ci bool xfer_end = false; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci spin_lock(&chan->vc.lock); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci chcr = usb_dmac_chan_read(chan, USB_DMACHCR); 6098c2ecf20Sopenharmony_ci if (chcr & (USB_DMACHCR_TE | USB_DMACHCR_SP)) { 6108c2ecf20Sopenharmony_ci mask |= USB_DMACHCR_DE | USB_DMACHCR_TE | USB_DMACHCR_SP; 6118c2ecf20Sopenharmony_ci if (chcr & USB_DMACHCR_DE) 6128c2ecf20Sopenharmony_ci xfer_end = true; 6138c2ecf20Sopenharmony_ci ret |= IRQ_HANDLED; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci if (chcr & USB_DMACHCR_NULL) { 6168c2ecf20Sopenharmony_ci /* An interruption of TE will happen after we set FTE */ 6178c2ecf20Sopenharmony_ci mask |= USB_DMACHCR_NULL; 6188c2ecf20Sopenharmony_ci chcr |= USB_DMACHCR_FTE; 6198c2ecf20Sopenharmony_ci ret |= IRQ_HANDLED; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci if (mask) 6228c2ecf20Sopenharmony_ci usb_dmac_chan_write(chan, USB_DMACHCR, chcr & ~mask); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (xfer_end) 6258c2ecf20Sopenharmony_ci usb_dmac_isr_transfer_end(chan); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci spin_unlock(&chan->vc.lock); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return ret; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 6338c2ecf20Sopenharmony_ci * OF xlate and channel filter 6348c2ecf20Sopenharmony_ci */ 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic bool usb_dmac_chan_filter(struct dma_chan *chan, void *arg) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); 6398c2ecf20Sopenharmony_ci struct of_phandle_args *dma_spec = arg; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* USB-DMAC should be used with fixed usb controller's FIFO */ 6428c2ecf20Sopenharmony_ci if (uchan->index != dma_spec->args[0]) 6438c2ecf20Sopenharmony_ci return false; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return true; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic struct dma_chan *usb_dmac_of_xlate(struct of_phandle_args *dma_spec, 6498c2ecf20Sopenharmony_ci struct of_dma *ofdma) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct dma_chan *chan; 6528c2ecf20Sopenharmony_ci dma_cap_mask_t mask; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (dma_spec->args_count != 1) 6558c2ecf20Sopenharmony_ci return NULL; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* Only slave DMA channels can be allocated via DT */ 6588c2ecf20Sopenharmony_ci dma_cap_zero(mask); 6598c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci chan = __dma_request_channel(&mask, usb_dmac_chan_filter, dma_spec, 6628c2ecf20Sopenharmony_ci ofdma->of_node); 6638c2ecf20Sopenharmony_ci if (!chan) 6648c2ecf20Sopenharmony_ci return NULL; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return chan; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 6708c2ecf20Sopenharmony_ci * Power management 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 6748c2ecf20Sopenharmony_cistatic int usb_dmac_runtime_suspend(struct device *dev) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct usb_dmac *dmac = dev_get_drvdata(dev); 6778c2ecf20Sopenharmony_ci int i; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci for (i = 0; i < dmac->n_channels; ++i) { 6808c2ecf20Sopenharmony_ci if (!dmac->channels[i].iomem) 6818c2ecf20Sopenharmony_ci break; 6828c2ecf20Sopenharmony_ci usb_dmac_chan_halt(&dmac->channels[i]); 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci return 0; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic int usb_dmac_runtime_resume(struct device *dev) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci struct usb_dmac *dmac = dev_get_drvdata(dev); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return usb_dmac_init(dmac); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic const struct dev_pm_ops usb_dmac_pm = { 6978c2ecf20Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 6988c2ecf20Sopenharmony_ci pm_runtime_force_resume) 6998c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume, 7008c2ecf20Sopenharmony_ci NULL) 7018c2ecf20Sopenharmony_ci}; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 7048c2ecf20Sopenharmony_ci * Probe and remove 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic int usb_dmac_chan_probe(struct usb_dmac *dmac, 7088c2ecf20Sopenharmony_ci struct usb_dmac_chan *uchan, 7098c2ecf20Sopenharmony_ci unsigned int index) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dmac->dev); 7128c2ecf20Sopenharmony_ci char pdev_irqname[5]; 7138c2ecf20Sopenharmony_ci char *irqname; 7148c2ecf20Sopenharmony_ci int ret; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci uchan->index = index; 7178c2ecf20Sopenharmony_ci uchan->iomem = dmac->iomem + USB_DMAC_CHAN_OFFSET(index); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* Request the channel interrupt. */ 7208c2ecf20Sopenharmony_ci sprintf(pdev_irqname, "ch%u", index); 7218c2ecf20Sopenharmony_ci uchan->irq = platform_get_irq_byname(pdev, pdev_irqname); 7228c2ecf20Sopenharmony_ci if (uchan->irq < 0) 7238c2ecf20Sopenharmony_ci return -ENODEV; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u", 7268c2ecf20Sopenharmony_ci dev_name(dmac->dev), index); 7278c2ecf20Sopenharmony_ci if (!irqname) 7288c2ecf20Sopenharmony_ci return -ENOMEM; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci ret = devm_request_irq(dmac->dev, uchan->irq, usb_dmac_isr_channel, 7318c2ecf20Sopenharmony_ci IRQF_SHARED, irqname, uchan); 7328c2ecf20Sopenharmony_ci if (ret) { 7338c2ecf20Sopenharmony_ci dev_err(dmac->dev, "failed to request IRQ %u (%d)\n", 7348c2ecf20Sopenharmony_ci uchan->irq, ret); 7358c2ecf20Sopenharmony_ci return ret; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci uchan->vc.desc_free = usb_dmac_virt_desc_free; 7398c2ecf20Sopenharmony_ci vchan_init(&uchan->vc, &dmac->engine); 7408c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&uchan->desc_freed); 7418c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&uchan->desc_got); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci return 0; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic int usb_dmac_parse_of(struct device *dev, struct usb_dmac *dmac) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 7498c2ecf20Sopenharmony_ci int ret; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "dma-channels", &dmac->n_channels); 7528c2ecf20Sopenharmony_ci if (ret < 0) { 7538c2ecf20Sopenharmony_ci dev_err(dev, "unable to read dma-channels property\n"); 7548c2ecf20Sopenharmony_ci return ret; 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (dmac->n_channels <= 0 || dmac->n_channels >= 100) { 7588c2ecf20Sopenharmony_ci dev_err(dev, "invalid number of channels %u\n", 7598c2ecf20Sopenharmony_ci dmac->n_channels); 7608c2ecf20Sopenharmony_ci return -EINVAL; 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return 0; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic int usb_dmac_probe(struct platform_device *pdev) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci const enum dma_slave_buswidth widths = USB_DMAC_SLAVE_BUSWIDTH; 7698c2ecf20Sopenharmony_ci struct dma_device *engine; 7708c2ecf20Sopenharmony_ci struct usb_dmac *dmac; 7718c2ecf20Sopenharmony_ci struct resource *mem; 7728c2ecf20Sopenharmony_ci unsigned int i; 7738c2ecf20Sopenharmony_ci int ret; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL); 7768c2ecf20Sopenharmony_ci if (!dmac) 7778c2ecf20Sopenharmony_ci return -ENOMEM; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci dmac->dev = &pdev->dev; 7808c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dmac); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci ret = usb_dmac_parse_of(&pdev->dev, dmac); 7838c2ecf20Sopenharmony_ci if (ret < 0) 7848c2ecf20Sopenharmony_ci return ret; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci dmac->channels = devm_kcalloc(&pdev->dev, dmac->n_channels, 7878c2ecf20Sopenharmony_ci sizeof(*dmac->channels), GFP_KERNEL); 7888c2ecf20Sopenharmony_ci if (!dmac->channels) 7898c2ecf20Sopenharmony_ci return -ENOMEM; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* Request resources. */ 7928c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 7938c2ecf20Sopenharmony_ci dmac->iomem = devm_ioremap_resource(&pdev->dev, mem); 7948c2ecf20Sopenharmony_ci if (IS_ERR(dmac->iomem)) 7958c2ecf20Sopenharmony_ci return PTR_ERR(dmac->iomem); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci /* Enable runtime PM and initialize the device. */ 7988c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 7998c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(&pdev->dev); 8008c2ecf20Sopenharmony_ci if (ret < 0) { 8018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "runtime PM get sync failed (%d)\n", ret); 8028c2ecf20Sopenharmony_ci goto error_pm; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci ret = usb_dmac_init(dmac); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (ret) { 8088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to reset device\n"); 8098c2ecf20Sopenharmony_ci goto error; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* Initialize the channels. */ 8138c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dmac->engine.channels); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci for (i = 0; i < dmac->n_channels; ++i) { 8168c2ecf20Sopenharmony_ci ret = usb_dmac_chan_probe(dmac, &dmac->channels[i], i); 8178c2ecf20Sopenharmony_ci if (ret < 0) 8188c2ecf20Sopenharmony_ci goto error; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* Register the DMAC as a DMA provider for DT. */ 8228c2ecf20Sopenharmony_ci ret = of_dma_controller_register(pdev->dev.of_node, usb_dmac_of_xlate, 8238c2ecf20Sopenharmony_ci NULL); 8248c2ecf20Sopenharmony_ci if (ret < 0) 8258c2ecf20Sopenharmony_ci goto error; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci /* 8288c2ecf20Sopenharmony_ci * Register the DMA engine device. 8298c2ecf20Sopenharmony_ci * 8308c2ecf20Sopenharmony_ci * Default transfer size of 32 bytes requires 32-byte alignment. 8318c2ecf20Sopenharmony_ci */ 8328c2ecf20Sopenharmony_ci engine = &dmac->engine; 8338c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, engine->cap_mask); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci engine->dev = &pdev->dev; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci engine->src_addr_widths = widths; 8388c2ecf20Sopenharmony_ci engine->dst_addr_widths = widths; 8398c2ecf20Sopenharmony_ci engine->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); 8408c2ecf20Sopenharmony_ci engine->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci engine->device_alloc_chan_resources = usb_dmac_alloc_chan_resources; 8438c2ecf20Sopenharmony_ci engine->device_free_chan_resources = usb_dmac_free_chan_resources; 8448c2ecf20Sopenharmony_ci engine->device_prep_slave_sg = usb_dmac_prep_slave_sg; 8458c2ecf20Sopenharmony_ci engine->device_terminate_all = usb_dmac_chan_terminate_all; 8468c2ecf20Sopenharmony_ci engine->device_tx_status = usb_dmac_tx_status; 8478c2ecf20Sopenharmony_ci engine->device_issue_pending = usb_dmac_issue_pending; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci ret = dma_async_device_register(engine); 8508c2ecf20Sopenharmony_ci if (ret < 0) 8518c2ecf20Sopenharmony_ci goto error; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci pm_runtime_put(&pdev->dev); 8548c2ecf20Sopenharmony_ci return 0; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cierror: 8578c2ecf20Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 8588c2ecf20Sopenharmony_cierror_pm: 8598c2ecf20Sopenharmony_ci pm_runtime_put(&pdev->dev); 8608c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 8618c2ecf20Sopenharmony_ci return ret; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic void usb_dmac_chan_remove(struct usb_dmac *dmac, 8658c2ecf20Sopenharmony_ci struct usb_dmac_chan *uchan) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci usb_dmac_chan_halt(uchan); 8688c2ecf20Sopenharmony_ci devm_free_irq(dmac->dev, uchan->irq, uchan); 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int usb_dmac_remove(struct platform_device *pdev) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct usb_dmac *dmac = platform_get_drvdata(pdev); 8748c2ecf20Sopenharmony_ci int i; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci for (i = 0; i < dmac->n_channels; ++i) 8778c2ecf20Sopenharmony_ci usb_dmac_chan_remove(dmac, &dmac->channels[i]); 8788c2ecf20Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 8798c2ecf20Sopenharmony_ci dma_async_device_unregister(&dmac->engine); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic void usb_dmac_shutdown(struct platform_device *pdev) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct usb_dmac *dmac = platform_get_drvdata(pdev); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci usb_dmac_stop(dmac); 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic const struct of_device_id usb_dmac_of_ids[] = { 8948c2ecf20Sopenharmony_ci { .compatible = "renesas,usb-dmac", }, 8958c2ecf20Sopenharmony_ci { /* Sentinel */ } 8968c2ecf20Sopenharmony_ci}; 8978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, usb_dmac_of_ids); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic struct platform_driver usb_dmac_driver = { 9008c2ecf20Sopenharmony_ci .driver = { 9018c2ecf20Sopenharmony_ci .pm = &usb_dmac_pm, 9028c2ecf20Sopenharmony_ci .name = "usb-dmac", 9038c2ecf20Sopenharmony_ci .of_match_table = usb_dmac_of_ids, 9048c2ecf20Sopenharmony_ci }, 9058c2ecf20Sopenharmony_ci .probe = usb_dmac_probe, 9068c2ecf20Sopenharmony_ci .remove = usb_dmac_remove, 9078c2ecf20Sopenharmony_ci .shutdown = usb_dmac_shutdown, 9088c2ecf20Sopenharmony_ci}; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cimodule_platform_driver(usb_dmac_driver); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Renesas USB DMA Controller Driver"); 9138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>"); 9148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 915