pckbc_acpi.c revision 1.38 1 1.38 jmcneill /* $NetBSD: pckbc_acpi.c,v 1.38 2020/12/06 12:23:13 jmcneill Exp $ */
2 1.1 matt
3 1.1 matt /*-
4 1.1 matt * Copyright (c) 2000 The NetBSD Foundation, Inc.
5 1.1 matt * All rights reserved.
6 1.1 matt *
7 1.1 matt * This code is derived from software contributed to The NetBSD Foundation
8 1.1 matt * by Jason R. Thorpe.
9 1.1 matt *
10 1.1 matt * Redistribution and use in source and binary forms, with or without
11 1.1 matt * modification, are permitted provided that the following conditions
12 1.1 matt * are met:
13 1.1 matt * 1. Redistributions of source code must retain the above copyright
14 1.1 matt * notice, this list of conditions and the following disclaimer.
15 1.1 matt * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 matt * notice, this list of conditions and the following disclaimer in the
17 1.1 matt * documentation and/or other materials provided with the distribution.
18 1.1 matt *
19 1.1 matt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 matt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 matt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 matt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 matt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 matt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 matt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 matt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 matt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 matt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 matt * POSSIBILITY OF SUCH DAMAGE.
30 1.1 matt */
31 1.1 matt
32 1.1 matt /*
33 1.2 matt * ACPI attachment for the PC Keyboard Controller driver.
34 1.1 matt *
35 1.1 matt * This is a little wonky. The keyboard controller actually
36 1.2 matt * has 2 ACPI nodes: one for the controller and the keyboard
37 1.1 matt * interrupt, and one for the aux port (mouse) interrupt.
38 1.1 matt *
39 1.1 matt * For this reason, we actually attach *two* instances of this
40 1.1 matt * driver. After both of them have been found, then we attach
41 1.1 matt * sub-devices.
42 1.1 matt */
43 1.1 matt
44 1.1 matt #include <sys/cdefs.h>
45 1.38 jmcneill __KERNEL_RCSID(0, "$NetBSD: pckbc_acpi.c,v 1.38 2020/12/06 12:23:13 jmcneill Exp $");
46 1.1 matt
47 1.1 matt #include <sys/param.h>
48 1.33 jruoho #include <sys/callout.h>
49 1.1 matt #include <sys/device.h>
50 1.1 matt #include <sys/malloc.h>
51 1.33 jruoho #include <sys/systm.h>
52 1.33 jruoho
53 1.33 jruoho #include <dev/acpi/acpivar.h>
54 1.38 jmcneill #include <dev/acpi/acpi_intr.h>
55 1.1 matt
56 1.1 matt #include <dev/isa/isareg.h>
57 1.1 matt
58 1.1 matt #include <dev/ic/i8042reg.h>
59 1.1 matt #include <dev/ic/pckbcvar.h>
60 1.1 matt
61 1.36 riastrad #include "ioconf.h"
62 1.36 riastrad
63 1.25 cube static int pckbc_acpi_match(device_t, cfdata_t, void *);
64 1.25 cube static void pckbc_acpi_attach(device_t, device_t, void *);
65 1.1 matt
66 1.1 matt struct pckbc_acpi_softc {
67 1.1 matt struct pckbc_softc sc_pckbc;
68 1.1 matt
69 1.38 jmcneill ACPI_HANDLE sc_handle;
70 1.1 matt pckbc_slot_t sc_slot;
71 1.1 matt };
72 1.1 matt
73 1.1 matt /* Save first port: */
74 1.1 matt static struct pckbc_acpi_softc *first;
75 1.1 matt
76 1.25 cube CFATTACH_DECL_NEW(pckbc_acpi, sizeof(struct pckbc_acpi_softc),
77 1.1 matt pckbc_acpi_match, pckbc_acpi_attach, NULL, NULL);
78 1.1 matt
79 1.15 kochi static void pckbc_acpi_intr_establish(struct pckbc_softc *, pckbc_slot_t);
80 1.25 cube static void pckbc_acpi_finish_attach(device_t);
81 1.1 matt
82 1.1 matt /*
83 1.1 matt * Supported Device IDs
84 1.1 matt */
85 1.1 matt
86 1.11 mycroft static const char * const pckbc_acpi_ids_kbd[] = {
87 1.11 mycroft "PNP03??", /* Standard PC KBD port */
88 1.11 mycroft NULL
89 1.11 mycroft };
90 1.11 mycroft
91 1.11 mycroft static const char * const pckbc_acpi_ids_ms[] = {
92 1.10 mycroft "PNP0F03",
93 1.10 mycroft "PNP0F0E",
94 1.10 mycroft "PNP0F12",
95 1.1 matt "PNP0F13",
96 1.10 mycroft "PNP0F19",
97 1.10 mycroft "PNP0F1B",
98 1.10 mycroft "PNP0F1C",
99 1.30 pgoyette "SYN0302",
100 1.1 matt NULL
101 1.1 matt };
102 1.1 matt
103 1.1 matt /*
104 1.2 matt * pckbc_acpi_match: autoconf(9) match routine
105 1.1 matt */
106 1.15 kochi static int
107 1.25 cube pckbc_acpi_match(device_t parent, cfdata_t match, void *aux)
108 1.1 matt {
109 1.1 matt struct acpi_attach_args *aa = aux;
110 1.11 mycroft int rv;
111 1.1 matt
112 1.1 matt if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
113 1.12 kochi return 0;
114 1.1 matt
115 1.11 mycroft rv = acpi_match_hid(aa->aa_node->ad_devinfo, pckbc_acpi_ids_kbd);
116 1.11 mycroft if (rv)
117 1.12 kochi return rv;
118 1.11 mycroft rv = acpi_match_hid(aa->aa_node->ad_devinfo, pckbc_acpi_ids_ms);
119 1.11 mycroft if (rv)
120 1.12 kochi return rv;
121 1.12 kochi return 0;
122 1.1 matt }
123 1.1 matt
124 1.15 kochi static void
125 1.25 cube pckbc_acpi_attach(device_t parent, device_t self, void *aux)
126 1.1 matt {
127 1.25 cube struct pckbc_acpi_softc *psc = device_private(self);
128 1.1 matt struct pckbc_softc *sc = &psc->sc_pckbc;
129 1.1 matt struct pckbc_internal *t;
130 1.1 matt struct acpi_attach_args *aa = aux;
131 1.1 matt bus_space_handle_t ioh_d, ioh_c;
132 1.1 matt struct acpi_resources res;
133 1.21 jmcneill struct acpi_io *io0, *io1, *ioswap;
134 1.1 matt struct acpi_irq *irq;
135 1.1 matt ACPI_STATUS rv;
136 1.1 matt
137 1.25 cube sc->sc_dv = self;
138 1.1 matt
139 1.11 mycroft if (acpi_match_hid(aa->aa_node->ad_devinfo, pckbc_acpi_ids_kbd)) {
140 1.1 matt psc->sc_slot = PCKBC_KBD_SLOT;
141 1.11 mycroft } else if (acpi_match_hid(aa->aa_node->ad_devinfo, pckbc_acpi_ids_ms)) {
142 1.1 matt psc->sc_slot = PCKBC_AUX_SLOT;
143 1.1 matt } else {
144 1.25 cube aprint_error(": unknown port!\n");
145 1.1 matt panic("pckbc_acpi_attach: impossible");
146 1.1 matt }
147 1.1 matt
148 1.31 jmcneill aprint_normal(" (%s port)", pckbc_slot_names[psc->sc_slot]);
149 1.1 matt
150 1.1 matt /* parse resources */
151 1.25 cube rv = acpi_resource_parse(sc->sc_dv, aa->aa_node->ad_handle, "_CRS",
152 1.13 kochi &res, &acpi_resource_parse_ops_default);
153 1.9 mycroft if (ACPI_FAILURE(rv))
154 1.1 matt return;
155 1.1 matt
156 1.1 matt /* find our IRQ */
157 1.1 matt irq = acpi_res_irq(&res, 0);
158 1.1 matt if (irq == NULL) {
159 1.25 cube aprint_error_dev(self, "unable to find irq resource\n");
160 1.14 kochi goto out;
161 1.1 matt }
162 1.38 jmcneill psc->sc_handle = aa->aa_node->ad_handle;
163 1.1 matt
164 1.3 matt if (psc->sc_slot == PCKBC_KBD_SLOT)
165 1.1 matt first = psc;
166 1.1 matt
167 1.3 matt if ((!first || !first->sc_pckbc.id) &&
168 1.3 matt (psc->sc_slot == PCKBC_KBD_SLOT)) {
169 1.1 matt
170 1.1 matt io0 = acpi_res_io(&res, 0);
171 1.21 jmcneill io1 = acpi_res_io(&res, 1);
172 1.21 jmcneill if (io0 == NULL || io1 == NULL) {
173 1.25 cube aprint_error_dev(self,
174 1.25 cube "unable to find i/o resources\n");
175 1.14 kochi goto out;
176 1.1 matt }
177 1.1 matt
178 1.21 jmcneill /*
179 1.21 jmcneill * JDM: Some firmware doesn't report resources in the order we
180 1.21 jmcneill * expect; sort IO resources here (lowest first)
181 1.21 jmcneill */
182 1.21 jmcneill if (io0->ar_base > io1->ar_base) {
183 1.21 jmcneill ioswap = io0;
184 1.21 jmcneill io0 = io1;
185 1.21 jmcneill io1 = ioswap;
186 1.21 jmcneill }
187 1.21 jmcneill
188 1.1 matt if (pckbc_is_console(aa->aa_iot, io0->ar_base)) {
189 1.1 matt t = &pckbc_consdata;
190 1.1 matt ioh_d = t->t_ioh_d;
191 1.1 matt ioh_c = t->t_ioh_c;
192 1.1 matt pckbc_console_attached = 1;
193 1.1 matt /* t->t_cmdbyte was initialized by cnattach */
194 1.1 matt } else {
195 1.1 matt if (bus_space_map(aa->aa_iot, io0->ar_base,
196 1.1 matt io0->ar_length, 0, &ioh_d) ||
197 1.1 matt bus_space_map(aa->aa_iot, io1->ar_base,
198 1.32 jmcneill io1->ar_length, 0, &ioh_c)) {
199 1.32 jmcneill aprint_error_dev(self,
200 1.32 jmcneill "unable to map registers\n");
201 1.32 jmcneill goto out;
202 1.32 jmcneill }
203 1.1 matt
204 1.1 matt t = malloc(sizeof(struct pckbc_internal),
205 1.28 jmcneill M_DEVBUF, M_WAITOK|M_ZERO);
206 1.1 matt t->t_iot = aa->aa_iot;
207 1.1 matt t->t_ioh_d = ioh_d;
208 1.1 matt t->t_ioh_c = ioh_c;
209 1.1 matt t->t_addr = io0->ar_base;
210 1.1 matt t->t_cmdbyte = KC8_CPU; /* Enable ports */
211 1.20 ad callout_init(&t->t_cleanup, 0);
212 1.1 matt }
213 1.1 matt
214 1.1 matt t->t_sc = &first->sc_pckbc;
215 1.1 matt first->sc_pckbc.id = t;
216 1.1 matt
217 1.23 jmcneill if (!pmf_device_register(self, NULL, pckbc_resume))
218 1.25 cube aprint_error_dev(self,
219 1.25 cube "couldn't establish power handler\n");
220 1.23 jmcneill
221 1.1 matt first->sc_pckbc.intr_establish = pckbc_acpi_intr_establish;
222 1.25 cube config_defer(first->sc_pckbc.sc_dv, pckbc_acpi_finish_attach);
223 1.32 jmcneill }
224 1.32 jmcneill
225 1.32 jmcneill out:
226 1.32 jmcneill if (!pmf_device_register(self, NULL, NULL))
227 1.23 jmcneill aprint_error_dev(self, "couldn't establish power handler\n");
228 1.14 kochi acpi_resource_cleanup(&res);
229 1.1 matt }
230 1.1 matt
231 1.15 kochi static void
232 1.25 cube pckbc_acpi_intr_establish(struct pckbc_softc *sc, pckbc_slot_t slot)
233 1.1 matt {
234 1.37 rin struct pckbc_acpi_softc *psc = NULL; /* XXX: gcc */
235 1.1 matt void *rv = NULL;
236 1.38 jmcneill ACPI_HANDLE handle;
237 1.38 jmcneill char intr_name[64];
238 1.1 matt int i;
239 1.1 matt
240 1.1 matt /*
241 1.1 matt * Note we're always called with sc == first.
242 1.1 matt */
243 1.1 matt for (i = 0; i < pckbc_cd.cd_ndevs; i++) {
244 1.29 cegger psc = device_lookup_private(&pckbc_cd, i);
245 1.1 matt if (psc && psc->sc_slot == slot) {
246 1.38 jmcneill handle = psc->sc_handle;
247 1.1 matt break;
248 1.1 matt }
249 1.1 matt }
250 1.35 jdolecek if (i < pckbc_cd.cd_ndevs) {
251 1.38 jmcneill snprintf(intr_name, sizeof(intr_name), "%s %s",
252 1.35 jdolecek device_xname(psc->sc_pckbc.sc_dv), pckbc_slot_names[slot]);
253 1.35 jdolecek
254 1.38 jmcneill rv = acpi_intr_establish(sc->sc_dv, (uint64_t)(uintptr_t)handle,
255 1.38 jmcneill IPL_TTY, false, pckbcintr, sc, intr_name);
256 1.35 jdolecek }
257 1.1 matt if (rv == NULL) {
258 1.25 cube aprint_error_dev(sc->sc_dv,
259 1.25 cube "unable to establish interrupt for %s slot\n",
260 1.25 cube pckbc_slot_names[slot]);
261 1.1 matt } else {
262 1.38 jmcneill aprint_normal_dev(sc->sc_dv, "using %s for %s slot\n",
263 1.38 jmcneill acpi_intr_string(rv, intr_name, sizeof(intr_name)),
264 1.38 jmcneill pckbc_slot_names[slot]);
265 1.1 matt }
266 1.1 matt }
267 1.25 cube
268 1.25 cube static void
269 1.25 cube pckbc_acpi_finish_attach(device_t dv)
270 1.25 cube {
271 1.25 cube
272 1.25 cube pckbc_attach(device_private(dv));
273 1.25 cube }
274