Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9e4cf237 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "soc: qcom: spcom: add spcom as DLKM for GKI"

parents 4bf60464 605a33c9
Loading
Loading
Loading
Loading
+175 −0
Original line number Diff line number Diff line
@@ -156,6 +156,7 @@ struct spcom_channel {
	struct cdev *cdev;
	struct device *dev;
	struct device_attribute attr;
	dev_t devt;

	/* rpmsg */
	struct rpmsg_driver *rpdrv;
@@ -227,6 +228,7 @@ struct spcom_device {

	struct completion rpmsg_state_change;
	atomic_t rpmsg_dev_count;
	atomic_t remove_in_progress;

	/* rx data path */
	struct list_head    rx_list_head;
@@ -255,6 +257,7 @@ static u32 spcom_soc2sp_rmb_sp_ssr_mask;

/* static functions declaration */
static int spcom_create_channel_chardev(const char *name, bool is_sharable);
static int spcom_destroy_channel_chardev(const char *name);
static struct spcom_channel *spcom_find_channel_by_name(const char *name);
static int spcom_register_rpmsg_drv(struct spcom_channel *ch);
static int spcom_unregister_rpmsg_drv(struct spcom_channel *ch);
@@ -319,6 +322,32 @@ static int spcom_create_predefined_channels_chardev(void)
	return 0;
}

static int spcom_destroy_predefined_channels_chardev(void)
{
	int i;
	int ret;
	static bool is_predefined_created;

	if (!is_predefined_created)
		return 0;

	for (i = 0; i < SPCOM_MAX_CHANNELS; i++) {
		const char *name = spcom_dev->predefined_ch_name[i];

		if (name[0] == 0)
			break;
		ret = spcom_destroy_channel_chardev(name);
		if (ret) {
			pr_err("failed to destroy chardev [%s], ret [%d]\n",
			       name, ret);
			return -EFAULT;
		}
	}

	is_predefined_created = false;

	return 0;
}
/*======================================================================*/
/*		UTILITIES						*/
/*======================================================================*/
@@ -639,6 +668,7 @@ static int spcom_handle_restart_sp_command(void *cmd_buf, int cmd_size)
	void *subsystem_get_retval = NULL;
	struct spcom_user_restart_sp_command *cmd = cmd_buf;
	struct subsys_desc *desc_p = NULL;
	int (*desc_powerup)(const struct subsys_desc *) = NULL;

	if (!cmd) {
		pr_err("NULL cmd_buf\n");
@@ -1474,6 +1504,11 @@ static int spcom_device_open(struct inode *inode, struct file *filp)

	pr_debug("open file [%s]\n", name);

	if (atomic_read(&spcom_dev->remove_in_progress)) {
		pr_err("module remove in progress\n");
		return -ENODEV;
	}

	if (strcmp(name, "unknown") == 0) {
		pr_err("name is unknown\n");
		return -EINVAL;
@@ -1668,6 +1703,11 @@ static ssize_t spcom_device_write(struct file *filp,
		return -EINVAL;
	}

	if (atomic_read(&spcom_dev->remove_in_progress)) {
		pr_err("module remove in progress\n");
		return -ENODEV;
	}

	if (*f_pos != 0) {
		pr_err("offset should be zero, no sparse buffer\n");
		return -EINVAL;
@@ -1754,6 +1794,11 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff,

	pr_debug("read file [%s], size = %d bytes\n", name, (int) size);

	if (atomic_read(&spcom_dev->remove_in_progress)) {
		pr_err("module remove in progress\n");
		return -ENODEV;
	}

	if (strcmp(name, "unknown") == 0) {
		pr_err("name is unknown\n");
		return -EINVAL;
@@ -1899,6 +1944,11 @@ static long spcom_device_ioctl(struct file *file,
	struct spcom_poll_param op = {0};
	int ret = 0;

	if (atomic_read(&spcom_dev->remove_in_progress)) {
		pr_err("module remove in progress\n");
		return -ENODEV;
	}

	if (strcmp(name, "unknown") == 0) {
		pr_err("name is unknown\n");
		return -EINVAL;
@@ -2016,6 +2066,7 @@ static int spcom_create_channel_chardev(const char *name, bool is_sharable)
	mutex_lock(&ch->lock);
	ch->cdev = cdev;
	ch->dev = dev;
	ch->devt = devt;
	mutex_unlock(&ch->lock);

	return 0;
@@ -2036,6 +2087,37 @@ static int spcom_create_channel_chardev(const char *name, bool is_sharable)
	return -EFAULT;
}

// TODO: error handling
static int spcom_destroy_channel_chardev(const char *name)
{
	int ret;
	struct spcom_channel *ch;

	pr_err("destroy channel [%s]\n", name);

	ch = spcom_find_channel_by_name(name);
	if (!ch) {
		pr_err("channel [%s] not exist\n", name);
		return -EINVAL;
	}

	ret = spcom_unregister_rpmsg_drv(ch);
	if (ret < 0) {
		pr_err("unregister rpmsg driver failed %d\n", ret);
		return ret;
	}

	mutex_lock(&ch->lock);
	device_destroy(spcom_dev->driver_class, ch->devt);
	kfree(ch->cdev);
	mutex_unlock(&ch->lock);

	atomic_dec(&spcom_dev->chdev_count);


	return 0;
}

static int spcom_register_chardev(void)
{
	int ret;
@@ -2098,6 +2180,7 @@ static void spcom_unregister_chrdev(void)
	class_destroy(spcom_dev->driver_class);
	unregister_chrdev_region(spcom_dev->device_no,
				 atomic_read(&spcom_dev->chdev_count));
	pr_debug("control spcom device removed\n");

}

@@ -2314,7 +2397,15 @@ static int spcom_rpdev_probe(struct rpmsg_device *rpdev)
		pr_err("rpdev is NULL\n");
		return -EINVAL;
	}

	name = rpdev->id.name;

	/* module exiting */
	if (atomic_read(&spcom_dev->remove_in_progress)) {
		pr_warn("remove in progress, ignore rpmsg probe for ch %s\n",
			name);
		return 0;
	}
	pr_debug("new channel %s rpmsg_device arrived\n", name);
	ch = spcom_find_channel_by_name(name);
	if (!ch) {
@@ -2482,6 +2573,7 @@ static int spcom_probe(struct platform_device *pdev)
	atomic_set(&spcom_dev->chdev_count, 1);
	init_completion(&spcom_dev->rpmsg_state_change);
	atomic_set(&spcom_dev->rpmsg_dev_count, 0);
	atomic_set(&spcom_dev->remove_in_progress, 0);

	INIT_LIST_HEAD(&spcom_dev->rx_list_head);
	spin_lock_init(&spcom_dev->rx_lock);
@@ -2516,6 +2608,82 @@ static int spcom_probe(struct platform_device *pdev)
	return -ENODEV;
}


static int spcom_remove(struct platform_device *pdev)
{
	int ret;
	void *subsystem_get_retval = NULL;
	struct rx_buff_list *rx_item;
	unsigned long flags;
	int i;

	atomic_inc(&spcom_dev->remove_in_progress);

	/* trigger SSR to release all connected channels */
	subsystem_get_retval = subsystem_get("spss");
	if (!subsystem_get_retval) {
		pr_err("unable to trigger spss SSR\n");
		atomic_dec(&spcom_dev->remove_in_progress);
		return -EINVAL;
	}

	pr_debug("wait for remove of %d rpmsg devices\n",
		 atomic_read(&spcom_dev->rpmsg_dev_count));

	reinit_completion(&spcom_dev->rpmsg_state_change);
	ret = wait_for_completion_interruptible(&spcom_dev->rpmsg_state_change);

	pr_debug("channels removed, : rpmsg_dev_count=%d\n",
	       atomic_read(&spcom_dev->rpmsg_dev_count));

	ret = spcom_destroy_predefined_channels_chardev();
	if (ret < 0) {
		pr_err("failed to destroy predefined channels %d\n", ret);
		goto remove_error;
	}
	/* destroy existing channel char devices */
	for (i = 0; i < SPCOM_MAX_CHANNELS; i++) {
		const char *name = spcom_dev->channels[i].name;

		if (name[0] == 0)
			break;
		ret = spcom_destroy_channel_chardev(name);
		if (ret) {
			pr_err("failed to destroy chardev [%s], ret [%d]\n",
			       name, ret);
			return -EFAULT;
		}
	}

	/* destroy control char device */
	spcom_unregister_chrdev();

	/* release uncompleted rx */
	spin_lock_irqsave(&spcom_dev->rx_lock, flags);
	while (!list_empty(&spcom_dev->rx_list_head)) {
		/* detach last entry */
		rx_item = list_last_entry(&spcom_dev->rx_list_head,
					  struct rx_buff_list, list);
		list_del(&rx_item->list);
		if (!rx_item) {
			pr_err("empty entry in pending rx list\n");
			spin_lock_irqsave(&spcom_dev->rx_lock, flags);
			continue;
		}
		kfree(rx_item);
	}
	spin_unlock_irqrestore(&spcom_dev->rx_lock, flags);


	// free global device struct
	kfree(spcom_dev);
	spcom_dev = NULL;
	pr_debug("successfully released all module resources\n");

	return 0;
remove_error:
	return ret; // TODO: find more precise error
}
static const struct of_device_id spcom_match_table[] = {
	{ .compatible = "qcom,spcom", },
	{ },
@@ -2523,6 +2691,7 @@ static const struct of_device_id spcom_match_table[] = {

static struct platform_driver spcom_driver = {
	.probe = spcom_probe,
	.remove = spcom_remove,
	.driver = {
		.name = DEVICE_NAME,
		.of_match_table = of_match_ptr(spcom_match_table),
@@ -2541,5 +2710,11 @@ static int __init spcom_init(void)
}
module_init(spcom_init);

static void __exit spcom_exit(void)
{
	platform_driver_unregister(&spcom_driver);
}
module_exit(spcom_exit)

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Secure Processor Communication");