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