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