xref: /illumos-gate/usr/src/uts/i86pc/io/isa.c (revision 843e1988)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  *	ISA bus nexus driver
30  */
31 
32 #include <sys/types.h>
33 #include <sys/cmn_err.h>
34 #include <sys/conf.h>
35 #include <sys/modctl.h>
36 #include <sys/autoconf.h>
37 #include <sys/errno.h>
38 #include <sys/debug.h>
39 #include <sys/kmem.h>
40 #include <sys/psm.h>
41 #include <sys/ddidmareq.h>
42 #include <sys/ddi_impldefs.h>
43 #include <sys/dma_engine.h>
44 #include <sys/ddi.h>
45 #include <sys/sunddi.h>
46 #include <sys/sunndi.h>
47 #include <sys/acpi/acpi_enum.h>
48 #if defined(__xpv)
49 #include <sys/hypervisor.h>
50 #include <sys/evtchn_impl.h>
51 #endif
52 
53 
54 extern int isa_resource_setup(void);
55 static char USED_RESOURCES[] = "used-resources";
56 static void isa_alloc_nodes(dev_info_t *);
57 static void enumerate_BIOS_serial(dev_info_t *);
58 
59 #define	BIOS_DATA_AREA	0x400
60 /*
61  * #define ISA_DEBUG 1
62  */
63 
64 /*
65  *      Local data
66  */
67 static ddi_dma_lim_t ISA_dma_limits = {
68 	0,		/* address low				*/
69 	0x00ffffff,	/* address high				*/
70 	0,		/* counter max				*/
71 	1,		/* burstsize				*/
72 	DMA_UNIT_8,	/* minimum xfer				*/
73 	0,		/* dma speed				*/
74 	(uint_t)DMALIM_VER0, /* version				*/
75 	0x0000ffff,	/* address register			*/
76 	0x0000ffff,	/* counter register			*/
77 	1,		/* sector size				*/
78 	0x00000001,	/* scatter/gather list length		*/
79 	(uint_t)0xffffffff /* request size			*/
80 };
81 
82 static ddi_dma_attr_t ISA_dma_attr = {
83 	DMA_ATTR_V0,
84 	(unsigned long long)0,
85 	(unsigned long long)0x00ffffff,
86 	0x0000ffff,
87 	1,
88 	1,
89 	1,
90 	(unsigned long long)0xffffffff,
91 	(unsigned long long)0x0000ffff,
92 	1,
93 	1,
94 	0
95 };
96 
97 
98 /*
99  * Config information
100  */
101 
102 static int
103 isa_dma_allochdl(dev_info_t *, dev_info_t *, ddi_dma_attr_t *,
104     int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *);
105 
106 static int
107 isa_dma_mctl(dev_info_t *, dev_info_t *, ddi_dma_handle_t, enum ddi_dma_ctlops,
108     off_t *, size_t *, caddr_t *, uint_t);
109 
110 static int
111 isa_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
112 
113 struct bus_ops isa_bus_ops = {
114 	BUSO_REV,
115 	i_ddi_bus_map,
116 	NULL,
117 	NULL,
118 	NULL,
119 	i_ddi_map_fault,
120 	ddi_dma_map,
121 	isa_dma_allochdl,
122 	ddi_dma_freehdl,
123 	ddi_dma_bindhdl,
124 	ddi_dma_unbindhdl,
125 	ddi_dma_flush,
126 	ddi_dma_win,
127 	isa_dma_mctl,
128 	isa_ctlops,
129 	ddi_bus_prop_op,
130 	NULL,		/* (*bus_get_eventcookie)();	*/
131 	NULL,		/* (*bus_add_eventcall)();	*/
132 	NULL,		/* (*bus_remove_eventcall)();	*/
133 	NULL,		/* (*bus_post_event)();		*/
134 	NULL,		/* (*bus_intr_ctl)(); */
135 	NULL,		/* (*bus_config)(); */
136 	NULL,		/* (*bus_unconfig)(); */
137 	NULL,		/* (*bus_fm_init)(); */
138 	NULL,		/* (*bus_fm_fini)(); */
139 	NULL,		/* (*bus_fm_access_enter)(); */
140 	NULL,		/* (*bus_fm_access_exit)(); */
141 	NULL,		/* (*bus_power)(); */
142 	i_ddi_intr_ops	/* (*bus_intr_op)(); */
143 };
144 
145 
146 static int isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
147 
148 /*
149  * Internal isa ctlops support routines
150  */
151 static int isa_initchild(dev_info_t *child);
152 
153 struct dev_ops isa_ops = {
154 	DEVO_REV,		/* devo_rev, */
155 	0,			/* refcnt  */
156 	ddi_no_info,		/* info */
157 	nulldev,		/* identify */
158 	nulldev,		/* probe */
159 	isa_attach,		/* attach */
160 	nodev,			/* detach */
161 	nodev,			/* reset */
162 	(struct cb_ops *)0,	/* driver operations */
163 	&isa_bus_ops	/* bus operations */
164 
165 };
166 
167 /*
168  * Module linkage information for the kernel.
169  */
170 
171 static struct modldrv modldrv = {
172 	&mod_driverops, /* Type of module.  This is ISA bus driver */
173 	"isa nexus driver for 'ISA' %I%",
174 	&isa_ops,	/* driver ops */
175 };
176 
177 static struct modlinkage modlinkage = {
178 	MODREV_1,
179 	&modldrv,
180 	NULL
181 };
182 
183 int
184 _init(void)
185 {
186 	return (mod_install(&modlinkage));
187 }
188 
189 int
190 _fini(void)
191 {
192 	return (mod_remove(&modlinkage));
193 }
194 
195 int
196 _info(struct modinfo *modinfop)
197 {
198 	return (mod_info(&modlinkage, modinfop));
199 }
200 
201 static int
202 isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
203 {
204 	int rval;
205 
206 #if defined(__xpv)
207 	/*
208 	 * don't allow isa to attach in domU. this can happen if someone sets
209 	 * the console wrong, etc. ISA devices assume the H/W is there and
210 	 * will cause the domU to panic.
211 	 */
212 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
213 		return (DDI_FAILURE);
214 	}
215 #endif
216 
217 	if (cmd != DDI_ATTACH)
218 		return (DDI_FAILURE);
219 
220 	if ((rval = i_dmae_init(devi)) == DDI_SUCCESS) {
221 		ddi_report_dev(devi);
222 		/*
223 		 * Enumerate children -- invoking ACPICA
224 		 * This is normally in bus_config(), but we need this
225 		 * to happen earlier to boot.
226 		 */
227 		isa_alloc_nodes(devi);
228 	}
229 	return (rval);
230 }
231 
232 static int
233 isa_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *dma_attr,
234     int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep)
235 {
236 	ddi_dma_attr_merge(dma_attr, &ISA_dma_attr);
237 	return (ddi_dma_allochdl(dip, rdip, dma_attr, waitfp, arg, handlep));
238 }
239 
240 static int
241 isa_dma_mctl(dev_info_t *dip, dev_info_t *rdip,
242     ddi_dma_handle_t handle, enum ddi_dma_ctlops request,
243     off_t *offp, size_t *lenp, caddr_t *objp, uint_t flags)
244 {
245 	int rval;
246 	ddi_dma_lim_t defalt;
247 	int arg = (int)(uintptr_t)objp;
248 
249 	switch (request) {
250 
251 	case DDI_DMA_E_PROG:
252 		return (i_dmae_prog(rdip, (struct ddi_dmae_req *)offp,
253 		    (ddi_dma_cookie_t *)lenp, arg));
254 
255 	case DDI_DMA_E_ACQUIRE:
256 		return (i_dmae_acquire(rdip, arg, (int(*)(caddr_t))offp,
257 		    (caddr_t)lenp));
258 
259 	case DDI_DMA_E_FREE:
260 		return (i_dmae_free(rdip, arg));
261 
262 	case DDI_DMA_E_STOP:
263 		i_dmae_stop(rdip, arg);
264 		return (DDI_SUCCESS);
265 
266 	case DDI_DMA_E_ENABLE:
267 		i_dmae_enable(rdip, arg);
268 		return (DDI_SUCCESS);
269 
270 	case DDI_DMA_E_DISABLE:
271 		i_dmae_disable(rdip, arg);
272 		return (DDI_SUCCESS);
273 
274 	case DDI_DMA_E_GETCNT:
275 		i_dmae_get_chan_stat(rdip, arg, NULL, (int *)lenp);
276 		return (DDI_SUCCESS);
277 
278 	case DDI_DMA_E_SWSETUP:
279 		return (i_dmae_swsetup(rdip, (struct ddi_dmae_req *)offp,
280 		    (ddi_dma_cookie_t *)lenp, arg));
281 
282 	case DDI_DMA_E_SWSTART:
283 		i_dmae_swstart(rdip, arg);
284 		return (DDI_SUCCESS);
285 
286 	case DDI_DMA_E_GETLIM:
287 		bcopy(&ISA_dma_limits, objp, sizeof (ddi_dma_lim_t));
288 		return (DDI_SUCCESS);
289 
290 	case DDI_DMA_E_GETATTR:
291 		bcopy(&ISA_dma_attr, objp, sizeof (ddi_dma_attr_t));
292 		return (DDI_SUCCESS);
293 
294 	case DDI_DMA_E_1STPTY:
295 		{
296 			struct ddi_dmae_req req1stpty =
297 			    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
298 			if (arg == 0) {
299 				req1stpty.der_command = DMAE_CMD_TRAN;
300 				req1stpty.der_trans = DMAE_TRANS_DMND;
301 			} else {
302 				req1stpty.der_trans = DMAE_TRANS_CSCD;
303 			}
304 			return (i_dmae_prog(rdip, &req1stpty, NULL, arg));
305 		}
306 
307 	case DDI_DMA_IOPB_ALLOC:	/* get contiguous DMA-able memory */
308 	case DDI_DMA_SMEM_ALLOC:
309 		if (!offp) {
310 			defalt = ISA_dma_limits;
311 			offp = (off_t *)&defalt;
312 		}
313 		/*FALLTHROUGH*/
314 	default:
315 		rval = ddi_dma_mctl(dip, rdip, handle, request, offp,
316 		    lenp, objp, flags);
317 	}
318 	return (rval);
319 }
320 
321 /*
322  * Check if driver should be treated as an old pre 2.6 driver
323  */
324 static int
325 old_driver(dev_info_t *dip)
326 {
327 	extern int ignore_hardware_nodes;	/* force flag from ddi_impl.c */
328 
329 	if (ndi_dev_is_persistent_node(dip)) {
330 		if (ignore_hardware_nodes)
331 			return (1);
332 		if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
333 		    "ignore-hardware-nodes", -1) != -1)
334 			return (1);
335 	}
336 	return (0);
337 }
338 
339 typedef struct {
340 	uint32_t phys_hi;
341 	uint32_t phys_lo;
342 	uint32_t size;
343 } isa_regs_t;
344 
345 /*
346  * Return non-zero if device in tree is a PnP isa device
347  */
348 static int
349 is_pnpisa(dev_info_t *dip)
350 {
351 	isa_regs_t *isa_regs;
352 	int proplen, pnpisa;
353 
354 	if (ndi_dev_is_persistent_node(dip) == 0)
355 		return (0);
356 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
357 	    (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) {
358 		return (0);
359 	}
360 	pnpisa = isa_regs[0].phys_hi & 0x80000000;
361 	/*
362 	 * free the memory allocated by ddi_getlongprop().
363 	 */
364 	kmem_free(isa_regs, proplen);
365 	if (pnpisa)
366 		return (1);
367 	else
368 		return (0);
369 }
370 
371 /*ARGSUSED*/
372 static int
373 isa_ctlops(dev_info_t *dip, dev_info_t *rdip,
374 	ddi_ctl_enum_t ctlop, void *arg, void *result)
375 {
376 	switch (ctlop) {
377 	case DDI_CTLOPS_REPORTDEV:
378 		if (rdip == (dev_info_t *)0)
379 			return (DDI_FAILURE);
380 		cmn_err(CE_CONT, "?ISA-device: %s%d\n",
381 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
382 		return (DDI_SUCCESS);
383 
384 	case DDI_CTLOPS_INITCHILD:
385 		/*
386 		 * older drivers aren't expecting the "standard" device
387 		 * node format used by the hardware nodes.  these drivers
388 		 * only expect their own properties set in their driver.conf
389 		 * files.  so they tell us not to call them with hardware
390 		 * nodes by setting the property "ignore-hardware-nodes".
391 		 */
392 		if (old_driver((dev_info_t *)arg)) {
393 			return (DDI_NOT_WELL_FORMED);
394 		}
395 
396 		return (isa_initchild((dev_info_t *)arg));
397 
398 	case DDI_CTLOPS_UNINITCHILD:
399 		impl_ddi_sunbus_removechild((dev_info_t *)arg);
400 		return (DDI_SUCCESS);
401 
402 	case DDI_CTLOPS_SIDDEV:
403 		/*
404 		 * All ISA devices need to do confirming probes
405 		 * unless they are PnP ISA.
406 		 */
407 		if (is_pnpisa(dip))
408 			return (DDI_SUCCESS);
409 		else
410 			return (DDI_FAILURE);
411 
412 	default:
413 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
414 	}
415 }
416 
417 static void
418 isa_vendor(uint32_t id, char *vendor)
419 {
420 	vendor[0] = '@' + ((id >> 26) & 0x1f);
421 	vendor[1] = '@' + ((id >> 21) & 0x1f);
422 	vendor[2] = '@' + ((id >> 16) & 0x1f);
423 	vendor[3] = 0;
424 }
425 
426 /*
427  * Name a child
428  */
429 static int
430 isa_name_child(dev_info_t *child, char *name, int namelen)
431 {
432 	char vendor[8];
433 	int device;
434 	uint32_t serial;
435 	int func;
436 	int bustype;
437 	uint32_t base;
438 	int proplen;
439 	int pnpisa = 0;
440 	isa_regs_t *isa_regs;
441 
442 	void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **);
443 
444 	/*
445 	 * older drivers aren't expecting the "standard" device
446 	 * node format used by the hardware nodes.  these drivers
447 	 * only expect their own properties set in their driver.conf
448 	 * files.  so they tell us not to call them with hardware
449 	 * nodes by setting the property "ignore-hardware-nodes".
450 	 */
451 	if (old_driver(child))
452 		return (DDI_FAILURE);
453 
454 	/*
455 	 * Fill in parent-private data
456 	 */
457 	if (ddi_get_parent_data(child) == NULL) {
458 		struct ddi_parent_private_data *pdptr;
459 		make_ddi_ppd(child, &pdptr);
460 		ddi_set_parent_data(child, pdptr);
461 	}
462 
463 	if (ndi_dev_is_persistent_node(child) == 0) {
464 		/*
465 		 * For .conf nodes, generate name from parent private data
466 		 */
467 		name[0] = '\0';
468 		if (sparc_pd_getnreg(child) > 0) {
469 			(void) snprintf(name, namelen, "%x,%x",
470 			    (uint_t)sparc_pd_getreg(child, 0)->regspec_bustype,
471 			    (uint_t)sparc_pd_getreg(child, 0)->regspec_addr);
472 		}
473 		return (DDI_SUCCESS);
474 	}
475 
476 	/*
477 	 * For hw nodes, look up "reg" property
478 	 */
479 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
480 	    (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) {
481 		return (DDI_FAILURE);
482 	}
483 
484 	/*
485 	 * extract the device identifications
486 	 */
487 	pnpisa = isa_regs[0].phys_hi & 0x80000000;
488 	if (pnpisa) {
489 		isa_vendor(isa_regs[0].phys_hi, vendor);
490 		device = isa_regs[0].phys_hi & 0xffff;
491 		serial = isa_regs[0].phys_lo;
492 		func = (isa_regs[0].size >> 24) & 0xff;
493 		if (func != 0)
494 			(void) snprintf(name, namelen, "pnp%s,%04x,%x,%x",
495 			    vendor, device, serial, func);
496 		else
497 			(void) snprintf(name, namelen, "pnp%s,%04x,%x",
498 			    vendor, device, serial);
499 	} else {
500 		bustype = isa_regs[0].phys_hi;
501 		base = isa_regs[0].phys_lo;
502 		(void) sprintf(name, "%x,%x", bustype, base);
503 	}
504 
505 	/*
506 	 * free the memory allocated by ddi_getlongprop().
507 	 */
508 	kmem_free(isa_regs, proplen);
509 
510 	return (DDI_SUCCESS);
511 }
512 
513 static int
514 isa_initchild(dev_info_t *child)
515 {
516 	char name[80];
517 
518 	if (isa_name_child(child, name, 80) != DDI_SUCCESS)
519 		return (DDI_FAILURE);
520 	ddi_set_name_addr(child, name);
521 
522 	if (ndi_dev_is_persistent_node(child) != 0)
523 		return (DDI_SUCCESS);
524 
525 	/*
526 	 * This is a .conf node, try merge properties onto a
527 	 * hw node with the same name.
528 	 */
529 	if (ndi_merge_node(child, isa_name_child) == DDI_SUCCESS) {
530 		/*
531 		 * Return failure to remove node
532 		 */
533 		impl_ddi_sunbus_removechild(child);
534 		return (DDI_FAILURE);
535 	}
536 	/*
537 	 * Cannot merge node, permit pseudo children
538 	 */
539 	return (DDI_SUCCESS);
540 }
541 
542 /*
543  * called when ACPI enumeration is not used
544  */
545 static void
546 add_known_used_resources(void)
547 {
548 	/* needs to be in increasing order */
549 	int intr[] = {0x1, 0x3, 0x4, 0x6, 0x7, 0xc};
550 	int dma[] = {0x2};
551 	int io[] = {0x60, 0x1, 0x64, 0x1, 0x2f8, 0x8, 0x378, 0x8, 0x3f0, 0x10,
552 	    0x778, 0x4};
553 	dev_info_t *usedrdip;
554 
555 	usedrdip = ddi_find_devinfo(USED_RESOURCES, -1, 0);
556 
557 	if (usedrdip == NULL) {
558 		(void) ndi_devi_alloc_sleep(ddi_root_node(), USED_RESOURCES,
559 		    (pnode_t)DEVI_SID_NODEID, &usedrdip);
560 	}
561 
562 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
563 	    "interrupts", (int *)intr, (int)(sizeof (intr) / sizeof (int)));
564 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
565 	    "io-space", (int *)io, (int)(sizeof (io) / sizeof (int)));
566 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
567 	    "dma-channels", (int *)dma, (int)(sizeof (dma) / sizeof (int)));
568 	(void) ndi_devi_bind_driver(usedrdip, 0);
569 
570 }
571 
572 static void
573 isa_alloc_nodes(dev_info_t *isa_dip)
574 {
575 	static int alloced = 0;
576 	int circ, i;
577 	dev_info_t *xdip;
578 
579 	/* hard coded isa stuff */
580 	struct regspec asy_regs[] = {
581 		{1, 0x3f8, 0x8},
582 		{1, 0x2f8, 0x8}
583 	};
584 	int asy_intrs[] = {0x4, 0x3};
585 
586 	struct regspec i8042_regs[] = {
587 		{1, 0x60, 0x1},
588 		{1, 0x64, 0x1}
589 	};
590 	int i8042_intrs[] = {0x1, 0xc};
591 	char *acpi_prop;
592 	int acpi_enum = 1; /* ACPI is default to be on */
593 
594 	if (alloced)
595 		return;
596 
597 	ndi_devi_enter(isa_dip, &circ);
598 	if (alloced) {	/* just in case we are multi-threaded */
599 		ndi_devi_exit(isa_dip, circ);
600 		return;
601 	}
602 	alloced = 1;
603 
604 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
605 	    DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) {
606 		acpi_enum = strcmp("off", acpi_prop);
607 		ddi_prop_free(acpi_prop);
608 	}
609 
610 	if (acpi_enum) {
611 		if (acpi_isa_device_enum(isa_dip)) {
612 			ndi_devi_exit(isa_dip, circ);
613 			if (isa_resource_setup() != NDI_SUCCESS) {
614 				cmn_err(CE_WARN, "isa nexus: isa "
615 				    "resource setup failed");
616 			}
617 
618 			/* serial ports? */
619 			enumerate_BIOS_serial(isa_dip);
620 			return;
621 		}
622 		cmn_err(CE_NOTE, "!Solaris did not detect ACPI BIOS");
623 	}
624 	cmn_err(CE_NOTE, "!ACPI is off");
625 
626 	/* serial ports */
627 	for (i = 0; i < 2; i++) {
628 #if defined(__xpv)
629 		/*
630 		 * the hypervisor may be reserving the serial ports for console
631 		 * and/or debug use.  Probe the irqs to see if they are
632 		 * available.
633 		 */
634 		if (ec_probe_pirq(asy_intrs[i]) == 0)
635 			continue; /* in use */
636 #endif
637 		ndi_devi_alloc_sleep(isa_dip, "asy",
638 		    (pnode_t)DEVI_SID_NODEID, &xdip);
639 		(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
640 		    "reg", (int *)&asy_regs[i], 3);
641 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
642 		    "interrupts", asy_intrs[i]);
643 		(void) ndi_devi_bind_driver(xdip, 0);
644 	}
645 
646 	/* i8042 node */
647 	ndi_devi_alloc_sleep(isa_dip, "i8042",
648 	    (pnode_t)DEVI_SID_NODEID, &xdip);
649 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
650 	    "reg", (int *)i8042_regs, 6);
651 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
652 	    "interrupts", (int *)i8042_intrs, 2);
653 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
654 	    "unit-address", "1,60");
655 	(void) ndi_devi_bind_driver(xdip, 0);
656 
657 	add_known_used_resources();
658 
659 	ndi_devi_exit(isa_dip, circ);
660 
661 }
662 
663 /*
664  * On some machines, serial port 2 isn't listed in the ACPI table.
665  * This function goes through the BIOS data area and makes sure all
666  * the serial ports there are in the dev_info tree.  If any are missing,
667  * this function will add them.
668  */
669 
670 static int num_BIOS_serial = 2;	/* number of BIOS serial ports to look at */
671 
672 static void
673 enumerate_BIOS_serial(dev_info_t *isa_dip)
674 {
675 	ushort_t *bios_data;
676 	int i;
677 	dev_info_t *xdip;
678 	int found;
679 	int ret;
680 	struct regspec *tmpregs;
681 	int tmpregs_len;
682 	static struct regspec tmp_asy_regs[] = {
683 		{1, 0x3f8, 0x8},
684 	};
685 	static int default_asy_intrs[] = { 4, 3, 4, 3 };
686 	static size_t size = 4;
687 
688 	/*
689 	 * The first four 2-byte quantities of the BIOS data area contain
690 	 * the base I/O addresses of the first four serial ports.
691 	 */
692 	bios_data = (ushort_t *)psm_map_new((paddr_t)BIOS_DATA_AREA, size,
693 	    PSM_PROT_READ);
694 	for (i = 0; i < num_BIOS_serial; i++) {
695 		if (bios_data[i] == 0) {
696 			/* no COM[i]: port */
697 			continue;
698 		}
699 
700 		/* Look for it in the dev_info tree */
701 		found = 0;
702 		for (xdip = ddi_get_child(isa_dip); xdip != NULL;
703 		    xdip = ddi_get_next_sibling(xdip)) {
704 			if (strncmp(ddi_node_name(xdip), "asy", 3) != 0) {
705 				/* skip non asy */
706 				continue;
707 			}
708 
709 			/* Match by addr */
710 			ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, xdip,
711 			    DDI_PROP_DONTPASS, "reg", (int **)&tmpregs,
712 			    (uint_t *)&tmpregs_len);
713 			if (ret != DDI_PROP_SUCCESS) {
714 				/* error */
715 				continue;
716 			}
717 
718 			if (tmpregs->regspec_addr == bios_data[i])
719 				found = 1;
720 			/*
721 			 * Free the memory allocated by
722 			 * ddi_prop_lookup_int_array().
723 			 */
724 			ddi_prop_free(tmpregs);
725 
726 		}
727 
728 		/* If not found, then add it */
729 		if (!found) {
730 			ndi_devi_alloc_sleep(isa_dip, "asy",
731 			    (pnode_t)DEVI_SID_NODEID, &xdip);
732 			(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
733 			    "compatible", "PNP0500");
734 			/* This should be gotten from master file: */
735 			(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
736 			    "model", "Standard PC COM port");
737 			tmp_asy_regs[0].regspec_addr = bios_data[i];
738 			(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
739 			    "reg", (int *)&tmp_asy_regs[0], 3);
740 			(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
741 			    "interrupts", default_asy_intrs[i]);
742 			(void) ndi_devi_bind_driver(xdip, 0);
743 		}
744 	}
745 #if defined(__xpv)
746 	/*
747 	 * Check each serial port to see if it is in use by the hypervisor.
748 	 * If it is in use, then remove the node from the device tree.
749 	 */
750 	i = 0;
751 	for (xdip = ddi_get_child(isa_dip); xdip != NULL; ) {
752 		int asy_intr;
753 		dev_info_t *curdip;
754 
755 		curdip = xdip;
756 		xdip = ddi_get_next_sibling(xdip);
757 		if (strncmp(ddi_node_name(curdip), "asy", 3) != 0) {
758 			/* skip non asy */
759 			continue;
760 		}
761 		/*
762 		 * Check if the hypervisor is using the serial port by probing
763 		 * the irq and if it is using it remove the node
764 		 * from the device tree
765 		 */
766 		asy_intr = ddi_prop_get_int(DDI_DEV_T_ANY, curdip,
767 		    DDI_PROP_DONTPASS, "interrupts", -1);
768 		if (asy_intr == -1) {
769 			/* error */
770 			continue;
771 		}
772 
773 		if (ec_probe_pirq(asy_intr)) {
774 			continue;
775 		}
776 		ret = ndi_devi_free(curdip);
777 		if (ret != DDI_SUCCESS)
778 			cmn_err(CE_WARN,
779 			    "could not remove asy%d node", i);
780 		else
781 			cmn_err(CE_NOTE, "!asy%d unavailable, reserved"
782 			    " to hypervisor", i);
783 		i++;
784 	}
785 #endif	/* __xpv */
786 
787 	psm_unmap((caddr_t)bios_data, size);
788 }
789