blob: 44e31797055724d085ef5742dfbe47cc8fbba12b [file] [log] [blame]
Maximilian Luzfc00bc82020-12-21 19:39:59 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Driver for the Surface ACPI Notify (SAN) interface/shim.
4 *
5 * Translates communication from ACPI to Surface System Aggregator Module
6 * (SSAM/SAM) requests and back, specifically SAM-over-SSH. Translates SSAM
7 * events back to ACPI notifications. Allows handling of discrete GPU
8 * notifications sent from ACPI via the SAN interface by providing them to any
9 * registered external driver.
10 *
Maximilian Luz221756e2022-06-24 22:58:00 +020011 * Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
Maximilian Luzfc00bc82020-12-21 19:39:59 +010012 */
13
14#include <asm/unaligned.h>
15#include <linux/acpi.h>
16#include <linux/delay.h>
17#include <linux/jiffies.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/notifier.h>
21#include <linux/platform_device.h>
22#include <linux/rwsem.h>
23
24#include <linux/surface_aggregator/controller.h>
25#include <linux/surface_acpi_notify.h>
26
27struct san_data {
28 struct device *dev;
29 struct ssam_controller *ctrl;
30
31 struct acpi_connection_info info;
32
33 struct ssam_event_notifier nf_bat;
34 struct ssam_event_notifier nf_tmp;
35};
36
37#define to_san_data(ptr, member) \
38 container_of(ptr, struct san_data, member)
39
Tetsuo Handa31a1e4a2022-06-10 14:41:58 +090040static struct workqueue_struct *san_wq;
Maximilian Luzfc00bc82020-12-21 19:39:59 +010041
42/* -- dGPU notifier interface. ---------------------------------------------- */
43
44struct san_rqsg_if {
45 struct rw_semaphore lock;
46 struct device *dev;
47 struct blocking_notifier_head nh;
48};
49
50static struct san_rqsg_if san_rqsg_if = {
51 .lock = __RWSEM_INITIALIZER(san_rqsg_if.lock),
52 .dev = NULL,
53 .nh = BLOCKING_NOTIFIER_INIT(san_rqsg_if.nh),
54};
55
56static int san_set_rqsg_interface_device(struct device *dev)
57{
58 int status = 0;
59
60 down_write(&san_rqsg_if.lock);
61 if (!san_rqsg_if.dev && dev)
62 san_rqsg_if.dev = dev;
63 else
64 status = -EBUSY;
65 up_write(&san_rqsg_if.lock);
66
67 return status;
68}
69
70/**
71 * san_client_link() - Link client as consumer to SAN device.
72 * @client: The client to link.
73 *
74 * Sets up a device link between the provided client device as consumer and
75 * the SAN device as provider. This function can be used to ensure that the
76 * SAN interface has been set up and will be set up for as long as the driver
77 * of the client device is bound. This guarantees that, during that time, all
78 * dGPU events will be received by any registered notifier.
79 *
80 * The link will be automatically removed once the client device's driver is
81 * unbound.
82 *
83 * Return: Returns zero on success, %-ENXIO if the SAN interface has not been
84 * set up yet, and %-ENOMEM if device link creation failed.
85 */
86int san_client_link(struct device *client)
87{
88 const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER;
89 struct device_link *link;
90
91 down_read(&san_rqsg_if.lock);
92
93 if (!san_rqsg_if.dev) {
94 up_read(&san_rqsg_if.lock);
95 return -ENXIO;
96 }
97
98 link = device_link_add(client, san_rqsg_if.dev, flags);
99 if (!link) {
100 up_read(&san_rqsg_if.lock);
101 return -ENOMEM;
102 }
103
104 if (READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND) {
105 up_read(&san_rqsg_if.lock);
106 return -ENXIO;
107 }
108
109 up_read(&san_rqsg_if.lock);
110 return 0;
111}
112EXPORT_SYMBOL_GPL(san_client_link);
113
114/**
115 * san_dgpu_notifier_register() - Register a SAN dGPU notifier.
116 * @nb: The notifier-block to register.
117 *
118 * Registers a SAN dGPU notifier, receiving any new SAN dGPU events sent from
119 * ACPI. The registered notifier will be called with &struct san_dgpu_event
120 * as notifier data and the command ID of that event as notifier action.
121 */
122int san_dgpu_notifier_register(struct notifier_block *nb)
123{
124 return blocking_notifier_chain_register(&san_rqsg_if.nh, nb);
125}
126EXPORT_SYMBOL_GPL(san_dgpu_notifier_register);
127
128/**
129 * san_dgpu_notifier_unregister() - Unregister a SAN dGPU notifier.
130 * @nb: The notifier-block to unregister.
131 */
132int san_dgpu_notifier_unregister(struct notifier_block *nb)
133{
134 return blocking_notifier_chain_unregister(&san_rqsg_if.nh, nb);
135}
136EXPORT_SYMBOL_GPL(san_dgpu_notifier_unregister);
137
138static int san_dgpu_notifier_call(struct san_dgpu_event *evt)
139{
140 int ret;
141
142 ret = blocking_notifier_call_chain(&san_rqsg_if.nh, evt->command, evt);
143 return notifier_to_errno(ret);
144}
145
146
147/* -- ACPI _DSM event relay. ------------------------------------------------ */
148
149#define SAN_DSM_REVISION 0
150
151/* 93b666c5-70c6-469f-a215-3d487c91ab3c */
152static const guid_t SAN_DSM_UUID =
153 GUID_INIT(0x93b666c5, 0x70c6, 0x469f, 0xa2, 0x15, 0x3d,
154 0x48, 0x7c, 0x91, 0xab, 0x3c);
155
156enum san_dsm_event_fn {
157 SAN_DSM_EVENT_FN_BAT1_STAT = 0x03,
158 SAN_DSM_EVENT_FN_BAT1_INFO = 0x04,
159 SAN_DSM_EVENT_FN_ADP1_STAT = 0x05,
160 SAN_DSM_EVENT_FN_ADP1_INFO = 0x06,
161 SAN_DSM_EVENT_FN_BAT2_STAT = 0x07,
162 SAN_DSM_EVENT_FN_BAT2_INFO = 0x08,
163 SAN_DSM_EVENT_FN_THERMAL = 0x09,
164 SAN_DSM_EVENT_FN_DPTF = 0x0a,
165};
166
167enum sam_event_cid_bat {
168 SAM_EVENT_CID_BAT_BIX = 0x15,
169 SAM_EVENT_CID_BAT_BST = 0x16,
170 SAM_EVENT_CID_BAT_ADP = 0x17,
171 SAM_EVENT_CID_BAT_PROT = 0x18,
172 SAM_EVENT_CID_BAT_DPTF = 0x4f,
173};
174
175enum sam_event_cid_tmp {
176 SAM_EVENT_CID_TMP_TRIP = 0x0b,
177};
178
179struct san_event_work {
180 struct delayed_work work;
181 struct device *dev;
182 struct ssam_event event; /* must be last */
183};
184
185static int san_acpi_notify_event(struct device *dev, u64 func,
186 union acpi_object *param)
187{
188 acpi_handle san = ACPI_HANDLE(dev);
189 union acpi_object *obj;
190 int status = 0;
191
Colin Ian Kingd26cbdd2021-01-11 14:46:48 +0000192 if (!acpi_check_dsm(san, &SAN_DSM_UUID, SAN_DSM_REVISION, BIT_ULL(func)))
Maximilian Luzfc00bc82020-12-21 19:39:59 +0100193 return 0;
194
195 dev_dbg(dev, "notify event %#04llx\n", func);
196
197 obj = acpi_evaluate_dsm_typed(san, &SAN_DSM_UUID, SAN_DSM_REVISION,
198 func, param, ACPI_TYPE_BUFFER);
199 if (!obj)
200 return -EFAULT;
201
202 if (obj->buffer.length != 1 || obj->buffer.pointer[0] != 0) {
203 dev_err(dev, "got unexpected result from _DSM\n");
204 status = -EPROTO;
205 }
206
207 ACPI_FREE(obj);
208 return status;
209}
210
211static int san_evt_bat_adp(struct device *dev, const struct ssam_event *event)
212{
213 int status;
214
215 status = san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_ADP1_STAT, NULL);
216 if (status)
217 return status;
218
219 /*
220 * Ensure that the battery states get updated correctly. When the
221 * battery is fully charged and an adapter is plugged in, it sometimes
222 * is not updated correctly, instead showing it as charging.
223 * Explicitly trigger battery updates to fix this.
224 */
225
226 status = san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_BAT1_STAT, NULL);
227 if (status)
228 return status;
229
230 return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_BAT2_STAT, NULL);
231}
232
233static int san_evt_bat_bix(struct device *dev, const struct ssam_event *event)
234{
235 enum san_dsm_event_fn fn;
236
237 if (event->instance_id == 0x02)
238 fn = SAN_DSM_EVENT_FN_BAT2_INFO;
239 else
240 fn = SAN_DSM_EVENT_FN_BAT1_INFO;
241
242 return san_acpi_notify_event(dev, fn, NULL);
243}
244
245static int san_evt_bat_bst(struct device *dev, const struct ssam_event *event)
246{
247 enum san_dsm_event_fn fn;
248
249 if (event->instance_id == 0x02)
250 fn = SAN_DSM_EVENT_FN_BAT2_STAT;
251 else
252 fn = SAN_DSM_EVENT_FN_BAT1_STAT;
253
254 return san_acpi_notify_event(dev, fn, NULL);
255}
256
257static int san_evt_bat_dptf(struct device *dev, const struct ssam_event *event)
258{
259 union acpi_object payload;
260
261 /*
262 * The Surface ACPI expects a buffer and not a package. It specifically
263 * checks for ObjectType (Arg3) == 0x03. This will cause a warning in
264 * acpica/nsarguments.c, but that warning can be safely ignored.
265 */
266 payload.type = ACPI_TYPE_BUFFER;
267 payload.buffer.length = event->length;
268 payload.buffer.pointer = (u8 *)&event->data[0];
269
270 return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_DPTF, &payload);
271}
272
273static unsigned long san_evt_bat_delay(u8 cid)
274{
275 switch (cid) {
276 case SAM_EVENT_CID_BAT_ADP:
277 /*
278 * Wait for battery state to update before signaling adapter
279 * change.
280 */
281 return msecs_to_jiffies(5000);
282
283 case SAM_EVENT_CID_BAT_BST:
284 /* Ensure we do not miss anything important due to caching. */
285 return msecs_to_jiffies(2000);
286
287 default:
288 return 0;
289 }
290}
291
292static bool san_evt_bat(const struct ssam_event *event, struct device *dev)
293{
294 int status;
295
296 switch (event->command_id) {
297 case SAM_EVENT_CID_BAT_BIX:
298 status = san_evt_bat_bix(dev, event);
299 break;
300
301 case SAM_EVENT_CID_BAT_BST:
302 status = san_evt_bat_bst(dev, event);
303 break;
304
305 case SAM_EVENT_CID_BAT_ADP:
306 status = san_evt_bat_adp(dev, event);
307 break;
308
309 case SAM_EVENT_CID_BAT_PROT:
310 /*
311 * TODO: Implement support for battery protection status change
312 * event.
313 */
314 return true;
315
316 case SAM_EVENT_CID_BAT_DPTF:
317 status = san_evt_bat_dptf(dev, event);
318 break;
319
320 default:
321 return false;
322 }
323
324 if (status) {
325 dev_err(dev, "error handling power event (cid = %#04x)\n",
326 event->command_id);
327 }
328
329 return true;
330}
331
332static void san_evt_bat_workfn(struct work_struct *work)
333{
334 struct san_event_work *ev;
335
336 ev = container_of(work, struct san_event_work, work.work);
337 san_evt_bat(&ev->event, ev->dev);
338 kfree(ev);
339}
340
341static u32 san_evt_bat_nf(struct ssam_event_notifier *nf,
342 const struct ssam_event *event)
343{
344 struct san_data *d = to_san_data(nf, nf_bat);
345 struct san_event_work *work;
346 unsigned long delay = san_evt_bat_delay(event->command_id);
347
348 if (delay == 0)
349 return san_evt_bat(event, d->dev) ? SSAM_NOTIF_HANDLED : 0;
350
351 work = kzalloc(sizeof(*work) + event->length, GFP_KERNEL);
352 if (!work)
353 return ssam_notifier_from_errno(-ENOMEM);
354
355 INIT_DELAYED_WORK(&work->work, san_evt_bat_workfn);
356 work->dev = d->dev;
357
358 memcpy(&work->event, event, sizeof(struct ssam_event) + event->length);
359
Tetsuo Handa31a1e4a2022-06-10 14:41:58 +0900360 queue_delayed_work(san_wq, &work->work, delay);
Maximilian Luzfc00bc82020-12-21 19:39:59 +0100361 return SSAM_NOTIF_HANDLED;
362}
363
364static int san_evt_tmp_trip(struct device *dev, const struct ssam_event *event)
365{
366 union acpi_object param;
367
368 /*
369 * The Surface ACPI expects an integer and not a package. This will
370 * cause a warning in acpica/nsarguments.c, but that warning can be
371 * safely ignored.
372 */
373 param.type = ACPI_TYPE_INTEGER;
374 param.integer.value = event->instance_id;
375
376 return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_THERMAL, &param);
377}
378
379static bool san_evt_tmp(const struct ssam_event *event, struct device *dev)
380{
381 int status;
382
383 switch (event->command_id) {
384 case SAM_EVENT_CID_TMP_TRIP:
385 status = san_evt_tmp_trip(dev, event);
386 break;
387
388 default:
389 return false;
390 }
391
392 if (status) {
393 dev_err(dev, "error handling thermal event (cid = %#04x)\n",
394 event->command_id);
395 }
396
397 return true;
398}
399
400static u32 san_evt_tmp_nf(struct ssam_event_notifier *nf,
401 const struct ssam_event *event)
402{
403 struct san_data *d = to_san_data(nf, nf_tmp);
404
405 return san_evt_tmp(event, d->dev) ? SSAM_NOTIF_HANDLED : 0;
406}
407
408
409/* -- ACPI GSB OperationRegion handler -------------------------------------- */
410
411struct gsb_data_in {
412 u8 cv;
413} __packed;
414
415struct gsb_data_rqsx {
416 u8 cv; /* Command value (san_gsb_request_cv). */
417 u8 tc; /* Target category. */
418 u8 tid; /* Target ID. */
419 u8 iid; /* Instance ID. */
420 u8 snc; /* Expect-response-flag. */
421 u8 cid; /* Command ID. */
422 u16 cdl; /* Payload length. */
423 u8 pld[]; /* Payload. */
424} __packed;
425
426struct gsb_data_etwl {
427 u8 cv; /* Command value (should be 0x02). */
428 u8 etw3; /* Unknown. */
429 u8 etw4; /* Unknown. */
430 u8 msg[]; /* Error message (ASCIIZ). */
431} __packed;
432
433struct gsb_data_out {
434 u8 status; /* _SSH communication status. */
435 u8 len; /* _SSH payload length. */
436 u8 pld[]; /* _SSH payload. */
437} __packed;
438
439union gsb_buffer_data {
440 struct gsb_data_in in; /* Common input. */
441 struct gsb_data_rqsx rqsx; /* RQSX input. */
442 struct gsb_data_etwl etwl; /* ETWL input. */
443 struct gsb_data_out out; /* Output. */
444};
445
446struct gsb_buffer {
447 u8 status; /* GSB AttribRawProcess status. */
448 u8 len; /* GSB AttribRawProcess length. */
449 union gsb_buffer_data data;
450} __packed;
451
452#define SAN_GSB_MAX_RQSX_PAYLOAD (U8_MAX - 2 - sizeof(struct gsb_data_rqsx))
453#define SAN_GSB_MAX_RESPONSE (U8_MAX - 2 - sizeof(struct gsb_data_out))
454
455#define SAN_GSB_COMMAND 0
456
457enum san_gsb_request_cv {
458 SAN_GSB_REQUEST_CV_RQST = 0x01,
459 SAN_GSB_REQUEST_CV_ETWL = 0x02,
460 SAN_GSB_REQUEST_CV_RQSG = 0x03,
461};
462
463#define SAN_REQUEST_NUM_TRIES 5
464
465static acpi_status san_etwl(struct san_data *d, struct gsb_buffer *b)
466{
467 struct gsb_data_etwl *etwl = &b->data.etwl;
468
469 if (b->len < sizeof(struct gsb_data_etwl)) {
470 dev_err(d->dev, "invalid ETWL package (len = %d)\n", b->len);
471 return AE_OK;
472 }
473
474 dev_err(d->dev, "ETWL(%#04x, %#04x): %.*s\n", etwl->etw3, etwl->etw4,
475 (unsigned int)(b->len - sizeof(struct gsb_data_etwl)),
476 (char *)etwl->msg);
477
478 /* Indicate success. */
479 b->status = 0x00;
480 b->len = 0x00;
481
482 return AE_OK;
483}
484
485static
486struct gsb_data_rqsx *san_validate_rqsx(struct device *dev, const char *type,
487 struct gsb_buffer *b)
488{
489 struct gsb_data_rqsx *rqsx = &b->data.rqsx;
490
491 if (b->len < sizeof(struct gsb_data_rqsx)) {
492 dev_err(dev, "invalid %s package (len = %d)\n", type, b->len);
493 return NULL;
494 }
495
496 if (get_unaligned(&rqsx->cdl) != b->len - sizeof(struct gsb_data_rqsx)) {
497 dev_err(dev, "bogus %s package (len = %d, cdl = %d)\n",
498 type, b->len, get_unaligned(&rqsx->cdl));
499 return NULL;
500 }
501
502 if (get_unaligned(&rqsx->cdl) > SAN_GSB_MAX_RQSX_PAYLOAD) {
503 dev_err(dev, "payload for %s package too large (cdl = %d)\n",
504 type, get_unaligned(&rqsx->cdl));
505 return NULL;
506 }
507
508 return rqsx;
509}
510
511static void gsb_rqsx_response_error(struct gsb_buffer *gsb, int status)
512{
513 gsb->status = 0x00;
514 gsb->len = 0x02;
515 gsb->data.out.status = (u8)(-status);
516 gsb->data.out.len = 0x00;
517}
518
519static void gsb_rqsx_response_success(struct gsb_buffer *gsb, u8 *ptr, size_t len)
520{
521 gsb->status = 0x00;
522 gsb->len = len + 2;
523 gsb->data.out.status = 0x00;
524 gsb->data.out.len = len;
525
526 if (len)
527 memcpy(&gsb->data.out.pld[0], ptr, len);
528}
529
530static acpi_status san_rqst_fixup_suspended(struct san_data *d,
531 struct ssam_request *rqst,
532 struct gsb_buffer *gsb)
533{
534 if (rqst->target_category == SSAM_SSH_TC_BAS && rqst->command_id == 0x0D) {
535 u8 base_state = 1;
536
537 /* Base state quirk:
538 * The base state may be queried from ACPI when the EC is still
539 * suspended. In this case it will return '-EPERM'. This query
540 * will only be triggered from the ACPI lid GPE interrupt, thus
541 * we are either in laptop or studio mode (base status 0x01 or
542 * 0x02). Furthermore, we will only get here if the device (and
543 * EC) have been suspended.
544 *
545 * We now assume that the device is in laptop mode (0x01). This
546 * has the drawback that it will wake the device when unfolding
547 * it in studio mode, but it also allows us to avoid actively
548 * waiting for the EC to wake up, which may incur a notable
549 * delay.
550 */
551
552 dev_dbg(d->dev, "rqst: fixup: base-state quirk\n");
553
554 gsb_rqsx_response_success(gsb, &base_state, sizeof(base_state));
555 return AE_OK;
556 }
557
558 gsb_rqsx_response_error(gsb, -ENXIO);
559 return AE_OK;
560}
561
562static acpi_status san_rqst(struct san_data *d, struct gsb_buffer *buffer)
563{
564 u8 rspbuf[SAN_GSB_MAX_RESPONSE];
565 struct gsb_data_rqsx *gsb_rqst;
566 struct ssam_request rqst;
567 struct ssam_response rsp;
568 int status = 0;
569
570 gsb_rqst = san_validate_rqsx(d->dev, "RQST", buffer);
571 if (!gsb_rqst)
572 return AE_OK;
573
574 rqst.target_category = gsb_rqst->tc;
575 rqst.target_id = gsb_rqst->tid;
576 rqst.command_id = gsb_rqst->cid;
577 rqst.instance_id = gsb_rqst->iid;
578 rqst.flags = gsb_rqst->snc ? SSAM_REQUEST_HAS_RESPONSE : 0;
579 rqst.length = get_unaligned(&gsb_rqst->cdl);
580 rqst.payload = &gsb_rqst->pld[0];
581
582 rsp.capacity = ARRAY_SIZE(rspbuf);
583 rsp.length = 0;
584 rsp.pointer = &rspbuf[0];
585
586 /* Handle suspended device. */
587 if (d->dev->power.is_suspended) {
588 dev_warn(d->dev, "rqst: device is suspended, not executing\n");
589 return san_rqst_fixup_suspended(d, &rqst, buffer);
590 }
591
592 status = __ssam_retry(ssam_request_sync_onstack, SAN_REQUEST_NUM_TRIES,
593 d->ctrl, &rqst, &rsp, SAN_GSB_MAX_RQSX_PAYLOAD);
594
595 if (!status) {
596 gsb_rqsx_response_success(buffer, rsp.pointer, rsp.length);
597 } else {
598 dev_err(d->dev, "rqst: failed with error %d\n", status);
599 gsb_rqsx_response_error(buffer, status);
600 }
601
602 return AE_OK;
603}
604
605static acpi_status san_rqsg(struct san_data *d, struct gsb_buffer *buffer)
606{
607 struct gsb_data_rqsx *gsb_rqsg;
608 struct san_dgpu_event evt;
609 int status;
610
611 gsb_rqsg = san_validate_rqsx(d->dev, "RQSG", buffer);
612 if (!gsb_rqsg)
613 return AE_OK;
614
615 evt.category = gsb_rqsg->tc;
616 evt.target = gsb_rqsg->tid;
617 evt.command = gsb_rqsg->cid;
618 evt.instance = gsb_rqsg->iid;
619 evt.length = get_unaligned(&gsb_rqsg->cdl);
620 evt.payload = &gsb_rqsg->pld[0];
621
622 status = san_dgpu_notifier_call(&evt);
623 if (!status) {
624 gsb_rqsx_response_success(buffer, NULL, 0);
625 } else {
626 dev_err(d->dev, "rqsg: failed with error %d\n", status);
627 gsb_rqsx_response_error(buffer, status);
628 }
629
630 return AE_OK;
631}
632
633static acpi_status san_opreg_handler(u32 function, acpi_physical_address command,
634 u32 bits, u64 *value64, void *opreg_context,
635 void *region_context)
636{
637 struct san_data *d = to_san_data(opreg_context, info);
638 struct gsb_buffer *buffer = (struct gsb_buffer *)value64;
639 int accessor_type = (function & 0xFFFF0000) >> 16;
640
641 if (command != SAN_GSB_COMMAND) {
642 dev_warn(d->dev, "unsupported command: %#04llx\n", command);
643 return AE_OK;
644 }
645
646 if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) {
647 dev_err(d->dev, "invalid access type: %#04x\n", accessor_type);
648 return AE_OK;
649 }
650
651 /* Buffer must have at least contain the command-value. */
652 if (buffer->len == 0) {
653 dev_err(d->dev, "request-package too small\n");
654 return AE_OK;
655 }
656
657 switch (buffer->data.in.cv) {
658 case SAN_GSB_REQUEST_CV_RQST:
659 return san_rqst(d, buffer);
660
661 case SAN_GSB_REQUEST_CV_ETWL:
662 return san_etwl(d, buffer);
663
664 case SAN_GSB_REQUEST_CV_RQSG:
665 return san_rqsg(d, buffer);
666
667 default:
668 dev_warn(d->dev, "unsupported SAN0 request (cv: %#04x)\n",
669 buffer->data.in.cv);
670 return AE_OK;
671 }
672}
673
674
675/* -- Driver setup. --------------------------------------------------------- */
676
677static int san_events_register(struct platform_device *pdev)
678{
679 struct san_data *d = platform_get_drvdata(pdev);
680 int status;
681
682 d->nf_bat.base.priority = 1;
683 d->nf_bat.base.fn = san_evt_bat_nf;
684 d->nf_bat.event.reg = SSAM_EVENT_REGISTRY_SAM;
685 d->nf_bat.event.id.target_category = SSAM_SSH_TC_BAT;
686 d->nf_bat.event.id.instance = 0;
687 d->nf_bat.event.mask = SSAM_EVENT_MASK_TARGET;
688 d->nf_bat.event.flags = SSAM_EVENT_SEQUENCED;
689
690 d->nf_tmp.base.priority = 1;
691 d->nf_tmp.base.fn = san_evt_tmp_nf;
692 d->nf_tmp.event.reg = SSAM_EVENT_REGISTRY_SAM;
693 d->nf_tmp.event.id.target_category = SSAM_SSH_TC_TMP;
694 d->nf_tmp.event.id.instance = 0;
695 d->nf_tmp.event.mask = SSAM_EVENT_MASK_TARGET;
696 d->nf_tmp.event.flags = SSAM_EVENT_SEQUENCED;
697
698 status = ssam_notifier_register(d->ctrl, &d->nf_bat);
699 if (status)
700 return status;
701
702 status = ssam_notifier_register(d->ctrl, &d->nf_tmp);
703 if (status)
704 ssam_notifier_unregister(d->ctrl, &d->nf_bat);
705
706 return status;
707}
708
709static void san_events_unregister(struct platform_device *pdev)
710{
711 struct san_data *d = platform_get_drvdata(pdev);
712
713 ssam_notifier_unregister(d->ctrl, &d->nf_bat);
714 ssam_notifier_unregister(d->ctrl, &d->nf_tmp);
715}
716
717#define san_consumer_printk(level, dev, handle, fmt, ...) \
718do { \
719 char *path = "<error getting consumer path>"; \
720 struct acpi_buffer buffer = { \
721 .length = ACPI_ALLOCATE_BUFFER, \
722 .pointer = NULL, \
723 }; \
724 \
725 if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer))) \
726 path = buffer.pointer; \
727 \
728 dev_##level(dev, "[%s]: " fmt, path, ##__VA_ARGS__); \
729 kfree(buffer.pointer); \
730} while (0)
731
732#define san_consumer_dbg(dev, handle, fmt, ...) \
733 san_consumer_printk(dbg, dev, handle, fmt, ##__VA_ARGS__)
734
735#define san_consumer_warn(dev, handle, fmt, ...) \
736 san_consumer_printk(warn, dev, handle, fmt, ##__VA_ARGS__)
737
738static bool is_san_consumer(struct platform_device *pdev, acpi_handle handle)
739{
740 struct acpi_handle_list dep_devices;
741 acpi_handle supplier = ACPI_HANDLE(&pdev->dev);
742 acpi_status status;
743 int i;
744
745 if (!acpi_has_method(handle, "_DEP"))
746 return false;
747
748 status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices);
749 if (ACPI_FAILURE(status)) {
750 san_consumer_dbg(&pdev->dev, handle, "failed to evaluate _DEP\n");
751 return false;
752 }
753
754 for (i = 0; i < dep_devices.count; i++) {
755 if (dep_devices.handles[i] == supplier)
756 return true;
757 }
758
759 return false;
760}
761
762static acpi_status san_consumer_setup(acpi_handle handle, u32 lvl,
763 void *context, void **rv)
764{
765 const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER;
766 struct platform_device *pdev = context;
767 struct acpi_device *adev;
768 struct device_link *link;
769
770 if (!is_san_consumer(pdev, handle))
771 return AE_OK;
772
773 /* Ignore ACPI devices that are not present. */
Rafael J. Wysocki6768bdd2022-01-26 20:41:27 +0100774 adev = acpi_fetch_acpi_dev(handle);
775 if (!adev)
Maximilian Luzfc00bc82020-12-21 19:39:59 +0100776 return AE_OK;
777
778 san_consumer_dbg(&pdev->dev, handle, "creating device link\n");
779
780 /* Try to set up device links, ignore but log errors. */
781 link = device_link_add(&adev->dev, &pdev->dev, flags);
782 if (!link) {
783 san_consumer_warn(&pdev->dev, handle, "failed to create device link\n");
784 return AE_OK;
785 }
786
787 return AE_OK;
788}
789
790static int san_consumer_links_setup(struct platform_device *pdev)
791{
792 acpi_status status;
793
794 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
795 ACPI_UINT32_MAX, san_consumer_setup, NULL,
796 pdev, NULL);
797
798 return status ? -EFAULT : 0;
799}
800
801static int san_probe(struct platform_device *pdev)
802{
Daniel Scallya9e10e52021-06-03 23:40:02 +0100803 struct acpi_device *san = ACPI_COMPANION(&pdev->dev);
Maximilian Luzfc00bc82020-12-21 19:39:59 +0100804 struct ssam_controller *ctrl;
805 struct san_data *data;
806 acpi_status astatus;
807 int status;
808
809 ctrl = ssam_client_bind(&pdev->dev);
810 if (IS_ERR(ctrl))
811 return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
812
813 status = san_consumer_links_setup(pdev);
814 if (status)
815 return status;
816
817 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
818 if (!data)
819 return -ENOMEM;
820
821 data->dev = &pdev->dev;
822 data->ctrl = ctrl;
823
824 platform_set_drvdata(pdev, data);
825
Daniel Scallya9e10e52021-06-03 23:40:02 +0100826 astatus = acpi_install_address_space_handler(san->handle,
827 ACPI_ADR_SPACE_GSBUS,
Maximilian Luzfc00bc82020-12-21 19:39:59 +0100828 &san_opreg_handler, NULL,
829 &data->info);
830 if (ACPI_FAILURE(astatus))
831 return -ENXIO;
832
833 status = san_events_register(pdev);
834 if (status)
835 goto err_enable_events;
836
837 status = san_set_rqsg_interface_device(&pdev->dev);
838 if (status)
839 goto err_install_dev;
840
Daniel Scallya9e10e52021-06-03 23:40:02 +0100841 acpi_dev_clear_dependencies(san);
Maximilian Luzfc00bc82020-12-21 19:39:59 +0100842 return 0;
843
844err_install_dev:
845 san_events_unregister(pdev);
846err_enable_events:
847 acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS,
848 &san_opreg_handler);
849 return status;
850}
851
852static int san_remove(struct platform_device *pdev)
853{
854 acpi_handle san = ACPI_HANDLE(&pdev->dev);
855
856 san_set_rqsg_interface_device(NULL);
857 acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS,
858 &san_opreg_handler);
859 san_events_unregister(pdev);
860
861 /*
862 * We have unregistered our event sources. Now we need to ensure that
863 * all delayed works they may have spawned are run to completion.
864 */
Tetsuo Handa31a1e4a2022-06-10 14:41:58 +0900865 flush_workqueue(san_wq);
Maximilian Luzfc00bc82020-12-21 19:39:59 +0100866
867 return 0;
868}
869
870static const struct acpi_device_id san_match[] = {
871 { "MSHW0091" },
872 { },
873};
874MODULE_DEVICE_TABLE(acpi, san_match);
875
876static struct platform_driver surface_acpi_notify = {
877 .probe = san_probe,
878 .remove = san_remove,
879 .driver = {
880 .name = "surface_acpi_notify",
881 .acpi_match_table = san_match,
882 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
883 },
884};
Tetsuo Handa31a1e4a2022-06-10 14:41:58 +0900885
886static int __init san_init(void)
887{
888 int ret;
889
890 san_wq = alloc_workqueue("san_wq", 0, 0);
891 if (!san_wq)
892 return -ENOMEM;
893 ret = platform_driver_register(&surface_acpi_notify);
894 if (ret)
895 destroy_workqueue(san_wq);
896 return ret;
897}
898module_init(san_init);
899
900static void __exit san_exit(void)
901{
902 platform_driver_unregister(&surface_acpi_notify);
903 destroy_workqueue(san_wq);
904}
905module_exit(san_exit);
Maximilian Luzfc00bc82020-12-21 19:39:59 +0100906
907MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
908MODULE_DESCRIPTION("Surface ACPI Notify driver for Surface System Aggregator Module");
909MODULE_LICENSE("GPL");