Linux-i3c Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v6 0/8] I3C mastership handover support
@ 2020-04-17 16:19 Parshuram Thombare
  2020-04-17 16:20 ` [PATCH v6 1/8] i3c: master: mastership handover document Parshuram Thombare
                   ` (7 more replies)
  0 siblings, 8 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:19 UTC (permalink / raw)
  To: bbrezillon, vitor.soares
  Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c

Hi,

This patch series is to add secondary master support, mastership
handover, DEFSLVS processing to I3C master subsystem and
Cadence's I3C master controller driver.
In comparison to previous patch set to enable mastership handover
support, there are some design changes and improvements listed
in v5-v6 change log below.

Also, I am adding flow diagram in documentation to help understand 
the code changes and intended results.

Please have look and provide your feedback.

Main changes between v5 and v6 are:
- Moved populate_bus() hook to master subsystem code.
- For secondary master initialization i3c_master_register
  spawan separate threads, as secondary master may have to
  wait for DEFSLVS and bus mastership.
- Populate bus info is based on DEFSLVS data and take care
  of hot plugged / unplugged I3C devices.
- Split bus_init into bus_init and master_set_info callbacks
- Moved mastership aquire and handover to separate state 
  machines.
- Added DEFSLVS processing code.
- Moved back all locks in side the subsystem code.
- Secondary mastership support to Cadence I3C master
  controller driver
- Sysfs key 'i3c_acquire_bus' to acauire bus.
- NULL check for pool pointer in i3c_generic_ibi_free_pool.

Main changes between v4 and v5 are:
- Add populate_bus() hook
- Split i3c_master_register into init and register pair
- Split device information retrieval, let add partialy discovered devices
- Make i3c_master_set_info private
- Add separate function to register secondary master
- Reworked secondary master register in CDNS driver
- Export i3c_bus_set_mode

Main changes between v3 and v4 are:
- Reworked acquire bus ownership
- Refactored the code

Main changes between v2 and v3 are:
- Added DEFSLVS devices are registered from master driver
- Reworked I2C registering on secondary master side
- Reworked Mastership event is enabled/disabled globally (for all devices)

Main changes between initial version and v2 are:
- Reworked devices registration on secondary master side
- Reworked mastership event disabling/enabling
- Reworked bus locking during mastership takeover process
- Added DEFSLVS devices registration during initialization
- Fixed style issues

Regards,
Parshuram Thombare

Parshuram Thombare (8):
  i3c: master: mastership handover document
  i3c: master: split bus_init callback into bus_init and master_set_info
  i3c: master: i3c mastership request and handover
  i3c: master: defslvs processing
  i3c: master: check for non null pointer
  i3c: master: secondary master initialization
  i3c: master: added sysfs key i3c_acquire_bus
  i3c: master: add mastership handover support to cdns i3c master driver

 Documentation/driver-api/i3c/index.rst        |   1 +
 .../i3c/mastership-handover-flow-diagram.rst  | 209 ++++++
 drivers/i3c/master.c                          | 627 ++++++++++++++++--
 drivers/i3c/master/dw-i3c-master.c            |  29 +-
 drivers/i3c/master/i3c-master-cdns.c          | 426 ++++++++++--
 include/linux/i3c/master.h                    |  48 +-
 6 files changed, 1235 insertions(+), 105 deletions(-)
 create mode 100644 Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst

-- 
2.17.1


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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 1/8] i3c: master: mastership handover document
  2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
@ 2020-04-17 16:20 ` 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
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:20 UTC (permalink / raw)
  To: bbrezillon, vitor.soares
  Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c

Flow diagram for I3C mastership handover, DEFSLVS
processing and secondary master initialization.

Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
 Documentation/driver-api/i3c/index.rst        |   1 +
 .../i3c/mastership-handover-flow-diagram.rst  | 209 ++++++++++++++++++
 2 files changed, 210 insertions(+)
 create mode 100644 Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst

diff --git a/Documentation/driver-api/i3c/index.rst b/Documentation/driver-api/i3c/index.rst
index 783d6dad054b..9a38c5ba87cb 100644
--- a/Documentation/driver-api/i3c/index.rst
+++ b/Documentation/driver-api/i3c/index.rst
@@ -9,3 +9,4 @@ I3C subsystem
    protocol
    device-driver-api
    master-driver-api
+   mastership-handover-flow-diagram
diff --git a/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst b/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
new file mode 100644
index 000000000000..04cd9f1283e0
--- /dev/null
+++ b/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
@@ -0,0 +1,209 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================
+I3C mastership handover flow
+============================
+
+Main master		Sec Master1		Sec Master 2
+
+1. Initialize I3C	1. Initialize I3C	1. Initialize I3C
+   bus in Pure mode	   bus in Pure mode	   bus in Pure mode
+
+defslvs_done flag	defslvs_done flag	defslvs_done flag
+    = true			= false		= false
+
+2. Do DAA + SETDASA	2. Spawn init. thread	2. Spawn init. thread
+			Initialization thread	Initialization thread
+			Wait for DEFSLVS	Wait for DEFSLVS
+			processing		processing
+			Wait for		Wait for
+			defslvs_done flag	defslvs_done flag
+				= true		= true
+
+3. Send DEFSLVS		DEFSLVS ISR		DEFSLVS ISR
+			Set flag to indicate	Set flag to indicate
+			DEFSLVS	processing is	DEFSLVS	processing is
+			pending			pending
+			defslvs_done flag	defslvs_done flag
+				= false			= false
+
+			Defer DEFSLVS		Defer DEFSLVS
+			processing to bottom	processing to bottom
+			half			half
+
+
+			DEFSLVS bottom half	DEFSLVS bottom half
+			Try to acquire bus	Try to acquire bus
+			i3c_master_acquire_bus	i3c_master_acquire_bus
+
+
+			i3c_sec_mst_acquire_bus i3c_sec_mst_acquire_bus
+			state machine
+			1. Wait until DA is
+			   assigned
+
+4. Register all
+   slaves and
+   master devive
+
+5. Program bus mode
+   as per board
+   info (DTS)
+
+6. Send ENEC MR,
+   HJ, SIR CCC
+   Initialization
+   complete
+
+			2. Check if DISEC MR	2. Check if DISEC MR
+			   is received		   is received
+			   If not, initiate	   If not, initiate
+			   MR request		   MR request
+
+
+		Sec master with highest priority (lowest address) get
+		mastership. Some controllers may not have way of knowing
+		if mastership is granted and can keep waiting for
+		GETACCMST.To avoid this, on reception of MR request
+		from highest priority secondary master, current master
+		send DISEC MR, HJ events to remaining master devices.
+
+MR request ISR
+1. Disable IBI
+   in controller
+
+MR request
+bottom half
+1. Send DISEC
+   MR, HJ
+   to all but
+   master device
+   to which MR
+   is granted
+						3. Received DISEC MR
+						   Go back to WAIT
+						   DA state
+
+						1. Wait until DA
+						   is assigned
+						2. Check if DISEC MR
+						   is received
+						   Wait until ENEC MR
+						   is received before
+						   sending next MR request
+
+2. Send GETACCMST
+   to secondary		3. Wait for MR DONE
+   master to which
+   mastership is to
+   be granted
+			4. MR granted
+			5. Update
+			   current_master
+
+
+			DEFSLVS bottom half
+			continue...
+			Controller driver read
+			out DEFSLVS data in
+			defslvs_data structure
+
+			Call
+			i3c_master_process_defslvs
+			until it is processed
+			successfully. And set
+			defslvs_done flag = true.
+
+			i3c_master_process_defslvs
+			1. Bus init to correct
+			   mode based on
+			   defslvs data
+			2. Register I2C devices
+			   from defslvs_data
+			   Since I3C devices are not
+			   hot pluggable this is
+			   done only once
+			3. Register all I3C devices
+			   from defslvs_data, make
+			   sure if device is already
+			   registered, i3cdev and
+			   IBI data is retained
+			   i3cdev is the link between
+			   I3C driver and I3C subsystem
+			4. Delete I3C device from
+			   older device list which are
+			   not found in defslvs_data
+			   (Unplugged ?)
+
+			Initialization thread
+			continue...
+			3. Register master device,
+			   and I3C/I2C device created
+			   based on defslvs_data
+			4. Enable controller IBI
+			5. Send ENEC MR, HJ
+			   Initialization complete
+							3. Initiate MR request
+
+
+			MR request ISR
+			Disable IBI in controller
+
+			MR request bottom half
+			Send DISEC MR, HJ to
+			other master devices
+
+			Send GETACCMST to secondary	4. Wait for MR DONE
+			master to which mastership
+			is to be granted
+
+							5. MR granted
+							6. Update
+							   current_master
+
+
+							DEFSLVS bottom half
+							continue...
+							Controller driver read
+							out DEFSLVS data in
+							defslvs_data structure
+
+							Call
+							i3c_master_process_defslvs
+							until it is processed
+							successfully. And set
+							defslvs_done flag = true
+
+							i3c_master_process_defslvs
+							1. Bus init to correct
+							   mode based on
+							   defslvs data
+							2. Register I2C devices
+							   from defslvs_data
+							   Since I3C devices are
+							   not hot pluggable
+							   this is done only once
+							3. Register all I3C devices
+							   from defslvs_data, make
+							   sure if device is
+							   already registered,
+							   i3cdev and IBI data is
+							   retained
+							   i3cdev is the link
+							   between I3C driver and
+							   I3C subsystem
+							4. Delete I3C device from
+							   older device list which
+							   are not found in
+							   defslvs_data
+							   (Unplugged ?)
+
+							Initialization thread
+							continue...
+							3. Register master
+							   device, and I3C/I2C
+							   device created
+							   based on defslvs_data
+							4. Enable controller IBI
+							5. Send ENEC MR, HJ
+							   Initialization complete
-- 
2.17.1


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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 2/8] i3c: master: split bus_init callback into bus_init and master_set_info
  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-17 16:20 ` 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
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:20 UTC (permalink / raw)
  To: bbrezillon, vitor.soares
  Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c

To support mastership handover procedure, this patch splits the
bus_init callback into bus_init and master_set_info callbacks

Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
 drivers/i3c/master.c                 | 10 +++--
 drivers/i3c/master/dw-i3c-master.c   | 29 ++++++++-----
 drivers/i3c/master/i3c-master-cdns.c | 63 ++++++++++++++++++----------
 include/linux/i3c/master.h           |  7 +++-
 4 files changed, 71 insertions(+), 38 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 5f4bd52121fe..0ec332e45737 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1716,6 +1716,10 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
 	if (ret)
 		goto err_detach_devs;
 
+	ret = master->ops->master_set_info(master);
+	if (ret)
+		goto err_detach_devs;
+
 	/*
 	 * The master device should have been instantiated in ->bus_init(),
 	 * complain if this was not the case.
@@ -2378,9 +2382,9 @@ EXPORT_SYMBOL_GPL(i3c_generic_ibi_recycle_slot);
 
 static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
 {
-	if (!ops || !ops->bus_init || !ops->priv_xfers ||
-	    !ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers ||
-	    !ops->i2c_funcs)
+	if (!ops || !ops->bus_init || !ops->master_set_info ||
+	    !ops->priv_xfers || !ops->send_ccc_cmd || !ops->do_daa ||
+	    !ops->i2c_xfers || !ops->i2c_funcs)
 		return -EINVAL;
 
 	if (ops->request_ibi &&
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 1d83c97431c7..5c9a72d68fb8 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -593,7 +593,6 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
 {
 	struct dw_i3c_master *master = to_dw_i3c_master(m);
 	struct i3c_bus *bus = i3c_master_get_bus(m);
-	struct i3c_device_info info = { };
 	u32 thld_ctrl;
 	int ret;
 
@@ -624,6 +623,24 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
 	writel(INTR_MASTER_MASK, master->regs + INTR_STATUS_EN);
 	writel(INTR_MASTER_MASK, master->regs + INTR_SIGNAL_EN);
 
+	writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
+	writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
+
+	/* For now don't support Hot-Join */
+	writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
+	       master->regs + DEVICE_CTRL);
+
+	dw_i3c_master_enable(master);
+
+	return 0;
+}
+
+static int dw_i3c_master_set_info(struct i3c_master_controller *m)
+{
+	struct dw_i3c_master *master = to_dw_i3c_master(m);
+	struct i3c_device_info info = { };
+	int ret;
+
 	ret = i3c_master_get_free_addr(m, 0);
 	if (ret < 0)
 		return ret;
@@ -638,15 +655,6 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
 	if (ret)
 		return ret;
 
-	writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
-	writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
-
-	/* For now don't support Hot-Join */
-	writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
-	       master->regs + DEVICE_CTRL);
-
-	dw_i3c_master_enable(master);
-
 	return 0;
 }
 
@@ -1088,6 +1096,7 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
 
 static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
 	.bus_init = dw_i3c_master_bus_init,
+	.master_set_info = dw_i3c_master_set_info,
 	.bus_cleanup = dw_i3c_master_bus_cleanup,
 	.attach_i3c_dev = dw_i3c_master_attach_i3c_dev,
 	.reattach_i3c_dev = dw_i3c_master_reattach_i3c_dev,
diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
index 8889a4fdb454..c2d1631a9e38 100644
--- a/drivers/i3c/master/i3c-master-cdns.c
+++ b/drivers/i3c/master/i3c-master-cdns.c
@@ -1199,21 +1199,20 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
 	struct cdns_i3c_master *master = to_cdns_i3c_master(m);
 	unsigned long pres_step, sysclk_rate, max_i2cfreq;
 	struct i3c_bus *bus = i3c_master_get_bus(m);
-	u32 ctrl, prescl0, prescl1, pres, low;
-	struct i3c_device_info info = { };
-	int ret, ncycles;
+	u32 ctrl, prescl0, prescl1, pres, low, bus_mode;
+	int ncycles;
 
 	switch (bus->mode) {
 	case I3C_BUS_MODE_PURE:
-		ctrl = CTRL_PURE_BUS_MODE;
+		bus_mode = CTRL_PURE_BUS_MODE;
 		break;
 
 	case I3C_BUS_MODE_MIXED_FAST:
-		ctrl = CTRL_MIXED_FAST_BUS_MODE;
+		bus_mode = CTRL_MIXED_FAST_BUS_MODE;
 		break;
 
 	case I3C_BUS_MODE_MIXED_SLOW:
-		ctrl = CTRL_MIXED_SLOW_BUS_MODE;
+		bus_mode = CTRL_MIXED_SLOW_BUS_MODE;
 		break;
 
 	default:
@@ -1244,7 +1243,6 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
 	bus->scl_rate.i2c = sysclk_rate / ((pres + 1) * 5);
 
 	prescl0 |= PRESCL_CTRL0_I2C(pres);
-	writel(prescl0, master->regs + PRESCL_CTRL0);
 
 	/* Calculate OD and PP low. */
 	pres_step = 1000000000 / (bus->scl_rate.i3c * 4);
@@ -1252,15 +1250,43 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
 	if (ncycles < 0)
 		ncycles = 0;
 	prescl1 = PRESCL_CTRL1_OD_LOW(ncycles);
+
+	ctrl = readl(master->regs + CTRL);
+	if (ctrl & CTRL_DEV_EN)
+		cdns_i3c_master_disable(master);
+	writel(prescl0, master->regs + PRESCL_CTRL0);
 	writel(prescl1, master->regs + PRESCL_CTRL1);
+	ctrl &= ~CTRL_BUS_MODE_MASK;
+	ctrl |= bus_mode | CTRL_HALT_EN | CTRL_MCS_EN;
+	/*
+	 * Enable Hot-Join, and, when a Hot-Join request happens,
+	 * disable all events coming from this device.
+	 * We will issue ENTDAA afterwards from the threaded IRQ
+	 * handler.
+	 */
+	if (!m->secondary)
+		ctrl |= CTRL_HJ_ACK | CTRL_HJ_DISEC;
+	writel(ctrl, master->regs + CTRL);
+	cdns_i3c_master_enable(master);
 
-	/* Get an address for the master. */
-	ret = i3c_master_get_free_addr(m, 0);
-	if (ret < 0)
-		return ret;
+	return 0;
+}
 
-	writel(prepare_rr0_dev_address(ret) | DEV_ID_RR0_IS_I3C,
-	       master->regs + DEV_ID_RR0(0));
+static int cdns_i3c_master_set_info(struct i3c_master_controller *m)
+{
+	struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+	struct i3c_device_info info = { };
+	int ret;
+
+	if (!m->secondary) {
+		/* Get an address for the master. */
+		ret = i3c_master_get_free_addr(m, 0);
+		if (ret < 0)
+			return ret;
+
+		writel(prepare_rr0_dev_address(ret) | DEV_ID_RR0_IS_I3C,
+		       master->regs + DEV_ID_RR0(0));
+	}
 
 	cdns_i3c_master_dev_rr_to_info(master, 0, &info);
 	if (info.bcr & I3C_BCR_HDR_CAP)
@@ -1270,16 +1296,6 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
 	if (ret)
 		return ret;
 
-	/*
-	 * Enable Hot-Join, and, when a Hot-Join request happens, disable all
-	 * events coming from this device.
-	 *
-	 * We will issue ENTDAA afterwards from the threaded IRQ handler.
-	 */
-	ctrl |= CTRL_HJ_ACK | CTRL_HJ_DISEC | CTRL_HALT_EN | CTRL_MCS_EN;
-	writel(ctrl, master->regs + CTRL);
-
-	cdns_i3c_master_enable(master);
 
 	return 0;
 }
@@ -1507,6 +1523,7 @@ static void cdns_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
 
 static const struct i3c_master_controller_ops cdns_i3c_master_ops = {
 	.bus_init = cdns_i3c_master_bus_init,
+	.master_set_info = cdns_i3c_master_set_info,
 	.bus_cleanup = cdns_i3c_master_bus_cleanup,
 	.do_daa = cdns_i3c_master_do_daa,
 	.attach_i3c_dev = cdns_i3c_master_attach_i3c_dev,
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index f13fd8b1dd79..3dc7eafe811a 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -337,10 +337,12 @@ struct i3c_bus {
 
 /**
  * struct i3c_master_controller_ops - I3C master methods
- * @bus_init: hook responsible for the I3C bus initialization. You should at
- *	      least call master_set_info() from there and set the bus mode.
+ * @bus_init: hook responsible for the I3C bus initialization.
  *	      You can also put controller specific initialization in there.
  *	      This method is mandatory.
+ * @master_set_info: hook responsible for assigning address to main master.
+ *			You should call i3c_master_set_info from here.
+ *			This method is mandatory.
  * @bus_cleanup: cleanup everything done in
  *		 &i3c_master_controller_ops->bus_init().
  *		 This method is optional.
@@ -421,6 +423,7 @@ struct i3c_bus {
  */
 struct i3c_master_controller_ops {
 	int (*bus_init)(struct i3c_master_controller *master);
+	int (*master_set_info)(struct i3c_master_controller *m);
 	void (*bus_cleanup)(struct i3c_master_controller *master);
 	int (*attach_i3c_dev)(struct i3c_dev_desc *dev);
 	int (*reattach_i3c_dev)(struct i3c_dev_desc *dev, u8 old_dyn_addr);
-- 
2.17.1


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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 3/8] i3c: master: i3c mastership request and handover
  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-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:21 ` Parshuram Thombare
  2020-04-30  8:07   ` Boris Brezillon
  2020-04-17 16:21 ` [PATCH v6 4/8] i3c: master: defslvs processing Parshuram Thombare
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:21 UTC (permalink / raw)
  To: bbrezillon, vitor.soares
  Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c

This patch add I3C mastership request and
handover infrasturcture to I3C master subsystem.

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

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 0ec332e45737..3598856a0b25 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1217,6 +1217,69 @@ static int i3c_master_getdcr_locked(struct i3c_master_controller *master,
 	return ret;
 }
 
+static int i3c_master_get_accmst_locked(struct i3c_master_controller *master,
+					u8 addr)
+{
+	struct i3c_ccc_getaccmst *accmst;
+	struct i3c_ccc_cmd_dest dest;
+	struct i3c_ccc_cmd cmd;
+	int ret;
+
+	accmst = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*accmst));
+	if (!accmst)
+		return -ENOMEM;
+
+	i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETACCMST, &dest, 1);
+
+	ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+	if (ret)
+		goto out;
+
+	if (dest.payload.len != sizeof(*accmst))
+		ret = -EIO;
+
+out:
+	i3c_ccc_cmd_dest_cleanup(&dest);
+
+	return ret;
+}
+
+static int i3c_master_enable_mr_events(struct i3c_master_controller *master)
+{
+	int ret;
+
+	master->ops->enable_mr_events(master);
+	i3c_bus_maintenance_lock(&master->bus);
+	ret = i3c_master_enec_locked(master, I3C_BROADCAST_ADDR,
+				     I3C_CCC_EVENT_MR | I3C_CCC_EVENT_HJ);
+	i3c_bus_maintenance_unlock(&master->bus);
+
+	return ret;
+}
+
+static int i3c_master_getaccmst(struct i3c_master_controller *master)
+{
+	int ret;
+
+	i3c_bus_maintenance_lock(&master->bus);
+	ret = i3c_master_get_accmst_locked(master, master->mr_addr);
+	i3c_bus_maintenance_unlock(&master->bus);
+
+	return ret;
+}
+
+static int i3c_master_request_mastership(struct i3c_master_controller *master)
+{
+	int ret;
+
+	/* request_mastership callback should handle EN/DIS EC MR.*/
+	i3c_bus_maintenance_lock(&master->bus);
+	ret = master->ops->request_mastership(master);
+	i3c_bus_maintenance_unlock(&master->bus);
+
+	return ret;
+}
+
 static int i3c_master_retrieve_dev_info(struct i3c_dev_desc *dev)
 {
 	struct i3c_master_controller *master = i3c_dev_get_master(dev);
@@ -1612,6 +1675,161 @@ static void i3c_master_detach_free_devs(struct i3c_master_controller *master)
 	}
 }
 
+static void i3c_mst_yield_bus(struct work_struct *work)
+{
+	struct i3c_master_controller *m;
+	struct i3c_dev_desc *i3cdev;
+	int ret;
+
+	m = container_of(work, struct i3c_master_controller, mst_work);
+
+	switch (m->mr_state) {
+	case I3C_MR_DISEC_MR:
+		/*
+		 * Disable MR on all but the secondary master first
+		 * reaching here.
+		 */
+		i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+			if (I3C_BCR_DEVICE_ROLE(i3cdev->info.bcr) !=
+			    I3C_BCR_I3C_MASTER ||
+			    i3cdev->info.dyn_addr == m->mr_addr ||
+			    m->this == i3cdev)
+				continue;
+			i3c_bus_maintenance_lock(&m->bus);
+			i3c_master_disec_locked(m, i3cdev->info.dyn_addr,
+						I3C_CCC_EVENT_MR |
+						I3C_CCC_EVENT_HJ);
+			i3c_bus_maintenance_unlock(&m->bus);
+		}
+		m->mr_state = I3C_MR_GETACCMST;
+		queue_work(m->wq, &m->mst_work);
+		break;
+
+	case I3C_MR_GETACCMST:
+		ret = i3c_master_getaccmst(m);
+		if (!ret)
+			m->mr_state = I3C_MR_DONE;
+		else
+			m->mr_state = I3C_MR_FAILED;
+		queue_work(m->wq, &m->mst_work);
+		break;
+
+	case I3C_MR_DONE:
+		i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+			if (m->mr_addr == i3cdev->info.dyn_addr) {
+				m->bus.cur_master = i3cdev;
+				break;
+			}
+		}
+		m->mr_state = I3C_MR_IDLE;
+		break;
+
+	default:
+	case I3C_MR_FAILED:
+		i3c_master_enable_mr_events(m);
+		m->mr_state = I3C_MR_IDLE;
+		break;
+	}
+}
+
+void
+i3c_master_yield_bus(struct i3c_master_controller *master, u8 sec_mst_dyn_addr)
+{
+	if (master->this && master->this == master->bus.cur_master) {
+		master->ops->disable_mr_events(master);
+		master->mr_addr = sec_mst_dyn_addr;
+		master->mr_state = I3C_MR_DISEC_MR;
+		queue_work(master->wq, &master->mst_work);
+	} else {
+		/* If not a current master, we should never come here */
+		WARN_ON(1);
+	}
+}
+EXPORT_SYMBOL_GPL(i3c_master_yield_bus);
+
+static void i3c_sec_mst_acquire_bus(struct work_struct *work)
+{
+	struct i3c_master_controller *m;
+	struct i3c_bus *i3cbus;
+	int ret;
+
+	m = container_of(work, struct i3c_master_controller, sec_mst_work);
+	i3cbus = i3c_master_get_bus(m);
+
+	switch (m->mr_state) {
+	case I3C_MR_WAIT_DA:
+		/* Wait until this master have dynamic address */
+		if (m->ops->check_event_set(m, I3C_SLV_DA_UPDATE))
+			m->mr_state = I3C_MR_REQUEST;
+		queue_work(m->wq, &m->sec_mst_work);
+		break;
+
+	case I3C_MR_REQUEST:
+		/* Wait until we can send MR */
+		ret = i3c_master_request_mastership(m);
+		if (!ret)
+			m->mr_state = I3C_MR_WAIT_MR_DONE;
+		queue_work(m->wq, &m->sec_mst_work);
+		break;
+
+	case I3C_MR_WAIT_MR_DONE:
+		if (m->ops->check_event_set(m, I3C_SLV_MR_DONE)) {
+			m->mr_state = I3C_MR_DONE;
+			m->bus.cur_master = m->this;
+			complete(&m->mr_comp);
+		} else {
+			queue_work(m->wq, &m->sec_mst_work);
+		}
+		break;
+
+	default:
+		m->mr_state = I3C_MR_FAILED;
+		complete(&m->mr_comp);
+		break;
+	}
+}
+
+void i3c_sec_mst_mr_dis_event(struct i3c_master_controller *m)
+{
+	if (m->mr_state != I3C_MR_IDLE)
+		m->mr_state = I3C_MR_WAIT_DA;
+}
+EXPORT_SYMBOL_GPL(i3c_sec_mst_mr_dis_event);
+
+/* This function is expected to be called with normaluse_lock */
+int i3c_master_acquire_bus(struct i3c_master_controller *master)
+{
+	int ret = 0;
+
+	if (!master->this || master->this != master->bus.cur_master) {
+		if (master->mr_state == I3C_MR_IDLE) {
+			master->mr_state = I3C_MR_WAIT_DA;
+			init_completion(&master->mr_comp);
+			queue_work(master->wq, &master->sec_mst_work);
+			/*
+			 * Bus acquire procedure may need write lock
+			 * so release read lock before yielding
+			 * to bus acquire state machine
+			 */
+			i3c_bus_normaluse_unlock(&master->bus);
+			wait_for_completion(&master->mr_comp);
+			i3c_bus_normaluse_lock(&master->bus);
+			if (master->mr_state != I3C_MR_DONE)
+				ret = -EAGAIN;
+			master->mr_state = I3C_MR_IDLE;
+		} else {
+			/*
+			 * MR request is already in process for
+			 * this master
+			 */
+			ret = -EAGAIN;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
+
 /**
  * i3c_master_bus_init() - initialize an I3C bus
  * @master: main master initializing the bus
@@ -2451,6 +2669,9 @@ int i3c_master_register(struct i3c_master_controller *master,
 	device_initialize(&master->dev);
 	dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
 
+	INIT_WORK(&master->sec_mst_work, i3c_sec_mst_acquire_bus);
+	INIT_WORK(&master->mst_work, i3c_mst_yield_bus);
+
 	ret = of_populate_i3c_bus(master);
 	if (ret)
 		goto err_put_dev;
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 3dc7eafe811a..c465c7792ccb 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -259,6 +259,27 @@ enum i3c_bus_mode {
 	I3C_BUS_MODE_MIXED_SLOW,
 };
 
+enum i3c_mr_state {
+	I3C_MR_IDLE,
+	I3C_MR_DISEC_MR,
+	I3C_MR_SEND_DEFSLVS,
+	I3C_MR_GETACCMST,
+	I3C_MR_WAIT_DA,
+	I3C_MR_CHECK_STATE,
+	I3C_MR_REQUEST,
+	I3C_MR_WAIT_DEFSLVS,
+	I3C_MR_WAIT_MR_DONE,
+	I3C_MR_DONE,
+	I3C_MR_FAILED,
+};
+
+enum i3c_event {
+	I3C_SLV_DA_UPDATE,
+	I3C_SLV_DEFSLVS_CCC,
+	I3C_SLV_MR_DIS,
+	I3C_SLV_MR_DONE,
+};
+
 /**
  * enum i3c_addr_slot_status - I3C address slot status
  * @I3C_ADDR_SLOT_FREE: address is free
@@ -448,6 +469,11 @@ struct i3c_master_controller_ops {
 	int (*disable_ibi)(struct i3c_dev_desc *dev);
 	void (*recycle_ibi_slot)(struct i3c_dev_desc *dev,
 				 struct i3c_ibi_slot *slot);
+	int (*request_mastership)(struct i3c_master_controller *master);
+	void (*enable_mr_events)(struct i3c_master_controller *m);
+	void (*disable_mr_events)(struct i3c_master_controller *m);
+	bool (*check_event_set)(struct i3c_master_controller *m,
+				enum i3c_event);
 };
 
 /**
@@ -489,6 +515,11 @@ struct i3c_master_controller {
 	} boardinfo;
 	struct i3c_bus bus;
 	struct workqueue_struct *wq;
+	struct work_struct mst_work;
+	struct work_struct sec_mst_work;
+	struct completion mr_comp;
+	enum i3c_mr_state mr_state;
+	u8 mr_addr;
 };
 
 /**
@@ -513,6 +544,9 @@ struct i3c_master_controller {
 #define i3c_bus_for_each_i3cdev(bus, dev)				\
 	list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
 
+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);
 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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 4/8] i3c: master: defslvs processing
  2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
                   ` (2 preceding siblings ...)
  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-17 16:22 ` [PATCH v6 5/8] i3c: master: check for non null pointer Parshuram Thombare
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:21 UTC (permalink / raw)
  To: bbrezillon, vitor.soares
  Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c

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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 5/8] i3c: master: check for non null pointer
  2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
                   ` (3 preceding siblings ...)
  2020-04-17 16:21 ` [PATCH v6 4/8] i3c: master: defslvs processing 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
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:22 UTC (permalink / raw)
  To: bbrezillon, vitor.soares
  Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c

This patch add NULL check for struct i3c_generic_ibi_pool *pool
argument of function i3c_generic_ibi_free_pool.

Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
 drivers/i3c/master.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 2690910d724c..19d4800ed573 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2667,6 +2667,9 @@ void i3c_generic_ibi_free_pool(struct i3c_generic_ibi_pool *pool)
 	struct i3c_generic_ibi_slot *slot;
 	unsigned int nslots = 0;
 
+	if (!pool)
+		return;
+
 	while (!list_empty(&pool->free_slots)) {
 		slot = list_first_entry(&pool->free_slots,
 					struct i3c_generic_ibi_slot, node);
-- 
2.17.1


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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 6/8] i3c: master: secondary master initialization
  2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
                   ` (4 preceding siblings ...)
  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 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
  7 siblings, 0 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:22 UTC (permalink / raw)
  To: bbrezillon, vitor.soares
  Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c

This patch add secondary master support to
I3C master subsystem.

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

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 19d4800ed573..39a412b32c59 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1621,10 +1621,6 @@ int i3c_master_set_info(struct i3c_master_controller *master,
 	if (!i3c_bus_dev_addr_is_avail(&master->bus, info->dyn_addr))
 		return -EINVAL;
 
-	if (I3C_BCR_DEVICE_ROLE(info->bcr) == I3C_BCR_I3C_MASTER &&
-	    master->secondary)
-		return -EINVAL;
-
 	if (master->this)
 		return -EINVAL;
 
@@ -2040,9 +2036,10 @@ EXPORT_SYMBOL_GPL(i3c_master_process_defslvs);
  * 1. Attach I2C and statically defined I3C devs to the master so that the
  *    master can fill its internal device table appropriately
  *
- * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
- *    the master controller. That's usually where the bus mode is selected
- *    (pure bus or mixed fast/slow bus)
+ * 2. Should have called &i3c_master_controller_ops->bus_init()
+ *    method with pure bus mode to initialize the master controller.
+ *    That's usually where the bus mode is selected (pure bus or
+ *    mixed fast/slow bus)
  *
  * 3. Instruct all devices on the bus to drop their dynamic address. This is
  *    particularly important when the bus was previously configured by someone
@@ -2126,14 +2123,6 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
 		}
 	}
 
-	/*
-	 * Now execute the controller specific ->bus_init() routine, which
-	 * might configure its internal logic to match the bus limitations.
-	 */
-	ret = master->ops->bus_init(master);
-	if (ret)
-		goto err_detach_devs;
-
 	ret = master->ops->master_set_info(master);
 	if (ret)
 		goto err_detach_devs;
@@ -2146,7 +2135,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
 		dev_err(&master->dev,
 			"master_set_info() was not called in ->bus_init()\n");
 		ret = -EINVAL;
-		goto err_bus_cleanup;
+		goto err_detach_devs;
 	}
 
 	/*
@@ -2155,14 +2144,14 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
 	 */
 	ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
 	if (ret && ret != I3C_ERROR_M2)
-		goto err_bus_cleanup;
+		goto err_detach_devs;
 
 	/* Disable all slave events before starting DAA. */
 	ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
 				      I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
 				      I3C_CCC_EVENT_HJ);
 	if (ret && ret != I3C_ERROR_M2)
-		goto err_bus_cleanup;
+		goto err_detach_devs;
 
 	/*
 	 * Pre-assign dynamic address and retrieve device information if
@@ -2180,10 +2169,6 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
 err_rstdaa:
 	i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
 
-err_bus_cleanup:
-	if (master->ops->bus_cleanup)
-		master->ops->bus_cleanup(master);
-
 err_detach_devs:
 	i3c_master_detach_free_devs(master);
 
@@ -2813,9 +2798,68 @@ static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
 	     !ops->recycle_ibi_slot))
 		return -EINVAL;
 
+	if (ops->request_mastership &&
+	    (!ops->enable_mr_events || !ops->disable_mr_events ||
+	     !ops->check_event_set))
+		return -EINVAL;
+
 	return 0;
 }
 
+static void i3c_secondary_master_register(struct work_struct *work)
+{
+	struct i3c_master_controller *master;
+	struct i3c_bus *i3cbus;
+	int ret;
+
+	master = container_of(work, struct i3c_master_controller,
+			      sec_mst_register_work);
+	i3cbus = i3c_master_get_bus(master);
+
+	i3c_bus_normaluse_lock(&master->bus);
+	if (!master->ops->check_event_set(master, I3C_SLV_DEFSLVS_CCC) ||
+	    i3c_master_acquire_bus(master)) {
+		i3c_bus_normaluse_unlock(&master->bus);
+		queue_work(master->wq, work);
+		return;
+	}
+	i3c_bus_normaluse_unlock(&master->bus);
+
+	ret = device_add(&master->dev);
+	if (ret)
+		goto err_cleanup_bus;
+
+	/*
+	 * Expose our I3C bus as an I2C adapter so that I2C devices are exposed
+	 * through the I2C subsystem.
+	 */
+	ret = i3c_master_i2c_adapter_init(master);
+	if (ret)
+		goto err_del_dev;
+
+	/*
+	 * We're done initializing the bus and the controller, we can now
+	 * register I3C devices from defslvs list.
+	 */
+	master->init_done = true;
+	i3c_bus_normaluse_lock(&master->bus);
+	i3c_master_register_new_i3c_devs(master);
+	i3c_bus_normaluse_unlock(&master->bus);
+
+	i3c_master_enable_mr_events(master);
+
+	return;
+
+err_del_dev:
+	device_del(&master->dev);
+
+err_cleanup_bus:
+	if (master->ops->bus_cleanup)
+		master->ops->bus_cleanup(master);
+
+	put_device(&master->dev);
+}
+
 /**
  * i3c_master_register() - register an I3C master
  * @master: master used to send frames on the bus
@@ -2845,10 +2889,10 @@ int i3c_master_register(struct i3c_master_controller *master,
 	struct i3c_bus *i3cbus = i3c_master_get_bus(master);
 	enum i3c_bus_mode mode = I3C_BUS_MODE_PURE;
 	struct i2c_dev_boardinfo *i2cbi;
-	int ret;
+	int ret, sz;
 
-	/* We do not support secondary masters yet. */
-	if (secondary)
+	/*Check if controller driver supports secondary masters. */
+	if (secondary && !ops->request_mastership)
 		return -ENOTSUPP;
 
 	ret = i3c_master_check_ops(ops);
@@ -2872,13 +2916,45 @@ int i3c_master_register(struct i3c_master_controller *master,
 	device_initialize(&master->dev);
 	dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
 
+	master->wq = alloc_workqueue("%s", 0, 0, dev_name(parent));
+	if (!master->wq) {
+		ret = -ENOMEM;
+		goto err_put_dev;
+	}
+
+	master->mr_state = I3C_MR_IDLE;
 	INIT_WORK(&master->sec_mst_work, i3c_sec_mst_acquire_bus);
 	INIT_WORK(&master->mst_work, i3c_mst_yield_bus);
 
-	ret = of_populate_i3c_bus(master);
+	ret = i3c_bus_set_mode(i3cbus, mode);
+	if (ret)
+		goto err_put_dev;
+
+	/*
+	 * Now execute the controller specific ->bus_init() routine, which
+	 * might configure its internal logic to match the bus limitations.
+	 */
+	ret = master->ops->bus_init(master);
 	if (ret)
 		goto err_put_dev;
 
+	if (secondary) {
+		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)
+			goto err_put_dev;
+
+		INIT_WORK(&master->sec_mst_register_work,
+			  i3c_secondary_master_register);
+		queue_work(master->wq, &master->sec_mst_register_work);
+		return 0;
+	}
+
+	ret = of_populate_i3c_bus(master);
+	if (ret)
+		goto err_cleanup_bus;
+
 	list_for_each_entry(i2cbi, &master->boardinfo.i2c, node) {
 		switch (i2cbi->lvr & I3C_LVR_I2C_INDEX_MASK) {
 		case I3C_LVR_I2C_INDEX(0):
@@ -2892,23 +2968,13 @@ int i3c_master_register(struct i3c_master_controller *master,
 			break;
 		default:
 			ret = -EINVAL;
-			goto err_put_dev;
+			goto err_cleanup_bus;
 		}
 	}
 
-	ret = i3c_bus_set_mode(i3cbus, mode);
-	if (ret)
-		goto err_put_dev;
-
-	master->wq = alloc_workqueue("%s", 0, 0, dev_name(parent));
-	if (!master->wq) {
-		ret = -ENOMEM;
-		goto err_put_dev;
-	}
-
 	ret = i3c_master_bus_init(master);
 	if (ret)
-		goto err_put_dev;
+		goto err_cleanup_bus;
 
 	ret = device_add(&master->dev);
 	if (ret)
@@ -2931,6 +2997,17 @@ int i3c_master_register(struct i3c_master_controller *master,
 	i3c_master_register_new_i3c_devs(master);
 	i3c_bus_normaluse_unlock(&master->bus);
 
+	ret = i3c_bus_set_mode(i3cbus, mode);
+	if (ret)
+		goto err_del_dev;
+
+	ret = master->ops->bus_init(master);
+	if (ret)
+		goto err_del_dev;
+
+	if (ops->request_mastership)
+		i3c_master_enable_mr_events(master);
+
 	return 0;
 
 err_del_dev:
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index cc482934803b..08b63e285b7b 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -515,6 +515,7 @@ struct i3c_master_controller {
 	} boardinfo;
 	struct i3c_bus bus;
 	struct workqueue_struct *wq;
+	struct work_struct sec_mst_register_work;
 	struct work_struct mst_work;
 	struct work_struct sec_mst_work;
 	struct completion mr_comp;
-- 
2.17.1


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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 7/8] i3c: master: added sysfs key i3c_acquire_bus
  2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
                   ` (5 preceding siblings ...)
  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:24 ` [PATCH v6 8/8] i3c: master: add mastership handover support to cdns i3c master driver Parshuram Thombare
  7 siblings, 0 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:22 UTC (permalink / raw)
  To: bbrezillon, vitor.soares
  Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c

This patch add sysfs key 'i3c_acquire_bus'
to acquire I3C bus.

Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
 drivers/i3c/master.c | 112 +++++++++++++++++++++++++------------------
 1 file changed, 65 insertions(+), 47 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 39a412b32c59..c0b6a0c658f0 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -467,6 +467,53 @@ static const char * const i3c_bus_mode_strings[] = {
 	[I3C_BUS_MODE_MIXED_SLOW] = "mixed-slow",
 };
 
+static int i3c_master_enable_mr_events(struct i3c_master_controller *master)
+{
+	int ret;
+
+	master->ops->enable_mr_events(master);
+	i3c_bus_maintenance_lock(&master->bus);
+	ret = i3c_master_enec_locked(master, I3C_BROADCAST_ADDR,
+				     I3C_CCC_EVENT_MR | I3C_CCC_EVENT_HJ);
+	i3c_bus_maintenance_unlock(&master->bus);
+
+	return ret;
+}
+
+/* This function is expected to be called with normaluse_lock */
+int i3c_master_acquire_bus(struct i3c_master_controller *master)
+{
+	int ret = 0;
+
+	if (!master->this || master->this != master->bus.cur_master) {
+		if (master->mr_state == I3C_MR_IDLE) {
+			master->mr_state = I3C_MR_WAIT_DA;
+			init_completion(&master->mr_comp);
+			queue_work(master->wq, &master->sec_mst_work);
+			/*
+			 * Bus acquire procedure may need write lock
+			 * so release read lock before yielding
+			 * to bus acquire state machine
+			 */
+			i3c_bus_normaluse_unlock(&master->bus);
+			wait_for_completion(&master->mr_comp);
+			i3c_bus_normaluse_lock(&master->bus);
+			if (master->mr_state != I3C_MR_DONE)
+				ret = -EAGAIN;
+			master->mr_state = I3C_MR_IDLE;
+		} else {
+			/*
+			 * MR request is already in process for
+			 * this master
+			 */
+			ret = -EAGAIN;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
+
 static ssize_t mode_show(struct device *dev,
 			 struct device_attribute *da,
 			 char *buf)
@@ -533,6 +580,23 @@ static ssize_t i2c_scl_frequency_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(i2c_scl_frequency);
 
+static ssize_t i3c_acquire_bus_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct i3c_master_controller *master = dev_to_i3cmaster(dev);
+	int ret;
+
+	i3c_bus_normaluse_lock(&master->bus);
+	ret = i3c_master_acquire_bus(master);
+	i3c_bus_normaluse_unlock(&master->bus);
+	if (!ret)
+		i3c_master_enable_mr_events(master);
+
+	return ret ?: count;
+}
+static DEVICE_ATTR_WO(i3c_acquire_bus);
+
 static struct attribute *i3c_masterdev_attrs[] = {
 	&dev_attr_mode.attr,
 	&dev_attr_current_master.attr,
@@ -543,6 +607,7 @@ static struct attribute *i3c_masterdev_attrs[] = {
 	&dev_attr_pid.attr,
 	&dev_attr_dynamic_address.attr,
 	&dev_attr_hdrcap.attr,
+	&dev_attr_i3c_acquire_bus.attr,
 	NULL,
 };
 ATTRIBUTE_GROUPS(i3c_masterdev);
@@ -1244,19 +1309,6 @@ static int i3c_master_get_accmst_locked(struct i3c_master_controller *master,
 	return ret;
 }
 
-static int i3c_master_enable_mr_events(struct i3c_master_controller *master)
-{
-	int ret;
-
-	master->ops->enable_mr_events(master);
-	i3c_bus_maintenance_lock(&master->bus);
-	ret = i3c_master_enec_locked(master, I3C_BROADCAST_ADDR,
-				     I3C_CCC_EVENT_MR | I3C_CCC_EVENT_HJ);
-	i3c_bus_maintenance_unlock(&master->bus);
-
-	return ret;
-}
-
 static int i3c_master_getaccmst(struct i3c_master_controller *master)
 {
 	int ret;
@@ -1792,40 +1844,6 @@ void i3c_sec_mst_mr_dis_event(struct i3c_master_controller *m)
 }
 EXPORT_SYMBOL_GPL(i3c_sec_mst_mr_dis_event);
 
-/* This function is expected to be called with normaluse_lock */
-int i3c_master_acquire_bus(struct i3c_master_controller *master)
-{
-	int ret = 0;
-
-	if (!master->this || master->this != master->bus.cur_master) {
-		if (master->mr_state == I3C_MR_IDLE) {
-			master->mr_state = I3C_MR_WAIT_DA;
-			init_completion(&master->mr_comp);
-			queue_work(master->wq, &master->sec_mst_work);
-			/*
-			 * Bus acquire procedure may need write lock
-			 * so release read lock before yielding
-			 * to bus acquire state machine
-			 */
-			i3c_bus_normaluse_unlock(&master->bus);
-			wait_for_completion(&master->mr_comp);
-			i3c_bus_normaluse_lock(&master->bus);
-			if (master->mr_state != I3C_MR_DONE)
-				ret = -EAGAIN;
-			master->mr_state = I3C_MR_IDLE;
-		} else {
-			/*
-			 * MR request is already in process for
-			 * this master
-			 */
-			ret = -EAGAIN;
-		}
-	}
-
-	return ret;
-}
-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)
-- 
2.17.1


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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 8/8] i3c: master: add mastership handover support to cdns i3c master driver
  2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
                   ` (6 preceding siblings ...)
  2020-04-17 16:22 ` [PATCH v6 7/8] i3c: master: added sysfs key i3c_acquire_bus Parshuram Thombare
@ 2020-04-17 16:24 ` Parshuram Thombare
  7 siblings, 0 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:24 UTC (permalink / raw)
  To: bbrezillon, vitor.soares
  Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c

This patch add secondary master support to
Cadence's I3C master controller driver.

Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
 drivers/i3c/master.c                 |  32 ++-
 drivers/i3c/master/i3c-master-cdns.c | 365 +++++++++++++++++++++++++--
 2 files changed, 362 insertions(+), 35 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index c0b6a0c658f0..c716c3461f7e 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -481,7 +481,7 @@ static int i3c_master_enable_mr_events(struct i3c_master_controller *master)
 }
 
 /* This function is expected to be called with normaluse_lock */
-int i3c_master_acquire_bus(struct i3c_master_controller *master)
+static int i3c_master_acquire_bus(struct i3c_master_controller *master)
 {
 	int ret = 0;
 
@@ -512,7 +512,6 @@ int i3c_master_acquire_bus(struct i3c_master_controller *master)
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
 
 static ssize_t mode_show(struct device *dev,
 			 struct device_attribute *da,
@@ -2526,12 +2525,19 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap,
 	}
 
 	i3c_bus_normaluse_lock(&master->bus);
-	dev = i3c_master_find_i2c_dev_by_addr(master, addr);
-	if (!dev)
-		ret = -ENOENT;
-	else
-		ret = master->ops->i2c_xfers(dev, xfers, nxfers);
-	i3c_bus_normaluse_unlock(&master->bus);
+	if (master->ops->check_event_set(master, I3C_SLV_DEFSLVS_CCC) &&
+	    !i3c_master_acquire_bus(master)) {
+		dev = i3c_master_find_i2c_dev_by_addr(master, addr);
+		if (!dev)
+			ret = -ENOENT;
+		else
+			ret = master->ops->i2c_xfers(dev, xfers, nxfers);
+		i3c_bus_normaluse_unlock(&master->bus);
+		i3c_master_enable_mr_events(master);
+	} else {
+		i3c_bus_normaluse_unlock(&master->bus);
+		ret = -EAGAIN;
+	}
 
 	return ret ? ret : nxfers;
 }
@@ -3065,6 +3071,7 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
 				 int nxfers)
 {
 	struct i3c_master_controller *master;
+	int ret;
 
 	if (!dev)
 		return -ENOENT;
@@ -3076,7 +3083,14 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
 	if (!master->ops->priv_xfers)
 		return -ENOTSUPP;
 
-	return master->ops->priv_xfers(dev, xfers, nxfers);
+	if (master->ops->check_event_set(master, I3C_SLV_DEFSLVS_CCC) &&
+	    !i3c_master_acquire_bus(master)) {
+		ret = master->ops->priv_xfers(dev, xfers, nxfers);
+		i3c_master_enable_mr_events(master);
+		return ret;
+	} else {
+		return -EAGAIN;
+	}
 }
 
 int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev)
diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
index c2d1631a9e38..1bc27f0de8ba 100644
--- a/drivers/i3c/master/i3c-master-cdns.c
+++ b/drivers/i3c/master/i3c-master-cdns.c
@@ -157,6 +157,7 @@
 #define SLV_IMR				0x48
 #define SLV_ICR				0x4c
 #define SLV_ISR				0x50
+#define SLV_INT_DEFSLVS			BIT(21)
 #define SLV_INT_TM			BIT(20)
 #define SLV_INT_ERROR			BIT(19)
 #define SLV_INT_EVENT_UP		BIT(18)
@@ -390,7 +391,14 @@ struct cdns_i3c_xfer {
 
 struct cdns_i3c_master {
 	struct work_struct hj_work;
+	struct work_struct defslvs_work;
+	bool defslvs_processed;
+	bool mr_done;
 	struct i3c_master_controller base;
+	struct {
+		struct work_struct work;
+		u32 ibir;
+	} events;
 	u32 free_rr_slots;
 	unsigned int maxdevs;
 	struct {
@@ -936,6 +944,27 @@ static int cdns_i3c_master_get_rr_slot(struct cdns_i3c_master *master,
 	return -EINVAL;
 }
 
+static int cdns_i3c_master_find_rr_slot(struct cdns_i3c_master *master,
+					u8 addr)
+{
+	u32 activedevs, rr;
+	int i;
+
+	activedevs = readl(master->regs + DEVS_CTRL) &
+		     DEVS_CTRL_DEVS_ACTIVE_MASK;
+
+	for (i = 1; i <= master->maxdevs; i++) {
+		if (!(BIT(i) & activedevs))
+			continue;
+
+		rr = readl(master->regs + DEV_ID_RR0(i));
+		if (DEV_ID_RR0_GET_DEV_ADDR(rr) == addr)
+			return i;
+	}
+
+	return -EINVAL;
+}
+
 static int cdns_i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
 					    u8 old_dyn_addr)
 {
@@ -955,7 +984,11 @@ static int cdns_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev)
 	if (!data)
 		return -ENOMEM;
 
-	slot = cdns_i3c_master_get_rr_slot(master, dev->info.dyn_addr);
+	if (m->secondary)
+		slot = cdns_i3c_master_find_rr_slot(master, dev->info.dyn_addr);
+	else
+		slot = cdns_i3c_master_get_rr_slot(master, dev->info.dyn_addr);
+
 	if (slot < 0) {
 		kfree(data);
 		return slot;
@@ -998,7 +1031,12 @@ static int cdns_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
 	struct cdns_i3c_i2c_dev_data *data;
 	int slot;
 
-	slot = cdns_i3c_master_get_rr_slot(master, 0);
+	if (m->secondary)
+		slot = cdns_i3c_master_find_rr_slot(master,
+						    dev->boardinfo->base.addr);
+	else
+		slot = cdns_i3c_master_get_rr_slot(master, 0);
+
 	if (slot < 0)
 		return slot;
 
@@ -1010,14 +1048,17 @@ static int cdns_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
 	master->free_rr_slots &= ~BIT(slot);
 	i2c_dev_set_master_data(dev, data);
 
-	writel(prepare_rr0_dev_address(dev->boardinfo->base.addr) |
-	       (dev->boardinfo->base.flags & I2C_CLIENT_TEN ?
-		DEV_ID_RR0_LVR_EXT_ADDR : 0),
-	       master->regs + DEV_ID_RR0(data->id));
-	writel(dev->boardinfo->lvr, master->regs + DEV_ID_RR2(data->id));
-	writel(readl(master->regs + DEVS_CTRL) |
-	       DEVS_CTRL_DEV_ACTIVE(data->id),
-	       master->regs + DEVS_CTRL);
+	if (!m->secondary) {
+		writel(prepare_rr0_dev_address(dev->boardinfo->base.addr) |
+		       (dev->boardinfo->base.flags & I2C_CLIENT_TEN ?
+			DEV_ID_RR0_LVR_EXT_ADDR : 0),
+			master->regs + DEV_ID_RR0(data->id));
+		writel(dev->boardinfo->lvr,
+		       master->regs + DEV_ID_RR2(data->id));
+		writel(readl(master->regs + DEVS_CTRL) |
+		       DEVS_CTRL_DEV_ACTIVE(data->id),
+		       master->regs + DEVS_CTRL);
+	}
 
 	return 0;
 }
@@ -1187,10 +1228,6 @@ static int cdns_i3c_master_do_daa(struct i3c_master_controller *m)
 
 	cdns_i3c_master_upd_i3c_scl_lim(master);
 
-	/* Unmask Hot-Join and Mastership request interrupts. */
-	i3c_master_enec_locked(m, I3C_BROADCAST_ADDR,
-			       I3C_CCC_EVENT_HJ | I3C_CCC_EVENT_MR);
-
 	return 0;
 }
 
@@ -1287,8 +1324,8 @@ static int cdns_i3c_master_set_info(struct i3c_master_controller *m)
 		writel(prepare_rr0_dev_address(ret) | DEV_ID_RR0_IS_I3C,
 		       master->regs + DEV_ID_RR0(0));
 	}
-
 	cdns_i3c_master_dev_rr_to_info(master, 0, &info);
+
 	if (info.bcr & I3C_BCR_HDR_CAP)
 		info.hdr_cap = I3C_CCC_HDR_MODE(I3C_HDR_DDR);
 
@@ -1296,7 +1333,6 @@ static int cdns_i3c_master_set_info(struct i3c_master_controller *m)
 	if (ret)
 		return ret;
 
-
 	return 0;
 }
 
@@ -1356,6 +1392,7 @@ static void cdns_i3c_master_handle_ibi(struct cdns_i3c_master *master,
 
 static void cnds_i3c_master_demux_ibis(struct cdns_i3c_master *master)
 {
+	struct i3c_dev_desc *dev;
 	u32 status0;
 
 	writel(MST_INT_IBIR_THR, master->regs + MST_ICR);
@@ -1377,27 +1414,120 @@ static void cnds_i3c_master_demux_ibis(struct cdns_i3c_master *master)
 
 		case IBIR_TYPE_MR:
 			WARN_ON(IBIR_XFER_BYTES(ibir) || (ibir & IBIR_ERROR));
+			if (ibir & IBIR_ACKED) {
+				dev = master->ibi.slots[IBIR_SLVID(ibir)];
+				i3c_master_yield_bus(&master->base,
+						     dev->info.dyn_addr);
+			}
+			break;
+
 		default:
 			break;
 		}
 	}
 }
 
+static void cdns_i3c_process_defslvs(struct cdns_i3c_master *master)
+{
+	enum i3c_bus_mode mode = I3C_BUS_MODE_PURE;
+	struct i3c_ccc_dev_desc *desc;
+	u32 devs, val, rr, slot;
+
+	desc = master->base.defslvs_data.devs;
+	master->base.defslvs_data.ndevs = 0;
+	devs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK;
+	for (slot = 1; slot < I3C_BUS_MAX_DEVS; slot++) {
+		if (!(devs & BIT(slot)))
+			continue;
+
+		master->base.defslvs_data.ndevs++;
+		memset(desc, 0, sizeof(struct i3c_ccc_dev_desc));
+		val = readl(master->regs + DEV_ID_RR0(slot));
+		if (val & DEV_ID_RR0_IS_I3C) {
+			rr = readl(master->regs + DEV_ID_RR0(slot));
+			desc->dyn_addr = DEV_ID_RR0_GET_DEV_ADDR(rr);
+			rr = readl(master->regs + DEV_ID_RR2(slot));
+			desc->dcr = rr;
+			desc->bcr = rr >> 8;
+		} else {
+			rr = readl(master->regs + DEV_ID_RR0(slot));
+			desc->static_addr = DEV_ID_RR0_GET_DEV_ADDR(rr);
+			rr = readl(master->regs + DEV_ID_RR2(slot));
+			desc->lvr = rr;
+			switch (desc->lvr & I3C_LVR_I2C_INDEX_MASK) {
+			case I3C_LVR_I2C_INDEX(0):
+				if (mode < I3C_BUS_MODE_MIXED_FAST)
+					mode = I3C_BUS_MODE_MIXED_FAST;
+				break;
+			case I3C_LVR_I2C_INDEX(1):
+			case I3C_LVR_I2C_INDEX(2):
+				if (mode < I3C_BUS_MODE_MIXED_SLOW)
+					mode = I3C_BUS_MODE_MIXED_SLOW;
+				break;
+			default:
+				break;
+			}
+		}
+		desc++;
+	}
+	master->base.defslvs_data.bus_mode = mode;
+}
+
+void cdns_i3c_handle_slv_events(struct cdns_i3c_master *master, u32 events)
+{
+	u32 status1;
+
+	status1 = readl(master->regs + SLV_STATUS1);
+
+	if (events & SLV_INT_EVENT_UP && status1 & SLV_STATUS1_MR_DIS)
+		i3c_sec_mst_mr_dis_event(&master->base);
+
+	if (events & SLV_INT_MR_DONE) {
+		writel(FLUSH_RX_FIFO | FLUSH_TX_FIFO | FLUSH_CMD_FIFO |
+		       FLUSH_CMD_RESP,
+		       master->regs + FLUSH_CTRL);
+		master->mr_done = true;
+	}
+
+	if (events & SLV_INT_DEFSLVS) {
+		master->defslvs_processed = false;
+		queue_work(master->base.wq, &master->defslvs_work);
+	}
+}
+
 static irqreturn_t cdns_i3c_master_interrupt(int irq, void *data)
 {
 	struct cdns_i3c_master *master = data;
 	u32 status;
 
-	status = readl(master->regs + MST_ISR);
-	if (!(status & readl(master->regs + MST_IMR)))
-		return IRQ_NONE;
+	if (!master->base.this ||
+	    master->base.this != master->base.bus.cur_master) {
+		status = (readl(master->regs + SLV_ISR) &
+			  readl(master->regs + SLV_IMR));
+		if (!status)
+			return IRQ_NONE;
+		writel(status, master->regs + SLV_ICR);
+
+		cdns_i3c_handle_slv_events(master, status);
+
+	} else {
+		status = readl(master->regs + MST_ISR);
+		if (!(status & readl(master->regs + MST_IMR)))
+			return IRQ_NONE;
+
+		spin_lock(&master->xferqueue.lock);
+		cdns_i3c_master_end_xfer_locked(master, status);
+		spin_unlock(&master->xferqueue.lock);
 
-	spin_lock(&master->xferqueue.lock);
-	cdns_i3c_master_end_xfer_locked(master, status);
-	spin_unlock(&master->xferqueue.lock);
+		if (status & MST_INT_IBIR_THR)
+			cnds_i3c_master_demux_ibis(master);
 
-	if (status & MST_INT_IBIR_THR)
-		cnds_i3c_master_demux_ibis(master);
+		if (status & MST_INT_MR_DONE) {
+			writel(MST_INT_MR_DONE, master->regs + MST_ICR);
+			writel(FLUSH_RX_FIFO | FLUSH_TX_FIFO | FLUSH_CMD_FIFO |
+			       FLUSH_CMD_RESP, master->regs + FLUSH_CTRL);
+		}
+	}
 
 	return IRQ_HANDLED;
 }
@@ -1521,6 +1651,161 @@ static void cdns_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
 	i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
 }
 
+static int cdns_i3c_master_find_ibi_slot(struct cdns_i3c_master *master,
+					 struct i3c_dev_desc *dev,
+					 s16 *slot)
+{
+	unsigned long flags;
+	unsigned int i;
+	int ret = -ENOENT;
+
+	spin_lock_irqsave(&master->ibi.lock, flags);
+		for (i = 0; i < master->ibi.num_slots; i++) {
+			if (master->ibi.slots[i] == dev) {
+				*slot = i;
+				ret = 0;
+				break;
+			}
+		}
+
+	if (ret) {
+		for (i = 0; i < master->ibi.num_slots; i++) {
+			if (!master->ibi.slots[i]) {
+				master->ibi.slots[i] = dev;
+				*slot = i;
+				ret = 0;
+				break;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&master->ibi.lock, flags);
+
+	return ret;
+}
+
+static int cdns_i3c_request_mastership(struct i3c_master_controller *m)
+{
+	struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+	int status;
+
+	status = readl(master->regs + SLV_STATUS1);
+
+	if (status & SLV_STATUS1_MR_DIS)
+		return -EACCES;
+
+	master->mr_done = false;
+	writel(readl(master->regs + CTRL) | CTRL_MST_INIT | CTRL_MST_ACK,
+	       master->regs + CTRL);
+
+	return 0;
+}
+
+static void
+cdns_i3c_master_disable_mastership_events(struct i3c_master_controller *m)
+{
+	struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+	struct cdns_i3c_i2c_dev_data *data;
+	struct i3c_dev_desc *i3cdev;
+	unsigned long flags;
+	u32 sirmap;
+
+	i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+		if (I3C_BCR_DEVICE_ROLE(i3cdev->info.bcr) !=
+		    I3C_BCR_I3C_MASTER ||
+		    m->this == i3cdev)
+			continue;
+
+		data = i3c_dev_get_master_data(i3cdev);
+
+		if (i3cdev->ibi && i3cdev->ibi->handler)
+			continue;
+
+		spin_lock_irqsave(&master->ibi.lock, flags);
+		sirmap = readl(master->regs + SIR_MAP_DEV_REG(data->ibi));
+		sirmap &= ~SIR_MAP_DEV_CONF_MASK(data->ibi);
+		sirmap |= SIR_MAP_DEV_CONF(data->ibi,
+					SIR_MAP_DEV_DA(I3C_BROADCAST_ADDR));
+		writel(sirmap, master->regs + SIR_MAP_DEV_REG(data->ibi));
+		spin_unlock_irqrestore(&master->ibi.lock, flags);
+
+		cdns_i3c_master_free_ibi(i3cdev);
+	}
+}
+
+static void
+cdns_i3c_master_enable_mastership_events(struct i3c_master_controller *m)
+{
+	struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+	struct cdns_i3c_i2c_dev_data *data;
+	struct i3c_dev_desc *i3cdev;
+	unsigned long flags;
+	u32 sircfg, sirmap;
+	int ret;
+
+	i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+		if (I3C_BCR_DEVICE_ROLE(i3cdev->info.bcr) !=
+		    I3C_BCR_I3C_MASTER ||
+		    m->this == i3cdev)
+			continue;
+
+		data = i3c_dev_get_master_data(i3cdev);
+		if (!data)
+			continue;
+
+		ret = cdns_i3c_master_find_ibi_slot(master, i3cdev, &data->ibi);
+		if (ret)
+			continue;
+
+		spin_lock_irqsave(&master->ibi.lock, flags);
+		sirmap = readl(master->regs + SIR_MAP_DEV_REG(data->ibi));
+		sirmap &= ~SIR_MAP_DEV_CONF_MASK(data->ibi);
+		sircfg = SIR_MAP_DEV_ROLE(i3cdev->info.bcr >> 6) |
+			SIR_MAP_DEV_DA(i3cdev->info.dyn_addr) |
+			SIR_MAP_DEV_PL(i3cdev->info.max_ibi_len) |
+			SIR_MAP_DEV_ACK;
+
+		if (i3cdev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+			sircfg |= SIR_MAP_DEV_SLOW;
+
+		sirmap |= SIR_MAP_DEV_CONF(data->ibi, sircfg);
+		writel(sirmap, master->regs + SIR_MAP_DEV_REG(data->ibi));
+		spin_unlock_irqrestore(&master->ibi.lock, flags);
+	}
+}
+
+static bool
+cdns_i3c_master_check_event_set(struct i3c_master_controller *m,
+				enum i3c_event event)
+{
+	struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+	bool ret = false;
+
+	switch (event) {
+	case I3C_SLV_DA_UPDATE:
+		if (readl(master->regs + SLV_STATUS1) & SLV_STATUS1_HAS_DA)
+			ret = true;
+		break;
+
+	case I3C_SLV_DEFSLVS_CCC:
+			ret = master->defslvs_processed;
+		break;
+
+	case I3C_SLV_MR_DIS:
+		if (readl(master->regs + SLV_STATUS1) & SLV_STATUS1_MR_DIS)
+			ret = true;
+		break;
+
+	case I3C_SLV_MR_DONE:
+		ret = master->mr_done;
+		break;
+
+	default:
+		break;
+	}
+
+	return ret;
+}
+
 static const struct i3c_master_controller_ops cdns_i3c_master_ops = {
 	.bus_init = cdns_i3c_master_bus_init,
 	.master_set_info = cdns_i3c_master_set_info,
@@ -1541,6 +1826,10 @@ static const struct i3c_master_controller_ops cdns_i3c_master_ops = {
 	.request_ibi = cdns_i3c_master_request_ibi,
 	.free_ibi = cdns_i3c_master_free_ibi,
 	.recycle_ibi_slot = cdns_i3c_master_recycle_ibi_slot,
+	.request_mastership = cdns_i3c_request_mastership,
+	.enable_mr_events = cdns_i3c_master_enable_mastership_events,
+	.disable_mr_events = cdns_i3c_master_disable_mastership_events,
+	.check_event_set = cdns_i3c_master_check_event_set,
 };
 
 static void cdns_i3c_master_hj(struct work_struct *work)
@@ -1552,10 +1841,24 @@ static void cdns_i3c_master_hj(struct work_struct *work)
 	i3c_master_do_daa(&master->base);
 }
 
+static void cdns_i3c_sec_master_defslvs(struct work_struct *work)
+{
+	struct cdns_i3c_master *master = container_of(work,
+						      struct cdns_i3c_master,
+						      defslvs_work);
+
+	cdns_i3c_process_defslvs(master);
+	if (i3c_master_process_defslvs(&master->base))
+		queue_work(master->base.wq, work);
+	else
+		master->defslvs_processed = true;
+}
+
 static int cdns_i3c_master_probe(struct platform_device *pdev)
 {
 	struct cdns_i3c_master *master;
 	struct resource *res;
+	bool secondary;
 	int ret, irq;
 	u32 val;
 
@@ -1607,6 +1910,7 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, master);
 
 	val = readl(master->regs + CONF_STATUS0);
+	secondary = (val & CONF_STATUS0_SEC_MASTER) ? true : false;
 
 	/* Device ID0 is reserved to describe this master. */
 	master->maxdevs = CONF_STATUS0_DEVS_NUM(val);
@@ -1627,12 +1931,21 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
 	if (!master->ibi.slots)
 		goto err_disable_sysclk;
 
+	if (secondary) {
+		INIT_WORK(&master->defslvs_work, cdns_i3c_sec_master_defslvs);
+		master->defslvs_processed = false;
+	} else {
+		master->defslvs_processed = true;
+	}
+
+	writel(SLV_INT_EVENT_UP | SLV_INT_DEFSLVS | SLV_INT_MR_DONE,
+	       master->regs + SLV_IER);
 	writel(IBIR_THR(1), master->regs + CMD_IBI_THR_CTRL);
-	writel(MST_INT_IBIR_THR, master->regs + MST_IER);
+	writel(MST_INT_IBIR_THR | MST_INT_MR_DONE, master->regs + MST_IER);
 	writel(DEVS_CTRL_DEV_CLR_ALL, master->regs + DEVS_CTRL);
 
 	ret = i3c_master_register(&master->base, &pdev->dev,
-				  &cdns_i3c_master_ops, false);
+				  &cdns_i3c_master_ops, secondary);
 	if (ret)
 		goto err_disable_sysclk;
 
-- 
2.17.1


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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 1/8] i3c: master: mastership handover document
  2020-04-17 16:20 ` [PATCH v6 1/8] i3c: master: mastership handover document Parshuram Thombare
@ 2020-04-30  7:40   ` Boris Brezillon
  0 siblings, 0 replies; 12+ messages in thread
From: Boris Brezillon @ 2020-04-30  7:40 UTC (permalink / raw)
  To: Parshuram Thombare
  Cc: mparab, bbrezillon, praneeth, linux-kernel, vitor.soares, pgaj,
	linux-i3c

On Fri, 17 Apr 2020 18:20:42 +0200
Parshuram Thombare <pthombar@cadence.com> wrote:

> Flow diagram for I3C mastership handover, DEFSLVS
> processing and secondary master initialization.
> 

Thanks for doing that, that's really appreciated, but the document
doesn't seem to be formatted properly. Would you mind fixing that (you
can build the docs with 'make htmldocs').

> Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
> ---
>  Documentation/driver-api/i3c/index.rst        |   1 +
>  .../i3c/mastership-handover-flow-diagram.rst  | 209 ++++++++++++++++++
>  2 files changed, 210 insertions(+)
>  create mode 100644 Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
> 
> diff --git a/Documentation/driver-api/i3c/index.rst b/Documentation/driver-api/i3c/index.rst
> index 783d6dad054b..9a38c5ba87cb 100644
> --- a/Documentation/driver-api/i3c/index.rst
> +++ b/Documentation/driver-api/i3c/index.rst
> @@ -9,3 +9,4 @@ I3C subsystem
>     protocol
>     device-driver-api
>     master-driver-api
> +   mastership-handover-flow-diagram
> diff --git a/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst b/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
> new file mode 100644
> index 000000000000..04cd9f1283e0
> --- /dev/null
> +++ b/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
> @@ -0,0 +1,209 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +============================
> +I3C mastership handover flow
> +============================
> +
> +Main master		Sec Master1		Sec Master 2
> +
> +1. Initialize I3C	1. Initialize I3C	1. Initialize I3C
> +   bus in Pure mode	   bus in Pure mode	   bus in Pure mode
> +
> +defslvs_done flag	defslvs_done flag	defslvs_done flag
> +    = true			= false		= false
> +
> +2. Do DAA + SETDASA	2. Spawn init. thread	2. Spawn init. thread
> +			Initialization thread	Initialization thread
> +			Wait for DEFSLVS	Wait for DEFSLVS
> +			processing		processing
> +			Wait for		Wait for
> +			defslvs_done flag	defslvs_done flag
> +				= true		= true
> +
> +3. Send DEFSLVS		DEFSLVS ISR		DEFSLVS ISR
> +			Set flag to indicate	Set flag to indicate
> +			DEFSLVS	processing is	DEFSLVS	processing is
> +			pending			pending
> +			defslvs_done flag	defslvs_done flag
> +				= false			= false
> +
> +			Defer DEFSLVS		Defer DEFSLVS
> +			processing to bottom	processing to bottom
> +			half			half
> +
> +
> +			DEFSLVS bottom half	DEFSLVS bottom half
> +			Try to acquire bus	Try to acquire bus
> +			i3c_master_acquire_bus	i3c_master_acquire_bus
> +
> +
> +			i3c_sec_mst_acquire_bus i3c_sec_mst_acquire_bus
> +			state machine
> +			1. Wait until DA is
> +			   assigned
> +
> +4. Register all
> +   slaves and
> +   master devive
> +
> +5. Program bus mode
> +   as per board
> +   info (DTS)
> +
> +6. Send ENEC MR,
> +   HJ, SIR CCC
> +   Initialization
> +   complete
> +
> +			2. Check if DISEC MR	2. Check if DISEC MR
> +			   is received		   is received
> +			   If not, initiate	   If not, initiate
> +			   MR request		   MR request
> +
> +
> +		Sec master with highest priority (lowest address) get
> +		mastership. Some controllers may not have way of knowing
> +		if mastership is granted and can keep waiting for
> +		GETACCMST.To avoid this, on reception of MR request
> +		from highest priority secondary master, current master
> +		send DISEC MR, HJ events to remaining master devices.
> +
> +MR request ISR
> +1. Disable IBI
> +   in controller
> +
> +MR request
> +bottom half
> +1. Send DISEC
> +   MR, HJ
> +   to all but
> +   master device
> +   to which MR
> +   is granted
> +						3. Received DISEC MR
> +						   Go back to WAIT
> +						   DA state
> +
> +						1. Wait until DA
> +						   is assigned
> +						2. Check if DISEC MR
> +						   is received
> +						   Wait until ENEC MR
> +						   is received before
> +						   sending next MR request
> +
> +2. Send GETACCMST
> +   to secondary		3. Wait for MR DONE
> +   master to which
> +   mastership is to
> +   be granted
> +			4. MR granted
> +			5. Update
> +			   current_master
> +
> +
> +			DEFSLVS bottom half
> +			continue...
> +			Controller driver read
> +			out DEFSLVS data in
> +			defslvs_data structure
> +
> +			Call
> +			i3c_master_process_defslvs
> +			until it is processed
> +			successfully. And set
> +			defslvs_done flag = true.
> +
> +			i3c_master_process_defslvs
> +			1. Bus init to correct
> +			   mode based on
> +			   defslvs data
> +			2. Register I2C devices
> +			   from defslvs_data
> +			   Since I3C devices are not
> +			   hot pluggable this is
> +			   done only once
> +			3. Register all I3C devices
> +			   from defslvs_data, make
> +			   sure if device is already
> +			   registered, i3cdev and
> +			   IBI data is retained
> +			   i3cdev is the link between
> +			   I3C driver and I3C subsystem
> +			4. Delete I3C device from
> +			   older device list which are
> +			   not found in defslvs_data
> +			   (Unplugged ?)
> +
> +			Initialization thread
> +			continue...
> +			3. Register master device,
> +			   and I3C/I2C device created
> +			   based on defslvs_data
> +			4. Enable controller IBI
> +			5. Send ENEC MR, HJ
> +			   Initialization complete
> +							3. Initiate MR request
> +
> +
> +			MR request ISR
> +			Disable IBI in controller
> +
> +			MR request bottom half
> +			Send DISEC MR, HJ to
> +			other master devices
> +
> +			Send GETACCMST to secondary	4. Wait for MR DONE
> +			master to which mastership
> +			is to be granted
> +
> +							5. MR granted
> +							6. Update
> +							   current_master
> +
> +
> +							DEFSLVS bottom half
> +							continue...
> +							Controller driver read
> +							out DEFSLVS data in
> +							defslvs_data structure
> +
> +							Call
> +							i3c_master_process_defslvs
> +							until it is processed
> +							successfully. And set
> +							defslvs_done flag = true
> +
> +							i3c_master_process_defslvs
> +							1. Bus init to correct
> +							   mode based on
> +							   defslvs data
> +							2. Register I2C devices
> +							   from defslvs_data
> +							   Since I3C devices are
> +							   not hot pluggable
> +							   this is done only once
> +							3. Register all I3C devices
> +							   from defslvs_data, make
> +							   sure if device is
> +							   already registered,
> +							   i3cdev and IBI data is
> +							   retained
> +							   i3cdev is the link
> +							   between I3C driver and
> +							   I3C subsystem
> +							4. Delete I3C device from
> +							   older device list which
> +							   are not found in
> +							   defslvs_data
> +							   (Unplugged ?)
> +
> +							Initialization thread
> +							continue...
> +							3. Register master
> +							   device, and I3C/I2C
> +							   device created
> +							   based on defslvs_data
> +							4. Enable controller IBI
> +							5. Send ENEC MR, HJ
> +							   Initialization complete


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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 2/8] i3c: master: split bus_init callback into bus_init and master_set_info
  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
  0 siblings, 0 replies; 12+ messages in thread
From: Boris Brezillon @ 2020-04-30  7:55 UTC (permalink / raw)
  To: Parshuram Thombare
  Cc: mparab, bbrezillon, praneeth, linux-kernel, vitor.soares, pgaj,
	linux-i3c

On Fri, 17 Apr 2020 18:20:52 +0200
Parshuram Thombare <pthombar@cadence.com> wrote:

> To support mastership handover procedure, this patch splits the
> bus_init callback into bus_init and master_set_info callbacks

Missing period at the end of this sentence.

IIRC, we discussed passing master info directly at controller
registration time for primary master registration, thus avoiding this
->master_set_info() step. Any good reason for doing that? I mean, I'd
expect the PID, BCR, DCR to be fixed, the only one that can be assigned
automatically is the address, and we can have a magic value for
'auto-assign the first available address', like '0'. The
write to DEV_ID_RR0() can be done conditionally in master_bus_init()
when '!secondary_master'.

> 
> Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
> ---
>  drivers/i3c/master.c                 | 10 +++--
>  drivers/i3c/master/dw-i3c-master.c   | 29 ++++++++-----
>  drivers/i3c/master/i3c-master-cdns.c | 63 ++++++++++++++++++----------
>  include/linux/i3c/master.h           |  7 +++-
>  4 files changed, 71 insertions(+), 38 deletions(-)
> 
> diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
> index 5f4bd52121fe..0ec332e45737 100644
> --- a/drivers/i3c/master.c
> +++ b/drivers/i3c/master.c
> @@ -1716,6 +1716,10 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
>  	if (ret)
>  		goto err_detach_devs;
>  
> +	ret = master->ops->master_set_info(master);
> +	if (ret)
> +		goto err_detach_devs;
> +
>  	/*
>  	 * The master device should have been instantiated in ->bus_init(),
>  	 * complain if this was not the case.
> @@ -2378,9 +2382,9 @@ EXPORT_SYMBOL_GPL(i3c_generic_ibi_recycle_slot);
>  
>  static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
>  {
> -	if (!ops || !ops->bus_init || !ops->priv_xfers ||
> -	    !ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers ||
> -	    !ops->i2c_funcs)
> +	if (!ops || !ops->bus_init || !ops->master_set_info ||
> +	    !ops->priv_xfers || !ops->send_ccc_cmd || !ops->do_daa ||
> +	    !ops->i2c_xfers || !ops->i2c_funcs)
>  		return -EINVAL;
>  
>  	if (ops->request_ibi &&
> diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
> index 1d83c97431c7..5c9a72d68fb8 100644
> --- a/drivers/i3c/master/dw-i3c-master.c
> +++ b/drivers/i3c/master/dw-i3c-master.c
> @@ -593,7 +593,6 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
>  {
>  	struct dw_i3c_master *master = to_dw_i3c_master(m);
>  	struct i3c_bus *bus = i3c_master_get_bus(m);
> -	struct i3c_device_info info = { };
>  	u32 thld_ctrl;
>  	int ret;
>  
> @@ -624,6 +623,24 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
>  	writel(INTR_MASTER_MASK, master->regs + INTR_STATUS_EN);
>  	writel(INTR_MASTER_MASK, master->regs + INTR_SIGNAL_EN);
>  
> +	writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
> +	writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
> +
> +	/* For now don't support Hot-Join */
> +	writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
> +	       master->regs + DEVICE_CTRL);
> +
> +	dw_i3c_master_enable(master);
> +
> +	return 0;
> +}
> +
> +static int dw_i3c_master_set_info(struct i3c_master_controller *m)
> +{
> +	struct dw_i3c_master *master = to_dw_i3c_master(m);
> +	struct i3c_device_info info = { };
> +	int ret;
> +
>  	ret = i3c_master_get_free_addr(m, 0);
>  	if (ret < 0)
>  		return ret;
> @@ -638,15 +655,6 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
>  	if (ret)
>  		return ret;
>  
> -	writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
> -	writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
> -
> -	/* For now don't support Hot-Join */
> -	writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
> -	       master->regs + DEVICE_CTRL);
> -
> -	dw_i3c_master_enable(master);
> -
>  	return 0;
>  }
>  
> @@ -1088,6 +1096,7 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
>  
>  static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
>  	.bus_init = dw_i3c_master_bus_init,
> +	.master_set_info = dw_i3c_master_set_info,
>  	.bus_cleanup = dw_i3c_master_bus_cleanup,
>  	.attach_i3c_dev = dw_i3c_master_attach_i3c_dev,
>  	.reattach_i3c_dev = dw_i3c_master_reattach_i3c_dev,
> diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
> index 8889a4fdb454..c2d1631a9e38 100644
> --- a/drivers/i3c/master/i3c-master-cdns.c
> +++ b/drivers/i3c/master/i3c-master-cdns.c
> @@ -1199,21 +1199,20 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
>  	struct cdns_i3c_master *master = to_cdns_i3c_master(m);
>  	unsigned long pres_step, sysclk_rate, max_i2cfreq;
>  	struct i3c_bus *bus = i3c_master_get_bus(m);
> -	u32 ctrl, prescl0, prescl1, pres, low;
> -	struct i3c_device_info info = { };
> -	int ret, ncycles;
> +	u32 ctrl, prescl0, prescl1, pres, low, bus_mode;
> +	int ncycles;
>  
>  	switch (bus->mode) {
>  	case I3C_BUS_MODE_PURE:
> -		ctrl = CTRL_PURE_BUS_MODE;
> +		bus_mode = CTRL_PURE_BUS_MODE;
>  		break;
>  
>  	case I3C_BUS_MODE_MIXED_FAST:
> -		ctrl = CTRL_MIXED_FAST_BUS_MODE;
> +		bus_mode = CTRL_MIXED_FAST_BUS_MODE;
>  		break;
>  
>  	case I3C_BUS_MODE_MIXED_SLOW:
> -		ctrl = CTRL_MIXED_SLOW_BUS_MODE;
> +		bus_mode = CTRL_MIXED_SLOW_BUS_MODE;
>  		break;
>  
>  	default:
> @@ -1244,7 +1243,6 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
>  	bus->scl_rate.i2c = sysclk_rate / ((pres + 1) * 5);
>  
>  	prescl0 |= PRESCL_CTRL0_I2C(pres);
> -	writel(prescl0, master->regs + PRESCL_CTRL0);
>  
>  	/* Calculate OD and PP low. */
>  	pres_step = 1000000000 / (bus->scl_rate.i3c * 4);
> @@ -1252,15 +1250,43 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
>  	if (ncycles < 0)
>  		ncycles = 0;
>  	prescl1 = PRESCL_CTRL1_OD_LOW(ncycles);
> +
> +	ctrl = readl(master->regs + CTRL);
> +	if (ctrl & CTRL_DEV_EN)
> +		cdns_i3c_master_disable(master);
> +	writel(prescl0, master->regs + PRESCL_CTRL0);
>  	writel(prescl1, master->regs + PRESCL_CTRL1);
> +	ctrl &= ~CTRL_BUS_MODE_MASK;
> +	ctrl |= bus_mode | CTRL_HALT_EN | CTRL_MCS_EN;
> +	/*
> +	 * Enable Hot-Join, and, when a Hot-Join request happens,
> +	 * disable all events coming from this device.
> +	 * We will issue ENTDAA afterwards from the threaded IRQ
> +	 * handler.
> +	 */
> +	if (!m->secondary)
> +		ctrl |= CTRL_HJ_ACK | CTRL_HJ_DISEC;
> +	writel(ctrl, master->regs + CTRL);
> +	cdns_i3c_master_enable(master);
>  
> -	/* Get an address for the master. */
> -	ret = i3c_master_get_free_addr(m, 0);
> -	if (ret < 0)
> -		return ret;
> +	return 0;
> +}
>  
> -	writel(prepare_rr0_dev_address(ret) | DEV_ID_RR0_IS_I3C,
> -	       master->regs + DEV_ID_RR0(0));
> +static int cdns_i3c_master_set_info(struct i3c_master_controller *m)
> +{
> +	struct cdns_i3c_master *master = to_cdns_i3c_master(m);
> +	struct i3c_device_info info = { };
> +	int ret;
> +
> +	if (!m->secondary) {
> +		/* Get an address for the master. */
> +		ret = i3c_master_get_free_addr(m, 0);
> +		if (ret < 0)
> +			return ret;
> +
> +		writel(prepare_rr0_dev_address(ret) | DEV_ID_RR0_IS_I3C,
> +		       master->regs + DEV_ID_RR0(0));
> +	}
>  
>  	cdns_i3c_master_dev_rr_to_info(master, 0, &info);
>  	if (info.bcr & I3C_BCR_HDR_CAP)
> @@ -1270,16 +1296,6 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
>  	if (ret)
>  		return ret;
>  
> -	/*
> -	 * Enable Hot-Join, and, when a Hot-Join request happens, disable all
> -	 * events coming from this device.
> -	 *
> -	 * We will issue ENTDAA afterwards from the threaded IRQ handler.
> -	 */
> -	ctrl |= CTRL_HJ_ACK | CTRL_HJ_DISEC | CTRL_HALT_EN | CTRL_MCS_EN;
> -	writel(ctrl, master->regs + CTRL);
> -
> -	cdns_i3c_master_enable(master);
>  
>  	return 0;
>  }
> @@ -1507,6 +1523,7 @@ static void cdns_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
>  
>  static const struct i3c_master_controller_ops cdns_i3c_master_ops = {
>  	.bus_init = cdns_i3c_master_bus_init,
> +	.master_set_info = cdns_i3c_master_set_info,
>  	.bus_cleanup = cdns_i3c_master_bus_cleanup,
>  	.do_daa = cdns_i3c_master_do_daa,
>  	.attach_i3c_dev = cdns_i3c_master_attach_i3c_dev,
> diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
> index f13fd8b1dd79..3dc7eafe811a 100644
> --- a/include/linux/i3c/master.h
> +++ b/include/linux/i3c/master.h
> @@ -337,10 +337,12 @@ struct i3c_bus {
>  
>  /**
>   * struct i3c_master_controller_ops - I3C master methods
> - * @bus_init: hook responsible for the I3C bus initialization. You should at
> - *	      least call master_set_info() from there and set the bus mode.
> + * @bus_init: hook responsible for the I3C bus initialization.
>   *	      You can also put controller specific initialization in there.
>   *	      This method is mandatory.
> + * @master_set_info: hook responsible for assigning address to main master.
> + *			You should call i3c_master_set_info from here.
> + *			This method is mandatory.
>   * @bus_cleanup: cleanup everything done in
>   *		 &i3c_master_controller_ops->bus_init().
>   *		 This method is optional.
> @@ -421,6 +423,7 @@ struct i3c_bus {
>   */
>  struct i3c_master_controller_ops {
>  	int (*bus_init)(struct i3c_master_controller *master);
> +	int (*master_set_info)(struct i3c_master_controller *m);
>  	void (*bus_cleanup)(struct i3c_master_controller *master);
>  	int (*attach_i3c_dev)(struct i3c_dev_desc *dev);
>  	int (*reattach_i3c_dev)(struct i3c_dev_desc *dev, u8 old_dyn_addr);


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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 3/8] i3c: master: i3c mastership request and handover
  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
  0 siblings, 0 replies; 12+ messages in thread
From: Boris Brezillon @ 2020-04-30  8:07 UTC (permalink / raw)
  To: Parshuram Thombare
  Cc: mparab, bbrezillon, praneeth, linux-kernel, vitor.soares, pgaj,
	linux-i3c

On Fri, 17 Apr 2020 18:21:02 +0200
Parshuram Thombare <pthombar@cadence.com> wrote:

> +
> +/* This function is expected to be called with normaluse_lock */
> +int i3c_master_acquire_bus(struct i3c_master_controller *master)
> +{
> +	int ret = 0;
> +
> +	if (!master->this || master->this != master->bus.cur_master) {

Let's limit the number of indentations by doing

	if (master->this == master->bus.cur_master)
		return 0;


> +		if (master->mr_state == I3C_MR_IDLE) {
> +			master->mr_state = I3C_MR_WAIT_DA;
> +			init_completion(&master->mr_comp);
> +			queue_work(master->wq, &master->sec_mst_work);
> +			/*
> +			 * Bus acquire procedure may need write lock
> +			 * so release read lock before yielding
> +			 * to bus acquire state machine
> +			 */
> +			i3c_bus_normaluse_unlock(&master->bus);
> +			wait_for_completion(&master->mr_comp);
> +			i3c_bus_normaluse_lock(&master->bus);

Is that really enough? I remember we had something a bit more complex
to handle the case where bus is acquired to send a message to a device,
and another master on the bus re-acquires it before we have a chance to
send this message message. i3c_master_acquire_bus_ownership() was
dealing with that in Przemek series. It seems you've rewritten a lot of
these things. Would you mind explaining why, and how that works?

> +			if (master->mr_state != I3C_MR_DONE)
> +				ret = -EAGAIN;
> +			master->mr_state = I3C_MR_IDLE;
> +		} else {
> +			/*
> +			 * MR request is already in process for
> +			 * this master
> +			 */
> +			ret = -EAGAIN;
> +		}
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);



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

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, back to index

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [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 ` [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

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