Linux-i3c Archive on lore.kernel.org
 help / color / Atom feed
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
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 index

Thread overview: 12+ 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:20 ` [PATCH v6 1/8] i3c: master: mastership handover document Parshuram Thombare
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-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-30  8:07   ` Boris Brezillon
2020-04-17 16:21 ` Parshuram Thombare [this message]
2020-04-17 16:22 ` [PATCH v6 5/8] i3c: master: check for non null pointer Parshuram Thombare
2020-04-17 16:22 ` [PATCH v6 6/8] i3c: master: secondary master initialization 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:24 ` [PATCH v6 8/8] i3c: master: add mastership handover support to cdns i3c master driver 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

Linux-i3c Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-i3c/0 linux-i3c/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-i3c linux-i3c/ https://lore.kernel.org/linux-i3c \
		linux-i3c@lists.infradead.org
	public-inbox-index linux-i3c

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.infradead.lists.linux-i3c


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git