18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Qualcomm Technologies HIDMA DMA engine interface 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 78c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 and 88c2ecf20Sopenharmony_ci * only version 2 as published by the Free Software Foundation. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 118c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 128c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138c2ecf20Sopenharmony_ci * GNU General Public License for more details. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * Copyright (C) Freescale Semicondutor, Inc. 2007, 2008. 188c2ecf20Sopenharmony_ci * Copyright (C) Semihalf 2009 198c2ecf20Sopenharmony_ci * Copyright (C) Ilya Yanok, Emcraft Systems 2010 208c2ecf20Sopenharmony_ci * Copyright (C) Alexander Popov, Promcontroller 2014 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description 238c2ecf20Sopenharmony_ci * (defines, structures and comments) was taken from MPC5121 DMA driver 248c2ecf20Sopenharmony_ci * written by Hongjun Chen <hong-jun.chen@freescale.com>. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Approved as OSADL project by a majority of OSADL members and funded 278c2ecf20Sopenharmony_ci * by OSADL membership fees in 2009; for details see www.osadl.org. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 308c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the Free 318c2ecf20Sopenharmony_ci * Software Foundation; either version 2 of the License, or (at your option) 328c2ecf20Sopenharmony_ci * any later version. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but WITHOUT 358c2ecf20Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 368c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 378c2ecf20Sopenharmony_ci * more details. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution in the 408c2ecf20Sopenharmony_ci * file called COPYING. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Linux Foundation elects GPLv2 license only. */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 468c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 478c2ecf20Sopenharmony_ci#include <linux/list.h> 488c2ecf20Sopenharmony_ci#include <linux/module.h> 498c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 508c2ecf20Sopenharmony_ci#include <linux/slab.h> 518c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 528c2ecf20Sopenharmony_ci#include <linux/of_dma.h> 538c2ecf20Sopenharmony_ci#include <linux/of_device.h> 548c2ecf20Sopenharmony_ci#include <linux/property.h> 558c2ecf20Sopenharmony_ci#include <linux/delay.h> 568c2ecf20Sopenharmony_ci#include <linux/acpi.h> 578c2ecf20Sopenharmony_ci#include <linux/irq.h> 588c2ecf20Sopenharmony_ci#include <linux/atomic.h> 598c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 608c2ecf20Sopenharmony_ci#include <linux/msi.h> 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#include "../dmaengine.h" 638c2ecf20Sopenharmony_ci#include "hidma.h" 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * Default idle time is 2 seconds. This parameter can 678c2ecf20Sopenharmony_ci * be overridden by changing the following 688c2ecf20Sopenharmony_ci * /sys/bus/platform/devices/QCOM8061:<xy>/power/autosuspend_delay_ms 698c2ecf20Sopenharmony_ci * during kernel boot. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci#define HIDMA_AUTOSUSPEND_TIMEOUT 2000 728c2ecf20Sopenharmony_ci#define HIDMA_ERR_INFO_SW 0xFF 738c2ecf20Sopenharmony_ci#define HIDMA_ERR_CODE_UNEXPECTED_TERMINATE 0x0 748c2ecf20Sopenharmony_ci#define HIDMA_NR_DEFAULT_DESC 10 758c2ecf20Sopenharmony_ci#define HIDMA_MSI_INTS 11 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic inline struct hidma_dev *to_hidma_dev(struct dma_device *dmadev) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return container_of(dmadev, struct hidma_dev, ddev); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline 838c2ecf20Sopenharmony_cistruct hidma_dev *to_hidma_dev_from_lldev(struct hidma_lldev **_lldevp) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci return container_of(_lldevp, struct hidma_dev, lldev); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic inline struct hidma_chan *to_hidma_chan(struct dma_chan *dmach) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci return container_of(dmach, struct hidma_chan, chan); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic inline 948c2ecf20Sopenharmony_cistruct hidma_desc *to_hidma_desc(struct dma_async_tx_descriptor *t) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci return container_of(t, struct hidma_desc, desc); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void hidma_free(struct hidma_dev *dmadev) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dmadev->ddev.channels); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic unsigned int nr_desc_prm; 1058c2ecf20Sopenharmony_cimodule_param(nr_desc_prm, uint, 0644); 1068c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nr_desc_prm, "number of descriptors (default: 0)"); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cienum hidma_cap { 1098c2ecf20Sopenharmony_ci HIDMA_MSI_CAP = 1, 1108c2ecf20Sopenharmony_ci HIDMA_IDENTITY_CAP, 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* process completed descriptors */ 1148c2ecf20Sopenharmony_cistatic void hidma_process_completed(struct hidma_chan *mchan) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct dma_device *ddev = mchan->chan.device; 1178c2ecf20Sopenharmony_ci struct hidma_dev *mdma = to_hidma_dev(ddev); 1188c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc; 1198c2ecf20Sopenharmony_ci dma_cookie_t last_cookie; 1208c2ecf20Sopenharmony_ci struct hidma_desc *mdesc; 1218c2ecf20Sopenharmony_ci struct hidma_desc *next; 1228c2ecf20Sopenharmony_ci unsigned long irqflags; 1238c2ecf20Sopenharmony_ci struct list_head list; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&list); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Get all completed descriptors */ 1288c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 1298c2ecf20Sopenharmony_ci list_splice_tail_init(&mchan->completed, &list); 1308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Execute callbacks and run dependencies */ 1338c2ecf20Sopenharmony_ci list_for_each_entry_safe(mdesc, next, &list, node) { 1348c2ecf20Sopenharmony_ci enum dma_status llstat; 1358c2ecf20Sopenharmony_ci struct dmaengine_desc_callback cb; 1368c2ecf20Sopenharmony_ci struct dmaengine_result result; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci desc = &mdesc->desc; 1398c2ecf20Sopenharmony_ci last_cookie = desc->cookie; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci llstat = hidma_ll_status(mdma->lldev, mdesc->tre_ch); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 1448c2ecf20Sopenharmony_ci if (llstat == DMA_COMPLETE) { 1458c2ecf20Sopenharmony_ci mchan->last_success = last_cookie; 1468c2ecf20Sopenharmony_ci result.result = DMA_TRANS_NOERROR; 1478c2ecf20Sopenharmony_ci } else { 1488c2ecf20Sopenharmony_ci result.result = DMA_TRANS_ABORTED; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci dma_cookie_complete(desc); 1528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci dmaengine_desc_get_callback(desc, &cb); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci dma_run_dependencies(desc); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 1598c2ecf20Sopenharmony_ci list_move(&mdesc->node, &mchan->free); 1608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci dmaengine_desc_callback_invoke(&cb, &result); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/* 1678c2ecf20Sopenharmony_ci * Called once for each submitted descriptor. 1688c2ecf20Sopenharmony_ci * PM is locked once for each descriptor that is currently 1698c2ecf20Sopenharmony_ci * in execution. 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_cistatic void hidma_callback(void *data) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct hidma_desc *mdesc = data; 1748c2ecf20Sopenharmony_ci struct hidma_chan *mchan = to_hidma_chan(mdesc->desc.chan); 1758c2ecf20Sopenharmony_ci struct dma_device *ddev = mchan->chan.device; 1768c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = to_hidma_dev(ddev); 1778c2ecf20Sopenharmony_ci unsigned long irqflags; 1788c2ecf20Sopenharmony_ci bool queued = false; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 1818c2ecf20Sopenharmony_ci if (mdesc->node.next) { 1828c2ecf20Sopenharmony_ci /* Delete from the active list, add to completed list */ 1838c2ecf20Sopenharmony_ci list_move_tail(&mdesc->node, &mchan->completed); 1848c2ecf20Sopenharmony_ci queued = true; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* calculate the next running descriptor */ 1878c2ecf20Sopenharmony_ci mchan->running = list_first_entry(&mchan->active, 1888c2ecf20Sopenharmony_ci struct hidma_desc, node); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci hidma_process_completed(mchan); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (queued) { 1958c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dmadev->ddev.dev); 1968c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dmadev->ddev.dev); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int hidma_chan_init(struct hidma_dev *dmadev, u32 dma_sig) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct hidma_chan *mchan; 2038c2ecf20Sopenharmony_ci struct dma_device *ddev; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci mchan = devm_kzalloc(dmadev->ddev.dev, sizeof(*mchan), GFP_KERNEL); 2068c2ecf20Sopenharmony_ci if (!mchan) 2078c2ecf20Sopenharmony_ci return -ENOMEM; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci ddev = &dmadev->ddev; 2108c2ecf20Sopenharmony_ci mchan->dma_sig = dma_sig; 2118c2ecf20Sopenharmony_ci mchan->dmadev = dmadev; 2128c2ecf20Sopenharmony_ci mchan->chan.device = ddev; 2138c2ecf20Sopenharmony_ci dma_cookie_init(&mchan->chan); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mchan->free); 2168c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mchan->prepared); 2178c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mchan->active); 2188c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mchan->completed); 2198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mchan->queued); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci spin_lock_init(&mchan->lock); 2228c2ecf20Sopenharmony_ci list_add_tail(&mchan->chan.device_node, &ddev->channels); 2238c2ecf20Sopenharmony_ci dmadev->ddev.chancnt++; 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic void hidma_issue_task(struct tasklet_struct *t) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = from_tasklet(dmadev, t, task); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci pm_runtime_get_sync(dmadev->ddev.dev); 2328c2ecf20Sopenharmony_ci hidma_ll_start(dmadev->lldev); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void hidma_issue_pending(struct dma_chan *dmach) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct hidma_chan *mchan = to_hidma_chan(dmach); 2388c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = mchan->dmadev; 2398c2ecf20Sopenharmony_ci unsigned long flags; 2408c2ecf20Sopenharmony_ci struct hidma_desc *qdesc, *next; 2418c2ecf20Sopenharmony_ci int status; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, flags); 2448c2ecf20Sopenharmony_ci list_for_each_entry_safe(qdesc, next, &mchan->queued, node) { 2458c2ecf20Sopenharmony_ci hidma_ll_queue_request(dmadev->lldev, qdesc->tre_ch); 2468c2ecf20Sopenharmony_ci list_move_tail(&qdesc->node, &mchan->active); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (!mchan->running) { 2508c2ecf20Sopenharmony_ci struct hidma_desc *desc = list_first_entry(&mchan->active, 2518c2ecf20Sopenharmony_ci struct hidma_desc, 2528c2ecf20Sopenharmony_ci node); 2538c2ecf20Sopenharmony_ci mchan->running = desc; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, flags); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* PM will be released in hidma_callback function. */ 2588c2ecf20Sopenharmony_ci status = pm_runtime_get(dmadev->ddev.dev); 2598c2ecf20Sopenharmony_ci if (status < 0) 2608c2ecf20Sopenharmony_ci tasklet_schedule(&dmadev->task); 2618c2ecf20Sopenharmony_ci else 2628c2ecf20Sopenharmony_ci hidma_ll_start(dmadev->lldev); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic inline bool hidma_txn_is_success(dma_cookie_t cookie, 2668c2ecf20Sopenharmony_ci dma_cookie_t last_success, dma_cookie_t last_used) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci if (last_success <= last_used) { 2698c2ecf20Sopenharmony_ci if ((cookie <= last_success) || (cookie > last_used)) 2708c2ecf20Sopenharmony_ci return true; 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci if ((cookie <= last_success) && (cookie > last_used)) 2738c2ecf20Sopenharmony_ci return true; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci return false; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic enum dma_status hidma_tx_status(struct dma_chan *dmach, 2798c2ecf20Sopenharmony_ci dma_cookie_t cookie, 2808c2ecf20Sopenharmony_ci struct dma_tx_state *txstate) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct hidma_chan *mchan = to_hidma_chan(dmach); 2838c2ecf20Sopenharmony_ci enum dma_status ret; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ret = dma_cookie_status(dmach, cookie, txstate); 2868c2ecf20Sopenharmony_ci if (ret == DMA_COMPLETE) { 2878c2ecf20Sopenharmony_ci bool is_success; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci is_success = hidma_txn_is_success(cookie, mchan->last_success, 2908c2ecf20Sopenharmony_ci dmach->cookie); 2918c2ecf20Sopenharmony_ci return is_success ? ret : DMA_ERROR; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (mchan->paused && (ret == DMA_IN_PROGRESS)) { 2958c2ecf20Sopenharmony_ci unsigned long flags; 2968c2ecf20Sopenharmony_ci dma_cookie_t runcookie; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, flags); 2998c2ecf20Sopenharmony_ci if (mchan->running) 3008c2ecf20Sopenharmony_ci runcookie = mchan->running->desc.cookie; 3018c2ecf20Sopenharmony_ci else 3028c2ecf20Sopenharmony_ci runcookie = -EINVAL; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (runcookie == cookie) 3058c2ecf20Sopenharmony_ci ret = DMA_PAUSED; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, flags); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return ret; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* 3148c2ecf20Sopenharmony_ci * Submit descriptor to hardware. 3158c2ecf20Sopenharmony_ci * Lock the PM for each descriptor we are sending. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_cistatic dma_cookie_t hidma_tx_submit(struct dma_async_tx_descriptor *txd) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct hidma_chan *mchan = to_hidma_chan(txd->chan); 3208c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = mchan->dmadev; 3218c2ecf20Sopenharmony_ci struct hidma_desc *mdesc; 3228c2ecf20Sopenharmony_ci unsigned long irqflags; 3238c2ecf20Sopenharmony_ci dma_cookie_t cookie; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci pm_runtime_get_sync(dmadev->ddev.dev); 3268c2ecf20Sopenharmony_ci if (!hidma_ll_isenabled(dmadev->lldev)) { 3278c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dmadev->ddev.dev); 3288c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dmadev->ddev.dev); 3298c2ecf20Sopenharmony_ci return -ENODEV; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dmadev->ddev.dev); 3328c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dmadev->ddev.dev); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci mdesc = container_of(txd, struct hidma_desc, desc); 3358c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Move descriptor to queued */ 3388c2ecf20Sopenharmony_ci list_move_tail(&mdesc->node, &mchan->queued); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Update cookie */ 3418c2ecf20Sopenharmony_ci cookie = dma_cookie_assign(txd); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return cookie; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int hidma_alloc_chan_resources(struct dma_chan *dmach) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct hidma_chan *mchan = to_hidma_chan(dmach); 3518c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = mchan->dmadev; 3528c2ecf20Sopenharmony_ci struct hidma_desc *mdesc, *tmp; 3538c2ecf20Sopenharmony_ci unsigned long irqflags; 3548c2ecf20Sopenharmony_ci LIST_HEAD(descs); 3558c2ecf20Sopenharmony_ci unsigned int i; 3568c2ecf20Sopenharmony_ci int rc = 0; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (mchan->allocated) 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Alloc descriptors for this channel */ 3628c2ecf20Sopenharmony_ci for (i = 0; i < dmadev->nr_descriptors; i++) { 3638c2ecf20Sopenharmony_ci mdesc = kzalloc(sizeof(struct hidma_desc), GFP_NOWAIT); 3648c2ecf20Sopenharmony_ci if (!mdesc) { 3658c2ecf20Sopenharmony_ci rc = -ENOMEM; 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci dma_async_tx_descriptor_init(&mdesc->desc, dmach); 3698c2ecf20Sopenharmony_ci mdesc->desc.tx_submit = hidma_tx_submit; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci rc = hidma_ll_request(dmadev->lldev, mchan->dma_sig, 3728c2ecf20Sopenharmony_ci "DMA engine", hidma_callback, mdesc, 3738c2ecf20Sopenharmony_ci &mdesc->tre_ch); 3748c2ecf20Sopenharmony_ci if (rc) { 3758c2ecf20Sopenharmony_ci dev_err(dmach->device->dev, 3768c2ecf20Sopenharmony_ci "channel alloc failed at %u\n", i); 3778c2ecf20Sopenharmony_ci kfree(mdesc); 3788c2ecf20Sopenharmony_ci break; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci list_add_tail(&mdesc->node, &descs); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (rc) { 3848c2ecf20Sopenharmony_ci /* return the allocated descriptors */ 3858c2ecf20Sopenharmony_ci list_for_each_entry_safe(mdesc, tmp, &descs, node) { 3868c2ecf20Sopenharmony_ci hidma_ll_free(dmadev->lldev, mdesc->tre_ch); 3878c2ecf20Sopenharmony_ci kfree(mdesc); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci return rc; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 3938c2ecf20Sopenharmony_ci list_splice_tail_init(&descs, &mchan->free); 3948c2ecf20Sopenharmony_ci mchan->allocated = true; 3958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 3968c2ecf20Sopenharmony_ci return 1; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor * 4008c2ecf20Sopenharmony_cihidma_prep_dma_memcpy(struct dma_chan *dmach, dma_addr_t dest, dma_addr_t src, 4018c2ecf20Sopenharmony_ci size_t len, unsigned long flags) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct hidma_chan *mchan = to_hidma_chan(dmach); 4048c2ecf20Sopenharmony_ci struct hidma_desc *mdesc = NULL; 4058c2ecf20Sopenharmony_ci struct hidma_dev *mdma = mchan->dmadev; 4068c2ecf20Sopenharmony_ci unsigned long irqflags; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Get free descriptor */ 4098c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 4108c2ecf20Sopenharmony_ci if (!list_empty(&mchan->free)) { 4118c2ecf20Sopenharmony_ci mdesc = list_first_entry(&mchan->free, struct hidma_desc, node); 4128c2ecf20Sopenharmony_ci list_del(&mdesc->node); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (!mdesc) 4178c2ecf20Sopenharmony_ci return NULL; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci mdesc->desc.flags = flags; 4208c2ecf20Sopenharmony_ci hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch, 4218c2ecf20Sopenharmony_ci src, dest, len, flags, 4228c2ecf20Sopenharmony_ci HIDMA_TRE_MEMCPY); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* Place descriptor in prepared list */ 4258c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 4268c2ecf20Sopenharmony_ci list_add_tail(&mdesc->node, &mchan->prepared); 4278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return &mdesc->desc; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor * 4338c2ecf20Sopenharmony_cihidma_prep_dma_memset(struct dma_chan *dmach, dma_addr_t dest, int value, 4348c2ecf20Sopenharmony_ci size_t len, unsigned long flags) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct hidma_chan *mchan = to_hidma_chan(dmach); 4378c2ecf20Sopenharmony_ci struct hidma_desc *mdesc = NULL; 4388c2ecf20Sopenharmony_ci struct hidma_dev *mdma = mchan->dmadev; 4398c2ecf20Sopenharmony_ci unsigned long irqflags; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Get free descriptor */ 4428c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 4438c2ecf20Sopenharmony_ci if (!list_empty(&mchan->free)) { 4448c2ecf20Sopenharmony_ci mdesc = list_first_entry(&mchan->free, struct hidma_desc, node); 4458c2ecf20Sopenharmony_ci list_del(&mdesc->node); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (!mdesc) 4508c2ecf20Sopenharmony_ci return NULL; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci mdesc->desc.flags = flags; 4538c2ecf20Sopenharmony_ci hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch, 4548c2ecf20Sopenharmony_ci value, dest, len, flags, 4558c2ecf20Sopenharmony_ci HIDMA_TRE_MEMSET); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Place descriptor in prepared list */ 4588c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 4598c2ecf20Sopenharmony_ci list_add_tail(&mdesc->node, &mchan->prepared); 4608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci return &mdesc->desc; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int hidma_terminate_channel(struct dma_chan *chan) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct hidma_chan *mchan = to_hidma_chan(chan); 4688c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = to_hidma_dev(mchan->chan.device); 4698c2ecf20Sopenharmony_ci struct hidma_desc *tmp, *mdesc; 4708c2ecf20Sopenharmony_ci unsigned long irqflags; 4718c2ecf20Sopenharmony_ci LIST_HEAD(list); 4728c2ecf20Sopenharmony_ci int rc; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci pm_runtime_get_sync(dmadev->ddev.dev); 4758c2ecf20Sopenharmony_ci /* give completed requests a chance to finish */ 4768c2ecf20Sopenharmony_ci hidma_process_completed(mchan); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 4798c2ecf20Sopenharmony_ci mchan->last_success = 0; 4808c2ecf20Sopenharmony_ci list_splice_init(&mchan->active, &list); 4818c2ecf20Sopenharmony_ci list_splice_init(&mchan->prepared, &list); 4828c2ecf20Sopenharmony_ci list_splice_init(&mchan->completed, &list); 4838c2ecf20Sopenharmony_ci list_splice_init(&mchan->queued, &list); 4848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* this suspends the existing transfer */ 4878c2ecf20Sopenharmony_ci rc = hidma_ll_disable(dmadev->lldev); 4888c2ecf20Sopenharmony_ci if (rc) { 4898c2ecf20Sopenharmony_ci dev_err(dmadev->ddev.dev, "channel did not pause\n"); 4908c2ecf20Sopenharmony_ci goto out; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* return all user requests */ 4948c2ecf20Sopenharmony_ci list_for_each_entry_safe(mdesc, tmp, &list, node) { 4958c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *txd = &mdesc->desc; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci dma_descriptor_unmap(txd); 4988c2ecf20Sopenharmony_ci dmaengine_desc_get_callback_invoke(txd, NULL); 4998c2ecf20Sopenharmony_ci dma_run_dependencies(txd); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* move myself to free_list */ 5028c2ecf20Sopenharmony_ci list_move(&mdesc->node, &mchan->free); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci rc = hidma_ll_enable(dmadev->lldev); 5068c2ecf20Sopenharmony_ciout: 5078c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dmadev->ddev.dev); 5088c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dmadev->ddev.dev); 5098c2ecf20Sopenharmony_ci return rc; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int hidma_terminate_all(struct dma_chan *chan) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct hidma_chan *mchan = to_hidma_chan(chan); 5158c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = to_hidma_dev(mchan->chan.device); 5168c2ecf20Sopenharmony_ci int rc; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci rc = hidma_terminate_channel(chan); 5198c2ecf20Sopenharmony_ci if (rc) 5208c2ecf20Sopenharmony_ci return rc; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* reinitialize the hardware */ 5238c2ecf20Sopenharmony_ci pm_runtime_get_sync(dmadev->ddev.dev); 5248c2ecf20Sopenharmony_ci rc = hidma_ll_setup(dmadev->lldev); 5258c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dmadev->ddev.dev); 5268c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dmadev->ddev.dev); 5278c2ecf20Sopenharmony_ci return rc; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic void hidma_free_chan_resources(struct dma_chan *dmach) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct hidma_chan *mchan = to_hidma_chan(dmach); 5338c2ecf20Sopenharmony_ci struct hidma_dev *mdma = mchan->dmadev; 5348c2ecf20Sopenharmony_ci struct hidma_desc *mdesc, *tmp; 5358c2ecf20Sopenharmony_ci unsigned long irqflags; 5368c2ecf20Sopenharmony_ci LIST_HEAD(descs); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* terminate running transactions and free descriptors */ 5398c2ecf20Sopenharmony_ci hidma_terminate_channel(dmach); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci spin_lock_irqsave(&mchan->lock, irqflags); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Move data */ 5448c2ecf20Sopenharmony_ci list_splice_tail_init(&mchan->free, &descs); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* Free descriptors */ 5478c2ecf20Sopenharmony_ci list_for_each_entry_safe(mdesc, tmp, &descs, node) { 5488c2ecf20Sopenharmony_ci hidma_ll_free(mdma->lldev, mdesc->tre_ch); 5498c2ecf20Sopenharmony_ci list_del(&mdesc->node); 5508c2ecf20Sopenharmony_ci kfree(mdesc); 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci mchan->allocated = false; 5548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mchan->lock, irqflags); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic int hidma_pause(struct dma_chan *chan) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct hidma_chan *mchan; 5608c2ecf20Sopenharmony_ci struct hidma_dev *dmadev; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci mchan = to_hidma_chan(chan); 5638c2ecf20Sopenharmony_ci dmadev = to_hidma_dev(mchan->chan.device); 5648c2ecf20Sopenharmony_ci if (!mchan->paused) { 5658c2ecf20Sopenharmony_ci pm_runtime_get_sync(dmadev->ddev.dev); 5668c2ecf20Sopenharmony_ci if (hidma_ll_disable(dmadev->lldev)) 5678c2ecf20Sopenharmony_ci dev_warn(dmadev->ddev.dev, "channel did not stop\n"); 5688c2ecf20Sopenharmony_ci mchan->paused = true; 5698c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dmadev->ddev.dev); 5708c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dmadev->ddev.dev); 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci return 0; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic int hidma_resume(struct dma_chan *chan) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct hidma_chan *mchan; 5788c2ecf20Sopenharmony_ci struct hidma_dev *dmadev; 5798c2ecf20Sopenharmony_ci int rc = 0; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci mchan = to_hidma_chan(chan); 5828c2ecf20Sopenharmony_ci dmadev = to_hidma_dev(mchan->chan.device); 5838c2ecf20Sopenharmony_ci if (mchan->paused) { 5848c2ecf20Sopenharmony_ci pm_runtime_get_sync(dmadev->ddev.dev); 5858c2ecf20Sopenharmony_ci rc = hidma_ll_enable(dmadev->lldev); 5868c2ecf20Sopenharmony_ci if (!rc) 5878c2ecf20Sopenharmony_ci mchan->paused = false; 5888c2ecf20Sopenharmony_ci else 5898c2ecf20Sopenharmony_ci dev_err(dmadev->ddev.dev, 5908c2ecf20Sopenharmony_ci "failed to resume the channel"); 5918c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dmadev->ddev.dev); 5928c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dmadev->ddev.dev); 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci return rc; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic irqreturn_t hidma_chirq_handler(int chirq, void *arg) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct hidma_lldev *lldev = arg; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* 6028c2ecf20Sopenharmony_ci * All interrupts are request driven. 6038c2ecf20Sopenharmony_ci * HW doesn't send an interrupt by itself. 6048c2ecf20Sopenharmony_ci */ 6058c2ecf20Sopenharmony_ci return hidma_ll_inthandler(chirq, lldev); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN 6098c2ecf20Sopenharmony_cistatic irqreturn_t hidma_chirq_handler_msi(int chirq, void *arg) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct hidma_lldev **lldevp = arg; 6128c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = to_hidma_dev_from_lldev(lldevp); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return hidma_ll_inthandler_msi(chirq, *lldevp, 6158c2ecf20Sopenharmony_ci 1 << (chirq - dmadev->msi_virqbase)); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci#endif 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic ssize_t hidma_show_values(struct device *dev, 6208c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci struct hidma_dev *mdev = dev_get_drvdata(dev); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci buf[0] = 0; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (strcmp(attr->attr.name, "chid") == 0) 6278c2ecf20Sopenharmony_ci sprintf(buf, "%d\n", mdev->chidx); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return strlen(buf); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic inline void hidma_sysfs_uninit(struct hidma_dev *dev) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci device_remove_file(dev->ddev.dev, dev->chid_attrs); 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic struct device_attribute* 6388c2ecf20Sopenharmony_cihidma_create_sysfs_entry(struct hidma_dev *dev, char *name, int mode) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci struct device_attribute *attrs; 6418c2ecf20Sopenharmony_ci char *name_copy; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci attrs = devm_kmalloc(dev->ddev.dev, sizeof(struct device_attribute), 6448c2ecf20Sopenharmony_ci GFP_KERNEL); 6458c2ecf20Sopenharmony_ci if (!attrs) 6468c2ecf20Sopenharmony_ci return NULL; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci name_copy = devm_kstrdup(dev->ddev.dev, name, GFP_KERNEL); 6498c2ecf20Sopenharmony_ci if (!name_copy) 6508c2ecf20Sopenharmony_ci return NULL; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci attrs->attr.name = name_copy; 6538c2ecf20Sopenharmony_ci attrs->attr.mode = mode; 6548c2ecf20Sopenharmony_ci attrs->show = hidma_show_values; 6558c2ecf20Sopenharmony_ci sysfs_attr_init(&attrs->attr); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return attrs; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int hidma_sysfs_init(struct hidma_dev *dev) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci dev->chid_attrs = hidma_create_sysfs_entry(dev, "chid", S_IRUGO); 6638c2ecf20Sopenharmony_ci if (!dev->chid_attrs) 6648c2ecf20Sopenharmony_ci return -ENOMEM; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return device_create_file(dev->ddev.dev, dev->chid_attrs); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN 6708c2ecf20Sopenharmony_cistatic void hidma_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct device *dev = msi_desc_to_dev(desc); 6738c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = dev_get_drvdata(dev); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (!desc->platform.msi_index) { 6768c2ecf20Sopenharmony_ci writel(msg->address_lo, dmadev->dev_evca + 0x118); 6778c2ecf20Sopenharmony_ci writel(msg->address_hi, dmadev->dev_evca + 0x11C); 6788c2ecf20Sopenharmony_ci writel(msg->data, dmadev->dev_evca + 0x120); 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci#endif 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic void hidma_free_msis(struct hidma_dev *dmadev) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN 6868c2ecf20Sopenharmony_ci struct device *dev = dmadev->ddev.dev; 6878c2ecf20Sopenharmony_ci struct msi_desc *desc; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* free allocated MSI interrupts above */ 6908c2ecf20Sopenharmony_ci for_each_msi_entry(desc, dev) 6918c2ecf20Sopenharmony_ci devm_free_irq(dev, desc->irq, &dmadev->lldev); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci platform_msi_domain_free_irqs(dev); 6948c2ecf20Sopenharmony_ci#endif 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic int hidma_request_msi(struct hidma_dev *dmadev, 6988c2ecf20Sopenharmony_ci struct platform_device *pdev) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN 7018c2ecf20Sopenharmony_ci int rc; 7028c2ecf20Sopenharmony_ci struct msi_desc *desc; 7038c2ecf20Sopenharmony_ci struct msi_desc *failed_desc = NULL; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci rc = platform_msi_domain_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS, 7068c2ecf20Sopenharmony_ci hidma_write_msi_msg); 7078c2ecf20Sopenharmony_ci if (rc) 7088c2ecf20Sopenharmony_ci return rc; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci for_each_msi_entry(desc, &pdev->dev) { 7118c2ecf20Sopenharmony_ci if (!desc->platform.msi_index) 7128c2ecf20Sopenharmony_ci dmadev->msi_virqbase = desc->irq; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci rc = devm_request_irq(&pdev->dev, desc->irq, 7158c2ecf20Sopenharmony_ci hidma_chirq_handler_msi, 7168c2ecf20Sopenharmony_ci 0, "qcom-hidma-msi", 7178c2ecf20Sopenharmony_ci &dmadev->lldev); 7188c2ecf20Sopenharmony_ci if (rc) { 7198c2ecf20Sopenharmony_ci failed_desc = desc; 7208c2ecf20Sopenharmony_ci break; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (rc) { 7258c2ecf20Sopenharmony_ci /* free allocated MSI interrupts above */ 7268c2ecf20Sopenharmony_ci for_each_msi_entry(desc, &pdev->dev) { 7278c2ecf20Sopenharmony_ci if (desc == failed_desc) 7288c2ecf20Sopenharmony_ci break; 7298c2ecf20Sopenharmony_ci devm_free_irq(&pdev->dev, desc->irq, 7308c2ecf20Sopenharmony_ci &dmadev->lldev); 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci } else { 7338c2ecf20Sopenharmony_ci /* Add callback to free MSIs on teardown */ 7348c2ecf20Sopenharmony_ci hidma_ll_setup_irq(dmadev->lldev, true); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci if (rc) 7388c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 7398c2ecf20Sopenharmony_ci "failed to request MSI irq, falling back to wired IRQ\n"); 7408c2ecf20Sopenharmony_ci return rc; 7418c2ecf20Sopenharmony_ci#else 7428c2ecf20Sopenharmony_ci return -EINVAL; 7438c2ecf20Sopenharmony_ci#endif 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic bool hidma_test_capability(struct device *dev, enum hidma_cap test_cap) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci enum hidma_cap cap; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci cap = (enum hidma_cap) device_get_match_data(dev); 7518c2ecf20Sopenharmony_ci return cap ? ((cap & test_cap) > 0) : 0; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic int hidma_probe(struct platform_device *pdev) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct hidma_dev *dmadev; 7578c2ecf20Sopenharmony_ci struct resource *trca_resource; 7588c2ecf20Sopenharmony_ci struct resource *evca_resource; 7598c2ecf20Sopenharmony_ci int chirq; 7608c2ecf20Sopenharmony_ci void __iomem *evca; 7618c2ecf20Sopenharmony_ci void __iomem *trca; 7628c2ecf20Sopenharmony_ci int rc; 7638c2ecf20Sopenharmony_ci bool msi; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT); 7668c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 7678c2ecf20Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 7688c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci trca_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); 7718c2ecf20Sopenharmony_ci trca = devm_ioremap_resource(&pdev->dev, trca_resource); 7728c2ecf20Sopenharmony_ci if (IS_ERR(trca)) { 7738c2ecf20Sopenharmony_ci rc = -ENOMEM; 7748c2ecf20Sopenharmony_ci goto bailout; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci evca_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); 7788c2ecf20Sopenharmony_ci evca = devm_ioremap_resource(&pdev->dev, evca_resource); 7798c2ecf20Sopenharmony_ci if (IS_ERR(evca)) { 7808c2ecf20Sopenharmony_ci rc = -ENOMEM; 7818c2ecf20Sopenharmony_ci goto bailout; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* 7858c2ecf20Sopenharmony_ci * This driver only handles the channel IRQs. 7868c2ecf20Sopenharmony_ci * Common IRQ is handled by the management driver. 7878c2ecf20Sopenharmony_ci */ 7888c2ecf20Sopenharmony_ci chirq = platform_get_irq(pdev, 0); 7898c2ecf20Sopenharmony_ci if (chirq < 0) { 7908c2ecf20Sopenharmony_ci rc = -ENODEV; 7918c2ecf20Sopenharmony_ci goto bailout; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL); 7958c2ecf20Sopenharmony_ci if (!dmadev) { 7968c2ecf20Sopenharmony_ci rc = -ENOMEM; 7978c2ecf20Sopenharmony_ci goto bailout; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dmadev->ddev.channels); 8018c2ecf20Sopenharmony_ci spin_lock_init(&dmadev->lock); 8028c2ecf20Sopenharmony_ci dmadev->ddev.dev = &pdev->dev; 8038c2ecf20Sopenharmony_ci pm_runtime_get_sync(dmadev->ddev.dev); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dmadev->ddev.cap_mask); 8068c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMSET, dmadev->ddev.cap_mask); 8078c2ecf20Sopenharmony_ci if (WARN_ON(!pdev->dev.dma_mask)) { 8088c2ecf20Sopenharmony_ci rc = -ENXIO; 8098c2ecf20Sopenharmony_ci goto dmafree; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci dmadev->dev_evca = evca; 8138c2ecf20Sopenharmony_ci dmadev->evca_resource = evca_resource; 8148c2ecf20Sopenharmony_ci dmadev->dev_trca = trca; 8158c2ecf20Sopenharmony_ci dmadev->trca_resource = trca_resource; 8168c2ecf20Sopenharmony_ci dmadev->ddev.device_prep_dma_memcpy = hidma_prep_dma_memcpy; 8178c2ecf20Sopenharmony_ci dmadev->ddev.device_prep_dma_memset = hidma_prep_dma_memset; 8188c2ecf20Sopenharmony_ci dmadev->ddev.device_alloc_chan_resources = hidma_alloc_chan_resources; 8198c2ecf20Sopenharmony_ci dmadev->ddev.device_free_chan_resources = hidma_free_chan_resources; 8208c2ecf20Sopenharmony_ci dmadev->ddev.device_tx_status = hidma_tx_status; 8218c2ecf20Sopenharmony_ci dmadev->ddev.device_issue_pending = hidma_issue_pending; 8228c2ecf20Sopenharmony_ci dmadev->ddev.device_pause = hidma_pause; 8238c2ecf20Sopenharmony_ci dmadev->ddev.device_resume = hidma_resume; 8248c2ecf20Sopenharmony_ci dmadev->ddev.device_terminate_all = hidma_terminate_all; 8258c2ecf20Sopenharmony_ci dmadev->ddev.copy_align = 8; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci /* 8288c2ecf20Sopenharmony_ci * Determine the MSI capability of the platform. Old HW doesn't 8298c2ecf20Sopenharmony_ci * support MSI. 8308c2ecf20Sopenharmony_ci */ 8318c2ecf20Sopenharmony_ci msi = hidma_test_capability(&pdev->dev, HIDMA_MSI_CAP); 8328c2ecf20Sopenharmony_ci device_property_read_u32(&pdev->dev, "desc-count", 8338c2ecf20Sopenharmony_ci &dmadev->nr_descriptors); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (nr_desc_prm) { 8368c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "overriding number of descriptors as %d\n", 8378c2ecf20Sopenharmony_ci nr_desc_prm); 8388c2ecf20Sopenharmony_ci dmadev->nr_descriptors = nr_desc_prm; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (!dmadev->nr_descriptors) 8428c2ecf20Sopenharmony_ci dmadev->nr_descriptors = HIDMA_NR_DEFAULT_DESC; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (hidma_test_capability(&pdev->dev, HIDMA_IDENTITY_CAP)) 8458c2ecf20Sopenharmony_ci dmadev->chidx = readl(dmadev->dev_trca + 0x40); 8468c2ecf20Sopenharmony_ci else 8478c2ecf20Sopenharmony_ci dmadev->chidx = readl(dmadev->dev_trca + 0x28); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* Set DMA mask to 64 bits. */ 8508c2ecf20Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 8518c2ecf20Sopenharmony_ci if (rc) { 8528c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "unable to set coherent mask to 64"); 8538c2ecf20Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 8548c2ecf20Sopenharmony_ci if (rc) 8558c2ecf20Sopenharmony_ci goto dmafree; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci dmadev->lldev = hidma_ll_init(dmadev->ddev.dev, 8598c2ecf20Sopenharmony_ci dmadev->nr_descriptors, dmadev->dev_trca, 8608c2ecf20Sopenharmony_ci dmadev->dev_evca, dmadev->chidx); 8618c2ecf20Sopenharmony_ci if (!dmadev->lldev) { 8628c2ecf20Sopenharmony_ci rc = -EPROBE_DEFER; 8638c2ecf20Sopenharmony_ci goto dmafree; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dmadev); 8678c2ecf20Sopenharmony_ci if (msi) 8688c2ecf20Sopenharmony_ci rc = hidma_request_msi(dmadev, pdev); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (!msi || rc) { 8718c2ecf20Sopenharmony_ci hidma_ll_setup_irq(dmadev->lldev, false); 8728c2ecf20Sopenharmony_ci rc = devm_request_irq(&pdev->dev, chirq, hidma_chirq_handler, 8738c2ecf20Sopenharmony_ci 0, "qcom-hidma", dmadev->lldev); 8748c2ecf20Sopenharmony_ci if (rc) 8758c2ecf20Sopenharmony_ci goto uninit; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dmadev->ddev.channels); 8798c2ecf20Sopenharmony_ci rc = hidma_chan_init(dmadev, 0); 8808c2ecf20Sopenharmony_ci if (rc) 8818c2ecf20Sopenharmony_ci goto uninit; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci rc = dma_async_device_register(&dmadev->ddev); 8848c2ecf20Sopenharmony_ci if (rc) 8858c2ecf20Sopenharmony_ci goto uninit; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci dmadev->irq = chirq; 8888c2ecf20Sopenharmony_ci tasklet_setup(&dmadev->task, hidma_issue_task); 8898c2ecf20Sopenharmony_ci hidma_debug_init(dmadev); 8908c2ecf20Sopenharmony_ci hidma_sysfs_init(dmadev); 8918c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "HI-DMA engine driver registration complete\n"); 8928c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dmadev->ddev.dev); 8938c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dmadev->ddev.dev); 8948c2ecf20Sopenharmony_ci return 0; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ciuninit: 8978c2ecf20Sopenharmony_ci if (msi) 8988c2ecf20Sopenharmony_ci hidma_free_msis(dmadev); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci hidma_ll_uninit(dmadev->lldev); 9018c2ecf20Sopenharmony_cidmafree: 9028c2ecf20Sopenharmony_ci if (dmadev) 9038c2ecf20Sopenharmony_ci hidma_free(dmadev); 9048c2ecf20Sopenharmony_cibailout: 9058c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 9068c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 9078c2ecf20Sopenharmony_ci return rc; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic void hidma_shutdown(struct platform_device *pdev) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = platform_get_drvdata(pdev); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci dev_info(dmadev->ddev.dev, "HI-DMA engine shutdown\n"); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci pm_runtime_get_sync(dmadev->ddev.dev); 9178c2ecf20Sopenharmony_ci if (hidma_ll_disable(dmadev->lldev)) 9188c2ecf20Sopenharmony_ci dev_warn(dmadev->ddev.dev, "channel did not stop\n"); 9198c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dmadev->ddev.dev); 9208c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dmadev->ddev.dev); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cistatic int hidma_remove(struct platform_device *pdev) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci struct hidma_dev *dmadev = platform_get_drvdata(pdev); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci pm_runtime_get_sync(dmadev->ddev.dev); 9298c2ecf20Sopenharmony_ci dma_async_device_unregister(&dmadev->ddev); 9308c2ecf20Sopenharmony_ci if (!dmadev->lldev->msi_support) 9318c2ecf20Sopenharmony_ci devm_free_irq(dmadev->ddev.dev, dmadev->irq, dmadev->lldev); 9328c2ecf20Sopenharmony_ci else 9338c2ecf20Sopenharmony_ci hidma_free_msis(dmadev); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci tasklet_kill(&dmadev->task); 9368c2ecf20Sopenharmony_ci hidma_sysfs_uninit(dmadev); 9378c2ecf20Sopenharmony_ci hidma_debug_uninit(dmadev); 9388c2ecf20Sopenharmony_ci hidma_ll_uninit(dmadev->lldev); 9398c2ecf20Sopenharmony_ci hidma_free(dmadev); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "HI-DMA engine removed\n"); 9428c2ecf20Sopenharmony_ci pm_runtime_put_sync_suspend(&pdev->dev); 9438c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci return 0; 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ACPI) 9498c2ecf20Sopenharmony_cistatic const struct acpi_device_id hidma_acpi_ids[] = { 9508c2ecf20Sopenharmony_ci {"QCOM8061"}, 9518c2ecf20Sopenharmony_ci {"QCOM8062", HIDMA_MSI_CAP}, 9528c2ecf20Sopenharmony_ci {"QCOM8063", (HIDMA_MSI_CAP | HIDMA_IDENTITY_CAP)}, 9538c2ecf20Sopenharmony_ci {}, 9548c2ecf20Sopenharmony_ci}; 9558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, hidma_acpi_ids); 9568c2ecf20Sopenharmony_ci#endif 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cistatic const struct of_device_id hidma_match[] = { 9598c2ecf20Sopenharmony_ci {.compatible = "qcom,hidma-1.0",}, 9608c2ecf20Sopenharmony_ci {.compatible = "qcom,hidma-1.1", .data = (void *)(HIDMA_MSI_CAP),}, 9618c2ecf20Sopenharmony_ci {.compatible = "qcom,hidma-1.2", 9628c2ecf20Sopenharmony_ci .data = (void *)(HIDMA_MSI_CAP | HIDMA_IDENTITY_CAP),}, 9638c2ecf20Sopenharmony_ci {}, 9648c2ecf20Sopenharmony_ci}; 9658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, hidma_match); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic struct platform_driver hidma_driver = { 9688c2ecf20Sopenharmony_ci .probe = hidma_probe, 9698c2ecf20Sopenharmony_ci .remove = hidma_remove, 9708c2ecf20Sopenharmony_ci .shutdown = hidma_shutdown, 9718c2ecf20Sopenharmony_ci .driver = { 9728c2ecf20Sopenharmony_ci .name = "hidma", 9738c2ecf20Sopenharmony_ci .of_match_table = hidma_match, 9748c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(hidma_acpi_ids), 9758c2ecf20Sopenharmony_ci }, 9768c2ecf20Sopenharmony_ci}; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cimodule_platform_driver(hidma_driver); 9798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 980