static DEFINE_IDR(i3c_bus_idr);
static DEFINE_MUTEX(i3c_core_lock);
static int __i3c_first_dynamic_bus_num;
+static BLOCKING_NOTIFIER_HEAD(i3c_bus_notifier);
/**
* i3c_bus_maintenance_lock - Lock the bus for a maintenance operation
return 0;
}
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+ void *data)
+{
+ struct i3c_bus *bus;
+ int id;
+
+ mutex_lock(&i3c_core_lock);
+ idr_for_each_entry(&i3c_bus_idr, bus, id)
+ fn(bus, data);
+ mutex_unlock(&i3c_core_lock);
+}
+EXPORT_SYMBOL_GPL(i3c_for_each_bus_locked);
+
+int i3c_register_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_register_notifier);
+
+int i3c_unregister_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_unregister_notifier);
+
+static void i3c_bus_notify(struct i3c_bus *bus, unsigned int action)
+{
+ blocking_notifier_call_chain(&i3c_bus_notifier, action, bus);
+}
+
static const char * const i3c_bus_mode_strings[] = {
[I3C_BUS_MODE_PURE] = "pure",
[I3C_BUS_MODE_MIXED_FAST] = "mixed-fast",
desc->dev->dev.of_node = desc->boardinfo->of_node;
ret = device_register(&desc->dev->dev);
- if (ret)
+ if (ret) {
dev_err(&master->dev,
"Failed to add I3C device (err = %d)\n", ret);
+ put_device(&desc->dev->dev);
+ }
}
}
adap->dev.parent = master->dev.parent;
adap->owner = master->dev.parent->driver->owner;
adap->algo = &i3c_master_i2c_algo;
- strncpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name));
+ strscpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name));
/* FIXME: Should we allow i3c masters to override these values? */
adap->timeout = 1000;
void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot)
{
atomic_inc(&dev->ibi->pending_ibis);
- queue_work(dev->common.master->wq, &slot->work);
+ queue_work(dev->ibi->wq, &slot->work);
}
EXPORT_SYMBOL_GPL(i3c_master_queue_ibi);
device_initialize(&master->dev);
dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
+ master->dev.dma_mask = parent->dma_mask;
+ master->dev.coherent_dma_mask = parent->coherent_dma_mask;
+ master->dev.dma_parms = parent->dma_parms;
+
ret = of_populate_i3c_bus(master);
if (ret)
goto err_put_dev;
if (ret)
goto err_del_dev;
+ i3c_bus_notify(i3cbus, I3C_NOTIFY_BUS_ADD);
+
/*
* We're done initializing the bus and the controller, we can now
* register I3C devices discovered during the initial DAA.
*/
void i3c_master_unregister(struct i3c_master_controller *master)
{
+ i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE);
+
i3c_master_i2c_adapter_cleanup(master);
i3c_master_unregister_i3c_devs(master);
i3c_master_bus_cleanup(master);
if (!ibi)
return -ENOMEM;
+ ibi->wq = alloc_ordered_workqueue(dev_name(i3cdev_to_dev(dev->dev)), WQ_MEM_RECLAIM);
+ if (!ibi->wq) {
+ kfree(ibi);
+ return -ENOMEM;
+ }
+
atomic_set(&ibi->pending_ibis, 0);
init_completion(&ibi->all_ibis_handled);
ibi->handler = req->handler;
WARN_ON(i3c_dev_disable_ibi_locked(dev));
master->ops->free_ibi(dev);
+
+ if (dev->ibi->wq) {
+ destroy_workqueue(dev->ibi->wq);
+ dev->ibi->wq = NULL;
+ }
+
kfree(dev->ibi);
dev->ibi = NULL;
}
struct i2c_client;
+/* notifier actions. notifier call data is the struct i3c_bus */
+enum {
+ I3C_NOTIFY_BUS_ADD,
+ I3C_NOTIFY_BUS_REMOVE,
+};
+
struct i3c_master_controller;
struct i3c_bus;
struct i3c_device;
* rejected by the master
* @num_slots: number of IBI slots reserved for this device
* @enabled: reflect the IBI status
+ * @wq: workqueue used to execute IBI handlers.
* @handler: IBI handler specified at i3c_device_request_ibi() call time. This
* handler will be called from the controller workqueue, and as such
* is allowed to sleep (though it is recommended to process the IBI
unsigned int max_payload_len;
unsigned int num_slots;
unsigned int enabled;
+ struct workqueue_struct *wq;
void (*handler)(struct i3c_device *dev,
const struct i3c_ibi_payload *payload);
};
* assigned a dynamic address by the master. Will be used during
* bus initialization to assign it a specific dynamic address
* before starting DAA (Dynamic Address Assignment)
- * @pid: I3C Provisional ID exposed by the device. This is a unique identifier
+ * @pid: I3C Provisioned ID exposed by the device. This is a unique identifier
* that may be used to attach boardinfo to i3c_dev_desc when the device
* does not have a static address
* @of_node: optional DT node in case the device has been described in the DT
* @boardinfo.i2c: list of I2C boardinfo objects
* @boardinfo: board-level information attached to devices connected on the bus
* @bus: I3C bus exposed by this master
- * @wq: workqueue used to execute IBI handlers. Can also be used by master
+ * @wq: workqueue which can be used by master
* drivers if they need to postpone operations that need to take place
* in a thread context. Typical examples are Hot Join processing which
* requires taking the bus lock in maintenance, which in turn, can only
struct i3c_ibi_slot *i3c_master_get_free_ibi_slot(struct i3c_dev_desc *dev);
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+ void *data);
+int i3c_register_notifier(struct notifier_block *nb);
+int i3c_unregister_notifier(struct notifier_block *nb);
+
#endif /* I3C_MASTER_H */