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