All of lore.kernel.org
 help / color / mirror / Atom feed
From: Parshuram Thombare <pthombar@cadence.com>
To: <bbrezillon@kernel.org>, <vitor.soares@synopsys.com>
Cc: <pgaj@cadence.com>, <linux-i3c@lists.infradead.org>,
	<linux-kernel@vger.kernel.org>, <mparab@cadence.com>,
	<praneeth@ti.com>, Parshuram Thombare <pthombar@cadence.com>
Subject: [PATCH v7 5/7] i3c: master: add defslvs processing
Date: Mon, 11 May 2020 15:15:28 +0200	[thread overview]
Message-ID: <1589202928-9912-1-git-send-email-pthombar@cadence.com> (raw)
In-Reply-To: <1589202702-4879-1-git-send-email-pthombar@cadence.com>

Added defslvs processing code to the I3C master subsystem.

Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
 drivers/i3c/master.c       | 142 ++++++++++++++++++++++++++++++++++++-
 include/linux/i3c/master.h |   7 ++
 2 files changed, 147 insertions(+), 2 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 9c8250a6a2b0..ea53fadeed99 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2639,7 +2639,8 @@ static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
 		return -EINVAL;
 
 	if (ops->request_mastership &&
-	    (!ops->enable_mr_events || !ops->check_event_set))
+	    (!ops->enable_mr_events || !ops->check_event_set ||
+	     !ops->sec_mst_dyn_addr))
 		return -EINVAL;
 
 	return 0;
@@ -2818,12 +2819,20 @@ int i3c_secondary_master_register(struct i3c_master_controller *master,
 				  struct device *parent,
 				  const struct i3c_master_controller_ops *ops)
 {
-	int ret;
+	int ret, sz;
 
 	ret = i3c_master_init(master, parent, ops, true);
 	if (ret)
 		return ret;
 
+	sz = sizeof(struct i3c_ccc_dev_desc) * I3C_BUS_MAX_DEVS;
+	master->defslvs_data.devs = devm_kzalloc(&master->dev, sz,
+						 GFP_KERNEL);
+	if (!master->defslvs_data.devs) {
+		ret = -ENOMEM;
+		goto err_cleanup_bus;
+	}
+
 	ret = device_add(&master->dev);
 	if (ret)
 		goto err_cleanup_bus;
@@ -2856,6 +2865,135 @@ int i3c_secondary_master_register(struct i3c_master_controller *master,
 }
 EXPORT_SYMBOL_GPL(i3c_secondary_master_register);
 
+static int i3c_master_populate_bus(struct i3c_master_controller *master)
+{
+	struct i3c_dev_desc *i3cdev, *olddev, *tmp;
+	struct i3c_ccc_dev_desc *desc;
+	struct list_head i3c_old;
+	struct i3c_bus *i3cbus;
+	int slot, dyn_addr, ret;
+
+	i3cbus = i3c_master_get_bus(master);
+
+	INIT_LIST_HEAD(&i3c_old);
+	list_for_each_entry_safe(olddev, tmp, &i3cbus->devs.i3c, common.node) {
+		i3c_master_put_i3c_addrs(olddev);
+		list_del(&olddev->common.node);
+		list_add(&olddev->common.node, &i3c_old);
+	}
+
+	dyn_addr = master->ops->sec_mst_dyn_addr(master);
+	master->this->info.dyn_addr = dyn_addr;
+	i3c_master_get_i3c_addrs(master->this);
+	list_del(&master->this->common.node);
+	list_add(&master->this->common.node, &i3cbus->devs.i3c);
+
+	desc = master->defslvs_data.devs;
+	for (slot = 0; slot < master->defslvs_data.ndevs; slot++, desc++) {
+		struct i3c_device_info info = {
+			.dyn_addr = desc->dyn_addr
+		};
+
+		if (dyn_addr == info.dyn_addr)
+			continue;
+
+		i3cdev = i3c_master_alloc_i3c_dev(master, &info);
+		if (IS_ERR(i3cdev)) {
+			ret = PTR_ERR(i3cdev);
+			goto populate_bus_fail;
+		}
+
+		i3c_master_get_i3c_addrs(i3cdev);
+		ret = i3c_master_retrieve_dev_info(i3cdev);
+		i3c_master_put_i3c_addrs(i3cdev);
+		if (ret) {
+			i3c_master_free_i3c_dev(i3cdev);
+			goto populate_bus_fail;
+		}
+
+		list_for_each_entry_safe(olddev, tmp, &i3c_old, common.node) {
+			if (olddev->info.pid == i3cdev->info.pid) {
+				olddev->info.dyn_addr = info.dyn_addr;
+				i3c_master_get_i3c_addrs(olddev);
+				list_del(&olddev->common.node);
+				list_add(&olddev->common.node,
+					 &i3cbus->devs.i3c);
+				i3c_master_free_i3c_dev(i3cdev);
+				i3cdev = NULL;
+				break;
+			}
+		}
+
+		if (i3cdev) {
+			ret = i3c_master_attach_i3c_dev(master, i3cdev);
+			if (ret) {
+				i3c_master_free_i3c_dev(i3cdev);
+				goto populate_bus_fail;
+			}
+		}
+	}
+
+	list_for_each_entry_safe(olddev, tmp, &i3c_old, common.node) {
+		if (olddev->dev) {
+			olddev->dev->desc = NULL;
+			if (device_is_registered(&olddev->dev->dev))
+				device_unregister(&olddev->dev->dev);
+			else
+				put_device(&olddev->dev->dev);
+			kfree(olddev->dev);
+		}
+		list_del(&olddev->common.node);
+		i3c_master_free_i3c_dev(olddev);
+	}
+
+	return 0;
+
+populate_bus_fail:
+	/*
+	 * Try to restore i3cbus->devs.i3c list, so far no i3c
+	 * device is deleted, only moved or added to the original
+	 * i3c list. Move rest of the i3c devices from old list,
+	 * to correctly process defslvs in rety.
+	 */
+	list_for_each_entry_safe(olddev, tmp, &i3c_old, common.node) {
+		list_del(&olddev->common.node);
+		list_add(&olddev->common.node, &i3cbus->devs.i3c);
+	}
+
+	return ret;
+}
+
+/**
+ * i3c_master_process_defslvs() - process I3C device list received in
+ * DEFSLVS for device plug/unplug and address change.
+ * @m: I3C master object
+ *
+ * This function may sleep, so should not be called in the atomic context.
+ */
+int i3c_master_process_defslvs(struct i3c_master_controller *m)
+{
+	int ret;
+
+	i3c_bus_normaluse_lock(&m->bus);
+	ret = i3c_master_acquire_bus(m);
+	i3c_bus_normaluse_unlock(&m->bus);
+	if (ret)
+		return ret;
+
+	i3c_bus_maintenance_lock(&m->bus);
+	ret = i3c_master_populate_bus(m);
+	i3c_bus_maintenance_unlock(&m->bus);
+	if (!ret) {
+		i3c_bus_normaluse_lock(&m->bus);
+		i3c_master_register_new_i3c_devs(m);
+		i3c_bus_normaluse_unlock(&m->bus);
+	}
+	i3c_master_enable_mr_events(m);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_process_defslvs);
+
 /**
  * i3c_master_unregister() - unregister an I3C master
  * @master: master used to send frames on the bus
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index dd67497ad8b1..688487c4a62a 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -488,6 +488,8 @@ struct i3c_master_controller_ops {
  *	in a thread context. Typical examples are Hot Join processing which
  *	requires taking the bus lock in maintenance, which in turn, can only
  *	be done from a sleep-able context
+ * @defslvs_data: list used to pass i3c device list received in DEFSLVS message,
+ *	from DEFSLVS controller driver to I3C core
  *
  * A &struct i3c_master_controller has to be registered to the I3C subsystem
  * through i3c_master_register(). None of &struct i3c_master_controller fields
@@ -507,6 +509,10 @@ struct i3c_master_controller {
 	} boardinfo;
 	struct i3c_bus bus;
 	struct workqueue_struct *wq;
+	struct {
+		u32 ndevs;
+		struct i3c_ccc_dev_desc *devs;
+	} defslvs_data;
 };
 
 /**
@@ -533,6 +539,7 @@ struct i3c_master_controller {
 
 void i3c_master_yield_bus(struct i3c_master_controller *m,
 			  u8 sec_mst_dyn_addr);
+int i3c_master_process_defslvs(struct i3c_master_controller *m);
 int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
 			    const struct i2c_msg *xfers,
 			    int nxfers);
-- 
2.17.1


WARNING: multiple messages have this Message-ID (diff)
From: Parshuram Thombare <pthombar@cadence.com>
To: <bbrezillon@kernel.org>, <vitor.soares@synopsys.com>
Cc: mparab@cadence.com, Parshuram Thombare <pthombar@cadence.com>,
	praneeth@ti.com, linux-kernel@vger.kernel.org, pgaj@cadence.com,
	linux-i3c@lists.infradead.org
Subject: [PATCH v7 5/7] i3c: master: add defslvs processing
Date: Mon, 11 May 2020 15:15:28 +0200	[thread overview]
Message-ID: <1589202928-9912-1-git-send-email-pthombar@cadence.com> (raw)
In-Reply-To: <1589202702-4879-1-git-send-email-pthombar@cadence.com>

Added defslvs processing code to the I3C master subsystem.

Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
 drivers/i3c/master.c       | 142 ++++++++++++++++++++++++++++++++++++-
 include/linux/i3c/master.h |   7 ++
 2 files changed, 147 insertions(+), 2 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 9c8250a6a2b0..ea53fadeed99 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2639,7 +2639,8 @@ static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
 		return -EINVAL;
 
 	if (ops->request_mastership &&
-	    (!ops->enable_mr_events || !ops->check_event_set))
+	    (!ops->enable_mr_events || !ops->check_event_set ||
+	     !ops->sec_mst_dyn_addr))
 		return -EINVAL;
 
 	return 0;
@@ -2818,12 +2819,20 @@ int i3c_secondary_master_register(struct i3c_master_controller *master,
 				  struct device *parent,
 				  const struct i3c_master_controller_ops *ops)
 {
-	int ret;
+	int ret, sz;
 
 	ret = i3c_master_init(master, parent, ops, true);
 	if (ret)
 		return ret;
 
+	sz = sizeof(struct i3c_ccc_dev_desc) * I3C_BUS_MAX_DEVS;
+	master->defslvs_data.devs = devm_kzalloc(&master->dev, sz,
+						 GFP_KERNEL);
+	if (!master->defslvs_data.devs) {
+		ret = -ENOMEM;
+		goto err_cleanup_bus;
+	}
+
 	ret = device_add(&master->dev);
 	if (ret)
 		goto err_cleanup_bus;
@@ -2856,6 +2865,135 @@ int i3c_secondary_master_register(struct i3c_master_controller *master,
 }
 EXPORT_SYMBOL_GPL(i3c_secondary_master_register);
 
+static int i3c_master_populate_bus(struct i3c_master_controller *master)
+{
+	struct i3c_dev_desc *i3cdev, *olddev, *tmp;
+	struct i3c_ccc_dev_desc *desc;
+	struct list_head i3c_old;
+	struct i3c_bus *i3cbus;
+	int slot, dyn_addr, ret;
+
+	i3cbus = i3c_master_get_bus(master);
+
+	INIT_LIST_HEAD(&i3c_old);
+	list_for_each_entry_safe(olddev, tmp, &i3cbus->devs.i3c, common.node) {
+		i3c_master_put_i3c_addrs(olddev);
+		list_del(&olddev->common.node);
+		list_add(&olddev->common.node, &i3c_old);
+	}
+
+	dyn_addr = master->ops->sec_mst_dyn_addr(master);
+	master->this->info.dyn_addr = dyn_addr;
+	i3c_master_get_i3c_addrs(master->this);
+	list_del(&master->this->common.node);
+	list_add(&master->this->common.node, &i3cbus->devs.i3c);
+
+	desc = master->defslvs_data.devs;
+	for (slot = 0; slot < master->defslvs_data.ndevs; slot++, desc++) {
+		struct i3c_device_info info = {
+			.dyn_addr = desc->dyn_addr
+		};
+
+		if (dyn_addr == info.dyn_addr)
+			continue;
+
+		i3cdev = i3c_master_alloc_i3c_dev(master, &info);
+		if (IS_ERR(i3cdev)) {
+			ret = PTR_ERR(i3cdev);
+			goto populate_bus_fail;
+		}
+
+		i3c_master_get_i3c_addrs(i3cdev);
+		ret = i3c_master_retrieve_dev_info(i3cdev);
+		i3c_master_put_i3c_addrs(i3cdev);
+		if (ret) {
+			i3c_master_free_i3c_dev(i3cdev);
+			goto populate_bus_fail;
+		}
+
+		list_for_each_entry_safe(olddev, tmp, &i3c_old, common.node) {
+			if (olddev->info.pid == i3cdev->info.pid) {
+				olddev->info.dyn_addr = info.dyn_addr;
+				i3c_master_get_i3c_addrs(olddev);
+				list_del(&olddev->common.node);
+				list_add(&olddev->common.node,
+					 &i3cbus->devs.i3c);
+				i3c_master_free_i3c_dev(i3cdev);
+				i3cdev = NULL;
+				break;
+			}
+		}
+
+		if (i3cdev) {
+			ret = i3c_master_attach_i3c_dev(master, i3cdev);
+			if (ret) {
+				i3c_master_free_i3c_dev(i3cdev);
+				goto populate_bus_fail;
+			}
+		}
+	}
+
+	list_for_each_entry_safe(olddev, tmp, &i3c_old, common.node) {
+		if (olddev->dev) {
+			olddev->dev->desc = NULL;
+			if (device_is_registered(&olddev->dev->dev))
+				device_unregister(&olddev->dev->dev);
+			else
+				put_device(&olddev->dev->dev);
+			kfree(olddev->dev);
+		}
+		list_del(&olddev->common.node);
+		i3c_master_free_i3c_dev(olddev);
+	}
+
+	return 0;
+
+populate_bus_fail:
+	/*
+	 * Try to restore i3cbus->devs.i3c list, so far no i3c
+	 * device is deleted, only moved or added to the original
+	 * i3c list. Move rest of the i3c devices from old list,
+	 * to correctly process defslvs in rety.
+	 */
+	list_for_each_entry_safe(olddev, tmp, &i3c_old, common.node) {
+		list_del(&olddev->common.node);
+		list_add(&olddev->common.node, &i3cbus->devs.i3c);
+	}
+
+	return ret;
+}
+
+/**
+ * i3c_master_process_defslvs() - process I3C device list received in
+ * DEFSLVS for device plug/unplug and address change.
+ * @m: I3C master object
+ *
+ * This function may sleep, so should not be called in the atomic context.
+ */
+int i3c_master_process_defslvs(struct i3c_master_controller *m)
+{
+	int ret;
+
+	i3c_bus_normaluse_lock(&m->bus);
+	ret = i3c_master_acquire_bus(m);
+	i3c_bus_normaluse_unlock(&m->bus);
+	if (ret)
+		return ret;
+
+	i3c_bus_maintenance_lock(&m->bus);
+	ret = i3c_master_populate_bus(m);
+	i3c_bus_maintenance_unlock(&m->bus);
+	if (!ret) {
+		i3c_bus_normaluse_lock(&m->bus);
+		i3c_master_register_new_i3c_devs(m);
+		i3c_bus_normaluse_unlock(&m->bus);
+	}
+	i3c_master_enable_mr_events(m);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_process_defslvs);
+
 /**
  * i3c_master_unregister() - unregister an I3C master
  * @master: master used to send frames on the bus
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index dd67497ad8b1..688487c4a62a 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -488,6 +488,8 @@ struct i3c_master_controller_ops {
  *	in a thread context. Typical examples are Hot Join processing which
  *	requires taking the bus lock in maintenance, which in turn, can only
  *	be done from a sleep-able context
+ * @defslvs_data: list used to pass i3c device list received in DEFSLVS message,
+ *	from DEFSLVS controller driver to I3C core
  *
  * A &struct i3c_master_controller has to be registered to the I3C subsystem
  * through i3c_master_register(). None of &struct i3c_master_controller fields
@@ -507,6 +509,10 @@ struct i3c_master_controller {
 	} boardinfo;
 	struct i3c_bus bus;
 	struct workqueue_struct *wq;
+	struct {
+		u32 ndevs;
+		struct i3c_ccc_dev_desc *devs;
+	} defslvs_data;
 };
 
 /**
@@ -533,6 +539,7 @@ struct i3c_master_controller {
 
 void i3c_master_yield_bus(struct i3c_master_controller *m,
 			  u8 sec_mst_dyn_addr);
+int i3c_master_process_defslvs(struct i3c_master_controller *m);
 int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
 			    const struct i2c_msg *xfers,
 			    int nxfers);
-- 
2.17.1


_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

  parent reply	other threads:[~2020-05-11 13:20 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-11 13:11 [PATCH v7 0/7] I3C mastership handover support Parshuram Thombare
2020-05-11 13:11 ` Parshuram Thombare
2020-05-11 13:12 ` [PATCH v7 1/7] i3c: master: secondary master initialization document Parshuram Thombare
2020-05-11 13:12   ` Parshuram Thombare
2020-05-11 16:05   ` Boris Brezillon
2020-05-11 16:05     ` Boris Brezillon
2020-05-12  5:03     ` Parshuram Raju Thombare
2020-05-12  5:03       ` Parshuram Raju Thombare
2020-05-12  7:35       ` Boris Brezillon
2020-05-12  7:35         ` Boris Brezillon
2020-05-11 13:13 ` [PATCH v7 2/7] i3c: master: use i3c_master_register only for main master Parshuram Thombare
2020-05-11 13:13   ` Parshuram Thombare
2020-05-11 15:44   ` Boris Brezillon
2020-05-11 15:44     ` Boris Brezillon
2020-05-12  4:57     ` Parshuram Raju Thombare
2020-05-12  4:57       ` Parshuram Raju Thombare
2020-05-11 13:13 ` [PATCH v7 3/7] i3c: master: add i3c_secondary_master_register Parshuram Thombare
2020-05-11 13:13   ` Parshuram Thombare
2020-05-11 16:14   ` Boris Brezillon
2020-05-11 16:14     ` Boris Brezillon
2020-05-12  6:42     ` Parshuram Raju Thombare
2020-05-12  6:42       ` Parshuram Raju Thombare
2020-05-11 13:14 ` [PATCH v7 4/7] i3c: master: add mastership handover support Parshuram Thombare
2020-05-11 13:14   ` Parshuram Thombare
2020-05-11 16:38   ` Boris Brezillon
2020-05-11 16:38     ` Boris Brezillon
2020-05-12  6:24     ` Parshuram Raju Thombare
2020-05-12  6:24       ` Parshuram Raju Thombare
2020-05-11 13:15 ` Parshuram Thombare [this message]
2020-05-11 13:15   ` [PATCH v7 5/7] i3c: master: add defslvs processing Parshuram Thombare
2020-05-11 13:16 ` [PATCH v7 6/7] i3c: master: sysfs key for acquire bus Parshuram Thombare
2020-05-11 13:16   ` Parshuram Thombare
2020-05-11 13:17 ` [PATCH v7 7/7] i3c: master: mastership handover, defslvs processing in cdns controller driver Parshuram Thombare
2020-05-11 13:17   ` Parshuram Thombare

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1589202928-9912-1-git-send-email-pthombar@cadence.com \
    --to=pthombar@cadence.com \
    --cc=bbrezillon@kernel.org \
    --cc=linux-i3c@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mparab@cadence.com \
    --cc=pgaj@cadence.com \
    --cc=praneeth@ti.com \
    --cc=vitor.soares@synopsys.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.