1*9108e412Spatrick /* $OpenBSD: acpidock.c,v 1.46 2022/08/10 16:58:16 patrick Exp $ */
2f2f7c420Smk /*
3f2f7c420Smk * Copyright (c) 2006,2007 Michael Knudsen <mk@openbsd.org>
4f2f7c420Smk *
5f2f7c420Smk * Permission to use, copy, modify, and distribute this software for any
6f2f7c420Smk * purpose with or without fee is hereby granted, provided that the above
7f2f7c420Smk * copyright notice and this permission notice appear in all copies.
8f2f7c420Smk *
9f2f7c420Smk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10f2f7c420Smk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11f2f7c420Smk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12f2f7c420Smk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13f2f7c420Smk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14f2f7c420Smk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15f2f7c420Smk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16f2f7c420Smk */
17f2f7c420Smk
18f2f7c420Smk #include <sys/param.h>
19f2f7c420Smk #include <sys/systm.h>
20f2f7c420Smk #include <sys/device.h>
21f2f7c420Smk #include <sys/malloc.h>
22eee1d4f9Smk #include <sys/sensors.h>
23f2f7c420Smk
24f2f7c420Smk #include <machine/bus.h>
25f2f7c420Smk
26f2f7c420Smk #include <dev/acpi/acpivar.h>
27f2f7c420Smk #include <dev/acpi/acpidev.h>
28f2f7c420Smk #include <dev/acpi/amltypes.h>
29f2f7c420Smk #include <dev/acpi/dsdt.h>
30f2f7c420Smk
312b11adf0Smk struct aml_nodelist {
322b11adf0Smk struct aml_node *node;
332b11adf0Smk TAILQ_ENTRY(aml_nodelist) entries;
342b11adf0Smk };
352b11adf0Smk
36f2f7c420Smk int acpidock_match(struct device *, void *, void *);
37f2f7c420Smk void acpidock_attach(struct device *, struct device *, void *);
38f2f7c420Smk
39471aeecfSnaddy const struct cfattach acpidock_ca = {
40f2f7c420Smk sizeof(struct acpidock_softc), acpidock_match, acpidock_attach
41f2f7c420Smk };
42f2f7c420Smk
43f2f7c420Smk struct cfdriver acpidock_cd = {
44f2f7c420Smk NULL, "acpidock", DV_DULL
45f2f7c420Smk };
46f2f7c420Smk
47f2f7c420Smk int acpidock_docklock(struct acpidock_softc *, int);
48f2f7c420Smk int acpidock_dockctl(struct acpidock_softc *, int);
49ac52191eSmk int acpidock_eject(struct acpidock_softc *, struct aml_node *);
50f2f7c420Smk int acpidock_notify(struct aml_node *, int, void *);
51f2f7c420Smk int acpidock_status(struct acpidock_softc *);
52f402adeaSjordan int acpidock_walkchildren(struct aml_node *, void *);
53f2f7c420Smk
547979ca52Sweingart int acpidock_foundejd(struct aml_node *, void *);
552b11adf0Smk
56f2f7c420Smk int
acpidock_match(struct device * parent,void * match,void * aux)57f2f7c420Smk acpidock_match(struct device *parent, void *match, void *aux)
58f2f7c420Smk {
59f2f7c420Smk struct acpi_attach_args *aaa = aux;
60f2f7c420Smk struct cfdata *cf = match;
61f2f7c420Smk
62f2f7c420Smk /* sanity */
63f2f7c420Smk if (aaa->aaa_name == NULL ||
64f2f7c420Smk strcmp(aaa->aaa_name, cf->cf_driver->cd_name) != 0 ||
65f2f7c420Smk aaa->aaa_table != NULL)
66f2f7c420Smk return (0);
67f2f7c420Smk
68f2f7c420Smk return (1);
69f2f7c420Smk }
70f2f7c420Smk
71f2f7c420Smk void
acpidock_attach(struct device * parent,struct device * self,void * aux)72f2f7c420Smk acpidock_attach(struct device *parent, struct device *self, void *aux)
73f2f7c420Smk {
74f2f7c420Smk struct acpidock_softc *sc = (struct acpidock_softc *)self;
75f2f7c420Smk struct acpi_attach_args *aa = aux;
76f2f7c420Smk
77f2f7c420Smk sc->sc_acpi = (struct acpi_softc *)parent;
783b455a03Smarco sc->sc_devnode = aa->aaa_node;
79f2f7c420Smk
803b455a03Smarco printf(": %s", sc->sc_devnode->name);
81f2f7c420Smk
8202694a45Smk acpidock_status(sc);
8302694a45Smk if (sc->sc_docked == ACPIDOCK_STATUS_DOCKED) {
84f2f7c420Smk acpidock_docklock(sc, 1);
85f2f7c420Smk acpidock_dockctl(sc, 1);
86f2f7c420Smk }
87f2f7c420Smk
88b5c69d4aSmk acpidock_status(sc);
89bdc52224Sderaadt printf("%s docked (%d)\n",
90b5c69d4aSmk sc->sc_docked == ACPIDOCK_STATUS_DOCKED ? "" : " not",
91b5c69d4aSmk sc->sc_sta);
92b5c69d4aSmk
93eee1d4f9Smk strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
94eee1d4f9Smk sizeof(sc->sc_sensdev.xname));
953ff55dfeSmk if (sc->sc_docked)
964ee99e23Smk strlcpy(sc->sc_sens.desc, "docked",
974ee99e23Smk sizeof(sc->sc_sens.desc));
983ff55dfeSmk else
994ee99e23Smk strlcpy(sc->sc_sens.desc, "not docked",
1004ee99e23Smk sizeof(sc->sc_sens.desc));
1013ff55dfeSmk
1024ee99e23Smk sc->sc_sens.type = SENSOR_INDICATOR;
1034ee99e23Smk sc->sc_sens.value = sc->sc_docked == ACPIDOCK_STATUS_DOCKED;
1043cb04c65Skspillner sc->sc_sens.status = sc->sc_docked ? SENSOR_S_OK : SENSOR_S_UNKNOWN;
1054ee99e23Smk sensor_attach(&sc->sc_sensdev, &sc->sc_sens);
106eee1d4f9Smk sensordev_install(&sc->sc_sensdev);
107eee1d4f9Smk
1082b11adf0Smk TAILQ_INIT(&sc->sc_deps_h);
109*9108e412Spatrick aml_find_node(sc->sc_acpi->sc_root, "_EJD", acpidock_foundejd, sc);
1102b11adf0Smk
1113b455a03Smarco aml_register_notify(sc->sc_devnode, aa->aaa_dev,
112f2f7c420Smk acpidock_notify, sc, ACPIDEV_NOPOLL);
113f2f7c420Smk }
114f2f7c420Smk
115f2f7c420Smk int
acpidock_status(struct acpidock_softc * sc)116f2f7c420Smk acpidock_status(struct acpidock_softc *sc)
117f2f7c420Smk {
118a95d1b72Sjordan int64_t sta;
119ffd69664Smk int rv;
120f2f7c420Smk
121fd421835Sjordan if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL,
122a95d1b72Sjordan &sta) != 0) {
123a95d1b72Sjordan sta = 0;
124f2f7c420Smk rv = 0;
12507675d65Sjordan } else
126f2f7c420Smk rv = 1;
127f2f7c420Smk
128a95d1b72Sjordan sc->sc_sta = sta;
129ffd69664Smk sc->sc_docked = sc->sc_sta & STA_PRESENT;
130f2f7c420Smk
131f2f7c420Smk return (rv);
132f2f7c420Smk }
133f2f7c420Smk
134f2f7c420Smk int
acpidock_docklock(struct acpidock_softc * sc,int lock)135f2f7c420Smk acpidock_docklock(struct acpidock_softc *sc, int lock)
136f2f7c420Smk {
137f2f7c420Smk struct aml_value cmd;
138f2f7c420Smk struct aml_value res;
1395cc6ff0cSmk int rv;
140f2f7c420Smk
141f2f7c420Smk memset(&cmd, 0, sizeof cmd);
142f2f7c420Smk cmd.v_integer = lock;
143f2f7c420Smk cmd.type = AML_OBJTYPE_INTEGER;
144fe16b7bbSmk if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_LCK", 1, &cmd,
145f2f7c420Smk &res) != 0) {
1463ebb5e6bSmk dnprintf(20, "%s: _LCK %d failed\n", DEVNAME(sc), lock);
1475cc6ff0cSmk rv = 0;
148f2f7c420Smk } else {
149ba930d8dSmk dnprintf(20, "%s: _LCK %d successful\n", DEVNAME(sc), lock);
1505cc6ff0cSmk rv = 1;
151f2f7c420Smk }
152f2f7c420Smk
1535cc6ff0cSmk aml_freevalue(&res);
1545cc6ff0cSmk
15507675d65Sjordan return (rv);
156f2f7c420Smk }
157f2f7c420Smk
158f2f7c420Smk int
acpidock_dockctl(struct acpidock_softc * sc,int dock)159f2f7c420Smk acpidock_dockctl(struct acpidock_softc *sc, int dock)
160f2f7c420Smk {
161f2f7c420Smk struct aml_value cmd;
162f2f7c420Smk struct aml_value res;
1635cc6ff0cSmk int rv;
164f2f7c420Smk
165f2f7c420Smk memset(&cmd, 0, sizeof cmd);
166435849e6Smk cmd.v_integer = dock;
167f2f7c420Smk cmd.type = AML_OBJTYPE_INTEGER;
168fe16b7bbSmk if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_DCK", 1, &cmd,
169f2f7c420Smk &res) != 0) {
170b100133bSmk dnprintf(15, "%s: _DCK %d failed\n", DEVNAME(sc), dock);
1715cc6ff0cSmk rv = 0;
172f2f7c420Smk } else {
173b100133bSmk dnprintf(15, "%s: _DCK %d successful\n", DEVNAME(sc), dock);
1745cc6ff0cSmk rv = 1;
175f2f7c420Smk }
176f2f7c420Smk
1775cc6ff0cSmk aml_freevalue(&res);
1785cc6ff0cSmk
17907675d65Sjordan return (rv);
180f2f7c420Smk }
181f2f7c420Smk
182f2f7c420Smk int
acpidock_eject(struct acpidock_softc * sc,struct aml_node * node)183ac52191eSmk acpidock_eject(struct acpidock_softc *sc, struct aml_node *node)
184f2f7c420Smk {
185f2f7c420Smk struct aml_value cmd;
186f2f7c420Smk struct aml_value res;
1875cc6ff0cSmk int rv;
188f2f7c420Smk
189f402adeaSjordan if (node != sc->sc_devnode)
190f402adeaSjordan aml_notify(node, 3);
19107675d65Sjordan
192f2f7c420Smk memset(&cmd, 0, sizeof cmd);
193f2f7c420Smk cmd.v_integer = 1;
194f2f7c420Smk cmd.type = AML_OBJTYPE_INTEGER;
195ac52191eSmk if (aml_evalname(sc->sc_acpi, node, "_EJ0", 1, &cmd,
196f2f7c420Smk &res) != 0) {
197e22ebbd4Smk dnprintf(15, "%s: _EJ0 failed\n", DEVNAME(sc));
1985cc6ff0cSmk rv = 0;
199f2f7c420Smk } else {
200e22ebbd4Smk dnprintf(15, "%s: _EJ0 successful\n", DEVNAME(sc));
2015cc6ff0cSmk rv = 1;
202f2f7c420Smk }
203f2f7c420Smk
2045cc6ff0cSmk aml_freevalue(&res);
2055cc6ff0cSmk
20607675d65Sjordan return (rv);
207f2f7c420Smk }
208f2f7c420Smk
209f2f7c420Smk int
acpidock_notify(struct aml_node * node,int notify_type,void * arg)210f2f7c420Smk acpidock_notify(struct aml_node *node, int notify_type, void *arg)
211f2f7c420Smk {
212f2f7c420Smk struct acpidock_softc *sc = arg;
213f402adeaSjordan struct aml_nodelist *n;
214f2f7c420Smk
215ac028446Smk dnprintf(5, "%s: acpidock_notify: notify %d\n", DEVNAME(sc),
216ac028446Smk notify_type);
217ac028446Smk
218f2f7c420Smk switch (notify_type) {
219f2f7c420Smk case ACPIDOCK_EVENT_INSERT:
220f2f7c420Smk acpidock_docklock(sc, 1);
221f2f7c420Smk acpidock_dockctl(sc, 1);
222ac028446Smk
22307675d65Sjordan TAILQ_FOREACH_REVERSE(n, &sc->sc_deps_h, aml_nodelisth, entries)
224f402adeaSjordan aml_notify(n->node, 0x00);
225f2f7c420Smk break;
22677d6f5e3Smk
22707675d65Sjordan case ACPIDOCK_EVENT_EJECT:
228e097d8aaSjordan case ACPIDOCK_EVENT_DEVCHECK:
229e097d8aaSjordan /* ACPI Spec says eject button press generates
230e097d8aaSjordan * a Notify(Device, 1); */
23177d6f5e3Smk TAILQ_FOREACH(n, &sc->sc_deps_h, entries)
23277d6f5e3Smk acpidock_eject(sc, n->node);
233f2f7c420Smk acpidock_dockctl(sc, 0);
234f2f7c420Smk acpidock_docklock(sc, 0);
235f2f7c420Smk
236f2f7c420Smk /* now actually undock */
237ac52191eSmk acpidock_eject(sc, sc->sc_devnode);
238f2f7c420Smk break;
239f2f7c420Smk }
240f2f7c420Smk
241f2f7c420Smk acpidock_status(sc);
2424ee99e23Smk sc->sc_sens.value = sc->sc_docked == ACPIDOCK_STATUS_DOCKED;
2433cb04c65Skspillner sc->sc_sens.status = sc->sc_docked ? SENSOR_S_OK : SENSOR_S_UNKNOWN;
2443ff55dfeSmk if (sc->sc_docked)
2454ee99e23Smk strlcpy(sc->sc_sens.desc, "docked",
2464ee99e23Smk sizeof(sc->sc_sens.desc));
2473ff55dfeSmk else
2484ee99e23Smk strlcpy(sc->sc_sens.desc, "not docked",
2494ee99e23Smk sizeof(sc->sc_sens.desc));
2503ff55dfeSmk
25107675d65Sjordan printf("%s: %s\n",
25207675d65Sjordan DEVNAME(sc), sc->sc_docked == ACPIDOCK_STATUS_DOCKED ?
25307675d65Sjordan "docked" : "undocked");
254f2f7c420Smk
255f2f7c420Smk return (0);
256f2f7c420Smk }
257f2f7c420Smk
258106fb80aSjordan int
acpidock_walkchildren(struct aml_node * node,void * arg)259106fb80aSjordan acpidock_walkchildren(struct aml_node *node, void *arg)
260f402adeaSjordan {
261f402adeaSjordan struct acpidock_softc *sc = arg;
262f402adeaSjordan struct aml_nodelist *n;
263f402adeaSjordan
264f402adeaSjordan if (node && node->value && node->value->type == AML_OBJTYPE_DEVICE) {
265f402adeaSjordan n = malloc(sizeof *n, M_DEVBUF, M_WAITOK | M_ZERO);
266f402adeaSjordan n->node = node;
267f402adeaSjordan dnprintf(10,"%s depends on", aml_nodename(node));
268f402adeaSjordan dnprintf(10,"%s\n", aml_nodename(sc->sc_devnode));
269f402adeaSjordan TAILQ_INSERT_TAIL(&sc->sc_deps_h, n, entries);
270f402adeaSjordan }
27107675d65Sjordan
27207675d65Sjordan return (0);
273f402adeaSjordan }
274f402adeaSjordan
2757979ca52Sweingart int
acpidock_foundejd(struct aml_node * node,void * arg)2762b11adf0Smk acpidock_foundejd(struct aml_node *node, void *arg)
2772b11adf0Smk {
2782b11adf0Smk struct acpidock_softc *sc = (struct acpidock_softc *)arg;
2792b11adf0Smk struct aml_value res;
280f402adeaSjordan struct aml_node *dock;
2812b11adf0Smk
2823b455a03Smarco dnprintf(15, "%s: %s", DEVNAME(sc), node->name);
2832b11adf0Smk
28407675d65Sjordan if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1)
2852b11adf0Smk printf(": error\n");
28607675d65Sjordan else {
287*9108e412Spatrick dock = aml_searchname(sc->sc_acpi->sc_root, res.v_string);
28807675d65Sjordan
28907675d65Sjordan if (dock == sc->sc_devnode)
290f402adeaSjordan /* Add all children devices of Device containing _EJD */
29107675d65Sjordan aml_walknodes(node->parent, AML_WALK_POST,
29207675d65Sjordan acpidock_walkchildren, sc);
2932b11adf0Smk aml_freevalue(&res);
294f402adeaSjordan }
2957979ca52Sweingart
29607675d65Sjordan return (0);
2972b11adf0Smk }
298