@@ -963,6 +963,33 @@ int i3c_master_defslvs_locked(struct i3c_master_controller *master)
}
EXPORT_SYMBOL_GPL(i3c_master_defslvs_locked);
+static int i3c_master_get_accmst_locked(struct i3c_master_controller *master,
+ u8 addr)
+{
+ struct i3c_ccc_getaccmst *accmst;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ accmst = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*accmst));
+ if (!accmst)
+ return -ENOMEM;
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETACCMST, &dest, 1);
+
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ goto out;
+
+ if (dest.payload.len != sizeof(*accmst))
+ ret = -EIO;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
static int i3c_master_setda_locked(struct i3c_master_controller *master,
u8 oldaddr, u8 newaddr, bool setdasa)
{
@@ -1525,6 +1552,185 @@ int i3c_master_do_daa(struct i3c_master_controller *master)
}
EXPORT_SYMBOL_GPL(i3c_master_do_daa);
+static int i3c_master_getaccmst(struct i3c_master_controller *master)
+{
+ int ret;
+
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = i3c_master_get_accmst_locked(master, master->mr_addr);
+ i3c_bus_maintenance_unlock(&master->bus);
+
+ return ret;
+}
+
+static int i3c_master_request_mastership(struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* request_mastership callback should handle EN/DIS EC MR.*/
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = master->ops->request_mastership(master);
+ i3c_bus_maintenance_unlock(&master->bus);
+
+ return ret;
+}
+
+static void i3c_mst_yield_bus(struct work_struct *work)
+{
+ struct i3c_master_controller *m;
+ struct i3c_dev_desc *i3cdev;
+ int ret;
+
+ m = container_of(work, struct i3c_master_controller, mst_work);
+ switch (m->mr_state) {
+ case I3C_MR_DISEC_MR:
+ /*
+ * Disable MR on all but the secondary master first
+ * reaching here.
+ */
+ i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+ if (I3C_BCR_DEVICE_ROLE(i3cdev->info.bcr) !=
+ I3C_BCR_I3C_MASTER ||
+ i3cdev->info.dyn_addr == m->mr_addr ||
+ m->this == i3cdev)
+ continue;
+ i3c_bus_maintenance_lock(&m->bus);
+ i3c_master_disec_locked(m, i3cdev->info.dyn_addr,
+ I3C_CCC_EVENT_MR);
+ i3c_bus_maintenance_unlock(&m->bus);
+ }
+ m->mr_state = I3C_MR_GETACCMST;
+ queue_work(m->wq, &m->mst_work);
+ break;
+
+ case I3C_MR_GETACCMST:
+ ret = i3c_master_getaccmst(m);
+ if (!ret)
+ m->mr_state = I3C_MR_DONE;
+ else
+ m->mr_state = I3C_MR_FAILED;
+ queue_work(m->wq, &m->mst_work);
+ break;
+
+ case I3C_MR_DONE:
+ i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+ if (m->mr_addr == i3cdev->info.dyn_addr) {
+ m->bus.cur_master = i3cdev;
+ break;
+ }
+ }
+ m->mr_state = I3C_MR_IDLE;
+ break;
+
+ case I3C_MR_FAILED:
+ m->ops->enable_mr_events(m);
+ i3c_bus_maintenance_lock(&m->bus);
+ i3c_master_enec_locked(m, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_MR);
+ i3c_bus_maintenance_unlock(&m->bus);
+ m->mr_state = I3C_MR_IDLE;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+i3c_master_yield_bus(struct i3c_master_controller *master, u8 sec_mst_dyn_addr)
+{
+ master->ops->disable_mr_events(master);
+ master->mr_addr = sec_mst_dyn_addr;
+ master->mr_state = I3C_MR_DISEC_MR;
+ queue_work(master->wq, &master->mst_work);
+}
+EXPORT_SYMBOL_GPL(i3c_master_yield_bus);
+
+static void i3c_sec_mst_acquire_bus(struct work_struct *work)
+{
+ struct i3c_master_controller *m;
+ struct i3c_bus *i3cbus;
+ int ret;
+
+ m = container_of(work, struct i3c_master_controller, sec_mst_work);
+ i3cbus = i3c_master_get_bus(m);
+ switch (m->mr_state) {
+ case I3C_MR_WAIT_DA:
+ /* Wait until this master have dynamic address */
+ if (m->ops->check_event_set(m, I3C_SLV_DA_UPDATE))
+ m->mr_state = I3C_MR_REQUEST;
+ queue_work(m->wq, &m->sec_mst_work);
+ break;
+
+ case I3C_MR_REQUEST:
+ /* Wait until we can send MR */
+ ret = i3c_master_request_mastership(m);
+ if (!ret)
+ m->mr_state = I3C_MR_WAIT_DEFSLVS;
+ queue_work(m->wq, &m->sec_mst_work);
+ break;
+
+ case I3C_MR_WAIT_DEFSLVS:
+ /*
+ * Wait for DEFSLVS, this event check should make sure
+ * that DEFSLVS list is read into m->defslvs_data
+ */
+ if (m->ops->check_event_set(m, I3C_SLV_DEFSLVS_CCC))
+ m->mr_state = I3C_MR_WAIT_MR_DONE;
+ queue_work(m->wq, &m->sec_mst_work);
+ break;
+
+ case I3C_MR_WAIT_MR_DONE:
+ if (m->ops->check_event_set(m, I3C_SLV_MR_DONE)) {
+ m->mr_state = I3C_MR_DONE;
+ complete(&m->mr_comp);
+ } else {
+ queue_work(m->wq, &m->sec_mst_work);
+ }
+ break;
+
+ default:
+ m->mr_state = I3C_MR_FAILED;
+ complete(&m->mr_comp);
+ break;
+ }
+}
+
+void i3c_sec_mst_mr_dis_event(struct i3c_master_controller *m)
+{
+ if (m->mr_state != I3C_MR_IDLE)
+ m->mr_state = I3C_MR_WAIT_DA;
+}
+EXPORT_SYMBOL_GPL(i3c_sec_mst_mr_dis_event);
+
+static int i3c_master_acquire_bus(struct i3c_master_controller *master)
+{
+ int ret = 0;
+
+ if (!master->this || master->this != master->bus.cur_master) {
+ i3c_bus_normaluse_lock(&master->bus);
+ if (master->mr_state == I3C_MR_IDLE) {
+ master->mr_state = I3C_MR_WAIT_DA;
+ i3c_bus_normaluse_unlock(&master->bus);
+ init_completion(&master->mr_comp);
+ queue_work(master->wq, &master->sec_mst_work);
+ wait_for_completion(&master->mr_comp);
+ 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;
+ i3c_bus_normaluse_unlock(&master->bus);
+ }
+ }
+
+ return ret;
+}
+
/**
* i3c_master_set_info() - set master device information
* @master: master used to send frames on the bus
@@ -1558,10 +1764,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;
@@ -1622,9 +1824,10 @@ static void i3c_master_detach_free_devs(struct i3c_master_controller *master)
* 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
@@ -1708,14 +1911,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;
@@ -1728,7 +1923,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;
}
/*
@@ -1737,14 +1932,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
@@ -1762,10 +1957,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);
@@ -2249,6 +2440,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);
@@ -2392,9 +2586,179 @@ 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 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 int i3c_secondary_master_bus_init(struct i3c_master_controller *master)
+{
+ struct i3c_ccc_dev_desc *desc;
+ struct i3c_dev_desc *i3cdev;
+ struct i2c_dev_desc *i2cdev;
+ struct i2c_dev_boardinfo *info;
+ int ret, slot;
+
+ desc = master->defslvs_data.devs;
+ for (slot = 1; slot <= master->defslvs_data.ndevs; slot++, desc++) {
+ if (desc->static_addr) {
+ 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) {
+ i3c_master_free_i3c_dev(i3cdev);
+ goto err_detach_devs;
+ }
+ }
+ }
+
+ ret = master->ops->master_set_info(master);
+ if (ret)
+ goto err_bus_cleanup;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev) {
+ if (master->this != i3cdev) {
+ ret = i3c_master_retrieve_dev_info(i3cdev);
+ if (ret)
+ goto err_bus_cleanup;
+ }
+ }
+
+ 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_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);
+
+ ret = i3c_master_acquire_bus(master);
+ if (ret)
+ goto err_cleanup_bus;
+
+ /* 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)
+ goto err_cleanup_bus;
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ goto err_cleanup_bus;
+
+ ret = i3c_secondary_master_bus_init(master);
+ if (ret)
+ goto err_cleanup_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);
+
+ 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_bus_maintenance_unlock(&master->bus);
+
+ 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
@@ -2424,10 +2788,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);
@@ -2451,10 +2815,45 @@ int i3c_master_register(struct i3c_master_controller *master,
device_initialize(&master->dev);
dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
- ret = of_populate_i3c_bus(master);
+ 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 = 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):
@@ -2468,23 +2867,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)
@@ -2507,6 +2896,22 @@ int i3c_master_register(struct i3c_master_controller *master,
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);
+ if (ops->request_mastership) {
+ master->ops->enable_mr_events(master);
+ i3c_bus_maintenance_lock(&master->bus);
+ i3c_master_enec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_MR);
+ i3c_bus_maintenance_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;
+
return 0;
err_del_dev:
@@ -2557,7 +2962,10 @@ 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 (!i3c_master_acquire_bus(master))
+ return master->ops->priv_xfers(dev, xfers, nxfers);
+ else
+ return -EAGAIN;
}
int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev)
@@ -259,6 +259,27 @@ enum i3c_bus_mode {
I3C_BUS_MODE_MIXED_SLOW,
};
+enum i3c_mr_state {
+ I3C_MR_IDLE,
+ I3C_MR_DISEC_MR,
+ I3C_MR_SEND_DEFSLVS,
+ I3C_MR_GETACCMST,
+ I3C_MR_WAIT_DA,
+ I3C_MR_CHECK_STATE,
+ I3C_MR_REQUEST,
+ I3C_MR_WAIT_DEFSLVS,
+ I3C_MR_WAIT_MR_DONE,
+ I3C_MR_DONE,
+ I3C_MR_FAILED,
+};
+
+enum i3c_event {
+ I3C_SLV_DA_UPDATE,
+ I3C_SLV_DEFSLVS_CCC,
+ I3C_SLV_MR_DIS,
+ I3C_SLV_MR_DONE,
+};
+
/**
* enum i3c_addr_slot_status - I3C address slot status
* @I3C_ADDR_SLOT_FREE: address is free
@@ -448,6 +469,11 @@ struct i3c_master_controller_ops {
int (*disable_ibi)(struct i3c_dev_desc *dev);
void (*recycle_ibi_slot)(struct i3c_dev_desc *dev,
struct i3c_ibi_slot *slot);
+ int (*request_mastership)(struct i3c_master_controller *master);
+ void (*enable_mr_events)(struct i3c_master_controller *m);
+ void (*disable_mr_events)(struct i3c_master_controller *m);
+ bool (*check_event_set)(struct i3c_master_controller *m,
+ enum i3c_event);
};
/**
@@ -489,6 +515,17 @@ 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;
+ 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;
};
/**
@@ -513,6 +550,9 @@ struct i3c_master_controller {
#define i3c_bus_for_each_i3cdev(bus, dev) \
list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
+void i3c_master_yield_bus(struct i3c_master_controller *master,
+ u8 slv_dyn_addr);
+void i3c_sec_mst_mr_dis_event(struct i3c_master_controller *m);
int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
const struct i2c_msg *xfers,
int nxfers);
This patch adds mastership handover support to I3C master subsystem as per MIPI I3C v1.0 specification. Signed-off-by: Parshuram Thombare <pthombar@cadence.com> --- drivers/i3c/master.c | 486 ++++++++++++++++++++++++++++++++++--- include/linux/i3c/master.h | 40 +++ 2 files changed, 487 insertions(+), 39 deletions(-)