Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

i3c: master: Introduce optional Runtime PM support

Master drivers currently manage Runtime PM individually, but all require
runtime resume for bus operations. This can be centralized in common code.

Add optional Runtime PM support to ensure the parent device is runtime
resumed before bus operations and auto-suspended afterward.

Notably, do not call ->bus_cleanup() if runtime resume fails. Master
drivers that opt-in to core runtime PM support must take that into account.

Also provide an option to allow IBIs and hot-joins while runtime suspended.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260113072702.16268-20-adrian.hunter@intel.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Adrian Hunter and committed by
Alexandre Belloni
990c149c 3c3de680

+138 -9
+43 -3
drivers/i3c/device.c
··· 46 46 return -EINVAL; 47 47 } 48 48 49 + ret = i3c_bus_rpm_get(dev->bus); 50 + if (ret) 51 + return ret; 52 + 49 53 i3c_bus_normaluse_lock(dev->bus); 50 54 ret = i3c_dev_do_xfers_locked(dev->desc, xfers, nxfers, mode); 51 55 i3c_bus_normaluse_unlock(dev->bus); 56 + 57 + i3c_bus_rpm_put(dev->bus); 52 58 53 59 return ret; 54 60 } ··· 72 66 { 73 67 int ret; 74 68 69 + ret = i3c_bus_rpm_get(dev->bus); 70 + if (ret) 71 + return ret; 72 + 75 73 i3c_bus_normaluse_lock(dev->bus); 76 74 ret = i3c_dev_setdasa_locked(dev->desc); 77 75 i3c_bus_normaluse_unlock(dev->bus); 76 + 77 + i3c_bus_rpm_put(dev->bus); 78 78 79 79 return ret; 80 80 } ··· 118 106 */ 119 107 int i3c_device_disable_ibi(struct i3c_device *dev) 120 108 { 121 - int ret = -ENOENT; 109 + int ret; 110 + 111 + if (i3c_bus_rpm_ibi_allowed(dev->bus)) { 112 + ret = i3c_bus_rpm_get(dev->bus); 113 + if (ret) 114 + return ret; 115 + } 122 116 123 117 i3c_bus_normaluse_lock(dev->bus); 124 118 if (dev->desc) { 125 119 mutex_lock(&dev->desc->ibi_lock); 126 120 ret = i3c_dev_disable_ibi_locked(dev->desc); 127 121 mutex_unlock(&dev->desc->ibi_lock); 122 + } else { 123 + ret = -ENOENT; 128 124 } 129 125 i3c_bus_normaluse_unlock(dev->bus); 126 + 127 + if (!ret || i3c_bus_rpm_ibi_allowed(dev->bus)) 128 + i3c_bus_rpm_put(dev->bus); 130 129 131 130 return ret; 132 131 } ··· 158 135 */ 159 136 int i3c_device_enable_ibi(struct i3c_device *dev) 160 137 { 161 - int ret = -ENOENT; 138 + int ret; 139 + 140 + ret = i3c_bus_rpm_get(dev->bus); 141 + if (ret) 142 + return ret; 162 143 163 144 i3c_bus_normaluse_lock(dev->bus); 164 145 if (dev->desc) { 165 146 mutex_lock(&dev->desc->ibi_lock); 166 147 ret = i3c_dev_enable_ibi_locked(dev->desc); 167 148 mutex_unlock(&dev->desc->ibi_lock); 149 + } else { 150 + ret = -ENOENT; 168 151 } 169 152 i3c_bus_normaluse_unlock(dev->bus); 153 + 154 + if (ret || i3c_bus_rpm_ibi_allowed(dev->bus)) 155 + i3c_bus_rpm_put(dev->bus); 170 156 171 157 return ret; 172 158 } ··· 195 163 int i3c_device_request_ibi(struct i3c_device *dev, 196 164 const struct i3c_ibi_setup *req) 197 165 { 198 - int ret = -ENOENT; 166 + int ret; 199 167 200 168 if (!req->handler || !req->num_slots) 201 169 return -EINVAL; 170 + 171 + ret = i3c_bus_rpm_get(dev->bus); 172 + if (ret) 173 + return ret; 202 174 203 175 i3c_bus_normaluse_lock(dev->bus); 204 176 if (dev->desc) { 205 177 mutex_lock(&dev->desc->ibi_lock); 206 178 ret = i3c_dev_request_ibi_locked(dev->desc, req); 207 179 mutex_unlock(&dev->desc->ibi_lock); 180 + } else { 181 + ret = -ENOENT; 208 182 } 209 183 i3c_bus_normaluse_unlock(dev->bus); 184 + 185 + i3c_bus_rpm_put(dev->bus); 210 186 211 187 return ret; 212 188 }
+4
drivers/i3c/internals.h
··· 11 11 #include <linux/i3c/master.h> 12 12 #include <linux/io.h> 13 13 14 + int __must_check i3c_bus_rpm_get(struct i3c_bus *bus); 15 + void i3c_bus_rpm_put(struct i3c_bus *bus); 16 + bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus); 17 + 14 18 void i3c_bus_normaluse_lock(struct i3c_bus *bus); 15 19 void i3c_bus_normaluse_unlock(struct i3c_bus *bus); 16 20
+87 -6
drivers/i3c/master.c
··· 106 106 return container_of(dev, struct i3c_master_controller, dev); 107 107 } 108 108 109 + static int __must_check i3c_master_rpm_get(struct i3c_master_controller *master) 110 + { 111 + int ret = master->rpm_allowed ? pm_runtime_resume_and_get(master->dev.parent) : 0; 112 + 113 + if (ret < 0) { 114 + dev_err(master->dev.parent, "runtime resume failed, error %d\n", ret); 115 + return ret; 116 + } 117 + return 0; 118 + } 119 + 120 + static void i3c_master_rpm_put(struct i3c_master_controller *master) 121 + { 122 + if (master->rpm_allowed) 123 + pm_runtime_put_autosuspend(master->dev.parent); 124 + } 125 + 126 + int i3c_bus_rpm_get(struct i3c_bus *bus) 127 + { 128 + return i3c_master_rpm_get(i3c_bus_to_i3c_master(bus)); 129 + } 130 + 131 + void i3c_bus_rpm_put(struct i3c_bus *bus) 132 + { 133 + i3c_master_rpm_put(i3c_bus_to_i3c_master(bus)); 134 + } 135 + 136 + bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus) 137 + { 138 + return i3c_bus_to_i3c_master(bus)->rpm_ibi_allowed; 139 + } 140 + 109 141 static const struct device_type i3c_device_type; 110 142 111 143 static struct i3c_bus *dev_to_i3cbus(struct device *dev) ··· 643 611 if (!master->ops->enable_hotjoin || !master->ops->disable_hotjoin) 644 612 return -EINVAL; 645 613 614 + if (enable || master->rpm_ibi_allowed) { 615 + ret = i3c_master_rpm_get(master); 616 + if (ret) 617 + return ret; 618 + } 619 + 646 620 i3c_bus_normaluse_lock(&master->bus); 647 621 648 622 if (enable) ··· 660 622 master->hotjoin = enable; 661 623 662 624 i3c_bus_normaluse_unlock(&master->bus); 625 + 626 + if ((enable && ret) || (!enable && !ret) || master->rpm_ibi_allowed) 627 + i3c_master_rpm_put(master); 663 628 664 629 return ret; 665 630 } ··· 1786 1745 { 1787 1746 int ret; 1788 1747 1748 + ret = i3c_master_rpm_get(master); 1749 + if (ret) 1750 + return ret; 1751 + 1789 1752 i3c_bus_maintenance_lock(&master->bus); 1790 1753 ret = master->ops->do_daa(master); 1791 1754 i3c_bus_maintenance_unlock(&master->bus); 1792 1755 1793 1756 if (ret) 1794 - return ret; 1757 + goto out; 1795 1758 1796 1759 i3c_bus_normaluse_lock(&master->bus); 1797 1760 i3c_master_register_new_i3c_devs(master); 1798 1761 i3c_bus_normaluse_unlock(&master->bus); 1799 - 1800 - return 0; 1762 + out: 1763 + i3c_master_rpm_put(master); 1764 + return ret; 1801 1765 } 1802 1766 EXPORT_SYMBOL_GPL(i3c_master_do_daa); 1803 1767 ··· 2144 2098 2145 2099 static void i3c_master_bus_cleanup(struct i3c_master_controller *master) 2146 2100 { 2147 - if (master->ops->bus_cleanup) 2148 - master->ops->bus_cleanup(master); 2101 + if (master->ops->bus_cleanup) { 2102 + int ret = i3c_master_rpm_get(master); 2103 + 2104 + if (ret) { 2105 + dev_err(&master->dev, 2106 + "runtime resume error: master bus_cleanup() not done\n"); 2107 + } else { 2108 + master->ops->bus_cleanup(master); 2109 + i3c_master_rpm_put(master); 2110 + } 2111 + } 2149 2112 2150 2113 i3c_master_detach_free_devs(master); 2151 2114 } ··· 2506 2451 return -EOPNOTSUPP; 2507 2452 } 2508 2453 2454 + ret = i3c_master_rpm_get(master); 2455 + if (ret) 2456 + return ret; 2457 + 2509 2458 i3c_bus_normaluse_lock(&master->bus); 2510 2459 dev = i3c_master_find_i2c_dev_by_addr(master, addr); 2511 2460 if (!dev) ··· 2517 2458 else 2518 2459 ret = master->ops->i2c_xfers(dev, xfers, nxfers); 2519 2460 i3c_bus_normaluse_unlock(&master->bus); 2461 + 2462 + i3c_master_rpm_put(master); 2520 2463 2521 2464 return ret ? ret : nxfers; 2522 2465 } ··· 2622 2561 2623 2562 master = i2c_adapter_to_i3c_master(adap); 2624 2563 2564 + ret = i3c_master_rpm_get(master); 2565 + if (ret) 2566 + return ret; 2567 + 2625 2568 i3c_bus_maintenance_lock(&master->bus); 2626 2569 switch (action) { 2627 2570 case BUS_NOTIFY_ADD_DEVICE: ··· 2638 2573 ret = -EINVAL; 2639 2574 } 2640 2575 i3c_bus_maintenance_unlock(&master->bus); 2576 + 2577 + i3c_master_rpm_put(master); 2641 2578 2642 2579 return ret; 2643 2580 } ··· 2978 2911 INIT_LIST_HEAD(&master->boardinfo.i2c); 2979 2912 INIT_LIST_HEAD(&master->boardinfo.i3c); 2980 2913 2914 + ret = i3c_master_rpm_get(master); 2915 + if (ret) 2916 + return ret; 2917 + 2981 2918 device_initialize(&master->dev); 2982 2919 2983 2920 master->dev.dma_mask = parent->dma_mask; ··· 3065 2994 if (master->ops->set_dev_nack_retry) 3066 2995 device_create_file(&master->dev, &dev_attr_dev_nack_retry_count); 3067 2996 2997 + i3c_master_rpm_put(master); 2998 + 3068 2999 return 0; 3069 3000 3070 3001 err_del_dev: ··· 3076 3003 i3c_master_bus_cleanup(master); 3077 3004 3078 3005 err_put_dev: 3006 + i3c_master_rpm_put(master); 3079 3007 put_device(&master->dev); 3080 3008 3081 3009 return ret; ··· 3225 3151 return; 3226 3152 3227 3153 if (dev->ibi->enabled) { 3154 + int ret; 3155 + 3228 3156 dev_err(&master->dev, "Freeing IBI that is still enabled\n"); 3229 - if (i3c_dev_disable_ibi_locked(dev)) 3157 + ret = i3c_master_rpm_get(master); 3158 + if (!ret) { 3159 + ret = i3c_dev_disable_ibi_locked(dev); 3160 + i3c_master_rpm_put(master); 3161 + } 3162 + if (ret) 3230 3163 dev_err(&master->dev, "Failed to disable IBI before freeing\n"); 3231 3164 } 3232 3165
+4
include/linux/i3c/master.h
··· 509 509 * @secondary: true if the master is a secondary master 510 510 * @init_done: true when the bus initialization is done 511 511 * @hotjoin: true if the master support hotjoin 512 + * @rpm_allowed: true if Runtime PM allowed 513 + * @rpm_ibi_allowed: true if IBI and Hot-Join allowed while runtime suspended 512 514 * @boardinfo.i3c: list of I3C boardinfo objects 513 515 * @boardinfo.i2c: list of I2C boardinfo objects 514 516 * @boardinfo: board-level information attached to devices connected on the bus ··· 535 533 unsigned int secondary : 1; 536 534 unsigned int init_done : 1; 537 535 unsigned int hotjoin: 1; 536 + unsigned int rpm_allowed: 1; 537 + unsigned int rpm_ibi_allowed: 1; 538 538 struct { 539 539 struct list_head i3c; 540 540 struct list_head i2c;