summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/i3c/device.c46
-rw-r--r--drivers/i3c/internals.h4
-rw-r--r--drivers/i3c/master.c93
-rw-r--r--include/linux/i3c/master.h4
4 files changed, 138 insertions, 9 deletions
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 <linux/i3c/master.h>
#include <linux/io.h>
+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;