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