diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c index 8a156f5ad692..101eaa77de68 100644 --- a/drivers/i3c/device.c +++ b/drivers/i3c/device.c @@ -46,10 +46,16 @@ int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers, return -EINVAL; } + ret = i3c_bus_rpm_get(dev->bus); + if (ret) + return ret; + i3c_bus_normaluse_lock(dev->bus); ret = i3c_dev_do_xfers_locked(dev->desc, xfers, nxfers, mode); i3c_bus_normaluse_unlock(dev->bus); + i3c_bus_rpm_put(dev->bus); + return ret; } EXPORT_SYMBOL_GPL(i3c_device_do_xfers); @@ -66,10 +72,16 @@ int i3c_device_do_setdasa(struct i3c_device *dev) { int ret; + ret = i3c_bus_rpm_get(dev->bus); + if (ret) + return ret; + i3c_bus_normaluse_lock(dev->bus); ret = i3c_dev_setdasa_locked(dev->desc); i3c_bus_normaluse_unlock(dev->bus); + i3c_bus_rpm_put(dev->bus); + return ret; } EXPORT_SYMBOL_GPL(i3c_device_do_setdasa); @@ -106,16 +118,27 @@ EXPORT_SYMBOL_GPL(i3c_device_get_info); */ int i3c_device_disable_ibi(struct i3c_device *dev) { - int ret = -ENOENT; + int ret; + + if (i3c_bus_rpm_ibi_allowed(dev->bus)) { + ret = i3c_bus_rpm_get(dev->bus); + if (ret) + return ret; + } i3c_bus_normaluse_lock(dev->bus); if (dev->desc) { mutex_lock(&dev->desc->ibi_lock); ret = i3c_dev_disable_ibi_locked(dev->desc); mutex_unlock(&dev->desc->ibi_lock); + } else { + ret = -ENOENT; } i3c_bus_normaluse_unlock(dev->bus); + if (!ret || i3c_bus_rpm_ibi_allowed(dev->bus)) + i3c_bus_rpm_put(dev->bus); + return ret; } EXPORT_SYMBOL_GPL(i3c_device_disable_ibi); @@ -135,16 +158,25 @@ EXPORT_SYMBOL_GPL(i3c_device_disable_ibi); */ int i3c_device_enable_ibi(struct i3c_device *dev) { - int ret = -ENOENT; + int ret; + + ret = i3c_bus_rpm_get(dev->bus); + if (ret) + return ret; i3c_bus_normaluse_lock(dev->bus); if (dev->desc) { mutex_lock(&dev->desc->ibi_lock); ret = i3c_dev_enable_ibi_locked(dev->desc); mutex_unlock(&dev->desc->ibi_lock); + } else { + ret = -ENOENT; } i3c_bus_normaluse_unlock(dev->bus); + if (ret || i3c_bus_rpm_ibi_allowed(dev->bus)) + i3c_bus_rpm_put(dev->bus); + return ret; } EXPORT_SYMBOL_GPL(i3c_device_enable_ibi); @@ -163,19 +195,27 @@ EXPORT_SYMBOL_GPL(i3c_device_enable_ibi); int i3c_device_request_ibi(struct i3c_device *dev, const struct i3c_ibi_setup *req) { - int ret = -ENOENT; + int ret; if (!req->handler || !req->num_slots) return -EINVAL; + ret = i3c_bus_rpm_get(dev->bus); + if (ret) + return ret; + i3c_bus_normaluse_lock(dev->bus); if (dev->desc) { mutex_lock(&dev->desc->ibi_lock); ret = i3c_dev_request_ibi_locked(dev->desc, req); mutex_unlock(&dev->desc->ibi_lock); + } else { + ret = -ENOENT; } i3c_bus_normaluse_unlock(dev->bus); + i3c_bus_rpm_put(dev->bus); + return ret; } EXPORT_SYMBOL_GPL(i3c_device_request_ibi); diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h index f609e5098137..0f1f3f766623 100644 --- a/drivers/i3c/internals.h +++ b/drivers/i3c/internals.h @@ -11,6 +11,10 @@ #include #include +int __must_check i3c_bus_rpm_get(struct i3c_bus *bus); +void i3c_bus_rpm_put(struct i3c_bus *bus); +bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus); + void i3c_bus_normaluse_lock(struct i3c_bus *bus); void i3c_bus_normaluse_unlock(struct i3c_bus *bus); diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 71583cc4d197..80dda0e85558 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -106,6 +106,38 @@ static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev) return container_of(dev, struct i3c_master_controller, dev); } +static int __must_check i3c_master_rpm_get(struct i3c_master_controller *master) +{ + int ret = master->rpm_allowed ? pm_runtime_resume_and_get(master->dev.parent) : 0; + + if (ret < 0) { + dev_err(master->dev.parent, "runtime resume failed, error %d\n", ret); + return ret; + } + return 0; +} + +static void i3c_master_rpm_put(struct i3c_master_controller *master) +{ + if (master->rpm_allowed) + pm_runtime_put_autosuspend(master->dev.parent); +} + +int i3c_bus_rpm_get(struct i3c_bus *bus) +{ + return i3c_master_rpm_get(i3c_bus_to_i3c_master(bus)); +} + +void i3c_bus_rpm_put(struct i3c_bus *bus) +{ + i3c_master_rpm_put(i3c_bus_to_i3c_master(bus)); +} + +bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus) +{ + return i3c_bus_to_i3c_master(bus)->rpm_ibi_allowed; +} + static const struct device_type i3c_device_type; static struct i3c_bus *dev_to_i3cbus(struct device *dev) @@ -611,6 +643,12 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) if (!master->ops->enable_hotjoin || !master->ops->disable_hotjoin) return -EINVAL; + if (enable || master->rpm_ibi_allowed) { + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + } + i3c_bus_normaluse_lock(&master->bus); if (enable) @@ -623,6 +661,9 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) i3c_bus_normaluse_unlock(&master->bus); + if ((enable && ret) || (!enable && !ret) || master->rpm_ibi_allowed) + i3c_master_rpm_put(master); + return ret; } @@ -1745,18 +1786,23 @@ int i3c_master_do_daa(struct i3c_master_controller *master) { int ret; + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + i3c_bus_maintenance_lock(&master->bus); ret = master->ops->do_daa(master); i3c_bus_maintenance_unlock(&master->bus); if (ret) - return ret; + goto out; i3c_bus_normaluse_lock(&master->bus); i3c_master_register_new_i3c_devs(master); i3c_bus_normaluse_unlock(&master->bus); - - return 0; +out: + i3c_master_rpm_put(master); + return ret; } EXPORT_SYMBOL_GPL(i3c_master_do_daa); @@ -2098,8 +2144,17 @@ err_detach_devs: static void i3c_master_bus_cleanup(struct i3c_master_controller *master) { - if (master->ops->bus_cleanup) - master->ops->bus_cleanup(master); + if (master->ops->bus_cleanup) { + int ret = i3c_master_rpm_get(master); + + if (ret) { + dev_err(&master->dev, + "runtime resume error: master bus_cleanup() not done\n"); + } else { + master->ops->bus_cleanup(master); + i3c_master_rpm_put(master); + } + } i3c_master_detach_free_devs(master); } @@ -2451,6 +2506,10 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap, return -EOPNOTSUPP; } + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + i3c_bus_normaluse_lock(&master->bus); dev = i3c_master_find_i2c_dev_by_addr(master, addr); if (!dev) @@ -2459,6 +2518,8 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap, ret = master->ops->i2c_xfers(dev, xfers, nxfers); i3c_bus_normaluse_unlock(&master->bus); + i3c_master_rpm_put(master); + return ret ? ret : nxfers; } @@ -2561,6 +2622,10 @@ static int i3c_i2c_notifier_call(struct notifier_block *nb, unsigned long action master = i2c_adapter_to_i3c_master(adap); + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + i3c_bus_maintenance_lock(&master->bus); switch (action) { case BUS_NOTIFY_ADD_DEVICE: @@ -2574,6 +2639,8 @@ static int i3c_i2c_notifier_call(struct notifier_block *nb, unsigned long action } i3c_bus_maintenance_unlock(&master->bus); + i3c_master_rpm_put(master); + return ret; } @@ -2911,6 +2978,10 @@ int i3c_master_register(struct i3c_master_controller *master, INIT_LIST_HEAD(&master->boardinfo.i2c); INIT_LIST_HEAD(&master->boardinfo.i3c); + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + device_initialize(&master->dev); master->dev.dma_mask = parent->dma_mask; @@ -2994,6 +3065,8 @@ int i3c_master_register(struct i3c_master_controller *master, if (master->ops->set_dev_nack_retry) device_create_file(&master->dev, &dev_attr_dev_nack_retry_count); + i3c_master_rpm_put(master); + return 0; err_del_dev: @@ -3003,6 +3076,7 @@ err_cleanup_bus: i3c_master_bus_cleanup(master); err_put_dev: + i3c_master_rpm_put(master); put_device(&master->dev); return ret; @@ -3151,8 +3225,15 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev) return; if (dev->ibi->enabled) { + int ret; + dev_err(&master->dev, "Freeing IBI that is still enabled\n"); - if (i3c_dev_disable_ibi_locked(dev)) + ret = i3c_master_rpm_get(master); + if (!ret) { + ret = i3c_dev_disable_ibi_locked(dev); + i3c_master_rpm_put(master); + } + if (ret) dev_err(&master->dev, "Failed to disable IBI before freeing\n"); } diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index d231c4fbc58b..38a821395426 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -509,6 +509,8 @@ struct i3c_master_controller_ops { * @secondary: true if the master is a secondary master * @init_done: true when the bus initialization is done * @hotjoin: true if the master support hotjoin + * @rpm_allowed: true if Runtime PM allowed + * @rpm_ibi_allowed: true if IBI and Hot-Join allowed while runtime suspended * @boardinfo.i3c: list of I3C boardinfo objects * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus @@ -533,6 +535,8 @@ struct i3c_master_controller { unsigned int secondary : 1; unsigned int init_done : 1; unsigned int hotjoin: 1; + unsigned int rpm_allowed: 1; + unsigned int rpm_ibi_allowed: 1; struct { struct list_head i3c; struct list_head i2c;