eisaconf.c revision 29674
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.30 1997/08/21 20:33:29 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 <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		e_dev->full_name = "Unattached Device";
151
152		e_dev->ioconf.slot = slot;
153
154		/* Initialize our lists of reserved addresses */
155		LIST_INIT(&(e_dev->ioconf.ioaddrs));
156		LIST_INIT(&(e_dev->ioconf.maddrs));
157		TAILQ_INIT(&(e_dev->ioconf.irqs));
158
159		*eisa_dev_list_tail = dev_node;
160		eisa_dev_list_tail = &dev_node->next;
161	}
162
163	dev_node = eisa_dev_list;
164
165	/*
166	 * "Attach" the system board
167	 */
168
169	/* The first will be the motherboard in a true EISA system */
170	if (dev_node && (dev_node->dev.ioconf.slot == 0)) {
171		char *idstring;
172
173		e_dev = &dev_node->dev;
174		e_dev->driver = &mainboard_drv;
175		e_dev->unit = (*e_dev->driver->unit)++;
176		idstring = (char *)malloc(8 + sizeof(" (System Board)") + 1,
177					  M_DEVBUF, M_NOWAIT);
178		if (idstring == NULL) {
179			panic("Eisa probe unable to malloc");
180		}
181		sprintf(idstring, "%c%c%c%x%x (System Board)",
182			EISA_MFCTR_CHAR0(e_dev->id),
183			EISA_MFCTR_CHAR1(e_dev->id),
184			EISA_MFCTR_CHAR2(e_dev->id),
185			EISA_PRODUCT_ID(e_dev->id),
186			EISA_REVISION_ID(e_dev->id));
187		e_dev->full_name = idstring;
188
189		printf("%s%ld: <%s>\n",
190		       e_dev->driver->name,
191		       e_dev->unit,
192		       e_dev->full_name);
193
194		/* Should set the iosize, but I don't have a spec handy */
195		printf("Probing for devices on the EISA bus\n");
196		dev_node = dev_node->next;
197	}
198
199	if (!eisa_dev_list) {
200		/*
201		 * No devices.
202		 */
203		return;
204    	}
205	/*
206	 * See what devices we recognize.
207	 */
208	while((e_drv = *e_drvp++)) {
209		if (e_drv->probe)
210			(*e_drv->probe)();
211	}
212
213	/*
214	 * Attach the devices we found in slot order
215	 */
216	for (; dev_node; dev_node=dev_node->next) {
217		e_dev = &dev_node->dev;
218		e_drv = e_dev->driver;
219
220		if (e_drv) {
221			/*
222			 * Determine the proper unit number for this device.
223			 * Here we should look in the device table generated
224			 * by config to see if this type of device is enabled
225			 * either generically or for this particular address
226			 * as well as determine if a reserved unit number
227			 * should be used.  We should also ensure that the
228			 * "next availible unit number" skips over "wired" unit
229			 * numbers. This will be done after config is fixed or
230			 * some other configuration method is chosen.
231			 */
232			e_dev->unit = (*e_drv->unit)++;
233			if ((*e_drv->attach)(e_dev) < 0) {
234				/* Ensure registration has ended */
235				reg_state.in_registration = 0;
236				printf("\n%s0:%d <%s> attach failed\n",
237					mainboard_drv.name,
238					e_dev->ioconf.slot,
239					e_dev->full_name);
240				continue;
241			}
242			/* Ensure registration has ended */
243			reg_state.in_registration = 0;
244		}
245		else {
246			/* Announce unattached device */
247			printf("%s0:%d <%c%c%c%x%x=0x%x> unknown device\n",
248				mainboard_drv.name,
249				e_dev->ioconf.slot,
250				EISA_MFCTR_CHAR0(e_dev->id),
251				EISA_MFCTR_CHAR1(e_dev->id),
252				EISA_MFCTR_CHAR2(e_dev->id),
253				EISA_PRODUCT_ID(e_dev->id),
254				EISA_REVISION_ID(e_dev->id),
255				e_dev->id);
256		}
257	}
258}
259
260struct eisa_device *
261eisa_match_dev(e_dev, match_func)
262	struct eisa_device *e_dev;
263	const char* (*match_func)(eisa_id_t);
264{
265	struct eisa_device_node *e_node = eisa_dev_list;
266
267	if (e_dev) {
268		/* Start our search from the last successful match */
269		e_node = ((struct eisa_device_node *)e_dev)->next;
270	}
271
272	for(; e_node; e_node = e_node->next) {
273		const char *result;
274		if (e_node->dev.driver) {
275			/* Already claimed */
276			continue;
277		}
278		result = (*match_func)(e_node->dev.id);
279		if (result) {
280			e_node->dev.full_name = result;
281			return (&(e_node->dev));
282		}
283	}
284	return NULL;
285}
286
287/* Interrupt and I/O space registration facitlities */
288void
289eisa_reg_start(e_dev)
290	struct eisa_device *e_dev;
291{
292	/*
293	 * Announce the device.
294	 */
295	char *string;
296
297	reg_state.in_registration = 1;
298	reg_state.num_interrupts = 0;
299	reg_state.num_ioaddrs = 0;
300	reg_state.num_maddrs = 0;
301	reg_state.column = 0;
302
303	string = malloc(strlen(e_dev->full_name) + sizeof(" <>") + /*NULL*/1,
304			M_TEMP, M_NOWAIT);
305	if(!string) {
306		printf("eisa0: cannot malloc device description string\n");
307		return;
308	}
309	sprintf(string, " <%s>", e_dev->full_name);
310	eisa_reg_print(e_dev, string, /*separator=*/NULL);
311	free(string, M_TEMP);
312}
313
314/*
315 * Output registration information mindfull of screen wrap.
316 * Output an optional character separator before the string
317 * if the line does not wrap.
318 */
319void
320eisa_reg_print(e_dev, string, separator)
321	struct eisa_device *e_dev;
322	char *string;
323	char *separator;
324{
325	int len = strlen(string);
326
327	if(separator)
328		len++;
329
330	if(reg_state.column + len > MAX_COL) {
331		printf("\n");
332		reg_state.column = 0;
333	}
334	else if(separator) {
335		printf("%c", *separator);
336		reg_state.column++;
337	}
338
339	if(reg_state.column == 0)
340		reg_state.column += printf("%s%ld:%s",
341					   e_dev->driver->name,
342					   e_dev->unit,
343					   string);
344	else
345		reg_state.column += printf("%s", string);
346}
347
348/* Interrupt and I/O space registration facitlities */
349void
350eisa_reg_end(e_dev)
351	struct eisa_device *e_dev;
352{
353	if( reg_state.in_registration )
354	{
355		char string[25];
356
357		sprintf(string, " on %s0 slot %d",
358			mainboard_drv.name,
359			e_dev->ioconf.slot);
360		eisa_reg_print(e_dev, string, NULL);
361		printf("\n");
362		reg_state.in_registration = 0;
363	}
364	else
365		printf("eisa_reg_end called outside of a "
366		       "registration session\n");
367}
368
369int
370eisa_add_intr(e_dev, irq)
371	struct eisa_device *e_dev;
372	int irq;
373{
374	struct	irq_node *irq_info;
375	void	*dev_instance = (void *)-1; /* XXX use cfg->devdata  */
376	void	*idesc;
377
378	irq_info = (struct irq_node *)malloc(sizeof(*irq_info), M_DEVBUF,
379					     M_NOWAIT);
380	if (irq_info == NULL)
381		return (1);
382
383	irq_info->irq_no = irq;
384	irq_info->idesc = NULL;
385	TAILQ_INSERT_TAIL(&e_dev->ioconf.irqs, irq_info, links);
386	return 0;
387}
388
389int
390eisa_reg_intr(e_dev, irq, func, arg, maskptr, shared)
391	struct eisa_device *e_dev;
392	int   irq;
393	void (*func)(void *);
394	void  *arg;
395	u_int *maskptr;
396	int   shared;
397{
398	int result;
399	int s;
400	char string[25];
401	char separator = ',';
402
403#if NOT_YET
404	/*
405	 * Punt on conflict detection for the moment.
406	 * I want to develop a generic routine to do
407	 * this for all device types.
408	 */
409	int checkthese = CC_IRQ;
410	if (haveseen_dev(dev, checkthese))
411        	return 1;
412#endif
413	if (reg_state.in_registration) {
414		/*
415		 * Find the first instance of this irq that has a
416		 * NULL idesc.
417		 */
418		struct irq_node *cur_irq;
419
420		cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
421		while (cur_irq != NULL) {
422			if (cur_irq->irq_no == irq
423			 && cur_irq->idesc == NULL) {
424				/* XXX use cfg->devdata  */
425                		void *dev_instance = (void *)-1;
426
427				cur_irq->idesc = intr_create(dev_instance,
428							     irq,
429							     func,
430							     arg,
431							     maskptr, 0);
432				break;
433			}
434			cur_irq = TAILQ_NEXT(cur_irq, links);
435		}
436
437		if (cur_irq == NULL || cur_irq->idesc == NULL)
438			return (-1);
439	} else {
440		return EPERM;
441	}
442
443	sprintf(string, " irq %d", irq);
444	eisa_reg_print(e_dev, string, reg_state.num_interrupts ?
445				      &separator : NULL);
446	reg_state.num_interrupts++;
447	return (0);
448}
449
450int
451eisa_release_intr(e_dev, irq, func)
452	struct eisa_device *e_dev;
453	int   irq;
454	void  (*func)(void *);
455{
456	int	result;
457	struct	irq_node *cur_irq;
458
459	result = -1;
460	cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
461       	while (cur_irq != NULL) {
462		if (cur_irq->irq_no == irq) {
463			if (cur_irq->idesc != NULL)
464				intr_destroy(cur_irq->idesc);
465			cur_irq = TAILQ_NEXT(cur_irq, links);
466			TAILQ_REMOVE(&e_dev->ioconf.irqs, cur_irq, links);
467			result = 0;
468		} else {
469			cur_irq = TAILQ_NEXT(cur_irq, links);
470		}
471	}
472	if (result != 0) {
473		printf("%s%ld: Attempted to release an interrupt (%d) "
474		       "it doesn't own\n", e_dev->driver->name,
475			e_dev->unit, irq);
476	}
477
478	return (result);
479}
480
481int
482eisa_enable_intr(e_dev, irq)
483	struct eisa_device *e_dev;
484	int irq;
485{
486	struct	irq_node *cur_irq;
487	int	result;
488
489	result = -1;
490	cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs);
491       	while (cur_irq != NULL) {
492		if (cur_irq->irq_no == irq
493		 && cur_irq->idesc != NULL) {
494			result = intr_connect(cur_irq->idesc);
495		}
496		cur_irq = TAILQ_NEXT(cur_irq, links);
497	}
498	return (result);
499}
500
501static int
502eisa_add_resvaddr(e_dev, head, base, size, flags)
503	struct eisa_device *e_dev;
504	struct resvlist *head;
505	u_long	base;
506	u_long	size;
507	int	flags;
508{
509	resvaddr_t *reservation;
510
511	reservation = (resvaddr_t *)malloc(sizeof(resvaddr_t),
512					   M_DEVBUF, M_NOWAIT);
513	if(!reservation)
514		return (ENOMEM);
515
516	reservation->addr = base;
517	reservation->size = size;
518	reservation->flags = flags;
519
520	if (!head->lh_first) {
521		LIST_INSERT_HEAD(head, reservation, links);
522	}
523	else {
524		resvaddr_t *node;
525		for(node = head->lh_first; node; node = node->links.le_next) {
526			if (node->addr > reservation->addr) {
527				/*
528				 * List is sorted in increasing
529				 * address order.
530				 */
531				LIST_INSERT_BEFORE(node, reservation, links);
532				break;
533			}
534
535			if (node->addr == reservation->addr) {
536				/*
537				 * If the entry we want to add
538				 * matches any already in here,
539				 * fail.
540				 */
541				free(reservation, M_DEVBUF);
542				return (EEXIST);
543			}
544
545			if (!node->links.le_next) {
546				LIST_INSERT_AFTER(node, reservation, links);
547				break;
548			}
549		}
550	}
551	return (0);
552}
553
554int
555eisa_add_mspace(e_dev, mbase, msize, flags)
556	struct eisa_device *e_dev;
557	u_long	mbase;
558	u_long	msize;
559	int	flags;
560{
561	return	eisa_add_resvaddr(e_dev, &(e_dev->ioconf.maddrs), mbase, msize,
562				  flags);
563}
564
565int
566eisa_add_iospace(e_dev, iobase, iosize, flags)
567	struct eisa_device *e_dev;
568	u_long	iobase;
569	u_long	iosize;
570	int	flags;
571{
572	return	eisa_add_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), iobase,
573				  iosize, flags);
574}
575
576static int
577eisa_reg_resvaddr(e_dev, head, resvaddr, reg_count)
578	struct eisa_device *e_dev;
579	struct resvlist *head;
580	resvaddr_t *resvaddr;
581	int *reg_count;
582{
583	if (reg_state.in_registration) {
584		resvaddr_t *node;
585		/*
586		 * Ensure that this resvaddr is actually in the devices'
587		 * reservation list.
588		 */
589		for(node = head->lh_first; node;
590		    node = node->links.le_next) {
591			if (node == resvaddr) {
592				char buf[35];
593				char separator = ',';
594				char *string = buf;
595
596				if (*reg_count == 0) {
597					/* First time */
598					string += sprintf(string, " at");
599				}
600
601				if (node->size == 1
602				  || (node->flags & RESVADDR_BITMASK))
603					sprintf(string, " 0x%lx", node->addr);
604				else
605					sprintf(string, " 0x%lx-0x%lx",
606						node->addr,
607						node->addr + node->size - 1);
608				eisa_reg_print(e_dev, buf,
609						*reg_count ? &separator : NULL);
610				(*reg_count)++;
611				return (0);
612			}
613		}
614		return (ENOENT);
615	}
616	return EPERM;
617}
618
619int
620eisa_reg_mspace(e_dev, resvaddr)
621	struct eisa_device *e_dev;
622	resvaddr_t *resvaddr;
623{
624#ifdef NOT_YET
625	/*
626	 * Punt on conflict detection for the moment.
627	 * I want to develop a generic routine to do
628	 * this for all device types.
629	 */
630	int checkthese = CC_MADDR;
631	if (haveseen_dev(dev, checkthese))
632		return -1;
633#endif
634	return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.maddrs), resvaddr,
635				  &(reg_state.num_maddrs)));
636}
637
638int
639eisa_reg_iospace(e_dev, resvaddr)
640	struct eisa_device *e_dev;
641	resvaddr_t *resvaddr;
642{
643#ifdef NOT_YET
644	/*
645	 * Punt on conflict detection for the moment.
646	 * I want to develop a generic routine to do
647	 * this for all device types.
648	 */
649	int checkthese = CC_IOADDR;
650	if (haveseen_dev(dev, checkthese))
651		return -1;
652#endif
653	return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), resvaddr,
654				  &(reg_state.num_ioaddrs)));
655}
656
657int
658eisa_registerdev(e_dev, driver)
659	struct eisa_device *e_dev;
660	struct eisa_driver *driver;
661{
662	e_dev->driver = driver;	/* Driver now owns this device */
663	return (0);
664}
665
666