eisaconf.c revision 12157
1/*
2 * EISA bus probe and attach routines
3 *
4 * Copyright (c) 1995 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.4 1995/11/06 05:20:59 gibbs Exp $
22 */
23#include <sys/param.h>
24#include <sys/systm.h>
25#include <sys/kernel.h>
26#include <sys/conf.h>
27#include <sys/malloc.h>
28#include <sys/devconf.h>
29
30#include "i386/isa/icu.h"	 /* Hmmm.  Interrupt stuff? */
31#include "eisaconf.h"
32
33struct eisa_device_node{
34	struct	eisa_device dev;
35	struct	eisa_device_node *next;
36};
37
38extern struct kern_devconf kdc_cpu0;
39extern int bootverbose;
40
41struct kern_devconf kdc_eisa0 = {
42	0, 0, 0,                /* filled in by dev_attach */
43	"eisa", 0, { MDDT_BUS, 0 },
44	0, 0, 0, BUS_EXTERNALLEN,
45	&kdc_cpu0,              /* parent is the CPU */
46	0,                      /* no parentdata */
47	DC_BUSY,                /* busses are always busy */
48	"EISA bus",
49	DC_CLS_BUS              /* class */
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** probe for EISA devices
72*/
73void
74eisa_configure()
75{
76	int i,slot;
77	char *id_string;
78	struct eisa_device_node *dev_node;
79	struct eisa_driver **e_drvp;
80	struct eisa_driver *e_drv;
81	struct eisa_device *e_dev;
82	int eisaBase = 0xc80;
83	eisa_id_t eisa_id;
84
85	e_drvp = (struct eisa_driver**)eisadriver_set.ls_items;
86
87	for (slot = 0; slot < EISA_SLOTS; eisaBase+=0x1000, slot++) {
88		int id_size = sizeof(eisa_id);
89		eisa_id = 0;
90    		for( i = 0; i < id_size; i++ ) {
91			outb(eisaBase,0xc80 + i); /*Some cards require priming*/
92			eisa_id |= inb(eisaBase+i) << ((id_size-i-1)*CHAR_BIT);
93		}
94		if (eisa_id & 0x80000000)
95			continue;  /* no EISA card in slot */
96
97		/* Prepare an eisa_device_node for this slot */
98		dev_node = (struct eisa_device_node *)malloc(sizeof(*dev_node),
99							    M_DEVBUF, M_NOWAIT);
100		if (!dev_node) {
101			printf("eisa0: cannot malloc eisa_device_node");
102			break; /* Try to attach what we have already */
103		}
104		bzero(dev_node, sizeof(*dev_node));
105		e_dev = &(dev_node->dev);
106		e_dev->id = eisa_id;
107		/*
108		 * Add an EISA ID based descriptive name incase we don't
109		 * have a driver for it.  We do this now instead of after all
110		 * probes because in the future, the eisa module will only
111		 * be responsible for creating the list of devices in the system
112		 * for the configuration manager to use.
113		 */
114		e_dev->full_name = (char *)malloc(14*sizeof(char),
115						  M_DEVBUF, M_NOWAIT);
116		if (!e_dev->full_name) {
117			panic("Eisa probe unable to malloc");
118		}
119		sprintf(e_dev->full_name, "%c%c%c%x rev %x",
120			EISA_MFCTR_CHAR0(e_dev->id),
121			EISA_MFCTR_CHAR1(e_dev->id),
122			EISA_MFCTR_CHAR2(e_dev->id),
123			EISA_PRODUCT_ID(e_dev->id),
124			EISA_REVISION_ID(e_dev->id));
125		e_dev->ioconf.slot = slot;
126		/* Is iobase defined in any EISA specs? */
127		e_dev->ioconf.iobase = eisaBase & 0xff00;
128		*eisa_dev_list_tail = dev_node;
129		eisa_dev_list_tail = &dev_node->next;
130	}
131
132	dev_node = eisa_dev_list;
133
134	/*
135	 * "Attach" the system board
136	 */
137
138	/* The first will be the motherboard in a true EISA system */
139	if (dev_node && (dev_node->dev.ioconf.slot == 0)) {
140		e_dev = &dev_node->dev;
141		e_dev->driver = &mainboard_drv;
142		e_dev->unit = (*e_dev->driver->unit)++;
143		id_string = e_dev->full_name;
144		e_dev->full_name = (char *)malloc(strlen(e_dev->full_name)
145						  + sizeof(" (System Board)")
146						  + 1, M_DEVBUF, M_NOWAIT);
147		if (!e_dev->full_name) {
148			panic("Eisa probe unable to malloc");
149		}
150		sprintf(e_dev->full_name, "%s (System Board)", id_string);
151		free(id_string, M_DEVBUF);
152
153		printf("%s%ld: <%s>\n",
154		       e_dev->driver->name,
155		       e_dev->unit,
156		       e_dev->full_name);
157
158		/* Should set the iosize, but I don't have a spec handy */
159		kdc_eisa0.kdc_parentdata = e_dev;
160		dev_attach(&kdc_eisa0);
161		printf("Probing for devices on the EISA bus\n");
162		dev_node = dev_node->next;
163	}
164
165	if (!eisa_dev_list) {
166		/*
167		 * No devices.
168		 */
169		return;
170    	}
171	/*
172	 * See what devices we recognize.
173	 */
174	while((e_drv = *e_drvp++)) {
175		(*e_drv->probe)();
176	}
177
178	/*
179	 * Attach the devices we found in slot order
180	 */
181	for (; dev_node; dev_node=dev_node->next) {
182		e_dev = &dev_node->dev;
183		e_drv = e_dev->driver;
184		if (e_drv) {
185			/*
186			 * Determine the proper unit number for this device.
187			 * Here we should look in the device table generated
188			 * by config to see if this type of device is enabled
189			 * either generically or for this particular address
190			 * as well as determine if a reserved unit number
191			 * should be used.  We should also ensure that the
192			 * "next availible unit number" skips over "wired" unit
193			 * numbers. This will be done after config is fixed or
194			 * some other configuration method is chosen.
195			 */
196			e_dev->unit = (*e_drv->unit)++;
197			if ((*e_drv->attach)(e_dev) < 0) {
198				printf("%s0:%d <%s> attach failed\n",
199					mainboard_drv.name,
200					e_dev->ioconf.slot,
201					e_dev->full_name);
202				continue;
203			}
204			e_dev->kdc->kdc_unit = e_dev->unit;
205		}
206		else {
207			/* Announce unattached device */
208			printf("%s0:%d <%s> unknown device\n",
209				mainboard_drv.name,
210				e_dev->ioconf.slot,
211				e_dev->full_name);
212		}
213	}
214}
215
216struct eisa_device *
217eisa_match_dev(e_dev, match_func)
218	struct eisa_device *e_dev;
219	char* (*match_func)(eisa_id_t);
220{
221	struct eisa_device_node *e_node = eisa_dev_list;
222
223	if (e_dev) {
224		/* Start our search from the last successful match */
225		e_node = ((struct eisa_device_node *)e_dev)->next;
226	}
227
228	for(; e_node; e_node = e_node->next) {
229		char *result;
230		if (e_node->dev.driver) {
231			/* Already claimed */
232			continue;
233		}
234		result = (*match_func)(e_node->dev.id);
235		if (result) {
236			free(e_node->dev.full_name, M_DEVBUF);
237			e_node->dev.full_name = result;
238			return (&(e_node->dev));
239		}
240	}
241	return NULL;
242}
243
244/* Interrupt and I/O space registration facitlities */
245void
246eisa_reg_start(e_dev)
247	struct eisa_device *e_dev;
248{
249	/*
250	 * Announce the device.
251	 */
252	printf("%s%ld: <%s>",
253		e_dev->driver->name,
254		e_dev->unit,
255		e_dev->full_name);
256}
257
258/* Interrupt and I/O space registration facitlities */
259void
260eisa_reg_end(e_dev)
261	struct eisa_device *e_dev;
262{
263	/*
264	 * The device should have called eisa_registerdev()
265	 * during its probe.  So hopefully we can use the kdc
266	 * to weed out ISA/VL devices that use EISA id registers.
267	 */
268	if (e_dev->kdc && (e_dev->kdc->kdc_parent == &kdc_isa0)) {
269		printf(" on isa\n");
270	}
271	else {
272		printf(" on %s0 slot %d\n",
273			mainboard_drv.name,
274			e_dev->ioconf.slot);
275	}
276}
277
278int
279eisa_add_intr(e_dev, irq)
280	struct eisa_device *e_dev;
281	int irq;
282{
283	e_dev->ioconf.irq |= 1ul << irq;
284	return 0;
285}
286
287int
288eisa_reg_intr(e_dev, irq, func, arg, maskptr, shared)
289	struct eisa_device *e_dev;
290	int   irq;
291	void  (*func)(void *);
292	void  *arg;
293	u_int *maskptr;
294	int   shared;
295{
296	int result;
297	int s;
298
299#if NOT_YET
300	/*
301	 * Punt on conflict detection for the moment.
302	 * I want to develop a generic routine to do
303	 * this for all device types.
304	 */
305	int checkthese = CC_IRQ;
306	if (haveseen_dev(dev, checkthese))
307        	return 1;
308#endif
309	s = splhigh();
310	/*
311	 * This should really go to a routine that can optionally
312	 * handle shared interrupts.
313	 */
314	result = register_intr(irq,			/* isa irq	*/
315			       0,			/* deviced??	*/
316			       0,			/* flags?	*/
317			       (inthand2_t*) func,	/* handler      */
318			       maskptr,			/* mask pointer	*/
319			       (int)arg);		/* handler arg  */
320
321	if (result) {
322		printf ("eisa_reg_int: result=%d\n", result);
323		splx(s);
324		return (result);
325	};
326	update_intr_masks();
327	splx(s);
328
329	e_dev->ioconf.irq |= 1ul << irq;
330	printf(" irq %d", irq);
331	return (0);
332}
333
334int
335eisa_release_intr(e_dev, irq, func)
336	struct eisa_device *e_dev;
337	int   irq;
338	void  (*func)(void *);
339{
340	int result;
341	int s;
342
343	if (!(e_dev->ioconf.irq & (1ul << irq))) {
344		printf("%s%ld: Attempted to release an interrupt (%d) "
345		       "it doesn't own\n", e_dev->driver->name,
346			e_dev->unit, irq);
347		return (-1);
348	}
349
350	s = splhigh();
351	INTRDIS ((1ul<<irq));
352
353	result = unregister_intr (irq, (inthand2_t*)func);
354
355	if (result)
356		printf ("eisa_release_intr: result=%d\n", result);
357
358	update_intr_masks();
359
360	splx(s);
361	return (result);
362}
363
364int
365eisa_enable_intr(e_dev, irq)
366	struct eisa_device *e_dev;
367	int irq;
368{
369	int s;
370
371	if (!(e_dev->ioconf.irq & (1ul << irq))) {
372		printf("%s%ld: Attempted to enable an interrupt (%d) "
373		       "it doesn't own\n", e_dev->driver->name,
374			e_dev->unit, irq);
375		return (-1);
376	}
377	s = splhigh();
378	INTREN((1ul << irq));
379	splx(s);
380	return 0;
381}
382
383int
384eisa_add_iospace(e_dev, iobase, iosize)
385	struct eisa_device *e_dev;
386	u_long	iobase;
387	int	iosize;
388{
389	/*
390	 * We should develop a scheme for storing the results of
391	 * multiple calls to this function.
392	 */
393	e_dev->ioconf.iobase = iobase;
394	e_dev->ioconf.iosize = iosize;
395	return 0;
396}
397
398int
399eisa_reg_iospace(e_dev, iobase, iosize)
400	struct eisa_device *e_dev;
401	u_long	iobase;
402	int	iosize;
403{
404	/*
405	 * We should develop a scheme for storing the results of
406	 * multiple calls to this function.
407	 */
408#ifdef NOT_YET
409	/*
410	 * Punt on conflict detection for the moment.
411	 * I want to develop a generic routine to do
412	 * this for all device types.
413	 */
414	int checkthese = CC_IOADDR;
415	if (haveseen_dev(dev, checkthese))
416		return -1;
417#endif
418	e_dev->ioconf.iobase = iobase;
419	e_dev->ioconf.iosize = iosize;
420
421	printf(" at 0x%lx-0x%lx", iobase, iobase + iosize - 1);
422	return (0);
423}
424
425int
426eisa_registerdev(e_dev, driver, kdc_template)
427	struct eisa_device *e_dev;
428	struct eisa_driver *driver;
429	struct kern_devconf *kdc_template;
430{
431	e_dev->driver = driver;	/* Driver now owns this device */
432	e_dev->kdc = (struct kern_devconf *)malloc(sizeof(struct kern_devconf),
433						   M_DEVBUF, M_NOWAIT);
434	if (!e_dev->kdc) {
435		printf("WARNING: eisa_registerdev unable to malloc! "
436		       "Device kdc will not be registerd\n");
437		return 1;
438	}
439	bcopy(kdc_template, e_dev->kdc, sizeof(*kdc_template));
440	e_dev->kdc->kdc_description = e_dev->full_name;
441	e_dev->kdc->kdc_parentdata = e_dev;
442	dev_attach(e_dev->kdc);
443	return (0);
444}
445
446/*
447 * Provide EISA-specific device information to user programs using the
448 * hw.devconf interface.
449 */
450int
451eisa_externalize(id, userp, maxlen)
452	struct eisa_device *id;
453	void *userp;
454	size_t *maxlen;
455{
456	if (*maxlen < (sizeof *id)) {
457		return ENOMEM;
458	}
459	*maxlen -= (sizeof *id);
460	return (copyout(id, userp, sizeof *id));
461}
462
463
464int
465eisa_generic_externalize(p, kdc, userp, l)
466	struct proc *p;
467	struct kern_devconf *kdc;
468	void *userp;
469	size_t l;
470{
471    return eisa_externalize(kdc->kdc_eisa, userp, &l);
472}
473