@@ -167,6 +167,115 @@ struct device *dev_pm_domain_attach_by_name(struct device *dev,
167
167
}
168
168
EXPORT_SYMBOL_GPL (dev_pm_domain_attach_by_name );
169
169
170
+ /**
171
+ * dev_pm_domain_attach_list - Associate a device with its PM domains.
172
+ * @dev: The device used to lookup the PM domains for.
173
+ * @data: The data used for attaching to the PM domains.
174
+ * @list: An out-parameter with an allocated list of attached PM domains.
175
+ *
176
+ * This function helps to attach a device to its multiple PM domains. The
177
+ * caller, which is typically a driver's probe function, may provide a list of
178
+ * names for the PM domains that we should try to attach the device to, but it
179
+ * may also provide an empty list, in case the attach should be done for all of
180
+ * the available PM domains.
181
+ *
182
+ * Callers must ensure proper synchronization of this function with power
183
+ * management callbacks.
184
+ *
185
+ * Returns the number of attached PM domains or a negative error code in case of
186
+ * a failure. Note that, to detach the list of PM domains, the driver shall call
187
+ * dev_pm_domain_detach_list(), typically during the remove phase.
188
+ */
189
+ int dev_pm_domain_attach_list (struct device * dev ,
190
+ const struct dev_pm_domain_attach_data * data ,
191
+ struct dev_pm_domain_list * * list )
192
+ {
193
+ struct device_node * np = dev -> of_node ;
194
+ struct dev_pm_domain_list * pds ;
195
+ struct device * pd_dev = NULL ;
196
+ int ret , i , num_pds = 0 ;
197
+ bool by_id = true;
198
+ u32 pd_flags = data ? data -> pd_flags : 0 ;
199
+ u32 link_flags = pd_flags & PD_FLAG_NO_DEV_LINK ? 0 :
200
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME ;
201
+
202
+ if (dev -> pm_domain )
203
+ return - EEXIST ;
204
+
205
+ /* For now this is limited to OF based platforms. */
206
+ if (!np )
207
+ return 0 ;
208
+
209
+ if (data && data -> pd_names ) {
210
+ num_pds = data -> num_pd_names ;
211
+ by_id = false;
212
+ } else {
213
+ num_pds = of_count_phandle_with_args (np , "power-domains" ,
214
+ "#power-domain-cells" );
215
+ }
216
+
217
+ if (num_pds <= 0 )
218
+ return 0 ;
219
+
220
+ pds = devm_kzalloc (dev , sizeof (* pds ), GFP_KERNEL );
221
+ if (!pds )
222
+ return - ENOMEM ;
223
+
224
+ pds -> pd_devs = devm_kcalloc (dev , num_pds , sizeof (* pds -> pd_devs ),
225
+ GFP_KERNEL );
226
+ if (!pds -> pd_devs )
227
+ return - ENOMEM ;
228
+
229
+ pds -> pd_links = devm_kcalloc (dev , num_pds , sizeof (* pds -> pd_links ),
230
+ GFP_KERNEL );
231
+ if (!pds -> pd_links )
232
+ return - ENOMEM ;
233
+
234
+ if (link_flags && pd_flags & PD_FLAG_DEV_LINK_ON )
235
+ link_flags |= DL_FLAG_RPM_ACTIVE ;
236
+
237
+ for (i = 0 ; i < num_pds ; i ++ ) {
238
+ if (by_id )
239
+ pd_dev = dev_pm_domain_attach_by_id (dev , i );
240
+ else
241
+ pd_dev = dev_pm_domain_attach_by_name (dev ,
242
+ data -> pd_names [i ]);
243
+ if (IS_ERR_OR_NULL (pd_dev )) {
244
+ ret = pd_dev ? PTR_ERR (pd_dev ) : - ENODEV ;
245
+ goto err_attach ;
246
+ }
247
+
248
+ if (link_flags ) {
249
+ struct device_link * link ;
250
+
251
+ link = device_link_add (dev , pd_dev , link_flags );
252
+ if (!link ) {
253
+ ret = - ENODEV ;
254
+ goto err_link ;
255
+ }
256
+
257
+ pds -> pd_links [i ] = link ;
258
+ }
259
+
260
+ pds -> pd_devs [i ] = pd_dev ;
261
+ }
262
+
263
+ pds -> num_pds = num_pds ;
264
+ * list = pds ;
265
+ return num_pds ;
266
+
267
+ err_link :
268
+ dev_pm_domain_detach (pd_dev , true);
269
+ err_attach :
270
+ while (-- i >= 0 ) {
271
+ if (pds -> pd_links [i ])
272
+ device_link_del (pds -> pd_links [i ]);
273
+ dev_pm_domain_detach (pds -> pd_devs [i ], true);
274
+ }
275
+ return ret ;
276
+ }
277
+ EXPORT_SYMBOL_GPL (dev_pm_domain_attach_list );
278
+
170
279
/**
171
280
* dev_pm_domain_detach - Detach a device from its PM domain.
172
281
* @dev: Device to detach.
@@ -187,6 +296,31 @@ void dev_pm_domain_detach(struct device *dev, bool power_off)
187
296
}
188
297
EXPORT_SYMBOL_GPL (dev_pm_domain_detach );
189
298
299
+ /**
300
+ * dev_pm_domain_detach_list - Detach a list of PM domains.
301
+ * @list: The list of PM domains to detach.
302
+ *
303
+ * This function reverse the actions from dev_pm_domain_attach_list().
304
+ * Typically it should be invoked during the remove phase from drivers.
305
+ *
306
+ * Callers must ensure proper synchronization of this function with power
307
+ * management callbacks.
308
+ */
309
+ void dev_pm_domain_detach_list (struct dev_pm_domain_list * list )
310
+ {
311
+ int i ;
312
+
313
+ if (!list )
314
+ return ;
315
+
316
+ for (i = 0 ; i < list -> num_pds ; i ++ ) {
317
+ if (list -> pd_links [i ])
318
+ device_link_del (list -> pd_links [i ]);
319
+ dev_pm_domain_detach (list -> pd_devs [i ], true);
320
+ }
321
+ }
322
+ EXPORT_SYMBOL_GPL (dev_pm_domain_detach_list );
323
+
190
324
/**
191
325
* dev_pm_domain_start - Start the device through its PM domain.
192
326
* @dev: Device to start.
0 commit comments