Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/atopcase/atopcase_acpi.c
39507 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2021-2023 Val Packett <[email protected]>
5
* Copyright (c) 2023 Vladimir Kondratyev <[email protected]>
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include "opt_acpi.h"
30
#include "opt_hid.h"
31
32
#include <sys/param.h>
33
#include <sys/bus.h>
34
#include <sys/kernel.h>
35
#include <sys/malloc.h>
36
#include <sys/mutex.h>
37
#include <sys/module.h>
38
#include <sys/rman.h>
39
#include <sys/sx.h>
40
#include <sys/taskqueue.h>
41
42
#include <contrib/dev/acpica/include/acpi.h>
43
#include <contrib/dev/acpica/include/accommon.h>
44
#include <contrib/dev/acpica/include/acevents.h>
45
#include <dev/acpica/acpivar.h>
46
#include <dev/acpica/acpiio.h>
47
48
#include <dev/backlight/backlight.h>
49
50
#include <dev/evdev/input.h>
51
52
#define HID_DEBUG_VAR atopcase_debug
53
#include <dev/hid/hid.h>
54
#include <dev/hid/hidquirk.h>
55
56
#include <dev/spibus/spi.h>
57
#include <dev/spibus/spibusvar.h>
58
59
#include "backlight_if.h"
60
#include "hid_if.h"
61
62
#include "atopcase_reg.h"
63
#include "atopcase_var.h"
64
65
/*
66
* XXX: The Linux driver only supports ACPI GPEs, but we only receive
67
* interrupts in this driver on a MacBookPro 12,1 and 14,1. This is because
68
* Linux responds to _OSI("Darwin") while we don't!
69
*
70
* ACPI GPE is enabled on FreeBSD by addition of following lines to
71
* /boot/loader.conf:
72
* hw.acpi.install_interface="Darwin"
73
* hw.acpi.remove_interface="Windows 2009, Windows 2012"
74
*/
75
76
static const char *atopcase_ids[] = { "APP000D", NULL };
77
78
static device_probe_t atopcase_acpi_probe;
79
static device_attach_t atopcase_acpi_attach;
80
static device_detach_t atopcase_acpi_detach;
81
static device_suspend_t atopcase_acpi_suspend;
82
static device_resume_t atopcase_acpi_resume;
83
84
static bool
85
acpi_is_atopcase(ACPI_HANDLE handle)
86
{
87
const char **ids;
88
UINT32 sta;
89
90
for (ids = atopcase_ids; *ids != NULL; ids++) {
91
if (acpi_MatchHid(handle, *ids))
92
break;
93
}
94
if (*ids == NULL)
95
return (false);
96
97
/*
98
* If no _STA method or if it failed, then assume that
99
* the device is present.
100
*/
101
if (ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) ||
102
ACPI_DEVICE_PRESENT(sta))
103
return (true);
104
105
return (false);
106
}
107
108
static int
109
atopcase_acpi_set_comm_enabled(struct atopcase_softc *sc, char *prop,
110
const bool on)
111
{
112
ACPI_OBJECT argobj;
113
ACPI_OBJECT_LIST args;
114
115
argobj.Type = ACPI_TYPE_INTEGER;
116
argobj.Integer.Value = on;
117
args.Count = 1;
118
args.Pointer = &argobj;
119
120
if (ACPI_FAILURE(
121
AcpiEvaluateObject(sc->sc_handle, prop, &args, NULL)))
122
return (ENXIO);
123
124
DELAY(100);
125
126
return (0);
127
}
128
129
static int
130
atopcase_acpi_test_comm_enabled(ACPI_HANDLE handle, char *prop, int *enabled)
131
{
132
if (ACPI_FAILURE(acpi_GetInteger(handle, prop, enabled)))
133
return (ENXIO);
134
135
return (0);
136
}
137
138
static void
139
atopcase_acpi_task(void *ctx, int pending __unused)
140
{
141
struct atopcase_softc *sc = ctx;
142
int err = EAGAIN;
143
144
DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Timer event\n");
145
146
sx_xlock(&sc->sc_write_sx);
147
err = atopcase_receive_packet(sc);
148
sx_xunlock(&sc->sc_write_sx);
149
150
/* Rearm timer */
151
taskqueue_enqueue_timeout(sc->sc_tq, &sc->sc_task,
152
hz / (err == EAGAIN ? 10 : 120));
153
}
154
155
static void
156
atopcase_acpi_gpe_task(void *ctx)
157
{
158
struct atopcase_softc *sc = ctx;
159
160
DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "GPE event\n");
161
162
sx_xlock(&sc->sc_sx);
163
(void)atopcase_intr(sc);
164
sx_xunlock(&sc->sc_sx);
165
166
/* Rearm GPE */
167
if (ACPI_FAILURE(AcpiFinishGpe(NULL, sc->sc_gpe_bit)))
168
device_printf(sc->sc_dev, "GPE rearm failed\n");
169
}
170
171
static UINT32
172
atopcase_acpi_notify(ACPI_HANDLE h __unused, UINT32 notify __unused, void *ctx)
173
{
174
AcpiOsExecute(OSL_GPE_HANDLER, atopcase_acpi_gpe_task, ctx);
175
return (0);
176
}
177
178
static void
179
atopcase_acpi_intr(void *ctx)
180
{
181
struct atopcase_softc *sc = ctx;
182
183
DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Interrupt event\n");
184
185
mtx_lock(&sc->sc_mtx);
186
sc->sc_intr_cnt++;
187
(void)atopcase_intr(sc);
188
mtx_unlock(&sc->sc_mtx);
189
}
190
191
static int
192
atopcase_acpi_probe(device_t dev)
193
{
194
ACPI_HANDLE handle;
195
int usb_enabled;
196
197
if (acpi_disabled("atopcase"))
198
return (ENXIO);
199
200
handle = acpi_get_handle(dev);
201
if (handle == NULL)
202
return (ENXIO);
203
204
if (!acpi_is_atopcase(handle))
205
return (ENXIO);
206
207
/* If USB interface exists and is enabled, use USB driver */
208
if (atopcase_acpi_test_comm_enabled(handle, "UIST", &usb_enabled) == 0
209
&& usb_enabled != 0)
210
return (ENXIO);
211
212
device_set_desc(dev, "Apple MacBook SPI Topcase");
213
214
return (BUS_PROBE_DEFAULT);
215
}
216
217
static int
218
atopcase_acpi_attach(device_t dev)
219
{
220
struct atopcase_softc *sc = device_get_softc(dev);
221
ACPI_DEVICE_INFO *device_info;
222
uint32_t cs_delay;
223
int spi_enabled, err;
224
225
sc->sc_dev = dev;
226
sc->sc_handle = acpi_get_handle(dev);
227
228
if (atopcase_acpi_test_comm_enabled(sc->sc_handle, "SIST",
229
&spi_enabled) != 0) {
230
device_printf(dev, "can't test SPI communication\n");
231
return (ENXIO);
232
}
233
234
/* Turn SPI off if enabled to force "booted" packet to appear */
235
if (spi_enabled != 0 &&
236
atopcase_acpi_set_comm_enabled(sc, "SIEN", false) != 0) {
237
device_printf(dev, "can't disable SPI communication\n");
238
return (ENXIO);
239
}
240
241
if (atopcase_acpi_set_comm_enabled(sc, "SIEN", true) != 0) {
242
device_printf(dev, "can't enable SPI communication\n");
243
return (ENXIO);
244
}
245
246
/*
247
* Apple encodes a CS delay in ACPI properties, but
248
* - they're encoded in a non-standard way that predates _DSD, and
249
* - they're only exported if you respond to _OSI(Darwin) which we don't
250
* - because that has more side effects than we're prepared to handle
251
* - Apple makes a Windows driver and Windows is not Darwin
252
* - so presumably that one uses hardcoded values too
253
*/
254
spibus_get_cs_delay(sc->sc_dev, &cs_delay);
255
if (cs_delay == 0)
256
spibus_set_cs_delay(sc->sc_dev, 10);
257
258
/* Retrieve ACPI _HID */
259
if (ACPI_FAILURE(AcpiGetObjectInfo(sc->sc_handle, &device_info)))
260
return (ENXIO);
261
if (device_info->Valid & ACPI_VALID_HID)
262
strlcpy(sc->sc_hid, device_info->HardwareId.String,
263
sizeof(sc->sc_hid));
264
AcpiOsFree(device_info);
265
266
sx_init(&sc->sc_write_sx, "atc_wr");
267
sx_init(&sc->sc_sx, "atc_sx");
268
mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
269
270
err = ENXIO;
271
sc->sc_irq_res = bus_alloc_resource_any(sc->sc_dev,
272
SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE);
273
if (sc->sc_irq_res != NULL) {
274
if (bus_setup_intr(dev, sc->sc_irq_res,
275
INTR_TYPE_MISC | INTR_MPSAFE, NULL,
276
atopcase_acpi_intr, sc, &sc->sc_irq_ih) != 0) {
277
device_printf(dev, "can't setup interrupt handler\n");
278
goto err;
279
}
280
device_printf(dev, "Using interrupts.\n");
281
/*
282
* On some hardware interrupts are not acked by SPI read for
283
* unknown reasons that leads to interrupt storm due to level
284
* triggering. GPE does not suffer from this problem.
285
*
286
* TODO: Find out what Windows driver does to ack IRQ.
287
*/
288
pause("atopcase", hz / 5);
289
DPRINTF("interrupts asserted: %u\n", sc->sc_intr_cnt);
290
if (sc->sc_intr_cnt > 2 || sc->sc_intr_cnt == 0) {
291
bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_ih);
292
sc->sc_irq_ih = NULL;
293
device_printf(dev, "Interrupt storm detected. "
294
"Falling back to polling\n");
295
sc->sc_tq = taskqueue_create("atc_tq", M_WAITOK|M_ZERO,
296
taskqueue_thread_enqueue, &sc->sc_tq);
297
TIMEOUT_TASK_INIT(sc->sc_tq, &sc->sc_task, 0,
298
atopcase_acpi_task, sc);
299
taskqueue_start_threads(&sc->sc_tq, 1, PI_TTY,
300
"%s taskq", device_get_nameunit(dev));
301
}
302
/*
303
* Interrupts does not work at all. It may happen if kernel
304
* erroneously detected stray irq at bus_teardown_intr() and
305
* completelly disabled it after than.
306
* Fetch "booted" packet manually to pass communication check.
307
*/
308
if (sc->sc_intr_cnt == 0)
309
atopcase_receive_packet(sc);
310
} else {
311
if (bootverbose)
312
device_printf(dev, "can't allocate IRQ resource\n");
313
if (ACPI_FAILURE(acpi_GetInteger(sc->sc_handle, "_GPE",
314
&sc->sc_gpe_bit))) {
315
device_printf(dev, "can't allocate nor IRQ nor GPE\n");
316
goto err;
317
}
318
if (ACPI_FAILURE(AcpiInstallGpeHandler(NULL, sc->sc_gpe_bit,
319
ACPI_GPE_LEVEL_TRIGGERED, atopcase_acpi_notify, sc))) {
320
device_printf(dev, "can't install ACPI GPE handler\n");
321
goto err;
322
}
323
if (ACPI_FAILURE(AcpiEnableGpe(NULL, sc->sc_gpe_bit))) {
324
device_printf(dev, "can't enable ACPI notification\n");
325
goto err;
326
}
327
device_printf(dev, "Using ACPI GPE.\n");
328
if (bootverbose)
329
device_printf(dev, "GPE int %d\n", sc->sc_gpe_bit);
330
}
331
332
err = atopcase_init(sc);
333
334
err:
335
if (err != 0)
336
atopcase_acpi_detach(dev);
337
return (err);
338
}
339
340
static int
341
atopcase_acpi_detach(device_t dev)
342
{
343
struct atopcase_softc *sc = device_get_softc(dev);
344
int err;
345
346
err = atopcase_destroy(sc);
347
if (err != 0)
348
return (err);
349
350
if (sc->sc_irq_ih)
351
bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_ih);
352
if (sc->sc_irq_res != NULL)
353
bus_release_resource(dev, SYS_RES_IRQ,
354
sc->sc_irq_rid, sc->sc_irq_res);
355
356
if (sc->sc_tq != NULL) {
357
while (taskqueue_cancel_timeout(sc->sc_tq, &sc->sc_task, NULL))
358
taskqueue_drain_timeout(sc->sc_tq, &sc->sc_task);
359
taskqueue_free(sc->sc_tq);
360
}
361
362
if (sc->sc_gpe_bit != 0 && ACPI_FAILURE(AcpiRemoveGpeHandler(NULL,
363
sc->sc_gpe_bit, atopcase_acpi_notify)))
364
device_printf(dev, "can't remove ACPI GPE handler\n");
365
366
if (atopcase_acpi_set_comm_enabled(sc, "SIEN", false) != 0)
367
device_printf(dev, "can't disable SPI communication\n");
368
369
mtx_destroy(&sc->sc_mtx);
370
sx_destroy(&sc->sc_sx);
371
sx_destroy(&sc->sc_write_sx);
372
373
return (0);
374
}
375
376
static int
377
atopcase_acpi_suspend(device_t dev)
378
{
379
struct atopcase_softc *sc = device_get_softc(dev);
380
int err;
381
382
err = bus_generic_suspend(dev);
383
if (err)
384
return (err);
385
386
if (sc->sc_gpe_bit != 0)
387
AcpiDisableGpe(NULL, sc->sc_gpe_bit);
388
389
if (sc->sc_tq != NULL)
390
while (taskqueue_cancel_timeout(sc->sc_tq, &sc->sc_task, NULL))
391
taskqueue_drain_timeout(sc->sc_tq, &sc->sc_task);
392
393
if (atopcase_acpi_set_comm_enabled(sc, "SIEN", false) != 0)
394
device_printf(dev, "can't disable SPI communication\n");
395
396
return (0);
397
}
398
399
static int
400
atopcase_acpi_resume(device_t dev)
401
{
402
struct atopcase_softc *sc = device_get_softc(dev);
403
404
if (sc->sc_gpe_bit != 0)
405
AcpiEnableGpe(NULL, sc->sc_gpe_bit);
406
407
if (sc->sc_tq != NULL)
408
taskqueue_enqueue_timeout(sc->sc_tq, &sc->sc_task, hz / 120);
409
410
if (atopcase_acpi_set_comm_enabled(sc, "SIEN", true) != 0) {
411
device_printf(dev, "can't enable SPI communication\n");
412
return (ENXIO);
413
}
414
415
return (bus_generic_resume(dev));
416
}
417
418
static device_method_t atopcase_methods[] = {
419
/* Device interface */
420
DEVMETHOD(device_probe, atopcase_acpi_probe),
421
DEVMETHOD(device_attach, atopcase_acpi_attach),
422
DEVMETHOD(device_detach, atopcase_acpi_detach),
423
DEVMETHOD(device_suspend, atopcase_acpi_suspend),
424
DEVMETHOD(device_resume, atopcase_acpi_resume),
425
426
/* HID interrupt interface */
427
DEVMETHOD(hid_intr_setup, atopcase_intr_setup),
428
DEVMETHOD(hid_intr_unsetup, atopcase_intr_unsetup),
429
DEVMETHOD(hid_intr_start, atopcase_intr_start),
430
DEVMETHOD(hid_intr_stop, atopcase_intr_stop),
431
DEVMETHOD(hid_intr_poll, atopcase_intr_poll),
432
433
/* HID interface */
434
DEVMETHOD(hid_get_rdesc, atopcase_get_rdesc),
435
DEVMETHOD(hid_set_report, atopcase_set_report),
436
437
/* Backlight interface */
438
DEVMETHOD(backlight_update_status, atopcase_backlight_update_status),
439
DEVMETHOD(backlight_get_status, atopcase_backlight_get_status),
440
DEVMETHOD(backlight_get_info, atopcase_backlight_get_info),
441
442
DEVMETHOD_END
443
};
444
445
static driver_t atopcase_driver = {
446
"atopcase",
447
atopcase_methods,
448
sizeof(struct atopcase_softc),
449
};
450
451
DRIVER_MODULE(atopcase, spibus, atopcase_driver, 0, 0);
452
MODULE_DEPEND(atopcase, acpi, 1, 1, 1);
453
MODULE_DEPEND(atopcase, backlight, 1, 1, 1);
454
MODULE_DEPEND(atopcase, hid, 1, 1, 1);
455
MODULE_DEPEND(atopcase, hidbus, 1, 1, 1);
456
MODULE_DEPEND(atopcase, spibus, 1, 1, 1);
457
MODULE_VERSION(atopcase, 1);
458
SPIBUS_ACPI_PNP_INFO(atopcase_ids);
459
460