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 v6 4/8] i3c: master: defslvs processing
Date: Fri, 17 Apr 2020 18:21:57 +0200	[thread overview]
Message-ID: <1587140518-30782-1-git-send-email-pthombar@cadence.com> (raw)
In-Reply-To: <1587140398-29473-1-git-send-email-pthombar@cadence.com>

This patch add DEFSLVS processing code
to I3C master subsystem.

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

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 3598856a0b25..2690910d724c 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1830,6 +1830,206 @@ int i3c_master_acquire_bus(struct i3c_master_controller *master)
 }
 EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
 
+static struct i2c_dev_boardinfo *
+i3c_master_alloc_i2c_boardinfo(struct i3c_master_controller *master,
+			       u16 addr, u8 lvr)
+{
+	struct i2c_dev_boardinfo *i2cboardinfo;
+
+	i2cboardinfo = kzalloc(sizeof(*i2cboardinfo), GFP_KERNEL);
+	if (!i2cboardinfo)
+		return ERR_PTR(-ENOMEM);
+
+	i2cboardinfo->base.addr = addr;
+	i2cboardinfo->lvr = lvr;
+
+	return i2cboardinfo;
+}
+
+static void
+i3c_master_copy_olddev(struct i3c_master_controller *master,
+		       struct i3c_dev_desc *newdev,
+		       struct i3c_dev_desc *olddev)
+{
+	struct i3c_ibi_setup ibireq = { };
+	bool enable_ibi = false;
+	int ret;
+
+	newdev->dev = olddev->dev;
+	if (newdev->dev)
+		newdev->dev->desc = newdev;
+
+	mutex_lock(&olddev->ibi_lock);
+	if (olddev->ibi) {
+		ibireq.handler = olddev->ibi->handler;
+		ibireq.max_payload_len = olddev->ibi->max_payload_len;
+		ibireq.num_slots = olddev->ibi->num_slots;
+
+		if (olddev->ibi->enabled) {
+			enable_ibi = true;
+			i3c_dev_disable_ibi_locked(olddev);
+		}
+
+		i3c_dev_free_ibi_locked(olddev);
+	}
+	mutex_unlock(&olddev->ibi_lock);
+
+	i3c_master_detach_i3c_dev(olddev);
+	i3c_master_free_i3c_dev(olddev);
+
+	if (ibireq.handler) {
+		mutex_lock(&newdev->ibi_lock);
+		ret = i3c_dev_request_ibi_locked(newdev, &ibireq);
+		if (ret) {
+			dev_err(&master->dev,
+				"Failed to request IBI on device %d-%llx",
+				master->bus.id, newdev->info.pid);
+		} else if (enable_ibi) {
+			ret = i3c_dev_enable_ibi_locked(newdev);
+			if (ret)
+				dev_err(&master->dev,
+					"Failed to re-enable IBI on device %d-%llx",
+					master->bus.id, newdev->info.pid);
+		}
+		mutex_unlock(&newdev->ibi_lock);
+	}
+}
+
+static int i3c_master_populate_bus(struct i3c_master_controller *master)
+{
+	struct i3c_ccc_dev_desc *desc;
+	struct i2c_dev_desc *i2cdev;
+	struct i2c_dev_boardinfo *info;
+	struct i3c_dev_desc *i3cdev, *olddev, *i3ctmp;
+	struct i3c_bus *i3cbus;
+	struct list_head i3c_old;
+	int slot, ret;
+
+	INIT_LIST_HEAD(&i3c_old);
+	i3cbus = i3c_master_get_bus(master);
+	list_add(&i3c_old, &i3cbus->devs.i3c);
+	list_del(&i3cbus->devs.i3c);
+	INIT_LIST_HEAD(&i3cbus->devs.i3c);
+	desc = master->defslvs_data.devs;
+	for (slot = 1; slot <= master->defslvs_data.ndevs; slot++, desc++) {
+		if (desc->static_addr && list_empty(&master->bus.devs.i2c)) {
+			i3c_bus_set_addr_slot_status(&master->bus,
+						     desc->static_addr,
+						     I3C_ADDR_SLOT_I2C_DEV);
+			info = i3c_master_alloc_i2c_boardinfo(master,
+							      desc->static_addr,
+							      desc->lvr);
+			if (IS_ERR(info)) {
+				ret = PTR_ERR(info);
+				goto err_detach_devs;
+			}
+
+			i2cdev = i3c_master_alloc_i2c_dev(master, info);
+			if (IS_ERR(i2cdev)) {
+				ret = PTR_ERR(i2cdev);
+				goto err_detach_devs;
+			}
+
+			ret = i3c_master_attach_i2c_dev(master, i2cdev);
+			if (ret) {
+				i3c_master_free_i2c_dev(i2cdev);
+				goto err_detach_devs;
+			}
+		} else {
+			struct i3c_device_info info = {
+				.dyn_addr = desc->dyn_addr
+			};
+
+			i3cdev = i3c_master_alloc_i3c_dev(master, &info);
+			if (IS_ERR(i3cdev)) {
+				ret = PTR_ERR(i3cdev);
+				goto err_detach_devs;
+			}
+
+			ret = i3c_master_attach_i3c_dev(master, i3cdev);
+			if (ret)
+				goto err_detach_devs;
+
+			ret = i3c_master_retrieve_dev_info(i3cdev);
+			if (ret)
+				goto err_detach_devs;
+
+			list_for_each_entry(olddev, &i3c_old, common.node) {
+				if (i3cdev != olddev &&
+				    i3cdev->info.pid == olddev->info.pid)
+					i3c_master_copy_olddev(master, i3cdev,
+							       olddev);
+			}
+		}
+	}
+
+	list_for_each_entry_safe(i3cdev, i3ctmp, &i3c_old,
+				 common.node) {
+		i3c_master_detach_i3c_dev(i3cdev);
+		i3c_master_free_i3c_dev(i3cdev);
+	}
+
+	return 0;
+
+err_detach_devs:
+	if (!master->init_done) {
+		i3c_master_detach_free_devs(master);
+	} else {
+		INIT_LIST_HEAD(&i3cbus->devs.i3c);
+		list_add(&i3cbus->devs.i3c, &i3c_old);
+		list_del(&i3c_old);
+	}
+
+	return ret;
+}
+
+/* This function may sleep, so should not be called from atomic context */
+int i3c_master_process_defslvs(struct i3c_master_controller *master)
+{
+	struct i3c_bus *i3cbus;
+	int ret;
+
+	i3cbus = i3c_master_get_bus(master);
+
+	i3c_bus_normaluse_lock(&master->bus);
+	ret = i3c_master_acquire_bus(master);
+	i3c_bus_normaluse_unlock(&master->bus);
+	if (ret)
+		return ret;
+
+	/* Again bus_init to bus_mode, based on data received in DEFSLVS */
+	ret = i3c_bus_set_mode(i3cbus, master->defslvs_data.bus_mode);
+	if (ret)
+		return ret;
+
+	ret = master->ops->bus_init(master);
+	if (ret)
+		goto err_cleanup_bus;
+
+	if (!ret) {
+		master->ops->master_set_info(master);
+		i3c_bus_maintenance_lock(&master->bus);
+		ret = i3c_master_populate_bus(master);
+		if (ret) {
+			i3c_bus_maintenance_unlock(&master->bus);
+			goto err_cleanup_bus;
+		}
+		i3c_master_register_new_i3c_devs(master);
+		i3c_bus_maintenance_unlock(&master->bus);
+	}
+
+	if (master->init_done)
+		i3c_master_enable_mr_events(master);
+	return 0;
+
+err_cleanup_bus:
+	i3c_master_enable_mr_events(master);
+	if (master->ops->bus_cleanup)
+		master->ops->bus_cleanup(master);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_process_defslvs);
+
 /**
  * i3c_master_bus_init() - initialize an I3C bus
  * @master: main master initializing the bus
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index c465c7792ccb..cc482934803b 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -520,6 +520,11 @@ struct i3c_master_controller {
 	struct completion mr_comp;
 	enum i3c_mr_state mr_state;
 	u8 mr_addr;
+	struct {
+		u32 ndevs;
+		enum i3c_bus_mode bus_mode;
+		struct i3c_ccc_dev_desc *devs;
+	} defslvs_data;
 };
 
 /**
@@ -544,6 +549,7 @@ struct i3c_master_controller {
 #define i3c_bus_for_each_i3cdev(bus, dev)				\
 	list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
 
+int i3c_master_process_defslvs(struct i3c_master_controller *master);
 void i3c_master_yield_bus(struct i3c_master_controller *master,
 			  u8 slv_dyn_addr);
 void i3c_sec_mst_mr_dis_event(struct i3c_master_controller *m);
-- 
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 v6 4/8] i3c: master: defslvs processing
Date: Fri, 17 Apr 2020 18:21:57 +0200	[thread overview]
Message-ID: <1587140518-30782-1-git-send-email-pthombar@cadence.com> (raw)
In-Reply-To: <1587140398-29473-1-git-send-email-pthombar@cadence.com>

This patch add DEFSLVS processing code
to I3C master subsystem.

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

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 3598856a0b25..2690910d724c 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1830,6 +1830,206 @@ int i3c_master_acquire_bus(struct i3c_master_controller *master)
 }
 EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
 
+static struct i2c_dev_boardinfo *
+i3c_master_alloc_i2c_boardinfo(struct i3c_master_controller *master,
+			       u16 addr, u8 lvr)
+{
+	struct i2c_dev_boardinfo *i2cboardinfo;
+
+	i2cboardinfo = kzalloc(sizeof(*i2cboardinfo), GFP_KERNEL);
+	if (!i2cboardinfo)
+		return ERR_PTR(-ENOMEM);
+
+	i2cboardinfo->base.addr = addr;
+	i2cboardinfo->lvr = lvr;
+
+	return i2cboardinfo;
+}
+
+static void
+i3c_master_copy_olddev(struct i3c_master_controller *master,
+		       struct i3c_dev_desc *newdev,
+		       struct i3c_dev_desc *olddev)
+{
+	struct i3c_ibi_setup ibireq = { };
+	bool enable_ibi = false;
+	int ret;
+
+	newdev->dev = olddev->dev;
+	if (newdev->dev)
+		newdev->dev->desc = newdev;
+
+	mutex_lock(&olddev->ibi_lock);
+	if (olddev->ibi) {
+		ibireq.handler = olddev->ibi->handler;
+		ibireq.max_payload_len = olddev->ibi->max_payload_len;
+		ibireq.num_slots = olddev->ibi->num_slots;
+
+		if (olddev->ibi->enabled) {
+			enable_ibi = true;
+			i3c_dev_disable_ibi_locked(olddev);
+		}
+
+		i3c_dev_free_ibi_locked(olddev);
+	}
+	mutex_unlock(&olddev->ibi_lock);
+
+	i3c_master_detach_i3c_dev(olddev);
+	i3c_master_free_i3c_dev(olddev);
+
+	if (ibireq.handler) {
+		mutex_lock(&newdev->ibi_lock);
+		ret = i3c_dev_request_ibi_locked(newdev, &ibireq);
+		if (ret) {
+			dev_err(&master->dev,
+				"Failed to request IBI on device %d-%llx",
+				master->bus.id, newdev->info.pid);
+		} else if (enable_ibi) {
+			ret = i3c_dev_enable_ibi_locked(newdev);
+			if (ret)
+				dev_err(&master->dev,
+					"Failed to re-enable IBI on device %d-%llx",
+					master->bus.id, newdev->info.pid);
+		}
+		mutex_unlock(&newdev->ibi_lock);
+	}
+}
+
+static int i3c_master_populate_bus(struct i3c_master_controller *master)
+{
+	struct i3c_ccc_dev_desc *desc;
+	struct i2c_dev_desc *i2cdev;
+	struct i2c_dev_boardinfo *info;
+	struct i3c_dev_desc *i3cdev, *olddev, *i3ctmp;
+	struct i3c_bus *i3cbus;
+	struct list_head i3c_old;
+	int slot, ret;
+
+	INIT_LIST_HEAD(&i3c_old);
+	i3cbus = i3c_master_get_bus(master);
+	list_add(&i3c_old, &i3cbus->devs.i3c);
+	list_del(&i3cbus->devs.i3c);
+	INIT_LIST_HEAD(&i3cbus->devs.i3c);
+	desc = master->defslvs_data.devs;
+	for (slot = 1; slot <= master->defslvs_data.ndevs; slot++, desc++) {
+		if (desc->static_addr && list_empty(&master->bus.devs.i2c)) {
+			i3c_bus_set_addr_slot_status(&master->bus,
+						     desc->static_addr,
+						     I3C_ADDR_SLOT_I2C_DEV);
+			info = i3c_master_alloc_i2c_boardinfo(master,
+							      desc->static_addr,
+							      desc->lvr);
+			if (IS_ERR(info)) {
+				ret = PTR_ERR(info);
+				goto err_detach_devs;
+			}
+
+			i2cdev = i3c_master_alloc_i2c_dev(master, info);
+			if (IS_ERR(i2cdev)) {
+				ret = PTR_ERR(i2cdev);
+				goto err_detach_devs;
+			}
+
+			ret = i3c_master_attach_i2c_dev(master, i2cdev);
+			if (ret) {
+				i3c_master_free_i2c_dev(i2cdev);
+				goto err_detach_devs;
+			}
+		} else {
+			struct i3c_device_info info = {
+				.dyn_addr = desc->dyn_addr
+			};
+
+			i3cdev = i3c_master_alloc_i3c_dev(master, &info);
+			if (IS_ERR(i3cdev)) {
+				ret = PTR_ERR(i3cdev);
+				goto err_detach_devs;
+			}
+
+			ret = i3c_master_attach_i3c_dev(master, i3cdev);
+			if (ret)
+				goto err_detach_devs;
+
+			ret = i3c_master_retrieve_dev_info(i3cdev);
+			if (ret)
+				goto err_detach_devs;
+
+			list_for_each_entry(olddev, &i3c_old, common.node) {
+				if (i3cdev != olddev &&
+				    i3cdev->info.pid == olddev->info.pid)
+					i3c_master_copy_olddev(master, i3cdev,
+							       olddev);
+			}
+		}
+	}
+
+	list_for_each_entry_safe(i3cdev, i3ctmp, &i3c_old,
+				 common.node) {
+		i3c_master_detach_i3c_dev(i3cdev);
+		i3c_master_free_i3c_dev(i3cdev);
+	}
+
+	return 0;
+
+err_detach_devs:
+	if (!master->init_done) {
+		i3c_master_detach_free_devs(master);
+	} else {
+		INIT_LIST_HEAD(&i3cbus->devs.i3c);
+		list_add(&i3cbus->devs.i3c, &i3c_old);
+		list_del(&i3c_old);
+	}
+
+	return ret;
+}
+
+/* This function may sleep, so should not be called from atomic context */
+int i3c_master_process_defslvs(struct i3c_master_controller *master)
+{
+	struct i3c_bus *i3cbus;
+	int ret;
+
+	i3cbus = i3c_master_get_bus(master);
+
+	i3c_bus_normaluse_lock(&master->bus);
+	ret = i3c_master_acquire_bus(master);
+	i3c_bus_normaluse_unlock(&master->bus);
+	if (ret)
+		return ret;
+
+	/* Again bus_init to bus_mode, based on data received in DEFSLVS */
+	ret = i3c_bus_set_mode(i3cbus, master->defslvs_data.bus_mode);
+	if (ret)
+		return ret;
+
+	ret = master->ops->bus_init(master);
+	if (ret)
+		goto err_cleanup_bus;
+
+	if (!ret) {
+		master->ops->master_set_info(master);
+		i3c_bus_maintenance_lock(&master->bus);
+		ret = i3c_master_populate_bus(master);
+		if (ret) {
+			i3c_bus_maintenance_unlock(&master->bus);
+			goto err_cleanup_bus;
+		}
+		i3c_master_register_new_i3c_devs(master);
+		i3c_bus_maintenance_unlock(&master->bus);
+	}
+
+	if (master->init_done)
+		i3c_master_enable_mr_events(master);
+	return 0;
+
+err_cleanup_bus:
+	i3c_master_enable_mr_events(master);
+	if (master->ops->bus_cleanup)
+		master->ops->bus_cleanup(master);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_process_defslvs);
+
 /**
  * i3c_master_bus_init() - initialize an I3C bus
  * @master: main master initializing the bus
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index c465c7792ccb..cc482934803b 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -520,6 +520,11 @@ struct i3c_master_controller {
 	struct completion mr_comp;
 	enum i3c_mr_state mr_state;
 	u8 mr_addr;
+	struct {
+		u32 ndevs;
+		enum i3c_bus_mode bus_mode;
+		struct i3c_ccc_dev_desc *devs;
+	} defslvs_data;
 };
 
 /**
@@ -544,6 +549,7 @@ struct i3c_master_controller {
 #define i3c_bus_for_each_i3cdev(bus, dev)				\
 	list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
 
+int i3c_master_process_defslvs(struct i3c_master_controller *master);
 void i3c_master_yield_bus(struct i3c_master_controller *master,
 			  u8 slv_dyn_addr);
 void i3c_sec_mst_mr_dis_event(struct i3c_master_controller *m);
-- 
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-04-17 16:22 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
2020-04-17 16:19 ` Parshuram Thombare
2020-04-17 16:20 ` [PATCH v6 1/8] i3c: master: mastership handover document Parshuram Thombare
2020-04-17 16:20   ` Parshuram Thombare
2020-04-30  7:40   ` Boris Brezillon
2020-04-30  7:40     ` Boris Brezillon
2020-04-17 16:20 ` [PATCH v6 2/8] i3c: master: split bus_init callback into bus_init and master_set_info Parshuram Thombare
2020-04-17 16:20   ` Parshuram Thombare
2020-04-30  7:55   ` Boris Brezillon
2020-04-30  7:55     ` Boris Brezillon
2020-04-17 16:21 ` [PATCH v6 3/8] i3c: master: i3c mastership request and handover Parshuram Thombare
2020-04-17 16:21   ` Parshuram Thombare
2020-04-30  8:07   ` Boris Brezillon
2020-04-30  8:07     ` Boris Brezillon
2020-04-17 16:21 ` Parshuram Thombare [this message]
2020-04-17 16:21   ` [PATCH v6 4/8] i3c: master: defslvs processing Parshuram Thombare
2020-04-17 16:22 ` [PATCH v6 5/8] i3c: master: check for non null pointer Parshuram Thombare
2020-04-17 16:22   ` Parshuram Thombare
2020-04-17 16:22 ` [PATCH v6 6/8] i3c: master: secondary master initialization Parshuram Thombare
2020-04-17 16:22   ` Parshuram Thombare
2020-04-17 16:22 ` [PATCH v6 7/8] i3c: master: added sysfs key i3c_acquire_bus Parshuram Thombare
2020-04-17 16:22   ` Parshuram Thombare
2020-04-17 16:24 ` [PATCH v6 8/8] i3c: master: add mastership handover support to cdns i3c master driver Parshuram Thombare
2020-04-17 16:24   ` 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=1587140518-30782-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.