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