2020-04-17 16:23:53

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v6 0/8] I3C mastership handover support

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


2020-04-17 16:24:05

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v6 4/8] i3c: master: defslvs processing

This patch add DEFSLVS processing code
to I3C master subsystem.

Signed-off-by: Parshuram Thombare <[email protected]>
---
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

2020-04-17 16:24:50

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v6 5/8] i3c: master: check for non null pointer

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 <[email protected]>
---
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

2020-04-17 16:25:28

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v6 2/8] i3c: master: split bus_init callback into bus_init and master_set_info

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 <[email protected]>
---
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

2020-04-17 16:25:58

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v6 7/8] i3c: master: added sysfs key i3c_acquire_bus

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

Signed-off-by: Parshuram Thombare <[email protected]>
---
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

2020-04-17 16:27:25

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v6 6/8] i3c: master: secondary master initialization

This patch add secondary master support to
I3C master subsystem.

Signed-off-by: Parshuram Thombare <[email protected]>
---
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

2020-04-17 16:28:40

by Parshuram Raju Thombare

[permalink] [raw]
Subject: [PATCH v6 8/8] i3c: master: add mastership handover support to cdns i3c master driver

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

Signed-off-by: Parshuram Thombare <[email protected]>
---
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

2020-04-30 07:57:51

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v6 2/8] i3c: master: split bus_init callback into bus_init and master_set_info

On Fri, 17 Apr 2020 18:20:52 +0200
Parshuram Thombare <[email protected]> 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 <[email protected]>
> ---
> 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);