@@ -55,13 +55,97 @@ struct arche_platform_drvdata {
55
55
56
56
enum svc_wakedetect_state wake_detect_state ;
57
57
int wake_detect_irq ;
58
- spinlock_t wake_lock ; /* Protect wake_detect_state */
58
+ spinlock_t wake_lock ; /* Protect wake_detect_state */
59
+ struct mutex platform_state_mutex ; /* Protect state */
59
60
unsigned long wake_detect_start ;
60
61
struct notifier_block pm_notifier ;
61
62
62
63
struct device * dev ;
63
64
};
64
65
66
+ /*
67
+ * arche_platform_change_state: Change the operational state
68
+ *
69
+ * This exported function allows external drivers to change the state
70
+ * of the arche-platform driver.
71
+ * Note that this function only supports transitions between two states
72
+ * with limited functionality.
73
+ *
74
+ * - ARCHE_PLATFORM_STATE_TIME_SYNC:
75
+ * Once set, allows timesync operations between SVC <=> AP and makes
76
+ * sure that arche-platform driver ignores any subsequent events/pulses
77
+ * from SVC over wake/detect.
78
+ *
79
+ * - ARCHE_PLATFORM_STATE_ACTIVE:
80
+ * Puts back driver to active state, where any pulse from SVC on wake/detect
81
+ * line would trigger either cold/standby boot.
82
+ * Note: Transition request from this function does not trigger cold/standby
83
+ * boot. It just puts back driver book keeping variable back to ACTIVE
84
+ * state and restores the interrupt.
85
+ *
86
+ * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently
87
+ * satisfy the requested state-transition or -EINVAL for all other
88
+ * state-transition requests.
89
+ */
90
+ int arche_platform_change_state (enum arche_platform_state state )
91
+ {
92
+ struct arche_platform_drvdata * arche_pdata ;
93
+ struct platform_device * pdev ;
94
+ struct device_node * np ;
95
+ int ret = - EAGAIN ;
96
+ unsigned long flags ;
97
+
98
+ np = of_find_compatible_node (NULL , NULL , "google,arche-platform" );
99
+ if (!np ) {
100
+ pr_err ("google,arche-platform device node not found\n" );
101
+ return - ENODEV ;
102
+ }
103
+
104
+ pdev = of_find_device_by_node (np );
105
+ if (!pdev ) {
106
+ pr_err ("arche-platform device not found\n" );
107
+ return - ENODEV ;
108
+ }
109
+
110
+ arche_pdata = platform_get_drvdata (pdev );
111
+
112
+ mutex_lock (& arche_pdata -> platform_state_mutex );
113
+ spin_lock_irqsave (& arche_pdata -> wake_lock , flags );
114
+ if (arche_pdata -> wake_detect_state != WD_STATE_IDLE ) {
115
+ dev_err (arche_pdata -> dev ,
116
+ "driver busy with wake/detect line ops\n" );
117
+ goto exit ;
118
+ }
119
+
120
+ if (arche_pdata -> state == state ) {
121
+ ret = 0 ;
122
+ goto exit ;
123
+ }
124
+
125
+ if (arche_pdata -> state == ARCHE_PLATFORM_STATE_OFF ||
126
+ arche_pdata -> state == ARCHE_PLATFORM_STATE_STANDBY ||
127
+ arche_pdata -> state == ARCHE_PLATFORM_STATE_FW_FLASHING ) {
128
+ dev_err (arche_pdata -> dev , "busy, request to retry later\n" );
129
+ goto exit ;
130
+ }
131
+
132
+ if (state == ARCHE_PLATFORM_STATE_TIME_SYNC ) {
133
+ disable_irq (arche_pdata -> wake_detect_irq );
134
+ arche_pdata -> state = ARCHE_PLATFORM_STATE_TIME_SYNC ;
135
+ } else if (state == ARCHE_PLATFORM_STATE_ACTIVE ) {
136
+ arche_pdata -> state = ARCHE_PLATFORM_STATE_ACTIVE ;
137
+ enable_irq (arche_pdata -> wake_detect_irq );
138
+ } else {
139
+ dev_err (arche_pdata -> dev , "invalid state transition request\n" );
140
+ }
141
+ exit :
142
+ spin_unlock_irqrestore (& arche_pdata -> wake_lock , flags );
143
+ mutex_unlock (& arche_pdata -> platform_state_mutex );
144
+ of_node_put (np );
145
+ return ret ;
146
+ }
147
+ EXPORT_SYMBOL_GPL (arche_platform_change_state );
148
+
65
149
static inline void svc_reset_onoff (unsigned int gpio , bool onoff )
66
150
{
67
151
gpio_set_value (gpio , onoff );
@@ -195,6 +279,9 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
195
279
return IRQ_HANDLED ;
196
280
}
197
281
282
+ /*
283
+ * Requires arche_pdata->platform_state_mutex to be held
284
+ */
198
285
static int arche_platform_coldboot_seq (struct arche_platform_drvdata * arche_pdata )
199
286
{
200
287
int ret ;
@@ -226,6 +313,9 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat
226
313
return 0 ;
227
314
}
228
315
316
+ /*
317
+ * Requires arche_pdata->platform_state_mutex to be held
318
+ */
229
319
static void arche_platform_fw_flashing_seq (struct arche_platform_drvdata * arche_pdata )
230
320
{
231
321
if (arche_pdata -> state == ARCHE_PLATFORM_STATE_FW_FLASHING )
@@ -246,6 +336,9 @@ static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_
246
336
247
337
}
248
338
339
+ /*
340
+ * Requires arche_pdata->platform_state_mutex to be held
341
+ */
249
342
static void arche_platform_poweroff_seq (struct arche_platform_drvdata * arche_pdata )
250
343
{
251
344
unsigned long flags ;
@@ -280,9 +373,11 @@ static ssize_t state_store(struct device *dev,
280
373
struct arche_platform_drvdata * arche_pdata = platform_get_drvdata (pdev );
281
374
int ret = 0 ;
282
375
376
+ mutex_lock (& arche_pdata -> platform_state_mutex );
377
+
283
378
if (sysfs_streq (buf , "off" )) {
284
379
if (arche_pdata -> state == ARCHE_PLATFORM_STATE_OFF )
285
- return count ;
380
+ goto exit ;
286
381
287
382
/* If SVC goes down, bring down APB's as well */
288
383
device_for_each_child (arche_pdata -> dev , NULL , apb_poweroff );
@@ -291,19 +386,19 @@ static ssize_t state_store(struct device *dev,
291
386
292
387
} else if (sysfs_streq (buf , "active" )) {
293
388
if (arche_pdata -> state == ARCHE_PLATFORM_STATE_ACTIVE )
294
- return count ;
389
+ goto exit ;
295
390
296
391
ret = arche_platform_coldboot_seq (arche_pdata );
297
392
298
393
assert_wakedetect (arche_pdata );
299
394
} else if (sysfs_streq (buf , "standby" )) {
300
395
if (arche_pdata -> state == ARCHE_PLATFORM_STATE_STANDBY )
301
- return count ;
396
+ goto exit ;
302
397
303
398
dev_warn (arche_pdata -> dev , "standby state not supported\n" );
304
399
} else if (sysfs_streq (buf , "fw_flashing" )) {
305
400
if (arche_pdata -> state == ARCHE_PLATFORM_STATE_FW_FLASHING )
306
- return count ;
401
+ goto exit ;
307
402
308
403
/* First we want to make sure we power off everything
309
404
* and then enter FW flashing state */
@@ -319,6 +414,8 @@ static ssize_t state_store(struct device *dev,
319
414
ret = - EINVAL ;
320
415
}
321
416
417
+ exit :
418
+ mutex_unlock (& arche_pdata -> platform_state_mutex );
322
419
return ret ? ret : count ;
323
420
}
324
421
@@ -336,6 +433,8 @@ static ssize_t state_show(struct device *dev,
336
433
return sprintf (buf , "standby\n" );
337
434
case ARCHE_PLATFORM_STATE_FW_FLASHING :
338
435
return sprintf (buf , "fw_flashing\n" );
436
+ case ARCHE_PLATFORM_STATE_TIME_SYNC :
437
+ return sprintf (buf , "time_sync\n" );
339
438
default :
340
439
return sprintf (buf , "unknown state\n" );
341
440
}
@@ -349,11 +448,15 @@ static int arche_platform_pm_notifier(struct notifier_block *notifier,
349
448
struct arche_platform_drvdata * arche_pdata =
350
449
container_of (notifier , struct arche_platform_drvdata ,
351
450
pm_notifier );
451
+ int ret = NOTIFY_DONE ;
352
452
453
+ mutex_lock (& arche_pdata -> platform_state_mutex );
353
454
switch (pm_event ) {
354
455
case PM_SUSPEND_PREPARE :
355
- if (arche_pdata -> state != ARCHE_PLATFORM_STATE_ACTIVE )
356
- return NOTIFY_STOP ;
456
+ if (arche_pdata -> state != ARCHE_PLATFORM_STATE_ACTIVE ) {
457
+ ret = NOTIFY_STOP ;
458
+ break ;
459
+ }
357
460
device_for_each_child (arche_pdata -> dev , NULL , apb_poweroff );
358
461
arche_platform_poweroff_seq (arche_pdata );
359
462
break ;
@@ -364,8 +467,9 @@ static int arche_platform_pm_notifier(struct notifier_block *notifier,
364
467
default :
365
468
break ;
366
469
}
470
+ mutex_unlock (& arche_pdata -> platform_state_mutex );
367
471
368
- return NOTIFY_DONE ;
472
+ return ret ;
369
473
}
370
474
371
475
static int arche_platform_probe (struct platform_device * pdev )
@@ -468,6 +572,7 @@ static int arche_platform_probe(struct platform_device *pdev)
468
572
arche_pdata -> dev = & pdev -> dev ;
469
573
470
574
spin_lock_init (& arche_pdata -> wake_lock );
575
+ mutex_init (& arche_pdata -> platform_state_mutex );
471
576
arche_pdata -> wake_detect_irq =
472
577
gpio_to_irq (arche_pdata -> wake_detect_gpio );
473
578
@@ -489,6 +594,7 @@ static int arche_platform_probe(struct platform_device *pdev)
489
594
return ret ;
490
595
}
491
596
597
+ mutex_lock (& arche_pdata -> platform_state_mutex );
492
598
ret = arche_platform_coldboot_seq (arche_pdata );
493
599
if (ret ) {
494
600
dev_err (dev , "Failed to cold boot svc %d\n" , ret );
@@ -505,6 +611,8 @@ static int arche_platform_probe(struct platform_device *pdev)
505
611
506
612
arche_pdata -> pm_notifier .notifier_call = arche_platform_pm_notifier ;
507
613
ret = register_pm_notifier (& arche_pdata -> pm_notifier );
614
+ mutex_unlock (& arche_pdata -> platform_state_mutex );
615
+
508
616
if (ret ) {
509
617
dev_err (dev , "failed to register pm notifier %d\n" , ret );
510
618
goto err_populate ;
@@ -516,6 +624,7 @@ static int arche_platform_probe(struct platform_device *pdev)
516
624
err_populate :
517
625
arche_platform_poweroff_seq (arche_pdata );
518
626
err_coldboot :
627
+ mutex_unlock (& arche_pdata -> platform_state_mutex );
519
628
device_remove_file (& pdev -> dev , & dev_attr_state );
520
629
return ret ;
521
630
}
0 commit comments