Commit 3312db01 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull rpmsg updates from Bjorn Andersson:
 "The major part of the rpmsg changes for v5.18 relates to improvements
  in the rpmsg char driver, which now allow automatically attaching to
  rpmsg channels as well as initiating new communication channels from
  the Linux side.

  The SMD driver is moved to arch_initcall with the purpose of
  registering root clocks earlier during boot.

  Also in the SMD driver, a workaround for the resource power management
  (RPM) channel is introduced to resolve an issue where both the RPM and
  Linux side waits for the other to close the communication established
  by the bootloader - this unblocks support for clocks and regulators on
  some older Qualcomm platforms"

* tag 'rpmsg-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux:
  rpmsg: ctrl: Introduce new RPMSG_CREATE/RELEASE_DEV_IOCTL controls
  rpmsg: char: Introduce the "rpmsg-raw" channel
  rpmsg: char: Add possibility to use default endpoint of the rpmsg device
  rpmsg: char: Refactor rpmsg_chrdev_eptdev_create function
  rpmsg: Update rpmsg_chrdev_register_device function
  rpmsg: Move the rpmsg control device from rpmsg_char to rpmsg_ctrl
  rpmsg: Create the rpmsg class in core instead of in rpmsg char
  rpmsg: char: Export eptdev create and destroy functions
  rpmsg: char: treat rpmsg_trysend() ENOMEM as EAGAIN
  rpmsg: qcom_smd: Fix redundant channel->registered assignment
  rpmsg: use struct_size over open coded arithmetic
  rpmsg: smd: allow opening rpm_requests even if already opened
  rpmsg: qcom_smd: Promote to arch_initcall
parents f18e345d 8109517b
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -15,6 +15,14 @@ config RPMSG_CHAR
	  in /dev. They make it possible for user-space programs to send and
	  receive rpmsg packets.

config RPMSG_CTRL
	tristate "RPMSG control interface"
	depends on RPMSG && ( RPMSG_CHAR || RPMSG_CHAR=n )
	help
	  Say Y here to enable the support of the /dev/rpmsg_ctrlX API. This API
	  allows user-space programs to create endpoints with specific service name,
	  source and destination addresses.

config RPMSG_NS
	tristate "RPMSG name service announcement"
	depends on RPMSG
+1 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_RPMSG)		+= rpmsg_core.o
obj-$(CONFIG_RPMSG_CHAR)	+= rpmsg_char.o
obj-$(CONFIG_RPMSG_CTRL)	+= rpmsg_ctrl.o
obj-$(CONFIG_RPMSG_NS)		+= rpmsg_ns.o
obj-$(CONFIG_RPMSG_MTK_SCP)	+= mtk_rpmsg.o
qcom_glink-objs			:= qcom_glink_native.o qcom_glink_ssr.o
+2 −2
Original line number Diff line number Diff line
@@ -792,7 +792,7 @@ static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
		return -ENXIO;
	}

	dcmd = kzalloc(sizeof(*dcmd) + extra, GFP_ATOMIC);
	dcmd = kzalloc(struct_size(dcmd, data, extra), GFP_ATOMIC);
	if (!dcmd)
		return -ENOMEM;

@@ -1715,7 +1715,7 @@ static int qcom_glink_create_chrdev(struct qcom_glink *glink)
	rpdev->dev.parent = glink->dev;
	rpdev->dev.release = qcom_glink_device_release;

	return rpmsg_chrdev_register_device(rpdev);
	return rpmsg_ctrldev_register_device(rpdev);
}

struct qcom_glink *qcom_glink_native_probe(struct device *dev,
+8 −5
Original line number Diff line number Diff line
@@ -1113,7 +1113,7 @@ static int qcom_smd_create_chrdev(struct qcom_smd_edge *edge)
	qsdev->rpdev.dev.parent = &edge->dev;
	qsdev->rpdev.dev.release = qcom_smd_release_device;

	return rpmsg_chrdev_register_device(&qsdev->rpdev);
	return rpmsg_ctrldev_register_device(&qsdev->rpdev);
}

/*
@@ -1288,9 +1288,14 @@ static void qcom_channel_state_worker(struct work_struct *work)
		if (channel->state != SMD_CHANNEL_CLOSED)
			continue;

		/*
		 * Always open rpm_requests, even when already opened which is
		 * required on some SoCs like msm8953.
		 */
		remote_state = GET_RX_CHANNEL_INFO(channel, state);
		if (remote_state != SMD_CHANNEL_OPENING &&
		    remote_state != SMD_CHANNEL_OPENED)
		    remote_state != SMD_CHANNEL_OPENED &&
		    strcmp(channel->name, "rpm_requests"))
			continue;

		if (channel->registered)
@@ -1298,9 +1303,7 @@ static void qcom_channel_state_worker(struct work_struct *work)

		spin_unlock_irqrestore(&edge->channels_lock, flags);
		qcom_smd_create_device(channel);
		channel->registered = true;
		spin_lock_irqsave(&edge->channels_lock, flags);

		channel->registered = true;
	}

@@ -1605,7 +1608,7 @@ static int __init qcom_smd_init(void)
{
	return platform_driver_register(&qcom_smd_driver);
}
subsys_initcall(qcom_smd_init);
arch_initcall(qcom_smd_init);

static void __exit qcom_smd_exit(void)
{
+90 −148
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2022, STMicroelectronics
 * Copyright (c) 2016, Linaro Ltd.
 * Copyright (c) 2012, Michal Simek <monstr@monstr.eu>
 * Copyright (c) 2012, PetaLogix
@@ -25,33 +26,19 @@
#include <linux/uaccess.h>
#include <uapi/linux/rpmsg.h>

#include "rpmsg_char.h"
#include "rpmsg_internal.h"

#define RPMSG_DEV_MAX	(MINORMASK + 1)

static dev_t rpmsg_major;
static struct class *rpmsg_class;

static DEFINE_IDA(rpmsg_ctrl_ida);
static DEFINE_IDA(rpmsg_ept_ida);
static DEFINE_IDA(rpmsg_minor_ida);

#define dev_to_eptdev(dev) container_of(dev, struct rpmsg_eptdev, dev)
#define cdev_to_eptdev(i_cdev) container_of(i_cdev, struct rpmsg_eptdev, cdev)

#define dev_to_ctrldev(dev) container_of(dev, struct rpmsg_ctrldev, dev)
#define cdev_to_ctrldev(i_cdev) container_of(i_cdev, struct rpmsg_ctrldev, cdev)

/**
 * struct rpmsg_ctrldev - control device for instantiating endpoint devices
 * @rpdev:	underlaying rpmsg device
 * @cdev:	cdev for the ctrl device
 * @dev:	device for the ctrl device
 */
struct rpmsg_ctrldev {
	struct rpmsg_device *rpdev;
	struct cdev cdev;
	struct device dev;
};

/**
 * struct rpmsg_eptdev - endpoint device context
 * @dev:	endpoint device
@@ -63,6 +50,8 @@ struct rpmsg_ctrldev {
 * @queue_lock:	synchronization of @queue operations
 * @queue:	incoming message queue
 * @readq:	wait object for incoming queue
 * @default_ept: set to channel default endpoint if the default endpoint should be re-used
 *              on device open to prevent endpoint address update.
 */
struct rpmsg_eptdev {
	struct device dev;
@@ -73,13 +62,15 @@ struct rpmsg_eptdev {

	struct mutex ept_lock;
	struct rpmsg_endpoint *ept;
	struct rpmsg_endpoint *default_ept;

	spinlock_t queue_lock;
	struct sk_buff_head queue;
	wait_queue_head_t readq;

};

static int rpmsg_eptdev_destroy(struct device *dev, void *data)
int rpmsg_chrdev_eptdev_destroy(struct device *dev, void *data)
{
	struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev);

@@ -98,6 +89,7 @@ static int rpmsg_eptdev_destroy(struct device *dev, void *data)

	return 0;
}
EXPORT_SYMBOL(rpmsg_chrdev_eptdev_destroy);

static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len,
			void *priv, u32 addr)
@@ -133,7 +125,15 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp)

	get_device(dev);

	/*
	 * If the default_ept is set, the rpmsg device default endpoint is used.
	 * Else a new endpoint is created on open that will be destroyed on release.
	 */
	if (eptdev->default_ept)
		ept = eptdev->default_ept;
	else
		ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);

	if (!ept) {
		dev_err(dev, "failed to open %s\n", eptdev->chinfo.name);
		put_device(dev);
@@ -154,6 +154,7 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp)
	/* Close the endpoint, if it's not already destroyed by the parent */
	mutex_lock(&eptdev->ept_lock);
	if (eptdev->ept) {
		if (!eptdev->default_ept)
			rpmsg_destroy_ept(eptdev->ept);
		eptdev->ept = NULL;
	}
@@ -242,10 +243,13 @@ static ssize_t rpmsg_eptdev_write_iter(struct kiocb *iocb,
		goto unlock_eptdev;
	}

	if (filp->f_flags & O_NONBLOCK)
	if (filp->f_flags & O_NONBLOCK) {
		ret = rpmsg_trysendto(eptdev->ept, kbuf, len, eptdev->chinfo.dst);
	else
		if (ret == -ENOMEM)
			ret = -EAGAIN;
	} else {
		ret = rpmsg_sendto(eptdev->ept, kbuf, len, eptdev->chinfo.dst);
	}

unlock_eptdev:
	mutex_unlock(&eptdev->ept_lock);
@@ -281,7 +285,11 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd,
	if (cmd != RPMSG_DESTROY_EPT_IOCTL)
		return -EINVAL;

	return rpmsg_eptdev_destroy(&eptdev->dev, NULL);
	/* Don't allow to destroy a default endpoint. */
	if (eptdev->default_ept)
		return -EINVAL;

	return rpmsg_chrdev_eptdev_destroy(&eptdev->dev, NULL);
}

static const struct file_operations rpmsg_eptdev_fops = {
@@ -339,21 +347,18 @@ static void rpmsg_eptdev_release_device(struct device *dev)
	kfree(eptdev);
}

static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev,
			       struct rpmsg_channel_info chinfo)
static struct rpmsg_eptdev *rpmsg_chrdev_eptdev_alloc(struct rpmsg_device *rpdev,
						      struct device *parent)
{
	struct rpmsg_device *rpdev = ctrldev->rpdev;
	struct rpmsg_eptdev *eptdev;
	struct device *dev;
	int ret;

	eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL);
	if (!eptdev)
		return -ENOMEM;
		return ERR_PTR(-ENOMEM);

	dev = &eptdev->dev;
	eptdev->rpdev = rpdev;
	eptdev->chinfo = chinfo;

	mutex_init(&eptdev->ept_lock);
	spin_lock_init(&eptdev->queue_lock);
@@ -362,13 +367,23 @@ static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev,

	device_initialize(dev);
	dev->class = rpmsg_class;
	dev->parent = &ctrldev->dev;
	dev->parent = parent;
	dev->groups = rpmsg_eptdev_groups;
	dev_set_drvdata(dev, eptdev);

	cdev_init(&eptdev->cdev, &rpmsg_eptdev_fops);
	eptdev->cdev.owner = THIS_MODULE;

	return eptdev;
}

static int rpmsg_chrdev_eptdev_add(struct rpmsg_eptdev *eptdev, struct rpmsg_channel_info chinfo)
{
	struct device *dev = &eptdev->dev;
	int ret;

	eptdev->chinfo = chinfo;

	ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);
	if (ret < 0)
		goto free_eptdev;
@@ -400,163 +415,91 @@ static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev,
	return ret;
}

static int rpmsg_ctrldev_open(struct inode *inode, struct file *filp)
{
	struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev);

	get_device(&ctrldev->dev);
	filp->private_data = ctrldev;

	return 0;
}

static int rpmsg_ctrldev_release(struct inode *inode, struct file *filp)
{
	struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev);

	put_device(&ctrldev->dev);

	return 0;
}

static long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd,
				unsigned long arg)
int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent,
			       struct rpmsg_channel_info chinfo)
{
	struct rpmsg_ctrldev *ctrldev = fp->private_data;
	void __user *argp = (void __user *)arg;
	struct rpmsg_endpoint_info eptinfo;
	struct rpmsg_channel_info chinfo;

	if (cmd != RPMSG_CREATE_EPT_IOCTL)
		return -EINVAL;

	if (copy_from_user(&eptinfo, argp, sizeof(eptinfo)))
		return -EFAULT;

	memcpy(chinfo.name, eptinfo.name, RPMSG_NAME_SIZE);
	chinfo.name[RPMSG_NAME_SIZE-1] = '\0';
	chinfo.src = eptinfo.src;
	chinfo.dst = eptinfo.dst;
	struct rpmsg_eptdev *eptdev;
	int ret;

	return rpmsg_eptdev_create(ctrldev, chinfo);
};
	eptdev = rpmsg_chrdev_eptdev_alloc(rpdev, parent);
	if (IS_ERR(eptdev))
		return PTR_ERR(eptdev);

static const struct file_operations rpmsg_ctrldev_fops = {
	.owner = THIS_MODULE,
	.open = rpmsg_ctrldev_open,
	.release = rpmsg_ctrldev_release,
	.unlocked_ioctl = rpmsg_ctrldev_ioctl,
	.compat_ioctl = compat_ptr_ioctl,
};
	ret = rpmsg_chrdev_eptdev_add(eptdev, chinfo);

static void rpmsg_ctrldev_release_device(struct device *dev)
{
	struct rpmsg_ctrldev *ctrldev = dev_to_ctrldev(dev);

	ida_simple_remove(&rpmsg_ctrl_ida, dev->id);
	ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt));
	kfree(ctrldev);
	return ret;
}
EXPORT_SYMBOL(rpmsg_chrdev_eptdev_create);

static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)
{
	struct rpmsg_ctrldev *ctrldev;
	struct device *dev;
	int ret;

	ctrldev = kzalloc(sizeof(*ctrldev), GFP_KERNEL);
	if (!ctrldev)
		return -ENOMEM;

	ctrldev->rpdev = rpdev;

	dev = &ctrldev->dev;
	device_initialize(dev);
	dev->parent = &rpdev->dev;
	dev->class = rpmsg_class;

	cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops);
	ctrldev->cdev.owner = THIS_MODULE;

	ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);
	if (ret < 0)
		goto free_ctrldev;
	dev->devt = MKDEV(MAJOR(rpmsg_major), ret);

	ret = ida_simple_get(&rpmsg_ctrl_ida, 0, 0, GFP_KERNEL);
	if (ret < 0)
		goto free_minor_ida;
	dev->id = ret;
	dev_set_name(&ctrldev->dev, "rpmsg_ctrl%d", ret);
	struct rpmsg_channel_info chinfo;
	struct rpmsg_eptdev *eptdev;
	struct device *dev = &rpdev->dev;

	ret = cdev_device_add(&ctrldev->cdev, &ctrldev->dev);
	if (ret)
		goto free_ctrl_ida;
	memcpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);
	chinfo.src = rpdev->src;
	chinfo.dst = rpdev->dst;

	/* We can now rely on the release function for cleanup */
	dev->release = rpmsg_ctrldev_release_device;
	eptdev = rpmsg_chrdev_eptdev_alloc(rpdev, dev);
	if (IS_ERR(eptdev))
		return PTR_ERR(eptdev);

	dev_set_drvdata(&rpdev->dev, ctrldev);
	/* Set the default_ept to the rpmsg device endpoint */
	eptdev->default_ept = rpdev->ept;

	return ret;

free_ctrl_ida:
	ida_simple_remove(&rpmsg_ctrl_ida, dev->id);
free_minor_ida:
	ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt));
free_ctrldev:
	put_device(dev);
	kfree(ctrldev);
	/*
	 * The rpmsg_ept_cb uses *priv parameter to get its rpmsg_eptdev context.
	 * Storedit in default_ept *priv field.
	 */
	eptdev->default_ept->priv = eptdev;

	return ret;
	return rpmsg_chrdev_eptdev_add(eptdev, chinfo);
}

static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev)
{
	struct rpmsg_ctrldev *ctrldev = dev_get_drvdata(&rpdev->dev);
	int ret;

	/* Destroy all endpoints */
	ret = device_for_each_child(&ctrldev->dev, NULL, rpmsg_eptdev_destroy);
	ret = device_for_each_child(&rpdev->dev, NULL, rpmsg_chrdev_eptdev_destroy);
	if (ret)
		dev_warn(&rpdev->dev, "failed to nuke endpoints: %d\n", ret);

	cdev_device_del(&ctrldev->cdev, &ctrldev->dev);
	put_device(&ctrldev->dev);
		dev_warn(&rpdev->dev, "failed to destroy endpoints: %d\n", ret);
}

static struct rpmsg_device_id rpmsg_chrdev_id_table[] = {
	{ .name	= "rpmsg-raw" },
	{ },
};

static struct rpmsg_driver rpmsg_chrdev_driver = {
	.probe = rpmsg_chrdev_probe,
	.remove = rpmsg_chrdev_remove,
	.drv = {
		.name = "rpmsg_chrdev",
	},
	.callback = rpmsg_ept_cb,
	.id_table = rpmsg_chrdev_id_table,
	.drv.name = "rpmsg_chrdev",
};

static int rpmsg_chrdev_init(void)
{
	int ret;

	ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg");
	ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg_char");
	if (ret < 0) {
		pr_err("failed to allocate char dev region\n");
		return ret;
	}

	rpmsg_class = class_create(THIS_MODULE, "rpmsg");
	if (IS_ERR(rpmsg_class)) {
		pr_err("failed to create rpmsg class\n");
		unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
		return PTR_ERR(rpmsg_class);
	}

	ret = register_rpmsg_driver(&rpmsg_chrdev_driver);
	if (ret < 0) {
		pr_err("failed to register rpmsg driver\n");
		class_destroy(rpmsg_class);
		unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
		pr_err("rpmsg: failed to register rpmsg raw driver\n");
		goto free_region;
	}

	return 0;

free_region:
	unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);

	return ret;
}
postcore_initcall(rpmsg_chrdev_init);
@@ -564,7 +507,6 @@ postcore_initcall(rpmsg_chrdev_init);
static void rpmsg_chrdev_exit(void)
{
	unregister_rpmsg_driver(&rpmsg_chrdev_driver);
	class_destroy(rpmsg_class);
	unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
}
module_exit(rpmsg_chrdev_exit);
Loading