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