eisaconf.c revision 18158
1/*
2 * EISA bus probe and attach routines
3 *
4 * Copyright (c) 1995, 1996 Justin T. Gibbs.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice immediately at the beginning of the file, without modification,
12 *    this list of conditions, and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	$Id: eisaconf.c,v 1.22 1996/09/06 23:06:57 phk Exp $
32 */
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/kernel.h>
36#include <sys/sysctl.h>
37#include <sys/conf.h>
38#include <sys/malloc.h>
39
40#include <i386/eisa/eisaconf.h>
41
42#include <i386/isa/icu.h>	 /* Hmmm.  Interrupt stuff? */
43
44struct eisa_device_node{
45	struct	eisa_device dev;
46	struct	eisa_device_node *next;
47};
48
49/*
50 * This should probably be a list of "struct device" once it exists.
51 * A struct device will incorperate ioconf and driver entry point data
52 * regardless of how its attached to the system (via unions) as well
53 * as more generic information that all device types should support (unit
54 * number, if its installed, etc).
55 */
56static struct eisa_device_node *eisa_dev_list;
57static struct eisa_device_node **eisa_dev_list_tail = &eisa_dev_list;
58static u_long eisa_unit;
59
60static struct eisa_driver mainboard_drv = {
61				     "eisa",
62				     NULL,
63				     NULL,
64				     NULL,
65				     &eisa_unit
66				   };
67
68/*
69 * Add the mainboard_drv to the eisa driver linkerset so that it is
70 * defined even if no EISA drivers are linked into the kernel.
71 */
72DATA_SET (eisadriver_set, mainboard_drv);
73
74/*
75 * Local function declarations and static variables
76 */
77void eisa_reg_print __P((struct eisa_device *e_dev, char *string,
78			 char *separator));
79static int eisa_add_resvaddr __P((struct eisa_device *e_dev,
80				  struct resvlist *head, u_long	base,
81				  u_long size, int flags));
82static int eisa_reg_resvaddr __P((struct eisa_device *e_dev,
83				  struct resvlist *head, resvaddr_t *resvaddr,
84				  int *reg_count));
85
86/*
87 * Keep some state about what we've printed so far
88 * to make probe output pretty.
89 */
90static struct {
91	int	in_registration;/* reg_start has been called */
92	int	num_interrupts;
93	int	num_ioaddrs;
94	int	num_maddrs;
95	int	column;		/* How much we have output so far. */
96#define	MAX_COL 80
97} reg_state;
98
99/*
100** probe for EISA devices
101*/
102void
103eisa_configure()
104{
105	int i,slot;
106	char *id_string;
107	struct eisa_device_node *dev_node;
108	struct eisa_driver **e_drvp;
109	struct eisa_driver *e_drv;
110	struct eisa_device *e_dev;
111	int eisaBase = 0xc80;
112	eisa_id_t eisa_id;
113
114	e_drvp = (struct eisa_driver**)eisadriver_set.ls_items;
115
116	for (slot = 0; slot < EISA_SLOTS; eisaBase+=0x1000, slot++) {
117		int id_size = sizeof(eisa_id);
118		eisa_id = 0;
119    		for( i = 0; i < id_size; i++ ) {
120			outb(eisaBase,0x80 + i); /*Some cards require priming*/
121			eisa_id |= inb(eisaBase+i) << ((id_size-i-1)*CHAR_BIT);
122		}
123		if (eisa_id & 0x80000000)
124			continue;  /* no EISA card in slot */
125
126		/* Prepare an eisa_device_node for this slot */
127		dev_node = (struct eisa_device_node *)malloc(sizeof(*dev_node),
128							    M_DEVBUF, M_NOWAIT);
129		if (!dev_node) {
130			printf("eisa0: cannot malloc eisa_device_node");
131			break; /* Try to attach what we have already */
132		}
133		bzero(dev_node, sizeof(*dev_node));
134		e_dev = &(dev_node->dev);
135
136		e_dev->id = eisa_id;
137		/*
138		 * Add an EISA ID based descriptive name incase we don't
139		 * have a driver for it.  We do this now instead of after all
140		 * probes because in the future, the eisa module will only
141		 * be responsible for creating the list of devices in the system
142		 * for the configuration manager to use.
143		 */
144		e_dev->full_name = (char *)malloc(8*sizeof(char),
145						  M_DEVBUF, M_NOWAIT);
146		if (!e_dev->full_name) {
147			panic("Eisa probe unable to malloc");
148		}
149		sprintf(e_dev->full_name, "%c%c%c%x%x",
150			EISA_MFCTR_CHAR0(e_dev->id),
151			EISA_MFCTR_CHAR1(e_dev->id),
152			EISA_MFCTR_CHAR2(e_dev->id),
153			EISA_PRODUCT_ID(e_dev->id),
154			EISA_REVISION_ID(e_dev->id));
155
156		e_dev->ioconf.slot = slot;
157
158		/* Initialize our lists of reserved addresses */
159		LIST_INIT(&(e_dev->ioconf.ioaddrs));
160		LIST_INIT(&(e_dev->ioconf.maddrs));
161
162		*eisa_dev_list_tail = dev_node;
163		eisa_dev_list_tail = &dev_node->next;
164	}
165
166	dev_node = eisa_dev_list;
167
168	/*
169	 * "Attach" the system board
170	 */
171
172	/* The first will be the motherboard in a true EISA system */
173	if (dev_node && (dev_node->dev.ioconf.slot == 0)) {
174		e_dev = &dev_node->dev;
175		e_dev->driver = &mainboard_drv;
176		e_dev->unit = (*e_dev->driver->unit)++;
177		id_string = e_dev->full_name;
178		e_dev->full_name = (char *)malloc(strlen(e_dev->full_name)
179						  + sizeof(" (System Board)")
180						  + 1, M_DEVBUF, M_NOWAIT);
181		if (!e_dev->full_name) {
182			panic("Eisa probe unable to malloc");
183		}
184		sprintf(e_dev->full_name, "%s (System Board)", id_string);
185		free(id_string, M_DEVBUF);
186
187		printf("%s%ld: <%s>\n",
188		       e_dev->driver->name,
189		       e_dev->unit,
190		       e_dev->full_name);
191
192		/* Should set the iosize, but I don't have a spec handy */
193		printf("Probing for devices on the EISA bus\n");
194		dev_node = dev_node->next;
195	}
196
197	if (!eisa_dev_list) {
198		/*
199		 * No devices.
200		 */
201		return;
202    	}
203	/*
204	 * See what devices we recognize.
205	 */
206	while((e_drv = *e_drvp++)) {
207		if (e_drv->probe)
208			(*e_drv->probe)();
209	}
210
211	/*
212	 * Attach the devices we found in slot order
213	 */
214	for (; dev_node; dev_node=dev_node->next) {
215		e_dev = &dev_node->dev;
216		e_drv = e_dev->driver;
217
218		if (e_drv) {
219			/*
220			 * Determine the proper unit number for this device.
221			 * Here we should look in the device table generated
222			 * by config to see if this type of device is enabled
223			 * either generically or for this particular address
224			 * as well as determine if a reserved unit number
225			 * should be used.  We should also ensure that the
226			 * "next availible unit number" skips over "wired" unit
227			 * numbers. This will be done after config is fixed or
228			 * some other configuration method is chosen.
229			 */
230			e_dev->unit = (*e_drv->unit)++;
231			if ((*e_drv->attach)(e_dev) < 0) {
232				/* Ensure registration has ended */
233				reg_state.in_registration = 0;
234				printf("\n%s0:%d <%s> attach failed\n",
235					mainboard_drv.name,
236					e_dev->ioconf.slot,
237					e_dev->full_name);
238				continue;
239			}
240			/* Ensure registration has ended */
241			reg_state.in_registration = 0;
242		}
243		else {
244			/* Announce unattached device */
245			printf("%s0:%d <%s=0x%x> unknown device\n",
246				mainboard_drv.name,
247				e_dev->ioconf.slot,
248				e_dev->full_name,
249				e_dev->id);
250		}
251	}
252}
253
254struct eisa_device *
255eisa_match_dev(e_dev, match_func)
256	struct eisa_device *e_dev;
257	char* (*match_func)(eisa_id_t);
258{
259	struct eisa_device_node *e_node = eisa_dev_list;
260
261	if (e_dev) {
262		/* Start our search from the last successful match */
263		e_node = ((struct eisa_device_node *)e_dev)->next;
264	}
265
266	for(; e_node; e_node = e_node->next) {
267		char *result;
268		if (e_node->dev.driver) {
269			/* Already claimed */
270			continue;
271		}
272		result = (*match_func)(e_node->dev.id);
273		if (result) {
274			free(e_node->dev.full_name, M_DEVBUF);
275			e_node->dev.full_name = result;
276			return (&(e_node->dev));
277		}
278	}
279	return NULL;
280}
281
282/* Interrupt and I/O space registration facitlities */
283void
284eisa_reg_start(e_dev)
285	struct eisa_device *e_dev;
286{
287	/*
288	 * Announce the device.
289	 */
290	char *string;
291
292	reg_state.in_registration = 1;
293	reg_state.num_interrupts = 0;
294	reg_state.num_ioaddrs = 0;
295	reg_state.num_maddrs = 0;
296	reg_state.column = 0;
297
298	string = malloc(strlen(e_dev->full_name) + sizeof(" <>") + /*NULL*/1,
299			M_TEMP, M_NOWAIT);
300	if(!string) {
301		printf("eisa0: cannot malloc device description string\n");
302		return;
303	}
304	sprintf(string, " <%s>", e_dev->full_name);
305	eisa_reg_print(e_dev, string, /*separator=*/NULL);
306	free(string, M_TEMP);
307}
308
309/*
310 * Output registration information mindfull of screen wrap.
311 * Output an optional character separator before the string
312 * if the line does not wrap.
313 */
314void
315eisa_reg_print(e_dev, string, separator)
316	struct eisa_device *e_dev;
317	char *string;
318	char *separator;
319{
320	int len = strlen(string);
321
322	if(separator)
323		len++;
324
325	if(reg_state.column + len > MAX_COL) {
326		printf("\n");
327		reg_state.column = 0;
328	}
329	else if(separator) {
330		printf("%c", *separator);
331		reg_state.column++;
332	}
333
334	if(reg_state.column == 0)
335		reg_state.column += printf("%s%ld:%s",
336					   e_dev->driver->name,
337					   e_dev->unit,
338					   string);
339	else
340		reg_state.column += printf("%s", string);
341}
342
343/* Interrupt and I/O space registration facitlities */
344void
345eisa_reg_end(e_dev)
346	struct eisa_device *e_dev;
347{
348	if( reg_state.in_registration )
349	{
350		char string[25];
351
352		sprintf(string, " on %s0 slot %d",
353			mainboard_drv.name,
354			e_dev->ioconf.slot);
355		eisa_reg_print(e_dev, string, NULL);
356		printf("\n");
357		reg_state.in_registration = 0;
358	}
359	else
360		printf("eisa_reg_end called outside of a "
361		       "registration session\n");
362}
363
364int
365eisa_add_intr(e_dev, irq)
366	struct eisa_device *e_dev;
367	int irq;
368{
369	e_dev->ioconf.irq |= 1ul << irq;
370	return 0;
371}
372
373int
374eisa_reg_intr(e_dev, irq, func, arg, maskptr, shared)
375	struct eisa_device *e_dev;
376	int   irq;
377	void  (*func)(void *);
378	void  *arg;
379	u_int *maskptr;
380	int   shared;
381{
382	int result;
383	int s;
384	char string[25];
385	char separator = ',';
386
387#if NOT_YET
388	/*
389	 * Punt on conflict detection for the moment.
390	 * I want to develop a generic routine to do
391	 * this for all device types.
392	 */
393	int checkthese = CC_IRQ;
394	if (haveseen_dev(dev, checkthese))
395        	return 1;
396#endif
397	if (reg_state.in_registration) {
398		s = splhigh();
399		/*
400		 * This should really go to a routine that can optionally
401		 * handle shared interrupts.
402		 */
403		result = register_intr(irq,		   /* isa irq	   */
404				       0,		   /* deviced??    */
405				       0,		   /* flags?       */
406				       (inthand2_t*) func, /* handler      */
407				       maskptr,		   /* mask pointer */
408				       (int)arg);	   /* handler arg  */
409
410		if (result) {
411			printf ("\neisa_reg_int: result=%d\n", result);
412			splx(s);
413			return (result);
414		};
415		update_intr_masks();
416		splx(s);
417	}
418	else
419		return EPERM;
420
421	e_dev->ioconf.irq |= 1ul << irq;
422	sprintf(string, " irq %d", irq);
423	eisa_reg_print(e_dev, string, reg_state.num_interrupts ?
424				      &separator : NULL);
425	reg_state.num_interrupts++;
426	return (0);
427}
428
429int
430eisa_release_intr(e_dev, irq, func)
431	struct eisa_device *e_dev;
432	int   irq;
433	void  (*func)(void *);
434{
435	int result;
436	int s;
437
438	if (!(e_dev->ioconf.irq & (1ul << irq))) {
439		printf("%s%ld: Attempted to release an interrupt (%d) "
440		       "it doesn't own\n", e_dev->driver->name,
441			e_dev->unit, irq);
442		return (-1);
443	}
444
445	s = splhigh();
446	INTRDIS ((1ul<<irq));
447
448	result = unregister_intr (irq, (inthand2_t*)func);
449
450	if (result)
451		printf ("eisa_release_intr: result=%d\n", result);
452
453	update_intr_masks();
454
455	splx(s);
456	return (result);
457}
458
459int
460eisa_enable_intr(e_dev, irq)
461	struct eisa_device *e_dev;
462	int irq;
463{
464	int s;
465
466	if (!(e_dev->ioconf.irq & (1ul << irq))) {
467		printf("%s%ld: Attempted to enable an interrupt (%d) "
468		       "it doesn't own\n", e_dev->driver->name,
469			e_dev->unit, irq);
470		return (-1);
471	}
472	s = splhigh();
473	INTREN((1ul << irq));
474	splx(s);
475	return 0;
476}
477
478static int
479eisa_add_resvaddr(e_dev, head, base, size, flags)
480	struct eisa_device *e_dev;
481	struct resvlist *head;
482	u_long	base;
483	u_long	size;
484	int	flags;
485{
486	resvaddr_t *reservation;
487
488	reservation = (resvaddr_t *)malloc(sizeof(resvaddr_t),
489					   M_DEVBUF, M_NOWAIT);
490	if(!reservation)
491		return (ENOMEM);
492
493	reservation->addr = base;
494	reservation->size = size;
495	reservation->flags = flags;
496
497	if (!head->lh_first) {
498		LIST_INSERT_HEAD(head, reservation, links);
499	}
500	else {
501		resvaddr_t *node;
502		for(node = head->lh_first; node; node = node->links.le_next) {
503			if (node->addr > reservation->addr) {
504				/*
505				 * List is sorted in increasing
506				 * address order.
507				 */
508				LIST_INSERT_BEFORE(node, reservation, links);
509				break;
510			}
511
512			if (node->addr == reservation->addr) {
513				/*
514				 * If the entry we want to add
515				 * matches any already in here,
516				 * fail.
517				 */
518				free(reservation, M_DEVBUF);
519				return (EEXIST);
520			}
521
522			if (!node->links.le_next) {
523				LIST_INSERT_AFTER(node, reservation, links);
524				break;
525			}
526		}
527	}
528	return (0);
529}
530
531int
532eisa_add_mspace(e_dev, mbase, msize, flags)
533	struct eisa_device *e_dev;
534	u_long	mbase;
535	u_long	msize;
536	int	flags;
537{
538	return	eisa_add_resvaddr(e_dev, &(e_dev->ioconf.maddrs), mbase, msize,
539				  flags);
540}
541
542int
543eisa_add_iospace(e_dev, iobase, iosize, flags)
544	struct eisa_device *e_dev;
545	u_long	iobase;
546	u_long	iosize;
547	int	flags;
548{
549	return	eisa_add_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), iobase,
550				  iosize, flags);
551}
552
553static int
554eisa_reg_resvaddr(e_dev, head, resvaddr, reg_count)
555	struct eisa_device *e_dev;
556	struct resvlist *head;
557	resvaddr_t *resvaddr;
558	int *reg_count;
559{
560	if (reg_state.in_registration) {
561		resvaddr_t *node;
562		/*
563		 * Ensure that this resvaddr is actually in the devices'
564		 * reservation list.
565		 */
566		for(node = head->lh_first; node;
567		    node = node->links.le_next) {
568			if (node == resvaddr) {
569				char buf[35];
570				char separator = ',';
571				char *string = buf;
572
573				if (*reg_count == 0) {
574					/* First time */
575					string += sprintf(string, " at");
576				}
577
578				if (node->size == 1
579				  || (node->flags & RESVADDR_BITMASK))
580					sprintf(string, " 0x%lx", node->addr);
581				else
582					sprintf(string, " 0x%lx-0x%lx",
583						node->addr,
584						node->addr + node->size - 1);
585				eisa_reg_print(e_dev, buf,
586						*reg_count ? &separator : NULL);
587				(*reg_count)++;
588				return (0);
589			}
590		}
591		return (ENOENT);
592	}
593	return EPERM;
594}
595
596int
597eisa_reg_mspace(e_dev, resvaddr)
598	struct eisa_device *e_dev;
599	resvaddr_t *resvaddr;
600{
601#ifdef NOT_YET
602	/*
603	 * Punt on conflict detection for the moment.
604	 * I want to develop a generic routine to do
605	 * this for all device types.
606	 */
607	int checkthese = CC_MADDR;
608	if (haveseen_dev(dev, checkthese))
609		return -1;
610#endif
611	return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.maddrs), resvaddr,
612				  &(reg_state.num_maddrs)));
613}
614
615int
616eisa_reg_iospace(e_dev, resvaddr)
617	struct eisa_device *e_dev;
618	resvaddr_t *resvaddr;
619{
620#ifdef NOT_YET
621	/*
622	 * Punt on conflict detection for the moment.
623	 * I want to develop a generic routine to do
624	 * this for all device types.
625	 */
626	int checkthese = CC_IOADDR;
627	if (haveseen_dev(dev, checkthese))
628		return -1;
629#endif
630	return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), resvaddr,
631				  &(reg_state.num_ioaddrs)));
632}
633
634int
635eisa_registerdev(e_dev, driver)
636	struct eisa_device *e_dev;
637	struct eisa_driver *driver;
638{
639	e_dev->driver = driver;	/* Driver now owns this device */
640	return (0);
641}
642
643