pckbc_acpi.c revision 1.20.6.3 1 1.20.6.3 jmcneill /* $NetBSD: pckbc_acpi.c,v 1.20.6.3 2007/10/05 02:23:44 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 * 3. All advertising materials mentioning features or use of this software
19 1.1 matt * must display the following acknowledgement:
20 1.1 matt * This product includes software developed by the NetBSD
21 1.1 matt * Foundation, Inc. and its contributors.
22 1.1 matt * 4. Neither the name of The NetBSD Foundation nor the names of its
23 1.1 matt * contributors may be used to endorse or promote products derived
24 1.1 matt * from this software without specific prior written permission.
25 1.1 matt *
26 1.1 matt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 1.1 matt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 1.1 matt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 1.1 matt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 1.1 matt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 1.1 matt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 1.1 matt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 1.1 matt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 1.1 matt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 1.1 matt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 1.1 matt * POSSIBILITY OF SUCH DAMAGE.
37 1.1 matt */
38 1.1 matt
39 1.1 matt /*
40 1.2 matt * ACPI attachment for the PC Keyboard Controller driver.
41 1.1 matt *
42 1.1 matt * This is a little wonky. The keyboard controller actually
43 1.2 matt * has 2 ACPI nodes: one for the controller and the keyboard
44 1.1 matt * interrupt, and one for the aux port (mouse) interrupt.
45 1.1 matt *
46 1.1 matt * For this reason, we actually attach *two* instances of this
47 1.1 matt * driver. After both of them have been found, then we attach
48 1.1 matt * sub-devices.
49 1.1 matt */
50 1.1 matt
51 1.1 matt #include <sys/cdefs.h>
52 1.20.6.3 jmcneill __KERNEL_RCSID(0, "$NetBSD: pckbc_acpi.c,v 1.20.6.3 2007/10/05 02:23:44 jmcneill Exp $");
53 1.1 matt
54 1.1 matt #include <sys/param.h>
55 1.1 matt #include <sys/systm.h>
56 1.1 matt #include <sys/kernel.h>
57 1.1 matt #include <sys/proc.h>
58 1.1 matt #include <sys/device.h>
59 1.1 matt #include <sys/malloc.h>
60 1.1 matt #include <sys/errno.h>
61 1.1 matt #include <sys/queue.h>
62 1.1 matt #include <sys/lock.h>
63 1.1 matt
64 1.1 matt #include <machine/bus.h>
65 1.1 matt
66 1.1 matt #include <dev/isa/isareg.h>
67 1.1 matt #include <dev/isa/isavar.h>
68 1.1 matt
69 1.1 matt #include <dev/ic/i8042reg.h>
70 1.1 matt #include <dev/ic/pckbcvar.h>
71 1.1 matt
72 1.1 matt #include <dev/acpi/acpivar.h>
73 1.1 matt
74 1.15 kochi static int pckbc_acpi_match(struct device *, struct cfdata *, void *);
75 1.15 kochi static void pckbc_acpi_attach(struct device *, struct device *, void *);
76 1.20.6.1 jmcneill static pnp_status_t pckbc_acpi_power(device_t, pnp_request_t, void *);
77 1.1 matt
78 1.1 matt struct pckbc_acpi_softc {
79 1.1 matt struct pckbc_softc sc_pckbc;
80 1.1 matt
81 1.1 matt isa_chipset_tag_t sc_ic;
82 1.1 matt int sc_irq;
83 1.1 matt int sc_ist;
84 1.1 matt pckbc_slot_t sc_slot;
85 1.1 matt };
86 1.1 matt
87 1.1 matt /* Save first port: */
88 1.1 matt static struct pckbc_acpi_softc *first;
89 1.1 matt
90 1.1 matt extern struct cfdriver pckbc_cd;
91 1.1 matt
92 1.1 matt CFATTACH_DECL(pckbc_acpi, sizeof(struct pckbc_acpi_softc),
93 1.1 matt pckbc_acpi_match, pckbc_acpi_attach, NULL, NULL);
94 1.1 matt
95 1.15 kochi static void pckbc_acpi_intr_establish(struct pckbc_softc *, pckbc_slot_t);
96 1.1 matt
97 1.1 matt /*
98 1.1 matt * Supported Device IDs
99 1.1 matt */
100 1.1 matt
101 1.11 mycroft static const char * const pckbc_acpi_ids_kbd[] = {
102 1.11 mycroft "PNP03??", /* Standard PC KBD port */
103 1.11 mycroft NULL
104 1.11 mycroft };
105 1.11 mycroft
106 1.11 mycroft static const char * const pckbc_acpi_ids_ms[] = {
107 1.10 mycroft "PNP0F03",
108 1.10 mycroft "PNP0F0E",
109 1.10 mycroft "PNP0F12",
110 1.1 matt "PNP0F13",
111 1.10 mycroft "PNP0F19",
112 1.10 mycroft "PNP0F1B",
113 1.10 mycroft "PNP0F1C",
114 1.1 matt NULL
115 1.1 matt };
116 1.1 matt
117 1.1 matt /*
118 1.2 matt * pckbc_acpi_match: autoconf(9) match routine
119 1.1 matt */
120 1.15 kochi static int
121 1.19 christos pckbc_acpi_match(struct device *parent, struct cfdata *match,
122 1.18 christos void *aux)
123 1.1 matt {
124 1.1 matt struct acpi_attach_args *aa = aux;
125 1.11 mycroft int rv;
126 1.1 matt
127 1.1 matt if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
128 1.12 kochi return 0;
129 1.1 matt
130 1.11 mycroft rv = acpi_match_hid(aa->aa_node->ad_devinfo, pckbc_acpi_ids_kbd);
131 1.11 mycroft if (rv)
132 1.12 kochi return rv;
133 1.11 mycroft rv = acpi_match_hid(aa->aa_node->ad_devinfo, pckbc_acpi_ids_ms);
134 1.11 mycroft if (rv)
135 1.12 kochi return rv;
136 1.12 kochi return 0;
137 1.1 matt }
138 1.1 matt
139 1.15 kochi static void
140 1.19 christos pckbc_acpi_attach(struct device *parent, struct device *self,
141 1.1 matt void *aux)
142 1.1 matt {
143 1.1 matt struct pckbc_acpi_softc *psc = (void *) self;
144 1.1 matt struct pckbc_softc *sc = &psc->sc_pckbc;
145 1.1 matt struct pckbc_internal *t;
146 1.1 matt struct acpi_attach_args *aa = aux;
147 1.1 matt bus_space_handle_t ioh_d, ioh_c;
148 1.1 matt pckbc_slot_t peer;
149 1.20.6.1 jmcneill pnp_status_t status;
150 1.1 matt struct acpi_resources res;
151 1.20.6.3 jmcneill struct acpi_io *io0, *io1, *ioswap;
152 1.1 matt struct acpi_irq *irq;
153 1.1 matt ACPI_STATUS rv;
154 1.1 matt
155 1.1 matt psc->sc_ic = aa->aa_ic;
156 1.1 matt
157 1.11 mycroft if (acpi_match_hid(aa->aa_node->ad_devinfo, pckbc_acpi_ids_kbd)) {
158 1.1 matt psc->sc_slot = PCKBC_KBD_SLOT;
159 1.1 matt peer = PCKBC_AUX_SLOT;
160 1.11 mycroft } else if (acpi_match_hid(aa->aa_node->ad_devinfo, pckbc_acpi_ids_ms)) {
161 1.1 matt psc->sc_slot = PCKBC_AUX_SLOT;
162 1.1 matt peer = PCKBC_KBD_SLOT;
163 1.1 matt } else {
164 1.1 matt printf(": unknown port!\n");
165 1.1 matt panic("pckbc_acpi_attach: impossible");
166 1.1 matt }
167 1.1 matt
168 1.17 kochi aprint_naive("\n");
169 1.17 kochi aprint_normal(": %s port\n", pckbc_slot_names[psc->sc_slot]);
170 1.1 matt
171 1.1 matt /* parse resources */
172 1.13 kochi rv = acpi_resource_parse(&sc->sc_dv, aa->aa_node->ad_handle, "_CRS",
173 1.13 kochi &res, &acpi_resource_parse_ops_default);
174 1.9 mycroft if (ACPI_FAILURE(rv))
175 1.1 matt return;
176 1.1 matt
177 1.1 matt /* find our IRQ */
178 1.1 matt irq = acpi_res_irq(&res, 0);
179 1.1 matt if (irq == NULL) {
180 1.17 kochi aprint_error("%s: unable to find irq resource\n", sc->sc_dv.dv_xname);
181 1.14 kochi goto out;
182 1.1 matt }
183 1.1 matt psc->sc_irq = irq->ar_irq;
184 1.1 matt psc->sc_ist = (irq->ar_type == ACPI_EDGE_SENSITIVE) ? IST_EDGE : IST_LEVEL;
185 1.1 matt
186 1.3 matt if (psc->sc_slot == PCKBC_KBD_SLOT)
187 1.1 matt first = psc;
188 1.1 matt
189 1.3 matt if ((!first || !first->sc_pckbc.id) &&
190 1.3 matt (psc->sc_slot == PCKBC_KBD_SLOT)) {
191 1.1 matt
192 1.1 matt io0 = acpi_res_io(&res, 0);
193 1.20.6.3 jmcneill io1 = acpi_res_io(&res, 1);
194 1.20.6.3 jmcneill if (io0 == NULL || io1 == NULL) {
195 1.17 kochi aprint_error("%s: unable to find i/o resources\n",
196 1.1 matt sc->sc_dv.dv_xname);
197 1.14 kochi goto out;
198 1.1 matt }
199 1.1 matt
200 1.20.6.3 jmcneill /*
201 1.20.6.3 jmcneill * JDM: Some firmware doesn't report resources in the order we
202 1.20.6.3 jmcneill * expect; sort IO resources here (lowest first)
203 1.20.6.3 jmcneill */
204 1.20.6.3 jmcneill if (io0->ar_base > io1->ar_base) {
205 1.20.6.3 jmcneill ioswap = io0;
206 1.20.6.3 jmcneill io0 = io1;
207 1.20.6.3 jmcneill io1 = ioswap;
208 1.20.6.3 jmcneill }
209 1.20.6.3 jmcneill
210 1.1 matt if (pckbc_is_console(aa->aa_iot, io0->ar_base)) {
211 1.1 matt t = &pckbc_consdata;
212 1.1 matt ioh_d = t->t_ioh_d;
213 1.1 matt ioh_c = t->t_ioh_c;
214 1.1 matt pckbc_console_attached = 1;
215 1.1 matt /* t->t_cmdbyte was initialized by cnattach */
216 1.1 matt } else {
217 1.1 matt if (bus_space_map(aa->aa_iot, io0->ar_base,
218 1.1 matt io0->ar_length, 0, &ioh_d) ||
219 1.1 matt bus_space_map(aa->aa_iot, io1->ar_base,
220 1.1 matt io1->ar_length, 0, &ioh_c))
221 1.1 matt panic("pckbc_acpi_attach: couldn't map");
222 1.1 matt
223 1.1 matt t = malloc(sizeof(struct pckbc_internal),
224 1.1 matt M_DEVBUF, M_WAITOK);
225 1.1 matt memset(t, 0, sizeof(*t));
226 1.1 matt t->t_iot = aa->aa_iot;
227 1.1 matt t->t_ioh_d = ioh_d;
228 1.1 matt t->t_ioh_c = ioh_c;
229 1.1 matt t->t_addr = io0->ar_base;
230 1.1 matt t->t_cmdbyte = KC8_CPU; /* Enable ports */
231 1.20 ad callout_init(&t->t_cleanup, 0);
232 1.1 matt }
233 1.1 matt
234 1.1 matt t->t_sc = &first->sc_pckbc;
235 1.1 matt first->sc_pckbc.id = t;
236 1.1 matt
237 1.20.6.1 jmcneill status = pnp_register(self, pckbc_acpi_power);
238 1.20.6.1 jmcneill if (status != PNP_STATUS_SUCCESS)
239 1.20.6.1 jmcneill aprint_error("%s: couldn't establish power handler\n",
240 1.20.6.1 jmcneill device_xname(self));
241 1.20.6.1 jmcneill
242 1.1 matt first->sc_pckbc.intr_establish = pckbc_acpi_intr_establish;
243 1.1 matt config_defer(&first->sc_pckbc.sc_dv,
244 1.1 matt (void(*)(struct device *))pckbc_attach);
245 1.20.6.2 jmcneill } else
246 1.20.6.2 jmcneill (void)pnp_register(self, pnp_generic_power);
247 1.14 kochi out:
248 1.14 kochi acpi_resource_cleanup(&res);
249 1.1 matt }
250 1.1 matt
251 1.15 kochi static void
252 1.1 matt pckbc_acpi_intr_establish(struct pckbc_softc *sc,
253 1.1 matt pckbc_slot_t slot)
254 1.1 matt {
255 1.1 matt struct pckbc_acpi_softc *psc;
256 1.1 matt isa_chipset_tag_t ic = NULL;
257 1.1 matt void *rv = NULL;
258 1.5 christos int irq = 0, ist = 0; /* XXX: gcc */
259 1.1 matt int i;
260 1.1 matt
261 1.1 matt /*
262 1.1 matt * Note we're always called with sc == first.
263 1.1 matt */
264 1.1 matt for (i = 0; i < pckbc_cd.cd_ndevs; i++) {
265 1.1 matt psc = pckbc_cd.cd_devs[i];
266 1.1 matt if (psc && psc->sc_slot == slot) {
267 1.1 matt irq = psc->sc_irq;
268 1.1 matt ist = psc->sc_ist;
269 1.1 matt ic = psc->sc_ic;
270 1.1 matt break;
271 1.1 matt }
272 1.1 matt }
273 1.1 matt if (i < pckbc_cd.cd_ndevs)
274 1.1 matt rv = isa_intr_establish(ic, irq, ist, IPL_TTY, pckbcintr, sc);
275 1.1 matt if (rv == NULL) {
276 1.17 kochi aprint_error("%s: unable to establish interrupt for %s slot\n",
277 1.1 matt sc->sc_dv.dv_xname, pckbc_slot_names[slot]);
278 1.1 matt } else {
279 1.17 kochi aprint_normal("%s: using irq %d for %s slot\n", sc->sc_dv.dv_xname,
280 1.1 matt irq, pckbc_slot_names[slot]);
281 1.1 matt }
282 1.1 matt }
283 1.20.6.1 jmcneill
284 1.20.6.1 jmcneill static pnp_status_t
285 1.20.6.1 jmcneill pckbc_acpi_power(device_t dv, pnp_request_t req, void *opaque)
286 1.20.6.1 jmcneill {
287 1.20.6.1 jmcneill return pckbc_power(dv, req, opaque);
288 1.20.6.1 jmcneill }
289