Received: by 2002:a25:8b91:0:0:0:0:0 with SMTP id j17csp17767005ybl; Thu, 2 Jan 2020 11:38:11 -0800 (PST) X-Google-Smtp-Source: APXvYqyuGiuqyZXetbZKc6F8rj15CxScEpEc3iooN1VC/+1Pb0eA5eF3WtAeeW+a8ceArQNrd32k X-Received: by 2002:a9d:7e8a:: with SMTP id m10mr89334003otp.27.1577993891737; Thu, 02 Jan 2020 11:38:11 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1577993891; cv=none; d=google.com; s=arc-20160816; b=gb0xl2V5ytDvKMQBV0XeWov8hcEJ/i1TWuKnkILjaKsAwI8bJLCpbxjmQ9BljrK6l6 ihXyLg3iEMz54ZWqc0weul2xfpNlFwlWNbfu0L7YX7ay72Bg+FyoYDa8jEmE5PcajxCd tcbgrU3NDfUGt5186xkWYf27IX1O7VxpvFMohHVLoHSzvuToOtFN18a/90uVWKONsXrI 5EY2oV3ex3ZWdVx8bm81QWY4jSQNnHjtdJkACjybw5IeSM0Img0fOvxCxNcO61S55tn7 zvUqrmkKaEF0azyOMeRQtQzqB9QXRRqifdOMB+c3iqGBYC1N55xL8lIM3KCOLQ0V8ipk P2Iw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from; bh=Z3K8jiHaprtNTVQwOWhiqrFBV9MZvFeQ2zNvIFMYk9g=; b=qR2jT4Xx7JmHYAOF26cZxM0MjnBlZlFFmNcXyK8JxFxcTXyxUHlvk9GR4Y03JDfxBF F/ZNI1sRt7PSd3BzkhlJAR6A3Kxclw57O1VdIgFC4kjcl6EzWk0h+GjxDu8lYs98ydxp Fp7gblypvnNpcRvbrMOryPDmekKiRajQt5ClYsrzXDLpKcOanJMXyMlH9dOVBaeG37yR DcZ8W6tzhtwrjCcXv2Fcg1SJ2NsDgHoHiFH6KWiI3iW192fsxbxNQUE7yDMzuRfALcBC 79DaTZe+p/VaQn2akMDZiTlQ3nWRJmGuSzOm2uklnnVZ4E//inJNyTjMdmDkBkegi3jc GPpw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=ibm.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id v11si19578637otr.158.2020.01.02.11.37.59; Thu, 02 Jan 2020 11:38:11 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=ibm.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728304AbgABThO (ORCPT + 99 others); Thu, 2 Jan 2020 14:37:14 -0500 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:5140 "EHLO mx0a-001b2d01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727951AbgABThN (ORCPT ); Thu, 2 Jan 2020 14:37:13 -0500 Received: from pps.filterd (m0098409.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 002Jau8K013733; Thu, 2 Jan 2020 14:36:57 -0500 Received: from ppma02wdc.us.ibm.com (aa.5b.37a9.ip4.static.sl-reverse.com [169.55.91.170]) by mx0a-001b2d01.pphosted.com with ESMTP id 2x87mtapbq-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 02 Jan 2020 14:36:57 -0500 Received: from pps.filterd (ppma02wdc.us.ibm.com [127.0.0.1]) by ppma02wdc.us.ibm.com (8.16.0.27/8.16.0.27) with SMTP id 002JFLFs000323; Thu, 2 Jan 2020 19:28:03 GMT Received: from b03cxnp07028.gho.boulder.ibm.com (b03cxnp07028.gho.boulder.ibm.com [9.17.130.15]) by ppma02wdc.us.ibm.com with ESMTP id 2x5xp6k0dc-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 02 Jan 2020 19:28:03 +0000 Received: from b03ledav005.gho.boulder.ibm.com (b03ledav005.gho.boulder.ibm.com [9.17.130.236]) by b03cxnp07028.gho.boulder.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 002JS28240370528 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 2 Jan 2020 19:28:02 GMT Received: from b03ledav005.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 8D628BE058; Thu, 2 Jan 2020 19:28:02 +0000 (GMT) Received: from b03ledav005.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id CF998BE051; Thu, 2 Jan 2020 19:28:01 +0000 (GMT) Received: from talon7.ibm.com (unknown [9.41.103.158]) by b03ledav005.gho.boulder.ibm.com (Postfix) with ESMTP; Thu, 2 Jan 2020 19:28:01 +0000 (GMT) From: Eddie James To: linux-aspeed@lists.ozlabs.org Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, mark.rutland@arm.com, jason@lakedaemon.net, maz@kernel.org, robh+dt@kernel.org, tglx@linutronix.de, joel@jms.id.au, andrew@aj.id.au, eajames@linux.ibm.com Subject: [PATCH v4 07/12] soc: aspeed: xdma: Add user interface Date: Thu, 2 Jan 2020 13:27:51 -0600 Message-Id: <1577993276-2184-8-git-send-email-eajames@linux.ibm.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1577993276-2184-1-git-send-email-eajames@linux.ibm.com> References: <1577993276-2184-1-git-send-email-eajames@linux.ibm.com> X-TM-AS-GCONF: 00 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.95,18.0.572 definitions=2020-01-02_06:2020-01-02,2020-01-02 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 priorityscore=1501 mlxscore=0 adultscore=0 phishscore=0 malwarescore=0 clxscore=1015 suspectscore=4 impostorscore=0 lowpriorityscore=0 spamscore=0 mlxlogscore=999 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-1910280000 definitions=main-2001020157 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This commits adds a miscdevice to provide a user interface to the XDMA engine. The interface provides the write operation to start DMA operations. The DMA parameters are passed as the data to the write call. The actual data to transfer is NOT passed through write. Note that both directions of DMA operation are accomplished through the write command; BMC to host and host to BMC. The XDMA driver reserves an area of physical memory for DMA operations, as the XDMA engine is restricted to accessing certain physical memory areas on some platforms. This memory forms a pool from which users can allocate pages for their usage with calls to mmap. The space allocated by a client will be the space used in the DMA operation. For an "upstream" (BMC to host) operation, the data in the client's area will be transferred to the host. For a "downstream" (host to BMC) operation, the host data will be placed in the client's memory area. Poll is also provided in order to determine when the DMA operation is complete for non-blocking IO. Signed-off-by: Eddie James --- Changes since v3: - Use READ_ONCE for current_client - add dev_warn for failed mmap drivers/soc/aspeed/aspeed-xdma.c | 205 +++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/drivers/soc/aspeed/aspeed-xdma.c b/drivers/soc/aspeed/aspeed-xdma.c index b79855016c8b..5ad0efaf6e05 100644 --- a/drivers/soc/aspeed/aspeed-xdma.c +++ b/drivers/soc/aspeed/aspeed-xdma.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -201,6 +202,8 @@ struct aspeed_xdma { struct clk *clock; struct reset_control *reset; + /* file_lock serializes reads of current_client */ + struct mutex file_lock; /* client_lock protects error and in_progress of the client */ spinlock_t client_lock; struct aspeed_xdma_client *current_client; @@ -223,6 +226,8 @@ struct aspeed_xdma { void __iomem *mem_virt; dma_addr_t cmdq_phys; struct gen_pool *pool; + + struct miscdevice misc; }; struct aspeed_xdma_client { @@ -527,6 +532,188 @@ static irqreturn_t aspeed_xdma_pcie_irq(int irq, void *arg) return IRQ_HANDLED; } +static ssize_t aspeed_xdma_write(struct file *file, const char __user *buf, + size_t len, loff_t *offset) +{ + int rc; + struct aspeed_xdma_op op; + struct aspeed_xdma_client *client = file->private_data; + struct aspeed_xdma *ctx = client->ctx; + + if (len != sizeof(op)) + return -EINVAL; + + rc = copy_from_user(&op, buf, len); + if (rc) + return rc; + + if (!op.len || op.len > client->size || + op.direction > ASPEED_XDMA_DIRECTION_UPSTREAM) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&ctx->file_lock)) + return -EAGAIN; + + if (READ_ONCE(ctx->current_client)) { + mutex_unlock(&ctx->file_lock); + return -EBUSY; + } + } else { + mutex_lock(&ctx->file_lock); + + rc = wait_event_interruptible(ctx->wait, !ctx->current_client); + if (rc) { + mutex_unlock(&ctx->file_lock); + return -EINTR; + } + } + + aspeed_xdma_start(ctx, &op, client->phys, client); + + mutex_unlock(&ctx->file_lock); + + if (!(file->f_flags & O_NONBLOCK)) { + rc = wait_event_interruptible(ctx->wait, !client->in_progress); + if (rc) + return -EINTR; + + if (client->error) + return -EIO; + } + + return len; +} + +static __poll_t aspeed_xdma_poll(struct file *file, + struct poll_table_struct *wait) +{ + __poll_t mask = 0; + __poll_t req = poll_requested_events(wait); + struct aspeed_xdma_client *client = file->private_data; + struct aspeed_xdma *ctx = client->ctx; + + if (req & (EPOLLIN | EPOLLRDNORM)) { + if (client->in_progress) + poll_wait(file, &ctx->wait, wait); + + if (!client->in_progress) { + if (client->error) + mask |= EPOLLERR; + else + mask |= EPOLLIN | EPOLLRDNORM; + } + } + + if (req & (EPOLLOUT | EPOLLWRNORM)) { + if (ctx->current_client) + poll_wait(file, &ctx->wait, wait); + + if (!ctx->current_client) + mask |= EPOLLOUT | EPOLLWRNORM; + } + + return mask; +} + +static void aspeed_xdma_vma_close(struct vm_area_struct *vma) +{ + int rc; + struct aspeed_xdma_client *client = vma->vm_private_data; + + rc = wait_event_interruptible(client->ctx->wait, !client->in_progress); + if (rc) + return; + + gen_pool_free(client->ctx->pool, (unsigned long)client->virt, + client->size); + + client->virt = NULL; + client->phys = 0; + client->size = 0; +} + +static const struct vm_operations_struct aspeed_xdma_vm_ops = { + .close = aspeed_xdma_vma_close, +}; + +static int aspeed_xdma_mmap(struct file *file, struct vm_area_struct *vma) +{ + int rc; + struct aspeed_xdma_client *client = file->private_data; + struct aspeed_xdma *ctx = client->ctx; + + /* restrict file to one mapping */ + if (client->size) + return -EBUSY; + + client->size = vma->vm_end - vma->vm_start; + client->virt = gen_pool_dma_alloc(ctx->pool, client->size, + &client->phys); + if (!client->virt) { + client->phys = 0; + client->size = 0; + return -ENOMEM; + } + + vma->vm_pgoff = (client->phys - ctx->mem_phys) >> PAGE_SHIFT; + vma->vm_ops = &aspeed_xdma_vm_ops; + vma->vm_private_data = client; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + rc = io_remap_pfn_range(vma, vma->vm_start, client->phys >> PAGE_SHIFT, + client->size, vma->vm_page_prot); + if (rc) { + dev_warn(ctx->dev, "mmap err: v[%08lx] to p[%08x], s[%08x]\n", + vma->vm_start, (u32)client->phys, client->size); + + gen_pool_free(ctx->pool, (unsigned long)client->virt, + client->size); + + client->virt = NULL; + client->phys = 0; + client->size = 0; + return rc; + } + + dev_dbg(ctx->dev, "mmap: v[%08lx] to p[%08x], s[%08x]\n", + vma->vm_start, (u32)client->phys, client->size); + + return 0; +} + +static int aspeed_xdma_open(struct inode *inode, struct file *file) +{ + struct miscdevice *misc = file->private_data; + struct aspeed_xdma *ctx = container_of(misc, struct aspeed_xdma, misc); + struct aspeed_xdma_client *client = kzalloc(sizeof(*client), + GFP_KERNEL); + + if (!client) + return -ENOMEM; + + client->ctx = ctx; + file->private_data = client; + return 0; +} + +static int aspeed_xdma_release(struct inode *inode, struct file *file) +{ + struct aspeed_xdma_client *client = file->private_data; + + kfree(client); + return 0; +} + +static const struct file_operations aspeed_xdma_fops = { + .owner = THIS_MODULE, + .write = aspeed_xdma_write, + .poll = aspeed_xdma_poll, + .mmap = aspeed_xdma_mmap, + .open = aspeed_xdma_open, + .release = aspeed_xdma_release, +}; + static int aspeed_xdma_init_scu(struct aspeed_xdma *ctx, struct device *dev) { struct regmap *scu = syscon_regmap_lookup_by_phandle(dev->of_node, @@ -592,6 +779,7 @@ static int aspeed_xdma_probe(struct platform_device *pdev) ctx->chip = md; ctx->dev = dev; platform_set_drvdata(pdev, ctx); + mutex_init(&ctx->file_lock); mutex_init(&ctx->start_lock); INIT_WORK(&ctx->reset_work, aspeed_xdma_reset_work); spin_lock_init(&ctx->client_lock); @@ -688,6 +876,22 @@ static int aspeed_xdma_probe(struct platform_device *pdev) aspeed_xdma_init_eng(ctx); + ctx->misc.minor = MISC_DYNAMIC_MINOR; + ctx->misc.fops = &aspeed_xdma_fops; + ctx->misc.name = "aspeed-xdma"; + ctx->misc.parent = dev; + rc = misc_register(&ctx->misc); + if (rc) { + dev_err(dev, "Failed to register xdma miscdevice.\n"); + + gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, + XDMA_CMDQ_SIZE); + + reset_control_assert(ctx->reset); + clk_disable_unprepare(ctx->clock); + return rc; + } + /* * This interrupt could fire immediately so only request it once the * engine and driver are initialized. @@ -709,6 +913,7 @@ static int aspeed_xdma_remove(struct platform_device *pdev) { struct aspeed_xdma *ctx = platform_get_drvdata(pdev); + misc_deregister(&ctx->misc); gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE); reset_control_assert(ctx->reset); -- 2.24.0