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