@@ -30,9 +30,27 @@ struct zynqmp_pm_work_struct {
30
30
u32 args [CB_ARG_CNT ];
31
31
};
32
32
33
- static struct zynqmp_pm_work_struct * zynqmp_pm_init_suspend_work ;
33
+ /**
34
+ * struct zynqmp_pm_event_info - event related information
35
+ * @cb_fun: Function pointer to store the callback function.
36
+ * @cb_type: Type of callback from pm_api_cb_id,
37
+ * PM_NOTIFY_CB - for Error Events,
38
+ * PM_INIT_SUSPEND_CB - for suspend callback.
39
+ * @node_id: Node-Id related to event.
40
+ * @event: Event Mask for the Error Event.
41
+ * @wake: Flag specifying whether the subsystem should be woken upon
42
+ * event notification.
43
+ */
44
+ struct zynqmp_pm_event_info {
45
+ event_cb_func_t cb_fun ;
46
+ enum pm_api_cb_id cb_type ;
47
+ u32 node_id ;
48
+ u32 event ;
49
+ bool wake ;
50
+ };
51
+
52
+ static struct zynqmp_pm_work_struct * zynqmp_pm_init_suspend_work , * zynqmp_pm_init_restart_work ;
34
53
static struct mbox_chan * rx_chan ;
35
- static bool event_registered ;
36
54
37
55
enum pm_suspend_mode {
38
56
PM_SUSPEND_MODE_FIRST = 0 ,
@@ -54,6 +72,19 @@ static void zynqmp_pm_get_callback_data(u32 *buf)
54
72
zynqmp_pm_invoke_fn (GET_CALLBACK_DATA , buf , 0 );
55
73
}
56
74
75
+ static void subsystem_restart_event_callback (const u32 * payload , void * data )
76
+ {
77
+ /* First element is callback API ID, others are callback arguments */
78
+ if (work_pending (& zynqmp_pm_init_restart_work -> callback_work ))
79
+ return ;
80
+
81
+ /* Copy callback arguments into work's structure */
82
+ memcpy (zynqmp_pm_init_restart_work -> args , & payload [0 ],
83
+ sizeof (zynqmp_pm_init_restart_work -> args ));
84
+
85
+ queue_work (system_unbound_wq , & zynqmp_pm_init_restart_work -> callback_work );
86
+ }
87
+
57
88
static void suspend_event_callback (const u32 * payload , void * data )
58
89
{
59
90
/* First element is callback API ID, others are callback arguments */
@@ -119,6 +150,37 @@ static void ipi_receive_callback(struct mbox_client *cl, void *data)
119
150
}
120
151
}
121
152
153
+ /**
154
+ * zynqmp_pm_subsystem_restart_work_fn - Initiate Subsystem restart
155
+ * @work: Pointer to work_struct
156
+ *
157
+ * Bottom-half of PM callback IRQ handler.
158
+ */
159
+ static void zynqmp_pm_subsystem_restart_work_fn (struct work_struct * work )
160
+ {
161
+ int ret ;
162
+ struct zynqmp_pm_work_struct * pm_work = container_of (work , struct zynqmp_pm_work_struct ,
163
+ callback_work );
164
+
165
+ /* First element is callback API ID, others are callback arguments */
166
+ if (pm_work -> args [0 ] == PM_NOTIFY_CB ) {
167
+ if (pm_work -> args [2 ] == EVENT_SUBSYSTEM_RESTART ) {
168
+ ret = zynqmp_pm_system_shutdown (ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY ,
169
+ ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM );
170
+ if (ret ) {
171
+ pr_err ("unable to set shutdown scope\n" );
172
+ return ;
173
+ }
174
+
175
+ kernel_restart (NULL );
176
+ } else {
177
+ pr_err ("%s Unsupported Event - %d\n" , __func__ , pm_work -> args [2 ]);
178
+ }
179
+ } else {
180
+ pr_err ("%s() Unsupported Callback %d\n" , __func__ , pm_work -> args [0 ]);
181
+ }
182
+ }
183
+
122
184
/**
123
185
* zynqmp_pm_init_suspend_work_fn - Initialize suspend
124
186
* @work: Pointer to work_struct
@@ -184,13 +246,51 @@ static ssize_t suspend_mode_store(struct device *dev,
184
246
185
247
static DEVICE_ATTR_RW (suspend_mode );
186
248
249
+ static void unregister_event (struct device * dev , void * res )
250
+ {
251
+ struct zynqmp_pm_event_info * event_info = res ;
252
+
253
+ xlnx_unregister_event (event_info -> cb_type , event_info -> node_id ,
254
+ event_info -> event , event_info -> cb_fun , NULL );
255
+ }
256
+
257
+ static int register_event (struct device * dev , const enum pm_api_cb_id cb_type , const u32 node_id ,
258
+ const u32 event , const bool wake , event_cb_func_t cb_fun )
259
+ {
260
+ int ret ;
261
+ struct zynqmp_pm_event_info * event_info ;
262
+
263
+ event_info = devres_alloc (unregister_event , sizeof (struct zynqmp_pm_event_info ),
264
+ GFP_KERNEL );
265
+ if (!event_info )
266
+ return - ENOMEM ;
267
+
268
+ event_info -> cb_type = cb_type ;
269
+ event_info -> node_id = node_id ;
270
+ event_info -> event = event ;
271
+ event_info -> wake = wake ;
272
+ event_info -> cb_fun = cb_fun ;
273
+
274
+ ret = xlnx_register_event (event_info -> cb_type , event_info -> node_id ,
275
+ event_info -> event , event_info -> wake , event_info -> cb_fun , NULL );
276
+ if (ret ) {
277
+ devres_free (event_info );
278
+ return ret ;
279
+ }
280
+
281
+ devres_add (dev , event_info );
282
+ return 0 ;
283
+ }
284
+
187
285
static int zynqmp_pm_probe (struct platform_device * pdev )
188
286
{
189
287
int ret , irq ;
190
- u32 pm_api_version ;
288
+ u32 pm_api_version , pm_family_code , pm_sub_family_code , node_id ;
191
289
struct mbox_client * client ;
192
290
193
- zynqmp_pm_get_api_version (& pm_api_version );
291
+ ret = zynqmp_pm_get_api_version (& pm_api_version );
292
+ if (ret )
293
+ return ret ;
194
294
195
295
/* Check PM API version number */
196
296
if (pm_api_version < ZYNQMP_PM_VERSION )
@@ -203,21 +303,43 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
203
303
* is not available to use) or -ENODEV(Xilinx Event Manager not compiled),
204
304
* then use ipi-mailbox or interrupt method.
205
305
*/
206
- ret = xlnx_register_event ( PM_INIT_SUSPEND_CB , 0 , 0 , false,
207
- suspend_event_callback , NULL );
306
+ ret = register_event ( & pdev -> dev , PM_INIT_SUSPEND_CB , 0 , 0 , false,
307
+ suspend_event_callback );
208
308
if (!ret ) {
209
309
zynqmp_pm_init_suspend_work = devm_kzalloc (& pdev -> dev ,
210
310
sizeof (struct zynqmp_pm_work_struct ),
211
311
GFP_KERNEL );
212
- if (!zynqmp_pm_init_suspend_work ) {
213
- xlnx_unregister_event (PM_INIT_SUSPEND_CB , 0 , 0 ,
214
- suspend_event_callback , NULL );
312
+ if (!zynqmp_pm_init_suspend_work )
215
313
return - ENOMEM ;
216
- }
217
- event_registered = true;
218
314
219
315
INIT_WORK (& zynqmp_pm_init_suspend_work -> callback_work ,
220
316
zynqmp_pm_init_suspend_work_fn );
317
+
318
+ ret = zynqmp_pm_get_family_info (& pm_family_code , & pm_sub_family_code );
319
+ if (ret < 0 )
320
+ return ret ;
321
+
322
+ if (pm_sub_family_code == VERSALNET_SUB_FAMILY_CODE )
323
+ node_id = PM_DEV_ACPU_0_0 ;
324
+ else
325
+ node_id = PM_DEV_ACPU_0 ;
326
+
327
+ ret = register_event (& pdev -> dev , PM_NOTIFY_CB , node_id , EVENT_SUBSYSTEM_RESTART ,
328
+ false, subsystem_restart_event_callback );
329
+ if (ret ) {
330
+ dev_err (& pdev -> dev , "Failed to Register with Xilinx Event manager %d\n" ,
331
+ ret );
332
+ return ret ;
333
+ }
334
+
335
+ zynqmp_pm_init_restart_work = devm_kzalloc (& pdev -> dev ,
336
+ sizeof (struct zynqmp_pm_work_struct ),
337
+ GFP_KERNEL );
338
+ if (!zynqmp_pm_init_restart_work )
339
+ return - ENOMEM ;
340
+
341
+ INIT_WORK (& zynqmp_pm_init_restart_work -> callback_work ,
342
+ zynqmp_pm_subsystem_restart_work_fn );
221
343
} else if (ret != - EACCES && ret != - ENODEV ) {
222
344
dev_err (& pdev -> dev , "Failed to Register with Xilinx Event manager %d\n" , ret );
223
345
return ret ;
@@ -264,24 +386,15 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
264
386
}
265
387
266
388
ret = sysfs_create_file (& pdev -> dev .kobj , & dev_attr_suspend_mode .attr );
267
- if (ret ) {
268
- if (event_registered ) {
269
- xlnx_unregister_event (PM_INIT_SUSPEND_CB , 0 , 0 , suspend_event_callback ,
270
- NULL );
271
- event_registered = false;
272
- }
273
- dev_err (& pdev -> dev , "unable to create sysfs interface\n" );
389
+ if (ret )
274
390
return ret ;
275
- }
276
391
277
392
return 0 ;
278
393
}
279
394
280
395
static void zynqmp_pm_remove (struct platform_device * pdev )
281
396
{
282
397
sysfs_remove_file (& pdev -> dev .kobj , & dev_attr_suspend_mode .attr );
283
- if (event_registered )
284
- xlnx_unregister_event (PM_INIT_SUSPEND_CB , 0 , 0 , suspend_event_callback , NULL );
285
398
286
399
if (!rx_chan )
287
400
mbox_free_channel (rx_chan );
0 commit comments