2020-11-29 14:26:58

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [RESEND PATCH v9 0/7] I3C mastership handover support

Main changes between v8 and v9 are:
- Fix NULL dereference issue in current_master_show when
cat'ing sysfs key current_master for secondary master
before primary master gets initialized.

Main changes between v7 and v8 are:
- Document format changed from table to DOT diagram
- Appropriate names for few functions
- Moved mastership request process entirely to the driver
- Reuse of i3c_master_add_i3c_dev_locked in core defslvs
processing

Main changes between v6 and v7 are:
- Added separate functions for main and secondary
master initialization
- Secondary master initialization don't wait for
DEFSLSVS.
- Change to use I2C device information from DTS,
and corresponding changes in controller driver
and I3C core DEFSLVS processing to ignore I2C
devices received in DEFSLVS
- Reverted bus_init split
- Fixed formatting issues in document

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

Parshuram Thombare (7):
i3c: master: master initialization flow document
i3c: master: use i3c_master_register only for main master
i3c: master: add i3c_secondary_master_register
i3c: master: add mastership handover support
i3c: master: add defslvs processing
i3c: master: sysfs key for acquire bus
i3c: master: mastership handover, defslvs processing in cdns
controller driver

Documentation/driver-api/i3c/index.rst | 1 +
.../driver-api/i3c/master-initialization-flow.rst | 187 ++++++++
drivers/i3c/master.c | 497 ++++++++++++++++++--
drivers/i3c/master/dw-i3c-master.c | 4 +-
drivers/i3c/master/i3c-master-cdns.c | 329 ++++++++++++-
include/linux/i3c/master.h | 23 +-
6 files changed, 968 insertions(+), 73 deletions(-)
create mode 100644 Documentation/driver-api/i3c/master-initialization-flow.rst


2020-11-29 14:26:58

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v9 1/7] i3c: master: master initialization flow document

Document describing master initialization, mastership handover
and DEFSLVS handling processes.

Signed-off-by: Parshuram Thombare <[email protected]>
---
Documentation/driver-api/i3c/index.rst | 1 +
.../driver-api/i3c/master-initialization-flow.rst | 187 ++++++++++++++++++++
2 files changed, 188 insertions(+), 0 deletions(-)
create mode 100644 Documentation/driver-api/i3c/master-initialization-flow.rst

diff --git a/Documentation/driver-api/i3c/index.rst b/Documentation/driver-api/i3c/index.rst
index 783d6da..604f1c5 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
+ master-initialization-flow
diff --git a/Documentation/driver-api/i3c/master-initialization-flow.rst b/Documentation/driver-api/i3c/master-initialization-flow.rst
new file mode 100644
index 0000000..e02b90d
--- /dev/null
+++ b/Documentation/driver-api/i3c/master-initialization-flow.rst
@@ -0,0 +1,187 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+I3C Master Initialization Flow
+==============================
+
+.. kernel-render:: DOT
+ :alt: I3C Master Initialization digraph
+ :caption: I3C Master Initialization Flow
+
+ digraph master_init {
+ ranksep=.25; size="35,35";
+ subgraph cluster_0 {
+ style=dashed
+ x0[shape=ellipse, label="I3C driver probe", style="filled"]
+ x1[shape=diamond, label="Secondary Master ?"]
+ a1[shape=box, label="Do I3C master controller specific initialization"]
+ b1[shape=box, label="Do I3C master controller specific initialization,\n
+ except enabling the DEFSLVS interrupt."]
+ a2[shape=box, label="Call i3c_primary_master_register"]
+ b2[shape=box, label="Call i3c_secondary_master_register"]
+ x0->x1;
+ x1->a1[label="No"];
+ x1->b1[label="Yes"];
+ a1->a2; b1->b2;
+ }
+
+ subgraph cluster_1 {
+ style=dashed
+ label="Current Master";
+ a3[shape=ellipse, label="i3c_primary_master_register", style="filled"]
+ a4[shape=box, label="Initialize I3C master controller object."]
+ a5[shape=box, label="Create I2C objects for devices in DTS and add to I2C list."]
+ a6[shape=box, label="Set appropriate bus mode based on I2C devices information."]
+ a7[shape=box, label="Create a work queue."]
+ a8[shape=box, label="Call i3c_primary_master_bus_init"]
+ a19[shape=box, label="Add I3C object representing this master to the system."]
+ a20[shape=box, label="Expose our I3C bus as an I2C adapter so that I2C devices\n
+ are exposed through the I2C subsystem."]
+ a21[shape=box, label="Register all I3C devices."]
+ a22[shape=box, label="Broadcast ENEC MR, HJ message."]
+ a3->a4->a5->a6->a7->a8; a19->a20->a21->a22;
+ a8->a19[style="invisible"];
+ }
+
+ subgraph cluster_2 {
+ style=dashed
+ label="Current Master";
+ a9[shape=ellipse, label="i3c_primary_master_bus_init", style="filled"]
+ a10[shape=box, label="Call bus_init to do controller specific bus initialization\n
+ and enabling the controller."]
+ a11[shape=box, label="Create I3C object representing the master and add it to\n
+ the I3C device list."]
+ a12[shape=box, label="Set current master to the object created to represent I3C\n
+ master device."]
+ a13[shape=box, label="Reset all dynamic address that may have been assigned before."]
+ a14[shape=box, label="Disable all slave events before starting DAA."]
+ a15[shape=box, label="Pre-assign dynamic address and retrieve device information."]
+ a16[shape=box, label="Do dynamic address assignment to all I3C devices currenly\n
+ present on the bus."]
+ a17[shape=box, label="Create I3C objects representing devices found during DAA."]
+ a18[shape=box, label="Send DEFSVLS message containing information about all\n
+ known I3C and I2C devices."]
+ a9->a10->a11->a12->a13->a14->a15->a16->a17->a18;
+ }
+
+ subgraph cluster_3 {
+ style=dashed
+ label="Current Master";
+ h1[shape=ellipse, label="MR request interrupt", style="filled"]
+ h2[shape=box, label="Bottom half i3c_master_yield_bus"]
+ h1->h2;
+ }
+
+ subgraph cluster_4 {
+ style=dashed
+ label="Current Master";
+ i1[shape=ellipse, label="i3c_master_yield_bus", style="filled"]
+ i2[shape=box, label="Check if this device is still a master to handle a case of\n
+ multiple MR requests from different devices at a same time."]
+ i3[shape=box, label="Broadcast DISEC MR, HJ message.\n
+ New master should broadcast ENEC MR, HJ once its usage of bus is done."]
+ i4[shape=box, label="Get accept mastership acknowldege from requesting master."]
+ i5[shape=box, label="In case of failure reenable MR requests by broadcasting ENEC MR, HJ."]
+ i6[shape=box, label="Mastership hand over is done."]
+ i1->i2->i3->i4->i5->i6;
+ }
+
+ subgraph cluster_5 {
+ style=dashed
+ b3[shape=ellipse, label="i3c_secondary_master_register", style="filled"]
+ b4[shape=box, label="Initialize I3C master controller object."]
+ b5[shape=box, label="Create I2C objects for devices in DTS and add to I2C list."]
+ b6[shape=box, label="Set appropriate bus mode based on I2C devices information."]
+ b7[shape=box, label="Create a work queue."]
+ b8[shape=box, label="Call i3c_secondary_master_bus_init."]
+ b12[shape=box, label="Allocate memory for defslvs_data, that will be used to pass I3C\n
+ device list received in DEFSLVS to the I3C core DEFSLVS processing."]
+ b13[shape=box, label="Add I3C object representing this master to the system."]
+ b14[shape=box, label="Expose our I3C bus as an I2C adapter so that I2C devices are\n
+ exposed through the I2C subsystem."]
+ b3->b4->b5->b6->b7->b8; b12->b13->b14;
+ b8->b12[style="invisible"]
+ }
+
+ subgraph cluster_6 {
+ style=dashed
+ b9[shape=ellipse, label="i3c_secondary_master_bus_init", style="filled"]
+ b10[shape=box, label="Call bus_init to do controller specific bus initialization\n
+ and enabling the controller."]
+ b11[shape=box, label="Create I3C object representing the master and add it to\n
+ the I3C device list."]
+ b9->b10->b11;
+ }
+
+ subgraph cluster_7 {
+ style=dashed
+ d1[shape=ellipse, label="DEFSLVS interrupt", style="filled"]
+ d2[shape=box, label="Controller driver can chose how to handle I2C devices received\n
+ in DEFSLVS."]
+ d3[shape=box, label="Read all I3C devices information from DEFSLVS message\n
+ to defslvs_data structure in the master object."]
+ d4[shape=box, label="Call i3c_master_process_defslvs."]
+ d1->d2->d3->d4;
+ }
+
+ subgraph cluster_8 {
+ style=dashed
+ d5[shape=ellipse, label="i3c_master_process_defslvs", style="filled"]
+ d6[shape=box, label="Acquire I3C bus mastership."]
+ d7[shape=diamond, label="Bus acquired ?"]
+ d8[shape=box, label="Handle I3C device list from DEFSLVS."]
+ d9[shape=box, label="Call i3c_master_populate_bus"]
+ d10[shape=box, label="Queue defslvs processing task for retry."]
+ d5->d6;
+ d7->d8[label="Yes"];
+ d8->d9;
+ d7->d10[label="No"];
+ d6->d7[style="invisible"];
+ d10->d5[style="dotted", constraint=false];
+ }
+
+ subgraph cluster_9 {
+ style=dashed
+ e1[shape=ellipse, label="i3c_master_acquire_bus", style="filled"]
+ e2[shape=box, label="If device is already holding the mastership,\n
+ just broadcast DISEC MR, HJ message and return success."]
+ e3[shape=box, label="Call request_mastership method"]
+ e1->e2->e3;
+ }
+
+ subgraph cluster_10 {
+ style=dashed
+ f1[shape=ellipse, label="request_mastership", style="filled"]
+ f2[shape=box, label="Return success if device is already in master mode."]
+ f3[shape=box, label="Return error if device don't have dyn_addr."]
+ f4[shape=box, label="Return error if MR requests are disabled by current master."]
+ f5[shape=box, label="Send MR request"]
+ f6[shape=box, label="Wait with timeout until MR_DONE interrupt is received."]
+ f1->f2->f3->f4->f5->f6;
+ }
+
+ subgraph cluster_11 {
+ style=dashed
+ g1[shape=ellipse, label="i3c_master_populate_bus", style="filled"]
+ g2[shape=box, label="Free up all I3C addresses to handle address\n
+ re assignment by main master."]
+ g3[shape=box, label="Master acquire dyn_addr received from the driver."]
+ g4[shape=box, label="For every I3C device in the defslvs_data\n
+ call i3c_master_add_i3c_dev_locked."]
+ g1->g2->g3->g4;
+ }
+
+ a2->a3; a8->a9; a18->a19;
+ b2->b3; b8->b9; b11->b12;
+ b14->d1[style=invisible];
+ a22->h1[style=invisible];
+ d4->d5;
+ d6->e1;
+ e3->f1;
+ f6->d7[constraint=false];
+ h2->i1;
+ d9->g1;
+ a18->d1[style="dotted", constraint=false];
+ f5->h1[style="dotted", constraint=false];
+ i4->f6[style="dotted", constraint=false];
+ }
--
1.7.1

2020-11-29 14:27:17

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v9 3/7] i3c: master: add i3c_secondary_master_register

add i3c_secondary_master_register which is used
to register secondary masters.

Signed-off-by: Parshuram Thombare <[email protected]>
---
drivers/i3c/master.c | 154 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/i3c/master.h | 3 +
2 files changed, 156 insertions(+), 1 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 56e8fe4..af0630a 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1797,6 +1797,90 @@ static int i3c_primary_master_bus_init(struct i3c_master_controller *master)
return ret;
}

+/**
+ * i3c_secondary_master_bus_init() - initialize an I3C bus for secondary
+ * master
+ * @master: secondary master initializing the bus
+ *
+ * This function does
+ *
+ * 1. Attach I2C devs to the master
+ *
+ * 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)
+ *
+ * Once this is done, I2C devices should be usable.
+ *
+ * Return: a 0 in case of success, an negative error code otherwise.
+ */
+static int i3c_secondary_master_bus_init(struct i3c_master_controller *master)
+{
+ enum i3c_addr_slot_status status;
+ struct i2c_dev_boardinfo *i2cboardinfo;
+ struct i2c_dev_desc *i2cdev;
+ int ret;
+
+ /*
+ * First attach all devices with static definitions provided by the
+ * FW.
+ */
+ list_for_each_entry(i2cboardinfo, &master->boardinfo.i2c, node) {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ i2cboardinfo->base.addr);
+ if (status != I3C_ADDR_SLOT_FREE) {
+ ret = -EBUSY;
+ goto err_detach_devs;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ i2cboardinfo->base.addr,
+ I3C_ADDR_SLOT_I2C_DEV);
+
+ i2cdev = i3c_master_alloc_i2c_dev(master, i2cboardinfo);
+ 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;
+ }
+ }
+
+ /*
+ * 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;
+
+ /*
+ * The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+ if (!master->this) {
+ dev_err(&master->dev,
+ "master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ master->ops->bus_cleanup(master);
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
static void i3c_master_bus_cleanup(struct i3c_master_controller *master)
{
if (master->ops->bus_cleanup)
@@ -2514,7 +2598,10 @@ static int i3c_master_init(struct i3c_master_controller *master,
goto err_put_dev;
}

- ret = i3c_primary_master_bus_init(master);
+ if (secondary)
+ ret = i3c_secondary_master_bus_init(master);
+ else
+ ret = i3c_primary_master_bus_init(master);
if (ret)
goto err_destroy_wq;

@@ -2595,6 +2682,71 @@ int i3c_primary_master_register(struct i3c_master_controller *master,
EXPORT_SYMBOL_GPL(i3c_primary_master_register);

/**
+ * i3c_secondary_master_register() - register an I3C secondary master
+ * @master: master used to send frames on the bus
+ * @parent: the parent device (the one that provides this I3C master
+ * controller)
+ * @ops: the master controller operations
+ *
+ * This function does minimal required initialization for secondary
+ * master, rest functionality like creating and registering I2C
+ * and I3C devices is done in defslvs processing.
+ *
+ * i3c_secondary_master_register() does following things -
+ * - creates and initializes the I3C bus
+ * - populates the bus with static I2C devs if @parent->of_node is not
+ * NULL
+ * initialization
+ * - allocate memory for defslvs_data.devs, which is used to receive
+ * defslvs list
+ * - create I3C device representing this master
+ * - registers the I2C adapter and all I2C devices
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int i3c_secondary_master_register(struct i3c_master_controller *master,
+ struct device *parent,
+ const struct i3c_master_controller_ops *ops)
+{
+ int ret;
+
+ ret = i3c_master_init(master, parent, ops, true);
+ if (ret)
+ return ret;
+
+ 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;
+
+ return 0;
+
+err_del_dev:
+ device_del(&master->dev);
+
+err_cleanup_bus:
+ i3c_master_bus_cleanup(master);
+
+ put_device(&master->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_secondary_master_register);
+
+/**
* i3c_master_unregister() - unregister an I3C master
* @master: master used to send frames on the bus
*
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index e543f68..e186d53 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -541,6 +541,9 @@ int i3c_master_set_info(struct i3c_master_controller *master,
int i3c_primary_master_register(struct i3c_master_controller *master,
struct device *parent,
const struct i3c_master_controller_ops *ops);
+int i3c_secondary_master_register(struct i3c_master_controller *master,
+ struct device *parent,
+ const struct i3c_master_controller_ops *ops);
int i3c_master_unregister(struct i3c_master_controller *master);

/**
--
1.7.1

2020-11-29 14:27:54

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v9 5/7] i3c: master: add defslvs processing

Added defslvs processing code to the I3C master subsystem.

Signed-off-by: Parshuram Thombare <[email protected]>
---
drivers/i3c/master.c | 68 ++++++++++++++++++++++++++++++++++++++++++-
include/linux/i3c/master.h | 7 ++++
2 files changed, 73 insertions(+), 2 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 51ef706..c01ba00 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2172,7 +2172,7 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
else
expected_dyn_addr = newdev->info.dyn_addr;

- if (newdev->info.dyn_addr != expected_dyn_addr) {
+ if (!master->secondary && newdev->info.dyn_addr != expected_dyn_addr) {
/*
* Try to apply the expected dynamic address. If it fails, keep
* the address assigned by the master.
@@ -2867,12 +2867,20 @@ int i3c_secondary_master_register(struct i3c_master_controller *master,
struct device *parent,
const struct i3c_master_controller_ops *ops)
{
- int ret;
+ int ret, sz;

ret = i3c_master_init(master, parent, ops, true);
if (ret)
return ret;

+ sz = sizeof(struct i3c_ccc_dev_desc) * I3C_BUS_MAX_DEVS;
+ master->defslvs_data.devs = devm_kzalloc(&master->dev, sz,
+ GFP_KERNEL);
+ if (!master->defslvs_data.devs) {
+ ret = -ENOMEM;
+ goto err_cleanup_bus;
+ }
+
ret = device_add(&master->dev);
if (ret)
goto err_cleanup_bus;
@@ -2905,6 +2913,62 @@ int i3c_secondary_master_register(struct i3c_master_controller *master,
}
EXPORT_SYMBOL_GPL(i3c_secondary_master_register);

+static int
+i3c_master_populate_bus(struct i3c_master_controller *master, u8 dyn_addr)
+{
+ struct i3c_dev_desc *olddev;
+ struct i3c_ccc_dev_desc *desc;
+ int ret, slot;
+
+ i3c_bus_for_each_i3cdev(&master->bus, olddev)
+ i3c_master_put_i3c_addrs(olddev);
+
+ master->this->info.dyn_addr = dyn_addr;
+ i3c_master_get_i3c_addrs(master->this);
+
+ desc = master->defslvs_data.devs;
+ for (slot = 1; slot < master->defslvs_data.ndevs; slot++) {
+ ret = i3c_master_add_i3c_dev_locked(master,
+ desc[slot].dyn_addr);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * i3c_master_process_defslvs() - process I3C device list received in
+ * DEFSLVS for device plug/unplug and address change.
+ * @m: I3C master object
+ * @dyn_addr: Current dynamic address of this device
+ *
+ * This function may sleep, so should not be called in the atomic context.
+ */
+int i3c_master_process_defslvs(struct i3c_master_controller *m, u8 dyn_addr)
+{
+ int ret;
+
+ i3c_bus_normaluse_lock(&m->bus);
+ ret = i3c_master_acquire_bus(m);
+ i3c_bus_normaluse_unlock(&m->bus);
+ if (ret)
+ return ret;
+
+ i3c_bus_maintenance_lock(&m->bus);
+ ret = i3c_master_populate_bus(m, dyn_addr);
+ i3c_bus_maintenance_unlock(&m->bus);
+ if (!ret) {
+ i3c_bus_normaluse_lock(&m->bus);
+ i3c_master_register_new_i3c_devs(m);
+ i3c_bus_normaluse_unlock(&m->bus);
+ }
+ i3c_master_enable_mr_events(m);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_process_defslvs);
+
/**
* i3c_master_unregister() - unregister an I3C master
* @master: master used to send frames on the bus
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 77c0422..835f823 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -478,6 +478,8 @@ struct i3c_master_controller_ops {
* in a thread context. Typical examples are Hot Join processing which
* requires taking the bus lock in maintenance, which in turn, can only
* be done from a sleep-able context
+ * @defslvs_data: list used to pass i3c device list received in DEFSLVS message,
+ * from DEFSLVS controller driver to I3C core
*
* A &struct i3c_master_controller has to be registered to the I3C subsystem
* through i3c_master_register(). None of &struct i3c_master_controller fields
@@ -497,6 +499,10 @@ struct i3c_master_controller {
} boardinfo;
struct i3c_bus bus;
struct workqueue_struct *wq;
+ struct {
+ u32 ndevs;
+ struct i3c_ccc_dev_desc *devs;
+ } defslvs_data;
};

/**
@@ -523,6 +529,7 @@ struct i3c_master_controller {

void i3c_master_yield_bus(struct i3c_master_controller *m,
u8 sec_mst_dyn_addr);
+int i3c_master_process_defslvs(struct i3c_master_controller *m, u8 dyn_addr);
int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
const struct i2c_msg *xfers,
int nxfers);
--
1.7.1

2020-11-29 14:28:01

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v9 2/7] i3c: master: use i3c_master_register only for main master

Removed last argument 'secondary' and restructured
i3c_master_register to move code that can be common
to i3c_secondary_master_register to separate function
i3c_master_init.

Signed-off-by: Parshuram Thombare <[email protected]>
---
drivers/i3c/master.c | 78 ++++++++++++++++++++-------------
drivers/i3c/master/dw-i3c-master.c | 4 +-
drivers/i3c/master/i3c-master-cdns.c | 4 +-
include/linux/i3c/master.h | 7 +--
4 files changed, 54 insertions(+), 39 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index b61bf53..56e8fe4 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1637,7 +1637,7 @@ static void i3c_master_detach_free_devs(struct i3c_master_controller *master)
}

/**
- * i3c_master_bus_init() - initialize an I3C bus
+ * i3c_primary_master_bus_init() - initialize an I3C bus
* @master: main master initializing the bus
*
* This function is following all initialisation steps described in the I3C
@@ -1668,7 +1668,7 @@ static void i3c_master_detach_free_devs(struct i3c_master_controller *master)
*
* Return: a 0 in case of success, an negative error code otherwise.
*/
-static int i3c_master_bus_init(struct i3c_master_controller *master)
+static int i3c_primary_master_bus_init(struct i3c_master_controller *master)
{
enum i3c_addr_slot_status status;
struct i2c_dev_boardinfo *i2cboardinfo;
@@ -2441,31 +2441,10 @@ static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
return 0;
}

-/**
- * i3c_master_register() - register an I3C master
- * @master: master used to send frames on the bus
- * @parent: the parent device (the one that provides this I3C master
- * controller)
- * @ops: the master controller operations
- * @secondary: true if you are registering a secondary master. Will return
- * -ENOTSUPP if set to true since secondary masters are not yet
- * supported
- *
- * This function takes care of everything for you:
- *
- * - creates and initializes the I3C bus
- * - populates the bus with static I2C devs if @parent->of_node is not
- * NULL
- * - registers all I3C devices added by the controller during bus
- * initialization
- * - registers the I2C adapter and all I2C devices
- *
- * Return: 0 in case of success, a negative error code otherwise.
- */
-int i3c_master_register(struct i3c_master_controller *master,
- struct device *parent,
- const struct i3c_master_controller_ops *ops,
- bool secondary)
+static int i3c_master_init(struct i3c_master_controller *master,
+ struct device *parent,
+ const struct i3c_master_controller_ops *ops,
+ bool secondary)
{
unsigned long i2c_scl_rate = I3C_BUS_I2C_FM_PLUS_SCL_RATE;
struct i3c_bus *i3cbus = i3c_master_get_bus(master);
@@ -2535,10 +2514,49 @@ int i3c_master_register(struct i3c_master_controller *master,
goto err_put_dev;
}

- ret = i3c_master_bus_init(master);
+ ret = i3c_primary_master_bus_init(master);
if (ret)
goto err_destroy_wq;

+ return 0;
+
+err_destroy_wq:
+ destroy_workqueue(master->wq);
+
+err_put_dev:
+ put_device(&master->dev);
+
+ return ret;
+}
+
+/**
+ * i3c_primary_master_register() - register an I3C master
+ * @master: master used to send frames on the bus
+ * @parent: the parent device (the one that provides this I3C master
+ * controller)
+ * @ops: the master controller operations
+ *
+ * This function takes care of everything for you:
+ *
+ * - creates and initializes the I3C bus
+ * - populates the bus with static I2C devs if @parent->of_node is not
+ * NULL
+ * - registers all I3C devices added by the controller during bus
+ * initialization
+ * - registers the I2C adapter and all I2C devices
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int i3c_primary_master_register(struct i3c_master_controller *master,
+ struct device *parent,
+ const struct i3c_master_controller_ops *ops)
+{
+ int ret;
+
+ ret = i3c_master_init(master, parent, ops, false);
+ if (ret)
+ return ret;
+
ret = device_add(&master->dev);
if (ret)
goto err_cleanup_bus;
@@ -2568,15 +2586,13 @@ int i3c_master_register(struct i3c_master_controller *master,
err_cleanup_bus:
i3c_master_bus_cleanup(master);

-err_destroy_wq:
destroy_workqueue(master->wq);

-err_put_dev:
put_device(&master->dev);

return ret;
}
-EXPORT_SYMBOL_GPL(i3c_master_register);
+EXPORT_SYMBOL_GPL(i3c_primary_master_register);

/**
* i3c_master_unregister() - unregister an I3C master
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 8513bd3..4f4d41f 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -1166,8 +1166,8 @@ static int dw_i3c_probe(struct platform_device *pdev)
master->maxdevs = ret >> 16;
master->free_pos = GENMASK(master->maxdevs - 1, 0);

- ret = i3c_master_register(&master->base, &pdev->dev,
- &dw_mipi_i3c_ops, false);
+ ret = i3c_primary_master_register(&master->base, &pdev->dev,
+ &dw_mipi_i3c_ops);
if (ret)
goto err_assert_rst;

diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
index 3f22269..f1d6d68 100644
--- a/drivers/i3c/master/i3c-master-cdns.c
+++ b/drivers/i3c/master/i3c-master-cdns.c
@@ -1644,8 +1644,8 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
writel(MST_INT_IBIR_THR, 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);
+ ret = i3c_primary_master_register(&master->base, &pdev->dev,
+ &cdns_i3c_master_ops);
if (ret)
goto err_disable_sysclk;

diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 9cb39d9..e543f68 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -538,10 +538,9 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
int i3c_master_set_info(struct i3c_master_controller *master,
const struct i3c_device_info *info);

-int i3c_master_register(struct i3c_master_controller *master,
- struct device *parent,
- const struct i3c_master_controller_ops *ops,
- bool secondary);
+int i3c_primary_master_register(struct i3c_master_controller *master,
+ struct device *parent,
+ const struct i3c_master_controller_ops *ops);
int i3c_master_unregister(struct i3c_master_controller *master);

/**
--
1.7.1

2020-11-29 14:28:51

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v9 6/7] i3c: master: sysfs key for acquire bus

Added support to acquire I3C bus through sysfs interface.

Signed-off-by: Parshuram Thombare <[email protected]>
---
drivers/i3c/master.c | 18 ++++++++++++++++++
1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index c01ba00..beb7495 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -581,6 +581,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,
@@ -591,6 +608,7 @@ static ssize_t i2c_scl_frequency_show(struct device *dev,
&dev_attr_pid.attr,
&dev_attr_dynamic_address.attr,
&dev_attr_hdrcap.attr,
+ &dev_attr_i3c_acquire_bus.attr,
NULL,
};
ATTRIBUTE_GROUPS(i3c_masterdev);
--
1.7.1

2020-11-29 14:29:19

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v9 4/7] i3c: master: add mastership handover support

Added mastership acquire and yield functions.

Signed-off-by: Parshuram Thombare <[email protected]>
---
drivers/i3c/master.c | 183 +++++++++++++++++++++++++++++++++++++++++---
include/linux/i3c/master.h | 6 ++
2 files changed, 177 insertions(+), 12 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index af0630a..51ef706 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -44,6 +44,16 @@ static void i3c_bus_maintenance_lock(struct i3c_bus *bus)
}

/**
+ * i3c_bus_maintenance_downgrade_lock - Downgrade maintenance lock to
+ * normaluse lock.
+ * @bus: I3C bus to take the lock on
+ */
+static void i3c_bus_maintenance_downgrade_lock(struct i3c_bus *bus)
+{
+ downgrade_write(&bus->lock);
+}
+
+/**
* i3c_bus_maintenance_unlock - Release the bus lock after a maintenance
* operation
* @bus: I3C bus to release the lock on
@@ -451,6 +461,59 @@ static int i3c_bus_init(struct i3c_bus *i3cbus)
[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;
+}
+
+/**
+ * i3c_master_acquire_bus() - acquire I3C bus mastership
+ * @m: I3C master object
+ *
+ * This function may sleep.
+ * It is expected to be called with normaluse_lock.
+ */
+static int i3c_master_acquire_bus(struct i3c_master_controller *m)
+{
+ int ret = 0;
+
+ /*
+ * Request mastership needs maintenance(write) lock. So, to avoid
+ * deadlock, release normaluse(read) lock, which is expected to be
+ * held before calling this function.
+ * normaluse(read) lock is expected to be held before calling
+ * this function to avoid race with maintenance activities
+ * like DEFSLVS processing etc
+ */
+ i3c_bus_normaluse_unlock(&m->bus);
+ i3c_bus_maintenance_lock(&m->bus);
+
+ if (m->this && m->this == m->bus.cur_master) {
+ i3c_master_disec_locked(m, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ goto mr_req_done;
+ }
+
+ ret = m->ops->request_mastership(m);
+ if (ret)
+ goto mr_req_done;
+
+ m->bus.cur_master = m->this;
+
+mr_req_done:
+ i3c_bus_maintenance_downgrade_lock(&m->bus);
+ return ret;
+}
+
static ssize_t mode_show(struct device *dev,
struct device_attribute *da,
char *buf)
@@ -476,11 +539,12 @@ static ssize_t current_master_show(struct device *dev,
char *buf)
{
struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
- ssize_t ret;
+ ssize_t ret = 0;

i3c_bus_normaluse_lock(i3cbus);
- ret = sprintf(buf, "%d-%llx\n", i3cbus->id,
- i3cbus->cur_master->info.pid);
+ if (i3cbus->cur_master)
+ ret = sprintf(buf, "%d-%llx\n", i3cbus->id,
+ i3cbus->cur_master->info.pid);
i3c_bus_normaluse_unlock(i3cbus);

return ret;
@@ -690,6 +754,33 @@ static int i3c_master_send_ccc_cmd_locked(struct i3c_master_controller *master,
return 0;
}

+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 struct i2c_dev_desc *
i3c_master_find_i2c_dev_by_addr(const struct i3c_master_controller *master,
u16 addr)
@@ -1582,10 +1673,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;

@@ -1594,7 +1681,9 @@ int i3c_master_set_info(struct i3c_master_controller *master,
return PTR_ERR(i3cdev);

master->this = i3cdev;
- master->bus.cur_master = master->this;
+
+ if (!master->secondary)
+ master->bus.cur_master = master->this;

ret = i3c_master_attach_i3c_dev(master, i3cdev);
if (ret)
@@ -1637,6 +1726,74 @@ static void i3c_master_detach_free_devs(struct i3c_master_controller *master)
}

/**
+ * i3c_master_yield_bus() - yield I3C bus mastership
+ * @m: I3C master object
+ * @sec_mst_dyn_addr: address of device requesting mastership
+ *
+ * This function may sleep.
+ * It is expected to be called with normaluse_lock.
+ */
+void
+i3c_master_yield_bus(struct i3c_master_controller *m, u8 sec_mst_dyn_addr)
+{
+ struct i3c_dev_desc *i3cdev;
+ bool dev_found = false;
+ int ret = 0;
+
+ i3c_bus_maintenance_lock(&m->bus);
+ if (m->this != m->bus.cur_master)
+ goto mr_yield_done;
+
+ i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+ if (sec_mst_dyn_addr == i3cdev->info.dyn_addr) {
+ dev_found = true;
+ break;
+ }
+ }
+
+ /* Requesting device not found on i3c list. This should never happen. */
+ if (!dev_found)
+ goto mr_yield_done;
+
+ /*
+ * Maintenance lock and master check above is used to
+ * avoid race amongst devices sending MR requests
+ * at the same time, as soon as ENEC MST is sent by the current
+ * master. It ensure that only one MR request is processed,
+ * rest MR requests on losing devices will timeout in wait MR
+ * DONE state. And next MR requests are blocked due to DISEC MST
+ * sent by current master in yield operation.
+ * New master should send ENEC MST once it's work is done.
+ * maintainanace lock is also needed for i3c_master_get_accmst_locked.
+ */
+
+ ret = i3c_master_disec_locked(m, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ /*
+ * Once mastership is given to the new master, it is expected that
+ * MR is disabled prior to that and new master is responsible to
+ * enable it by broadcasting ENEC MR when it's work is done.
+ * If DISEC MR fails and we still go ahead with handover, chances
+ * are new master will get interrupted by unexpected MR requests.
+ */
+ if (ret)
+ goto mr_yield_done;
+
+ ret = i3c_master_get_accmst_locked(m, sec_mst_dyn_addr);
+ if (ret)
+ goto mr_yield_done;
+
+ m->bus.cur_master = i3cdev;
+
+mr_yield_done:
+ i3c_bus_maintenance_unlock(&m->bus);
+ if (ret)
+ i3c_master_enable_mr_events(m);
+}
+EXPORT_SYMBOL_GPL(i3c_master_yield_bus);
+
+/**
* i3c_primary_master_bus_init() - initialize an I3C bus
* @master: main master initializing the bus
*
@@ -2522,6 +2679,9 @@ 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)
+ return -EINVAL;
+
return 0;
}

@@ -2536,10 +2696,6 @@ static int i3c_master_init(struct i3c_master_controller *master,
struct i2c_dev_boardinfo *i2cbi;
int ret;

- /* We do not support secondary masters yet. */
- if (secondary)
- return -ENOTSUPP;
-
ret = i3c_master_check_ops(ops);
if (ret)
return ret;
@@ -2665,6 +2821,9 @@ int i3c_primary_master_register(struct i3c_master_controller *master,
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);

+ if (ops->request_mastership)
+ ret = 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 e186d53..77c0422 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -426,6 +426,8 @@ struct i3c_bus {
* for a future IBI
* This method is mandatory only if ->request_ibi is not
* NULL.
+ * @request_mastership: send mastership request to the current master
+ * @enable_mr_events: enable mastership request handling by the controller
*/
struct i3c_master_controller_ops {
int (*bus_init)(struct i3c_master_controller *master);
@@ -452,6 +454,8 @@ 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);
};

/**
@@ -517,6 +521,8 @@ 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 *m,
+ u8 sec_mst_dyn_addr);
int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
const struct i2c_msg *xfers,
int nxfers);
--
1.7.1

2020-11-29 14:30:56

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v9 7/7] i3c: master: mastership handover, defslvs processing in cdns controller driver

Added I3C bus mastership handover and DEFSLVS message handling
code to Cadence's I3C master controller driver.

Signed-off-by: Parshuram Thombare <[email protected]>
---
drivers/i3c/master/i3c-master-cdns.c | 329 +++++++++++++++++++++++++++++++---
1 files changed, 306 insertions(+), 23 deletions(-)

diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
index f1d6d68..ff07862 100644
--- a/drivers/i3c/master/i3c-master-cdns.c
+++ b/drivers/i3c/master/i3c-master-cdns.c
@@ -160,6 +160,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)
@@ -192,7 +193,7 @@
#define SLV_STATUS1_HJ_DIS BIT(18)
#define SLV_STATUS1_MR_DIS BIT(17)
#define SLV_STATUS1_PROT_ERR BIT(16)
-#define SLV_STATUS1_DA(x) (((s) & GENMASK(15, 9)) >> 9)
+#define SLV_STATUS1_DA(s) (((s) & GENMASK(15, 9)) >> 9)
#define SLV_STATUS1_HAS_DA BIT(8)
#define SLV_STATUS1_DDR_RX_FULL BIT(7)
#define SLV_STATUS1_DDR_TX_FULL BIT(6)
@@ -397,6 +398,9 @@ struct cdns_i3c_data {

struct cdns_i3c_master {
struct work_struct hj_work;
+ struct work_struct mr_yield_work;
+ struct work_struct defslvs_work;
+ struct completion mr_done;
struct i3c_master_controller base;
u32 free_rr_slots;
unsigned int maxdevs;
@@ -416,6 +420,7 @@ struct cdns_i3c_master {
struct cdns_i3c_master_caps caps;
unsigned long i3c_scl_lim;
const struct cdns_i3c_data *devdata;
+ u8 mr_addr;
};

static inline struct cdns_i3c_master *
@@ -1182,10 +1187,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;
}

@@ -1208,21 +1209,21 @@ 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;
+ u32 ctrl, prescl0, prescl1, pres, low, bus_mode;
struct i3c_device_info info = { };
int ret, 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:
@@ -1253,7 +1254,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);
@@ -1261,7 +1261,6 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
if (ncycles < 0)
ncycles = 0;
prescl1 = PRESCL_CTRL1_OD_LOW(ncycles);
- writel(prescl1, master->regs + PRESCL_CTRL1);

/* Get an address for the master. */
ret = i3c_master_get_free_addr(m, 0);
@@ -1279,13 +1278,21 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
if (ret)
return ret;

+ 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.
*/
- ctrl |= CTRL_HJ_ACK | CTRL_HJ_DISEC | CTRL_HALT_EN | CTRL_MCS_EN;
+ if (!m->secondary)
+ ctrl |= CTRL_HJ_ACK | CTRL_HJ_DISEC;

/*
* Configure data hold delay based on device-specific data.
@@ -1358,6 +1365,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);
@@ -1379,6 +1387,14 @@ 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)];
+ master->mr_addr = dev->info.dyn_addr;
+ queue_work(master->base.wq,
+ &master->mr_yield_work);
+ }
+ break;
+
default:
break;
}
@@ -1390,16 +1406,42 @@ 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 + 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);
+
+ if (status & MST_INT_IBIR_THR)
+ cnds_i3c_master_demux_ibis(master);
+
+ if (status & MST_INT_MR_DONE) {
+ writel(FLUSH_RX_FIFO | FLUSH_TX_FIFO,
+ master->regs + FLUSH_CTRL);
+ writel(MST_INT_MR_DONE, master->regs + MST_ICR);
+ }
+ } else {
+ status = (readl(master->regs + SLV_ISR) &
+ readl(master->regs + SLV_IMR));
+
+ if (!status)
+ return IRQ_NONE;
+
+ if (status & SLV_INT_MR_DONE) {
+ writel(FLUSH_RX_FIFO | FLUSH_TX_FIFO,
+ master->regs + FLUSH_CTRL);
+ complete(&master->mr_done);
+ }

- spin_lock(&master->xferqueue.lock);
- cdns_i3c_master_end_xfer_locked(master, status);
- spin_unlock(&master->xferqueue.lock);
+ if (status & SLV_INT_DEFSLVS)
+ queue_work(master->base.wq, &master->defslvs_work);

- if (status & MST_INT_IBIR_THR)
- cnds_i3c_master_demux_ibis(master);
+ writel(status, master->regs + SLV_ICR);
+ }

return IRQ_HANDLED;
}
@@ -1523,6 +1565,106 @@ 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 + MST_STATUS0);
+ if (status & MST_STATUS0_MASTER_MODE)
+ return 0;
+
+ status = readl(master->regs + SLV_STATUS1);
+ if (!(status & SLV_STATUS1_HAS_DA))
+ return -EACCES;
+
+ if (status & SLV_STATUS1_MR_DIS)
+ return -EACCES;
+
+ writel(readl(master->regs + CTRL) | CTRL_MST_INIT | CTRL_MST_ACK,
+ master->regs + CTRL);
+
+ init_completion(&master->mr_done);
+ if (!wait_for_completion_timeout(&master->mr_done,
+ msecs_to_jiffies(1000)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+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 const struct i3c_master_controller_ops cdns_i3c_master_ops = {
.bus_init = cdns_i3c_master_bus_init,
.bus_cleanup = cdns_i3c_master_bus_cleanup,
@@ -1541,6 +1683,8 @@ static void cdns_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
.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,
};

static void cdns_i3c_master_hj(struct work_struct *work)
@@ -1561,9 +1705,134 @@ static void cdns_i3c_master_hj(struct work_struct *work)
{ /* sentinel */ },
};

+static void cdns_i3c_master_yield(struct work_struct *work)
+{
+ struct cdns_i3c_master *master = container_of(work,
+ struct cdns_i3c_master,
+ mr_yield_work);
+
+ i3c_master_yield_bus(&master->base, master->mr_addr);
+}
+
+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);
+ struct i3c_master_controller *m = &master->base;
+ struct i3c_ccc_dev_desc *desc;
+ struct cdns_i3c_i2c_dev_data *data;
+ struct i2c_dev_desc *i2cdev;
+ u32 devs, val, rr, slot;
+ u32 r0, r1, r2;
+ u8 saddr;
+
+ devs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK;
+ master->free_rr_slots = GENMASK(master->maxdevs, 1) & ~devs;
+
+ /*
+ * We chose to ignore I2C devices received from
+ * main master and use I2C device info from boardinfo.
+ * Since I2C device from boardinfo are already
+ * registered during bus_init, we just use same slot
+ * and if any I3C device is received in DEFSLVS in that
+ * place, just move that I3C device to other free slot.
+ * If there is no free slot, then such I3C devices
+ * are ignored.
+ * Master controller driver can chose how to handle I2C
+ * devices in DEFSLVS and pass only I3C devices list to
+ * I3C core DEFSVLS processing to handle hotplug and
+ * I3C device address changes.
+ */
+ for (slot = 0; slot < master->maxdevs; slot++) {
+ if (!(devs & BIT(slot)))
+ continue;
+
+ val = readl(master->regs + DEV_ID_RR0(slot));
+ if (!(val & DEV_ID_RR0_IS_I3C)) {
+ writel(readl(master->regs + DEVS_CTRL) |
+ DEVS_CTRL_DEV_CLR(slot),
+ master->regs + DEVS_CTRL);
+ master->free_rr_slots |= BIT(slot);
+ }
+ }
+
+ val = 0;
+ devs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK;
+ i3c_bus_for_each_i2cdev(&m->bus, i2cdev) {
+ data = i2c_dev_get_master_data(i2cdev);
+ if (devs & BIT(data->id)) {
+ rr = readl(master->regs + DEV_ID_RR0(data->id));
+ saddr = DEV_ID_RR0_GET_DEV_ADDR(rr);
+ if (saddr != i2cdev->boardinfo->base.addr) {
+ r0 = readl(master->regs + DEV_ID_RR0(data->id));
+ r1 = readl(master->regs + DEV_ID_RR1(data->id));
+ r2 = readl(master->regs + DEV_ID_RR2(data->id));
+ slot = ffs(master->free_rr_slots) - 1;
+ if (slot > 0) {
+ writel(r0,
+ master->regs + DEV_ID_RR0(slot));
+ writel(r1,
+ master->regs + DEV_ID_RR1(slot));
+ writel(r2,
+ master->regs + DEV_ID_RR2(slot));
+ writel(readl(master->regs + DEVS_CTRL) |
+ DEVS_CTRL_DEV_ACTIVE(slot),
+ master->regs + DEVS_CTRL);
+ master->free_rr_slots &= ~BIT(slot);
+ }
+ } else {
+ continue;
+ }
+ }
+ writel(readl(master->regs + DEVS_CTRL) |
+ DEVS_CTRL_DEV_CLR(data->id),
+ master->regs + DEVS_CTRL);
+ writel(readl(master->regs + DEVS_CTRL) |
+ DEVS_CTRL_DEV_ACTIVE(data->id),
+ master->regs + DEVS_CTRL);
+ writel(prepare_rr0_dev_address(i2cdev->boardinfo->base.addr) |
+ (i2cdev->boardinfo->base.flags & I2C_CLIENT_TEN ?
+ DEV_ID_RR0_LVR_EXT_ADDR : 0),
+ master->regs + DEV_ID_RR0(data->id));
+ writel(i2cdev->boardinfo->lvr,
+ master->regs + DEV_ID_RR2(data->id));
+ master->free_rr_slots &= ~BIT(data->id);
+ }
+
+ master->base.defslvs_data.ndevs = 0;
+ desc = master->base.defslvs_data.devs;
+ devs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK;
+
+ for (slot = 0; slot < master->maxdevs; slot++) {
+ if (!(devs & BIT(slot)))
+ continue;
+
+ val = readl(master->regs + DEV_ID_RR0(slot));
+ if (val & DEV_ID_RR0_IS_I3C) {
+ memset(desc, 0, sizeof(struct i3c_ccc_dev_desc));
+ 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;
+ master->base.defslvs_data.ndevs++;
+ desc++;
+ }
+ }
+
+ val = readl(master->regs + SLV_STATUS1);
+ if (!(val & SLV_STATUS1_HAS_DA) ||
+ i3c_master_process_defslvs(m, SLV_STATUS1_DA(val))) {
+ queue_work(master->base.wq, work);
+ return;
+ }
+}
+
static int cdns_i3c_master_probe(struct platform_device *pdev)
{
struct cdns_i3c_master *master;
+ bool secondary;
int ret, irq;
u32 val;

@@ -1607,6 +1876,7 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
spin_lock_init(&master->xferqueue.lock);
INIT_LIST_HEAD(&master->xferqueue.list);

+ INIT_WORK(&master->mr_yield_work, cdns_i3c_master_yield);
INIT_WORK(&master->hj_work, cdns_i3c_master_hj);
writel(0xffffffff, master->regs + MST_IDR);
writel(0xffffffff, master->regs + SLV_IDR);
@@ -1618,6 +1888,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);
@@ -1640,12 +1911,24 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
goto err_disable_sysclk;
}

+ if (secondary)
+ INIT_WORK(&master->defslvs_work, cdns_i3c_sec_master_defslvs);
+
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_primary_master_register(&master->base, &pdev->dev,
- &cdns_i3c_master_ops);
+ if (secondary) {
+ ret = i3c_secondary_master_register(&master->base, &pdev->dev,
+ &cdns_i3c_master_ops);
+ if (!ret)
+ writel(SLV_INT_DEFSLVS | SLV_INT_MR_DONE,
+ master->regs + SLV_IER);
+ } else {
+ ret = i3c_primary_master_register(&master->base, &pdev->dev,
+ &cdns_i3c_master_ops);
+ }
+
if (ret)
goto err_disable_sysclk;

--
1.7.1

2020-11-29 17:15:58

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v9 2/7] i3c: master: use i3c_master_register only for main master

Hi Parshuram,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on next-20201127]
[cannot apply to linus/master v5.10-rc5 v5.10-rc4 v5.10-rc3 v5.10-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/0day-ci/linux/commits/Parshuram-Thombare/I3C-mastership-handover-support/20201129-222847
base: 6174f05255e65622ff3340257879a4c0f858b0df
config: i386-randconfig-a012-20201129 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce (this is a W=1 build):
# https://github.com/0day-ci/linux/commit/963007469aa0a76446a899aa8b019802dfc82cb6
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Parshuram-Thombare/I3C-mastership-handover-support/20201129-222847
git checkout 963007469aa0a76446a899aa8b019802dfc82cb6
# save the attached .config to linux build tree
make W=1 ARCH=i386

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

drivers/i3c/master/mipi-i3c-hci/core.c: In function 'i3c_hci_probe':
>> drivers/i3c/master/mipi-i3c-hci/core.c:760:8: error: implicit declaration of function 'i3c_master_register'; did you mean 'i3c_master_unregister'? [-Werror=implicit-function-declaration]
760 | ret = i3c_master_register(&hci->master, &pdev->dev,
| ^~~~~~~~~~~~~~~~~~~
| i3c_master_unregister
cc1: some warnings being treated as errors

vim +760 drivers/i3c/master/mipi-i3c-hci/core.c

9ad9a52cce2828d Nicolas Pitre 2020-11-11 733
9ad9a52cce2828d Nicolas Pitre 2020-11-11 734 static int i3c_hci_probe(struct platform_device *pdev)
9ad9a52cce2828d Nicolas Pitre 2020-11-11 735 {
9ad9a52cce2828d Nicolas Pitre 2020-11-11 736 struct i3c_hci *hci;
9ad9a52cce2828d Nicolas Pitre 2020-11-11 737 int irq, ret;
9ad9a52cce2828d Nicolas Pitre 2020-11-11 738
9ad9a52cce2828d Nicolas Pitre 2020-11-11 739 hci = devm_kzalloc(&pdev->dev, sizeof(*hci), GFP_KERNEL);
9ad9a52cce2828d Nicolas Pitre 2020-11-11 740 if (!hci)
9ad9a52cce2828d Nicolas Pitre 2020-11-11 741 return -ENOMEM;
9ad9a52cce2828d Nicolas Pitre 2020-11-11 742 hci->base_regs = devm_platform_ioremap_resource(pdev, 0);
9ad9a52cce2828d Nicolas Pitre 2020-11-11 743 if (IS_ERR(hci->base_regs))
9ad9a52cce2828d Nicolas Pitre 2020-11-11 744 return PTR_ERR(hci->base_regs);
9ad9a52cce2828d Nicolas Pitre 2020-11-11 745
9ad9a52cce2828d Nicolas Pitre 2020-11-11 746 platform_set_drvdata(pdev, hci);
9ad9a52cce2828d Nicolas Pitre 2020-11-11 747 /* temporary for dev_printk's, to be replaced in i3c_master_register */
9ad9a52cce2828d Nicolas Pitre 2020-11-11 748 hci->master.dev.init_name = dev_name(&pdev->dev);
9ad9a52cce2828d Nicolas Pitre 2020-11-11 749
9ad9a52cce2828d Nicolas Pitre 2020-11-11 750 ret = i3c_hci_init(hci);
9ad9a52cce2828d Nicolas Pitre 2020-11-11 751 if (ret)
9ad9a52cce2828d Nicolas Pitre 2020-11-11 752 return ret;
9ad9a52cce2828d Nicolas Pitre 2020-11-11 753
9ad9a52cce2828d Nicolas Pitre 2020-11-11 754 irq = platform_get_irq(pdev, 0);
9ad9a52cce2828d Nicolas Pitre 2020-11-11 755 ret = devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler,
9ad9a52cce2828d Nicolas Pitre 2020-11-11 756 0, NULL, hci);
9ad9a52cce2828d Nicolas Pitre 2020-11-11 757 if (ret)
9ad9a52cce2828d Nicolas Pitre 2020-11-11 758 return ret;
9ad9a52cce2828d Nicolas Pitre 2020-11-11 759
9ad9a52cce2828d Nicolas Pitre 2020-11-11 @760 ret = i3c_master_register(&hci->master, &pdev->dev,
9ad9a52cce2828d Nicolas Pitre 2020-11-11 761 &i3c_hci_ops, false);
9ad9a52cce2828d Nicolas Pitre 2020-11-11 762 if (ret)
9ad9a52cce2828d Nicolas Pitre 2020-11-11 763 return ret;
9ad9a52cce2828d Nicolas Pitre 2020-11-11 764
9ad9a52cce2828d Nicolas Pitre 2020-11-11 765 return 0;
9ad9a52cce2828d Nicolas Pitre 2020-11-11 766 }
9ad9a52cce2828d Nicolas Pitre 2020-11-11 767

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (4.24 kB)
.config.gz (29.48 kB)
Download all attachments

2021-03-05 00:08:34

by Parshuram Raju Thombare

[permalink] [raw]
Subject: RE: [RESEND PATCH v9 0/7] I3C mastership handover support

Ping !

>-----Original Message-----
>From: Parshuram Thombare <[email protected]>
>Sent: Sunday, November 29, 2020 7:49 PM
>To: [email protected]; [email protected];
>[email protected]
>Cc: [email protected]; [email protected]; Milind Parab
><[email protected]>; [email protected]; Parshuram Raju Thombare
><[email protected]>
>Subject: [RESEND PATCH v9 0/7] I3C mastership handover support
>
>Main changes between v8 and v9 are:
>- Fix NULL dereference issue in current_master_show when
> cat'ing sysfs key current_master for secondary master
> before primary master gets initialized.
>
>Main changes between v7 and v8 are:
>- Document format changed from table to DOT diagram
>- Appropriate names for few functions
>- Moved mastership request process entirely to the driver
>- Reuse of i3c_master_add_i3c_dev_locked in core defslvs
> processing
>
>Main changes between v6 and v7 are:
>- Added separate functions for main and secondary
> master initialization
>- Secondary master initialization don't wait for
> DEFSLSVS.
>- Change to use I2C device information from DTS,
> and corresponding changes in controller driver
> and I3C core DEFSLVS processing to ignore I2C
> devices received in DEFSLVS
>- Reverted bus_init split
>- Fixed formatting issues in document
>
>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
>
>Parshuram Thombare (7):
> i3c: master: master initialization flow document
> i3c: master: use i3c_master_register only for main master
> i3c: master: add i3c_secondary_master_register
> i3c: master: add mastership handover support
> i3c: master: add defslvs processing
> i3c: master: sysfs key for acquire bus
> i3c: master: mastership handover, defslvs processing in cdns
> controller driver
>
> Documentation/driver-api/i3c/index.rst | 1 +
> .../driver-api/i3c/master-initialization-flow.rst | 187 ++++++++
> drivers/i3c/master.c | 497 ++++++++++++++++++--
> drivers/i3c/master/dw-i3c-master.c | 4 +-
> drivers/i3c/master/i3c-master-cdns.c | 329 ++++++++++++-
> include/linux/i3c/master.h | 23 +-
> 6 files changed, 968 insertions(+), 73 deletions(-)
> create mode 100644 Documentation/driver-api/i3c/master-initialization-flow.rst