From patchwork Mon Feb 4 14:22:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roger Quadros X-Patchwork-Id: 10795751 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9AA95159A for ; Mon, 4 Feb 2019 14:24:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 865292B753 for ; Mon, 4 Feb 2019 14:24:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 844A22B764; Mon, 4 Feb 2019 14:24:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6FBDB2B753 for ; Mon, 4 Feb 2019 14:24:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730880AbfBDOYt (ORCPT ); Mon, 4 Feb 2019 09:24:49 -0500 Received: from fllv0015.ext.ti.com ([198.47.19.141]:43436 "EHLO fllv0015.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730085AbfBDOYm (ORCPT ); Mon, 4 Feb 2019 09:24:42 -0500 Received: from lelv0265.itg.ti.com ([10.180.67.224]) by fllv0015.ext.ti.com (8.15.2/8.15.2) with ESMTP id x14ENpDm032277; Mon, 4 Feb 2019 08:23:51 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1549290231; bh=XXQ80qS5M5gOIl4GkFMwJ6ZJ9BZqab716Oc1/PpEWl4=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=nIrf++DD7YwqfxcUqbBOAd2nICgzUk+rJslqurEXrR/0ZzydexJqzobqg7RHuS9B5 mjz8fNOVgicS94esEx6V5PLExIET+Y6C2hywaUZfRZLPQW3Z5+EH9vJ8hcV9mYZxIC kkqIOd4wcRvqwsoeVSDfG/Fr4qI3naDwSeVR4nJg= Received: from DFLE107.ent.ti.com (dfle107.ent.ti.com [10.64.6.28]) by lelv0265.itg.ti.com (8.15.2/8.15.2) with ESMTPS id x14ENpor049097 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Mon, 4 Feb 2019 08:23:51 -0600 Received: from DFLE107.ent.ti.com (10.64.6.28) by DFLE107.ent.ti.com (10.64.6.28) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1591.10; Mon, 4 Feb 2019 08:23:50 -0600 Received: from dflp32.itg.ti.com (10.64.6.15) by DFLE107.ent.ti.com (10.64.6.28) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.1591.10 via Frontend Transport; Mon, 4 Feb 2019 08:23:50 -0600 Received: from localhost.localdomain (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp32.itg.ti.com (8.14.3/8.13.8) with ESMTP id x14EMoKm012232; Mon, 4 Feb 2019 08:23:47 -0600 From: Roger Quadros To: , , CC: , , , , , , , , , , , , Subject: [PATCH v2 14/14] rpmsg: pru: add a PRU RPMsg driver Date: Mon, 4 Feb 2019 16:22:47 +0200 Message-ID: <1549290167-876-15-git-send-email-rogerq@ti.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1549290167-876-1-git-send-email-rogerq@ti.com> References: <1549290167-876-1-git-send-email-rogerq@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Sender: linux-remoteproc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-remoteproc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Jason Reeder An RPMsg driver that exposes interfaces to user space, to allow applications to communicate with the PRU processors on available TI SoCs has been added. This is restricted to SoCs that have the PRUSS remoteproc support. Signed-off-by: Jason Reeder [s-anna@ti.com: various cleanups, rpmsg rebase fixes for 4.14] Signed-off-by: Suman Anna Signed-off-by: Roger Quadros --- drivers/rpmsg/Kconfig | 14 ++ drivers/rpmsg/Makefile | 1 + drivers/rpmsg/rpmsg_pru.c | 360 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 375 insertions(+) create mode 100644 drivers/rpmsg/rpmsg_pru.c diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index d0322b4..5beb5e4 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -55,4 +55,18 @@ config RPMSG_VIRTIO select RPMSG select VIRTIO +config RPMSG_PRU + tristate "PRU RPMsg Communication driver" + default n + depends on RPMSG_VIRTIO + depends on REMOTEPROC + depends on PRUSS_REMOTEPROC + help + An rpmsg driver that exposes interfaces to user space, to allow + applications to communicate with the PRU processors on available + TI SoCs. This is restricted to SoCs that have the PRUSS remoteproc + support. + + If unsure, say N. + endmenu diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index 9aa8595..b66a5dd 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_RPMSG) += rpmsg_core.o obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o +obj-$(CONFIG_RPMSG_PRU) += rpmsg_pru.o obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o diff --git a/drivers/rpmsg/rpmsg_pru.c b/drivers/rpmsg/rpmsg_pru.c new file mode 100644 index 0000000..2e8d2b1 --- /dev/null +++ b/drivers/rpmsg/rpmsg_pru.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PRU Remote Processor Messaging Driver + * + * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Jason Reeder + * Suman Anna + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRU_MAX_DEVICES (8) +/* Matches the definition in virtio_rpmsg_bus.c */ +#define RPMSG_BUF_SIZE (512) +#define MAX_FIFO_MSG (32) +#define FIFO_MSG_SIZE RPMSG_BUF_SIZE + +/** + * struct rpmsg_pru_dev - Structure that contains the per-device data + * @rpdev: rpmsg channel device that is associated with this rpmsg_pru device + * @dev: device + * @cdev: character device + * @locked: boolean used to determine whether or not the device file is in use + * @devt: dev_t structure for the rpmsg_pru device + * @msg_fifo: kernel fifo used to buffer the messages between userspace and PRU + * @msg_len: array storing the lengths of each message in the kernel fifo + * @msg_idx_rd: kernel fifo read index + * @msg_idx_wr: kernel fifo write index + * @wait_list: wait queue used to implement the poll operation of the character + * device + * + * Each rpmsg_pru device provides an interface, using an rpmsg channel (rpdev), + * between a user space character device (cdev) and a PRU core. A kernel fifo + * (msg_fifo) is used to buffer the messages in the kernel that are + * being passed between the character device and the PRU. + */ +struct rpmsg_pru_dev { + struct rpmsg_device *rpdev; + struct device *dev; + struct cdev cdev; + bool locked; + dev_t devt; + struct kfifo msg_fifo; + u32 msg_len[MAX_FIFO_MSG]; + int msg_idx_rd; + int msg_idx_wr; + wait_queue_head_t wait_list; +}; + +static struct class *rpmsg_pru_class; +static dev_t rpmsg_pru_devt; +static DEFINE_MUTEX(rpmsg_pru_lock); +static DEFINE_IDR(rpmsg_pru_minors); + +static int rpmsg_pru_open(struct inode *inode, struct file *filp) +{ + struct rpmsg_pru_dev *prudev; + int ret = -EACCES; + + prudev = container_of(inode->i_cdev, struct rpmsg_pru_dev, cdev); + + mutex_lock(&rpmsg_pru_lock); + if (!prudev->locked) { + prudev->locked = true; + filp->private_data = prudev; + ret = 0; + } + mutex_unlock(&rpmsg_pru_lock); + + if (ret) + dev_err(prudev->dev, "Device already open\n"); + + return ret; +} + +static int rpmsg_pru_release(struct inode *inode, struct file *filp) +{ + struct rpmsg_pru_dev *prudev; + + prudev = container_of(inode->i_cdev, struct rpmsg_pru_dev, cdev); + mutex_lock(&rpmsg_pru_lock); + prudev->locked = false; + mutex_unlock(&rpmsg_pru_lock); + return 0; +} + +static ssize_t rpmsg_pru_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int ret; + u32 length; + struct rpmsg_pru_dev *prudev; + + prudev = filp->private_data; + + if (kfifo_is_empty(&prudev->msg_fifo) && + (filp->f_flags & O_NONBLOCK)) + return -EAGAIN; + + ret = wait_event_interruptible(prudev->wait_list, + !kfifo_is_empty(&prudev->msg_fifo)); + if (ret) + return -EINTR; + + ret = kfifo_to_user(&prudev->msg_fifo, buf, + prudev->msg_len[prudev->msg_idx_rd], &length); + prudev->msg_idx_rd = (prudev->msg_idx_rd + 1) % MAX_FIFO_MSG; + + return ret ? ret : length; +} + +static ssize_t rpmsg_pru_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + int ret; + struct rpmsg_pru_dev *prudev; + static char rpmsg_pru_buf[RPMSG_BUF_SIZE]; + + prudev = filp->private_data; + + if (count > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) { + dev_err(prudev->dev, "Data too large for RPMsg Buffer\n"); + return -EINVAL; + } + + if (copy_from_user(rpmsg_pru_buf, buf, count)) { + dev_err(prudev->dev, "Error copying buffer from user space"); + return -EFAULT; + } + + ret = rpmsg_send(prudev->rpdev->ept, (void *)rpmsg_pru_buf, count); + if (ret) + dev_err(prudev->dev, "rpmsg_send failed: %d\n", ret); + + return ret ? ret : count; +} + +static unsigned int rpmsg_pru_poll(struct file *filp, + struct poll_table_struct *wait) +{ + int mask; + struct rpmsg_pru_dev *prudev; + + prudev = filp->private_data; + + poll_wait(filp, &prudev->wait_list, wait); + + mask = POLLOUT | POLLWRNORM; + + if (!kfifo_is_empty(&prudev->msg_fifo)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static const struct file_operations rpmsg_pru_fops = { + .owner = THIS_MODULE, + .open = rpmsg_pru_open, + .release = rpmsg_pru_release, + .read = rpmsg_pru_read, + .write = rpmsg_pru_write, + .poll = rpmsg_pru_poll, +}; + +static int rpmsg_pru_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + u32 length; + struct rpmsg_pru_dev *prudev; + + prudev = dev_get_drvdata(&rpdev->dev); + + if (kfifo_avail(&prudev->msg_fifo) < len) { + dev_err(&rpdev->dev, "Not enough space on the FIFO\n"); + return -ENOSPC; + } + + if ((prudev->msg_idx_wr + 1) % MAX_FIFO_MSG == + prudev->msg_idx_rd) { + dev_err(&rpdev->dev, "Message length table is full\n"); + return -ENOSPC; + } + + length = kfifo_in(&prudev->msg_fifo, data, len); + prudev->msg_len[prudev->msg_idx_wr] = length; + prudev->msg_idx_wr = (prudev->msg_idx_wr + 1) % MAX_FIFO_MSG; + + wake_up_interruptible(&prudev->wait_list); + + return 0; +} + +static int rpmsg_pru_probe(struct rpmsg_device *rpdev) +{ + int ret; + struct rpmsg_pru_dev *prudev; + int minor_got; + + prudev = devm_kzalloc(&rpdev->dev, sizeof(*prudev), GFP_KERNEL); + if (!prudev) + return -ENOMEM; + + mutex_lock(&rpmsg_pru_lock); + minor_got = idr_alloc(&rpmsg_pru_minors, prudev, 0, PRU_MAX_DEVICES, + GFP_KERNEL); + mutex_unlock(&rpmsg_pru_lock); + if (minor_got < 0) { + ret = minor_got; + dev_err(&rpdev->dev, "Failed to get a minor number for the rpmsg_pru device: %d\n", + ret); + goto fail_alloc_minor; + } + + prudev->devt = MKDEV(MAJOR(rpmsg_pru_devt), minor_got); + + cdev_init(&prudev->cdev, &rpmsg_pru_fops); + prudev->cdev.owner = THIS_MODULE; + ret = cdev_add(&prudev->cdev, prudev->devt, 1); + if (ret) { + dev_err(&rpdev->dev, "Unable to add cdev for the rpmsg_pru device\n"); + goto fail_add_cdev; + } + + prudev->dev = device_create(rpmsg_pru_class, &rpdev->dev, prudev->devt, + NULL, "rpmsg_pru%d", rpdev->dst); + if (IS_ERR(prudev->dev)) { + dev_err(&rpdev->dev, "Unable to create the rpmsg_pru device\n"); + ret = PTR_ERR(prudev->dev); + goto fail_create_device; + } + + prudev->rpdev = rpdev; + + ret = kfifo_alloc(&prudev->msg_fifo, MAX_FIFO_MSG * FIFO_MSG_SIZE, + GFP_KERNEL); + if (ret) { + dev_err(&rpdev->dev, "Unable to allocate fifo for the rpmsg_pru device\n"); + goto fail_alloc_fifo; + } + + init_waitqueue_head(&prudev->wait_list); + + dev_set_drvdata(&rpdev->dev, prudev); + + dev_info(&rpdev->dev, "new rpmsg_pru device: /dev/rpmsg_pru%d", + rpdev->dst); + + return 0; + +fail_alloc_fifo: + device_destroy(rpmsg_pru_class, prudev->devt); +fail_create_device: + cdev_del(&prudev->cdev); +fail_add_cdev: + mutex_lock(&rpmsg_pru_lock); + idr_remove(&rpmsg_pru_minors, minor_got); + mutex_unlock(&rpmsg_pru_lock); +fail_alloc_minor: + return ret; +} + +static void rpmsg_pru_remove(struct rpmsg_device *rpdev) +{ + struct rpmsg_pru_dev *prudev; + + prudev = dev_get_drvdata(&rpdev->dev); + + kfifo_free(&prudev->msg_fifo); + device_destroy(rpmsg_pru_class, prudev->devt); + cdev_del(&prudev->cdev); + mutex_lock(&rpmsg_pru_lock); + idr_remove(&rpmsg_pru_minors, MINOR(prudev->devt)); + mutex_unlock(&rpmsg_pru_lock); +} + +/* .name matches on RPMsg Channels and causes a probe */ +static const struct rpmsg_device_id rpmsg_driver_pru_id_table[] = { + { .name = "rpmsg-pru" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_pru_id_table); + +static struct rpmsg_driver rpmsg_pru_driver = { + .drv.name = KBUILD_MODNAME, + .id_table = rpmsg_driver_pru_id_table, + .probe = rpmsg_pru_probe, + .callback = rpmsg_pru_cb, + .remove = rpmsg_pru_remove, +}; + +static int __init rpmsg_pru_init(void) +{ + int ret; + + rpmsg_pru_class = class_create(THIS_MODULE, "rpmsg_pru"); + if (IS_ERR(rpmsg_pru_class)) { + pr_err("Unable to create class\n"); + ret = PTR_ERR(rpmsg_pru_class); + goto fail_create_class; + } + + ret = alloc_chrdev_region(&rpmsg_pru_devt, 0, PRU_MAX_DEVICES, + "rpmsg_pru"); + if (ret) { + pr_err("Unable to allocate chrdev region\n"); + goto fail_alloc_region; + } + + ret = register_rpmsg_driver(&rpmsg_pru_driver); + if (ret) { + pr_err("Unable to register rpmsg driver"); + goto fail_register_rpmsg_driver; + } + + return 0; + +fail_register_rpmsg_driver: + unregister_chrdev_region(rpmsg_pru_devt, PRU_MAX_DEVICES); +fail_alloc_region: + class_destroy(rpmsg_pru_class); +fail_create_class: + return ret; +} + +static void __exit rpmsg_pru_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_pru_driver); + idr_destroy(&rpmsg_pru_minors); + mutex_destroy(&rpmsg_pru_lock); + class_destroy(rpmsg_pru_class); + unregister_chrdev_region(rpmsg_pru_devt, PRU_MAX_DEVICES); +} + +module_init(rpmsg_pru_init); +module_exit(rpmsg_pru_exit); + +MODULE_AUTHOR("Jason Reeder "); +MODULE_ALIAS("rpmsg:rpmsg-pru"); +MODULE_DESCRIPTION("PRU Remote Processor Messaging Driver"); +MODULE_LICENSE("GPL v2");