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

greybus: gbphy: add gbphy runtime pm support

Since GBphy is a child of the Bundle device driver, for those runtime pm
settings that are common to all the protocol drivers need to go in to
the GBphy bus driver.

Testing Done:
- Check gbphy driver can be autosuspended

Signed-off-by: David Lin <dtwlin@google.com>
Signed-off-by: Axel Haslam <ahaslam@baylibre.com>
Reviewed-by: Johan Hovold <johan@hovoldconsulting.com>
Signed-off-by: Alex Elder <elder@linaro.org>

authored by

David Lin and committed by
Alex Elder
af5dc7f8 211634f2

+98 -1
+58 -1
drivers/staging/greybus/gbphy.c
··· 19 19 #include "greybus.h" 20 20 #include "gbphy.h" 21 21 22 + #define GB_GBPHY_AUTOSUSPEND_MS 3000 23 + 22 24 struct gbphy_host { 23 25 struct gb_bundle *bundle; 24 26 struct list_head devices; ··· 52 50 kfree(gbphy_dev); 53 51 } 54 52 53 + #ifdef CONFIG_PM_RUNTIME 54 + static int gb_gbphy_idle(struct device *dev) 55 + { 56 + pm_runtime_mark_last_busy(dev); 57 + pm_request_autosuspend(dev); 58 + return 0; 59 + } 60 + #endif 61 + 62 + static const struct dev_pm_ops gb_gbphy_pm_ops = { 63 + SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, 64 + pm_generic_runtime_resume, 65 + gb_gbphy_idle) 66 + }; 67 + 55 68 static struct device_type greybus_gbphy_dev_type = { 56 69 .name = "gbphy_device", 57 70 .release = gbphy_dev_release, 71 + .pm = &gb_gbphy_pm_ops, 58 72 }; 59 73 60 74 static int gbphy_dev_uevent(struct device *dev, struct kobj_uevent_env *env) ··· 136 118 struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver); 137 119 struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); 138 120 const struct gbphy_device_id *id; 121 + int ret; 139 122 140 123 id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); 141 124 if (!id) 142 125 return -ENODEV; 143 126 144 - return gbphy_drv->probe(gbphy_dev, id); 127 + /* for old kernels we need get_sync to resume parent devices */ 128 + ret = gb_pm_runtime_get_sync(gbphy_dev->bundle); 129 + if (ret < 0) 130 + return ret; 131 + 132 + pm_runtime_set_autosuspend_delay(dev, GB_GBPHY_AUTOSUSPEND_MS); 133 + pm_runtime_use_autosuspend(dev); 134 + pm_runtime_get_noresume(dev); 135 + pm_runtime_set_active(dev); 136 + pm_runtime_enable(dev); 137 + 138 + /* 139 + * Drivers should call put on the gbphy dev before returning 140 + * from probe if they support runtime pm. 141 + */ 142 + ret = gbphy_drv->probe(gbphy_dev, id); 143 + if (ret) { 144 + pm_runtime_disable(dev); 145 + pm_runtime_set_suspended(dev); 146 + pm_runtime_put_noidle(dev); 147 + pm_runtime_dont_use_autosuspend(dev); 148 + } 149 + 150 + gb_pm_runtime_put_autosuspend(gbphy_dev->bundle); 151 + 152 + return ret; 145 153 } 146 154 147 155 static int gbphy_dev_remove(struct device *dev) ··· 176 132 struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); 177 133 178 134 gbphy_drv->remove(gbphy_dev); 135 + 136 + pm_runtime_disable(dev); 137 + pm_runtime_set_suspended(dev); 138 + pm_runtime_put_noidle(dev); 139 + pm_runtime_dont_use_autosuspend(dev); 140 + 179 141 return 0; 180 142 } 181 143 ··· 261 211 { 262 212 struct gbphy_host *gbphy_host = greybus_get_drvdata(bundle); 263 213 struct gbphy_device *gbphy_dev, *temp; 214 + int ret; 215 + 216 + ret = gb_pm_runtime_get_sync(bundle); 217 + if (ret < 0) 218 + gb_pm_runtime_get_noresume(bundle); 264 219 265 220 list_for_each_entry_safe(gbphy_dev, temp, &gbphy_host->devices, list) { 266 221 list_del(&gbphy_dev->list); ··· 305 250 } 306 251 list_add(&gbphy_dev->list, &gbphy_host->devices); 307 252 } 253 + 254 + gb_pm_runtime_put_autosuspend(bundle); 308 255 309 256 return 0; 310 257 }
+40
drivers/staging/greybus/gbphy.h
··· 66 66 #define module_gbphy_driver(__gbphy_driver) \ 67 67 module_driver(__gbphy_driver, gb_gbphy_register, gb_gbphy_deregister) 68 68 69 + #ifdef CONFIG_PM_RUNTIME 70 + static inline int gbphy_runtime_get_sync(struct gbphy_device *gbphy_dev) 71 + { 72 + struct device *dev = &gbphy_dev->dev; 73 + int ret; 74 + 75 + ret = pm_runtime_get_sync(dev); 76 + if (ret < 0) { 77 + dev_err(dev, "pm_runtime_get_sync failed: %d\n", ret); 78 + pm_runtime_put_noidle(dev); 79 + return ret; 80 + } 81 + 82 + return 0; 83 + } 84 + 85 + static inline void gbphy_runtime_put_autosuspend(struct gbphy_device *gbphy_dev) 86 + { 87 + struct device *dev = &gbphy_dev->dev; 88 + 89 + pm_runtime_mark_last_busy(dev); 90 + pm_runtime_put_autosuspend(dev); 91 + } 92 + 93 + static inline void gbphy_runtime_get_noresume(struct gbphy_device *gbphy_dev) 94 + { 95 + pm_runtime_get_noresume(&gbphy_dev->dev); 96 + } 97 + 98 + static inline void gbphy_runtime_put_noidle(struct gbphy_device *gbphy_dev) 99 + { 100 + pm_runtime_put_noidle(&gbphy_dev->dev); 101 + } 102 + #else 103 + static inline int gbphy_runtime_get_sync(struct device *dev) { return 0; } 104 + static inline void gbphy_runtime_put_autosuspend(struct device *dev) {} 105 + static inline void gbphy_runtime_get_noresume(struct gbphy_device *gbphy_dev) {} 106 + static inline void gbphy_runtime_put_noidle(struct gbphy_device *gbphy_dev) {} 107 + #endif 108 + 69 109 #endif /* __GBPHY_H */ 70 110