18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MediaTek UART APDMA driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2019 MediaTek Inc. 68c2ecf20Sopenharmony_ci * Author: Long Cheng <long.cheng@mediatek.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/list.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/of_device.h> 208c2ecf20Sopenharmony_ci#include <linux/of_dma.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 "../virt-dma.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* The default number of virtual channel */ 298c2ecf20Sopenharmony_ci#define MTK_UART_APDMA_NR_VCHANS 8 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define VFF_EN_B BIT(0) 328c2ecf20Sopenharmony_ci#define VFF_STOP_B BIT(0) 338c2ecf20Sopenharmony_ci#define VFF_FLUSH_B BIT(0) 348c2ecf20Sopenharmony_ci#define VFF_4G_EN_B BIT(0) 358c2ecf20Sopenharmony_ci/* rx valid size >= vff thre */ 368c2ecf20Sopenharmony_ci#define VFF_RX_INT_EN_B (BIT(0) | BIT(1)) 378c2ecf20Sopenharmony_ci/* tx left size >= vff thre */ 388c2ecf20Sopenharmony_ci#define VFF_TX_INT_EN_B BIT(0) 398c2ecf20Sopenharmony_ci#define VFF_WARM_RST_B BIT(0) 408c2ecf20Sopenharmony_ci#define VFF_RX_INT_CLR_B (BIT(0) | BIT(1)) 418c2ecf20Sopenharmony_ci#define VFF_TX_INT_CLR_B 0 428c2ecf20Sopenharmony_ci#define VFF_STOP_CLR_B 0 438c2ecf20Sopenharmony_ci#define VFF_EN_CLR_B 0 448c2ecf20Sopenharmony_ci#define VFF_INT_EN_CLR_B 0 458c2ecf20Sopenharmony_ci#define VFF_4G_SUPPORT_CLR_B 0 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * interrupt trigger level for tx 498c2ecf20Sopenharmony_ci * if threshold is n, no polling is required to start tx. 508c2ecf20Sopenharmony_ci * otherwise need polling VFF_FLUSH. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci#define VFF_TX_THRE(n) (n) 538c2ecf20Sopenharmony_ci/* interrupt trigger level for rx */ 548c2ecf20Sopenharmony_ci#define VFF_RX_THRE(n) ((n) * 3 / 4) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define VFF_RING_SIZE 0xffff 578c2ecf20Sopenharmony_ci/* invert this bit when wrap ring head again */ 588c2ecf20Sopenharmony_ci#define VFF_RING_WRAP 0x10000 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define VFF_INT_FLAG 0x00 618c2ecf20Sopenharmony_ci#define VFF_INT_EN 0x04 628c2ecf20Sopenharmony_ci#define VFF_EN 0x08 638c2ecf20Sopenharmony_ci#define VFF_RST 0x0c 648c2ecf20Sopenharmony_ci#define VFF_STOP 0x10 658c2ecf20Sopenharmony_ci#define VFF_FLUSH 0x14 668c2ecf20Sopenharmony_ci#define VFF_ADDR 0x1c 678c2ecf20Sopenharmony_ci#define VFF_LEN 0x24 688c2ecf20Sopenharmony_ci#define VFF_THRE 0x28 698c2ecf20Sopenharmony_ci#define VFF_WPT 0x2c 708c2ecf20Sopenharmony_ci#define VFF_RPT 0x30 718c2ecf20Sopenharmony_ci/* TX: the buffer size HW can read. RX: the buffer size SW can read. */ 728c2ecf20Sopenharmony_ci#define VFF_VALID_SIZE 0x3c 738c2ecf20Sopenharmony_ci/* TX: the buffer size SW can write. RX: the buffer size HW can write. */ 748c2ecf20Sopenharmony_ci#define VFF_LEFT_SIZE 0x40 758c2ecf20Sopenharmony_ci#define VFF_DEBUG_STATUS 0x50 768c2ecf20Sopenharmony_ci#define VFF_4G_SUPPORT 0x54 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistruct mtk_uart_apdmadev { 798c2ecf20Sopenharmony_ci struct dma_device ddev; 808c2ecf20Sopenharmony_ci struct clk *clk; 818c2ecf20Sopenharmony_ci bool support_33bits; 828c2ecf20Sopenharmony_ci unsigned int dma_requests; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct mtk_uart_apdma_desc { 868c2ecf20Sopenharmony_ci struct virt_dma_desc vd; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci dma_addr_t addr; 898c2ecf20Sopenharmony_ci unsigned int avail_len; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct mtk_chan { 938c2ecf20Sopenharmony_ci struct virt_dma_chan vc; 948c2ecf20Sopenharmony_ci struct dma_slave_config cfg; 958c2ecf20Sopenharmony_ci struct mtk_uart_apdma_desc *desc; 968c2ecf20Sopenharmony_ci enum dma_transfer_direction dir; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci void __iomem *base; 998c2ecf20Sopenharmony_ci unsigned int irq; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci unsigned int rx_status; 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic inline struct mtk_uart_apdmadev * 1058c2ecf20Sopenharmony_cito_mtk_uart_apdma_dev(struct dma_device *d) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci return container_of(d, struct mtk_uart_apdmadev, ddev); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic inline struct mtk_chan *to_mtk_uart_apdma_chan(struct dma_chan *c) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return container_of(c, struct mtk_chan, vc.chan); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic inline struct mtk_uart_apdma_desc *to_mtk_uart_apdma_desc 1168c2ecf20Sopenharmony_ci (struct dma_async_tx_descriptor *t) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci return container_of(t, struct mtk_uart_apdma_desc, vd.tx); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void mtk_uart_apdma_write(struct mtk_chan *c, 1228c2ecf20Sopenharmony_ci unsigned int reg, unsigned int val) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci writel(val, c->base + reg); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic unsigned int mtk_uart_apdma_read(struct mtk_chan *c, unsigned int reg) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci return readl(c->base + reg); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void mtk_uart_apdma_desc_free(struct virt_dma_desc *vd) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci kfree(container_of(vd, struct mtk_uart_apdma_desc, vd)); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void mtk_uart_apdma_start_tx(struct mtk_chan *c) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct mtk_uart_apdmadev *mtkd = 1408c2ecf20Sopenharmony_ci to_mtk_uart_apdma_dev(c->vc.chan.device); 1418c2ecf20Sopenharmony_ci struct mtk_uart_apdma_desc *d = c->desc; 1428c2ecf20Sopenharmony_ci unsigned int wpt, vff_sz; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci vff_sz = c->cfg.dst_port_window_size; 1458c2ecf20Sopenharmony_ci if (!mtk_uart_apdma_read(c, VFF_LEN)) { 1468c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_ADDR, d->addr); 1478c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_LEN, vff_sz); 1488c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(vff_sz)); 1498c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_WPT, 0); 1508c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (mtkd->support_33bits) 1538c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_EN_B); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B); 1578c2ecf20Sopenharmony_ci if (mtk_uart_apdma_read(c, VFF_EN) != VFF_EN_B) 1588c2ecf20Sopenharmony_ci dev_err(c->vc.chan.device->dev, "Enable TX fail\n"); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!mtk_uart_apdma_read(c, VFF_LEFT_SIZE)) { 1618c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B); 1628c2ecf20Sopenharmony_ci return; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci wpt = mtk_uart_apdma_read(c, VFF_WPT); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci wpt += c->desc->avail_len; 1688c2ecf20Sopenharmony_ci if ((wpt & VFF_RING_SIZE) == vff_sz) 1698c2ecf20Sopenharmony_ci wpt = (wpt & VFF_RING_WRAP) ^ VFF_RING_WRAP; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Let DMA start moving data */ 1728c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_WPT, wpt); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* HW auto set to 0 when left size >= threshold */ 1758c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B); 1768c2ecf20Sopenharmony_ci if (!mtk_uart_apdma_read(c, VFF_FLUSH)) 1778c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void mtk_uart_apdma_start_rx(struct mtk_chan *c) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct mtk_uart_apdmadev *mtkd = 1838c2ecf20Sopenharmony_ci to_mtk_uart_apdma_dev(c->vc.chan.device); 1848c2ecf20Sopenharmony_ci struct mtk_uart_apdma_desc *d = c->desc; 1858c2ecf20Sopenharmony_ci unsigned int vff_sz; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci vff_sz = c->cfg.src_port_window_size; 1888c2ecf20Sopenharmony_ci if (!mtk_uart_apdma_read(c, VFF_LEN)) { 1898c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_ADDR, d->addr); 1908c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_LEN, vff_sz); 1918c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(vff_sz)); 1928c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_RPT, 0); 1938c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (mtkd->support_33bits) 1968c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_EN_B); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_EN, VFF_RX_INT_EN_B); 2008c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B); 2018c2ecf20Sopenharmony_ci if (mtk_uart_apdma_read(c, VFF_EN) != VFF_EN_B) 2028c2ecf20Sopenharmony_ci dev_err(c->vc.chan.device->dev, "Enable RX fail\n"); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void mtk_uart_apdma_tx_handler(struct mtk_chan *c) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); 2088c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B); 2098c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_EN, VFF_EN_CLR_B); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void mtk_uart_apdma_rx_handler(struct mtk_chan *c) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct mtk_uart_apdma_desc *d = c->desc; 2158c2ecf20Sopenharmony_ci unsigned int len, wg, rg; 2168c2ecf20Sopenharmony_ci int cnt; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (!mtk_uart_apdma_read(c, VFF_VALID_SIZE)) 2218c2ecf20Sopenharmony_ci return; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_EN, VFF_EN_CLR_B); 2248c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci len = c->cfg.src_port_window_size; 2278c2ecf20Sopenharmony_ci rg = mtk_uart_apdma_read(c, VFF_RPT); 2288c2ecf20Sopenharmony_ci wg = mtk_uart_apdma_read(c, VFF_WPT); 2298c2ecf20Sopenharmony_ci cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* 2328c2ecf20Sopenharmony_ci * The buffer is ring buffer. If wrap bit different, 2338c2ecf20Sopenharmony_ci * represents the start of the next cycle for WPT 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci if ((rg ^ wg) & VFF_RING_WRAP) 2368c2ecf20Sopenharmony_ci cnt += len; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci c->rx_status = d->avail_len - cnt; 2398c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_RPT, wg); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic void mtk_uart_apdma_chan_complete_handler(struct mtk_chan *c) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct mtk_uart_apdma_desc *d = c->desc; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (d) { 2478c2ecf20Sopenharmony_ci list_del(&d->vd.node); 2488c2ecf20Sopenharmony_ci vchan_cookie_complete(&d->vd); 2498c2ecf20Sopenharmony_ci c->desc = NULL; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic irqreturn_t mtk_uart_apdma_irq_handler(int irq, void *dev_id) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct dma_chan *chan = (struct dma_chan *)dev_id; 2568c2ecf20Sopenharmony_ci struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); 2578c2ecf20Sopenharmony_ci unsigned long flags; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 2608c2ecf20Sopenharmony_ci if (c->dir == DMA_DEV_TO_MEM) 2618c2ecf20Sopenharmony_ci mtk_uart_apdma_rx_handler(c); 2628c2ecf20Sopenharmony_ci else if (c->dir == DMA_MEM_TO_DEV) 2638c2ecf20Sopenharmony_ci mtk_uart_apdma_tx_handler(c); 2648c2ecf20Sopenharmony_ci mtk_uart_apdma_chan_complete_handler(c); 2658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int mtk_uart_apdma_alloc_chan_resources(struct dma_chan *chan) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device); 2738c2ecf20Sopenharmony_ci struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); 2748c2ecf20Sopenharmony_ci unsigned int status; 2758c2ecf20Sopenharmony_ci int ret; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(mtkd->ddev.dev); 2788c2ecf20Sopenharmony_ci if (ret < 0) { 2798c2ecf20Sopenharmony_ci pm_runtime_put_noidle(chan->device->dev); 2808c2ecf20Sopenharmony_ci return ret; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_ADDR, 0); 2848c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_THRE, 0); 2858c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_LEN, 0); 2868c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_RST, VFF_WARM_RST_B); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = readx_poll_timeout(readl, c->base + VFF_EN, 2898c2ecf20Sopenharmony_ci status, !status, 10, 100); 2908c2ecf20Sopenharmony_ci if (ret) 2918c2ecf20Sopenharmony_ci goto err_pm; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ret = request_irq(c->irq, mtk_uart_apdma_irq_handler, 2948c2ecf20Sopenharmony_ci IRQF_TRIGGER_NONE, KBUILD_MODNAME, chan); 2958c2ecf20Sopenharmony_ci if (ret < 0) { 2968c2ecf20Sopenharmony_ci dev_err(chan->device->dev, "Can't request dma IRQ\n"); 2978c2ecf20Sopenharmony_ci ret = -EINVAL; 2988c2ecf20Sopenharmony_ci goto err_pm; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (mtkd->support_33bits) 3028c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cierr_pm: 3058c2ecf20Sopenharmony_ci pm_runtime_put_noidle(mtkd->ddev.dev); 3068c2ecf20Sopenharmony_ci return ret; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic void mtk_uart_apdma_free_chan_resources(struct dma_chan *chan) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device); 3128c2ecf20Sopenharmony_ci struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci free_irq(c->irq, chan); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci tasklet_kill(&c->vc.task); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci vchan_free_chan_resources(&c->vc); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci pm_runtime_put_sync(mtkd->ddev.dev); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic enum dma_status mtk_uart_apdma_tx_status(struct dma_chan *chan, 3248c2ecf20Sopenharmony_ci dma_cookie_t cookie, 3258c2ecf20Sopenharmony_ci struct dma_tx_state *txstate) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); 3288c2ecf20Sopenharmony_ci enum dma_status ret; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 3318c2ecf20Sopenharmony_ci if (!txstate) 3328c2ecf20Sopenharmony_ci return ret; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci dma_set_residue(txstate, c->rx_status); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return ret; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/* 3408c2ecf20Sopenharmony_ci * dmaengine_prep_slave_single will call the function. and sglen is 1. 3418c2ecf20Sopenharmony_ci * 8250 uart using one ring buffer, and deal with one sg. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *mtk_uart_apdma_prep_slave_sg 3448c2ecf20Sopenharmony_ci (struct dma_chan *chan, struct scatterlist *sgl, 3458c2ecf20Sopenharmony_ci unsigned int sglen, enum dma_transfer_direction dir, 3468c2ecf20Sopenharmony_ci unsigned long tx_flags, void *context) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); 3498c2ecf20Sopenharmony_ci struct mtk_uart_apdma_desc *d; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (!is_slave_direction(dir) || sglen != 1) 3528c2ecf20Sopenharmony_ci return NULL; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Now allocate and setup the descriptor */ 3558c2ecf20Sopenharmony_ci d = kzalloc(sizeof(*d), GFP_NOWAIT); 3568c2ecf20Sopenharmony_ci if (!d) 3578c2ecf20Sopenharmony_ci return NULL; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci d->avail_len = sg_dma_len(sgl); 3608c2ecf20Sopenharmony_ci d->addr = sg_dma_address(sgl); 3618c2ecf20Sopenharmony_ci c->dir = dir; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return vchan_tx_prep(&c->vc, &d->vd, tx_flags); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic void mtk_uart_apdma_issue_pending(struct dma_chan *chan) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); 3698c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 3708c2ecf20Sopenharmony_ci unsigned long flags; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 3738c2ecf20Sopenharmony_ci if (vchan_issue_pending(&c->vc) && !c->desc) { 3748c2ecf20Sopenharmony_ci vd = vchan_next_desc(&c->vc); 3758c2ecf20Sopenharmony_ci c->desc = to_mtk_uart_apdma_desc(&vd->tx); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (c->dir == DMA_DEV_TO_MEM) 3788c2ecf20Sopenharmony_ci mtk_uart_apdma_start_rx(c); 3798c2ecf20Sopenharmony_ci else if (c->dir == DMA_MEM_TO_DEV) 3808c2ecf20Sopenharmony_ci mtk_uart_apdma_start_tx(c); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int mtk_uart_apdma_slave_config(struct dma_chan *chan, 3878c2ecf20Sopenharmony_ci struct dma_slave_config *config) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci memcpy(&c->cfg, config, sizeof(*config)); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int mtk_uart_apdma_terminate_all(struct dma_chan *chan) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); 3998c2ecf20Sopenharmony_ci unsigned long flags; 4008c2ecf20Sopenharmony_ci unsigned int status; 4018c2ecf20Sopenharmony_ci LIST_HEAD(head); 4028c2ecf20Sopenharmony_ci int ret; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, 4078c2ecf20Sopenharmony_ci status, status != VFF_FLUSH_B, 10, 100); 4088c2ecf20Sopenharmony_ci if (ret) 4098c2ecf20Sopenharmony_ci dev_err(c->vc.chan.device->dev, "flush: fail, status=0x%x\n", 4108c2ecf20Sopenharmony_ci mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* 4138c2ecf20Sopenharmony_ci * Stop need 3 steps. 4148c2ecf20Sopenharmony_ci * 1. set stop to 1 4158c2ecf20Sopenharmony_ci * 2. wait en to 0 4168c2ecf20Sopenharmony_ci * 3. set stop as 0 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_B); 4198c2ecf20Sopenharmony_ci ret = readx_poll_timeout(readl, c->base + VFF_EN, 4208c2ecf20Sopenharmony_ci status, !status, 10, 100); 4218c2ecf20Sopenharmony_ci if (ret) 4228c2ecf20Sopenharmony_ci dev_err(c->vc.chan.device->dev, "stop: fail, status=0x%x\n", 4238c2ecf20Sopenharmony_ci mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_CLR_B); 4268c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (c->dir == DMA_DEV_TO_MEM) 4298c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); 4308c2ecf20Sopenharmony_ci else if (c->dir == DMA_MEM_TO_DEV) 4318c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci synchronize_irq(c->irq); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 4368c2ecf20Sopenharmony_ci vchan_get_all_descriptors(&c->vc, &head); 4378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci vchan_dma_desc_free_list(&c->vc, &head); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int mtk_uart_apdma_device_pause(struct dma_chan *chan) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); 4478c2ecf20Sopenharmony_ci unsigned long flags; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_EN, VFF_EN_CLR_B); 4528c2ecf20Sopenharmony_ci mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 4558c2ecf20Sopenharmony_ci synchronize_irq(c->irq); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic void mtk_uart_apdma_free(struct mtk_uart_apdmadev *mtkd) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci while (!list_empty(&mtkd->ddev.channels)) { 4638c2ecf20Sopenharmony_ci struct mtk_chan *c = list_first_entry(&mtkd->ddev.channels, 4648c2ecf20Sopenharmony_ci struct mtk_chan, vc.chan.device_node); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci list_del(&c->vc.chan.device_node); 4678c2ecf20Sopenharmony_ci tasklet_kill(&c->vc.task); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic const struct of_device_id mtk_uart_apdma_match[] = { 4728c2ecf20Sopenharmony_ci { .compatible = "mediatek,mt6577-uart-dma", }, 4738c2ecf20Sopenharmony_ci { /* sentinel */ }, 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_uart_apdma_match); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int mtk_uart_apdma_probe(struct platform_device *pdev) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 4808c2ecf20Sopenharmony_ci struct mtk_uart_apdmadev *mtkd; 4818c2ecf20Sopenharmony_ci int bit_mask = 32, rc; 4828c2ecf20Sopenharmony_ci struct mtk_chan *c; 4838c2ecf20Sopenharmony_ci unsigned int i; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL); 4868c2ecf20Sopenharmony_ci if (!mtkd) 4878c2ecf20Sopenharmony_ci return -ENOMEM; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci mtkd->clk = devm_clk_get(&pdev->dev, NULL); 4908c2ecf20Sopenharmony_ci if (IS_ERR(mtkd->clk)) { 4918c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No clock specified\n"); 4928c2ecf20Sopenharmony_ci rc = PTR_ERR(mtkd->clk); 4938c2ecf20Sopenharmony_ci return rc; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "mediatek,dma-33bits")) 4978c2ecf20Sopenharmony_ci mtkd->support_33bits = true; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (mtkd->support_33bits) 5008c2ecf20Sopenharmony_ci bit_mask = 33; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(bit_mask)); 5038c2ecf20Sopenharmony_ci if (rc) 5048c2ecf20Sopenharmony_ci return rc; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, mtkd->ddev.cap_mask); 5078c2ecf20Sopenharmony_ci mtkd->ddev.device_alloc_chan_resources = 5088c2ecf20Sopenharmony_ci mtk_uart_apdma_alloc_chan_resources; 5098c2ecf20Sopenharmony_ci mtkd->ddev.device_free_chan_resources = 5108c2ecf20Sopenharmony_ci mtk_uart_apdma_free_chan_resources; 5118c2ecf20Sopenharmony_ci mtkd->ddev.device_tx_status = mtk_uart_apdma_tx_status; 5128c2ecf20Sopenharmony_ci mtkd->ddev.device_issue_pending = mtk_uart_apdma_issue_pending; 5138c2ecf20Sopenharmony_ci mtkd->ddev.device_prep_slave_sg = mtk_uart_apdma_prep_slave_sg; 5148c2ecf20Sopenharmony_ci mtkd->ddev.device_config = mtk_uart_apdma_slave_config; 5158c2ecf20Sopenharmony_ci mtkd->ddev.device_pause = mtk_uart_apdma_device_pause; 5168c2ecf20Sopenharmony_ci mtkd->ddev.device_terminate_all = mtk_uart_apdma_terminate_all; 5178c2ecf20Sopenharmony_ci mtkd->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE); 5188c2ecf20Sopenharmony_ci mtkd->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE); 5198c2ecf20Sopenharmony_ci mtkd->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 5208c2ecf20Sopenharmony_ci mtkd->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; 5218c2ecf20Sopenharmony_ci mtkd->ddev.dev = &pdev->dev; 5228c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mtkd->ddev.channels); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci mtkd->dma_requests = MTK_UART_APDMA_NR_VCHANS; 5258c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "dma-requests", &mtkd->dma_requests)) { 5268c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 5278c2ecf20Sopenharmony_ci "Using %u as missing dma-requests property\n", 5288c2ecf20Sopenharmony_ci MTK_UART_APDMA_NR_VCHANS); 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci for (i = 0; i < mtkd->dma_requests; i++) { 5328c2ecf20Sopenharmony_ci c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL); 5338c2ecf20Sopenharmony_ci if (!c) { 5348c2ecf20Sopenharmony_ci rc = -ENODEV; 5358c2ecf20Sopenharmony_ci goto err_no_dma; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci c->base = devm_platform_ioremap_resource(pdev, i); 5398c2ecf20Sopenharmony_ci if (IS_ERR(c->base)) { 5408c2ecf20Sopenharmony_ci rc = PTR_ERR(c->base); 5418c2ecf20Sopenharmony_ci goto err_no_dma; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci c->vc.desc_free = mtk_uart_apdma_desc_free; 5448c2ecf20Sopenharmony_ci vchan_init(&c->vc, &mtkd->ddev); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci rc = platform_get_irq(pdev, i); 5478c2ecf20Sopenharmony_ci if (rc < 0) 5488c2ecf20Sopenharmony_ci goto err_no_dma; 5498c2ecf20Sopenharmony_ci c->irq = rc; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 5538c2ecf20Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci rc = dma_async_device_register(&mtkd->ddev); 5568c2ecf20Sopenharmony_ci if (rc) 5578c2ecf20Sopenharmony_ci goto rpm_disable; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mtkd); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Device-tree DMA controller registration */ 5628c2ecf20Sopenharmony_ci rc = of_dma_controller_register(np, of_dma_xlate_by_chan_id, mtkd); 5638c2ecf20Sopenharmony_ci if (rc) 5648c2ecf20Sopenharmony_ci goto dma_remove; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci return rc; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cidma_remove: 5698c2ecf20Sopenharmony_ci dma_async_device_unregister(&mtkd->ddev); 5708c2ecf20Sopenharmony_cirpm_disable: 5718c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5728c2ecf20Sopenharmony_cierr_no_dma: 5738c2ecf20Sopenharmony_ci mtk_uart_apdma_free(mtkd); 5748c2ecf20Sopenharmony_ci return rc; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic int mtk_uart_apdma_remove(struct platform_device *pdev) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct mtk_uart_apdmadev *mtkd = platform_get_drvdata(pdev); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci mtk_uart_apdma_free(mtkd); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci dma_async_device_unregister(&mtkd->ddev); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci return 0; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5938c2ecf20Sopenharmony_cistatic int mtk_uart_apdma_suspend(struct device *dev) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(dev)) 5988c2ecf20Sopenharmony_ci clk_disable_unprepare(mtkd->clk); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci return 0; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic int mtk_uart_apdma_resume(struct device *dev) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci int ret; 6068c2ecf20Sopenharmony_ci struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(dev)) { 6098c2ecf20Sopenharmony_ci ret = clk_prepare_enable(mtkd->clk); 6108c2ecf20Sopenharmony_ci if (ret) 6118c2ecf20Sopenharmony_ci return ret; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 6198c2ecf20Sopenharmony_cistatic int mtk_uart_apdma_runtime_suspend(struct device *dev) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci clk_disable_unprepare(mtkd->clk); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return 0; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic int mtk_uart_apdma_runtime_resume(struct device *dev) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return clk_prepare_enable(mtkd->clk); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mtk_uart_apdma_pm_ops = { 6378c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(mtk_uart_apdma_suspend, mtk_uart_apdma_resume) 6388c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(mtk_uart_apdma_runtime_suspend, 6398c2ecf20Sopenharmony_ci mtk_uart_apdma_runtime_resume, NULL) 6408c2ecf20Sopenharmony_ci}; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic struct platform_driver mtk_uart_apdma_driver = { 6438c2ecf20Sopenharmony_ci .probe = mtk_uart_apdma_probe, 6448c2ecf20Sopenharmony_ci .remove = mtk_uart_apdma_remove, 6458c2ecf20Sopenharmony_ci .driver = { 6468c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 6478c2ecf20Sopenharmony_ci .pm = &mtk_uart_apdma_pm_ops, 6488c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mtk_uart_apdma_match), 6498c2ecf20Sopenharmony_ci }, 6508c2ecf20Sopenharmony_ci}; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cimodule_platform_driver(mtk_uart_apdma_driver); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MediaTek UART APDMA Controller Driver"); 6558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Long Cheng <long.cheng@mediatek.com>"); 6568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 657