xref: /NextBSD/sys/dev/mca/mca_bus.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /*-
2  * Copyright (c) 1999 Matthew N. Dodd <winter@jurai.net>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 /*
32  * References:
33  *		The CMU Mach3 microkernel
34  *		NetBSD MCA patches by Scott Telford
35  *		Linux MCA code.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/queue.h>
41 #include <sys/malloc.h>
42 #include <sys/kernel.h>
43 #include <sys/limits.h>
44 #include <sys/module.h>
45 #include <sys/bus.h>
46 
47 #include <machine/bus.h>
48 #include <machine/resource.h>
49 #include <sys/rman.h>
50 
51 #include <dev/mca/mca_busreg.h>
52 #include <dev/mca/mca_busvar.h>
53 
54 #include <sys/interrupt.h>
55 
56 #define MAX_COL	 79
57 
58 static void	mca_reg_print	(device_t, char *, char *, int *);
59 
60 struct mca_device {
61 	struct resource_list rl;	/* Resources */
62 
63 	mca_id_t	id;
64 	u_int8_t	slot;
65 	u_int8_t	enabled;
66 	u_int8_t	pos[8];		/* Programable Option Select Regs. */
67 };
68 
69 /* Not supposed to use this function! */
70 void
mca_pos_set(device_t dev,u_int8_t reg,u_int8_t data)71 mca_pos_set (device_t dev, u_int8_t reg, u_int8_t data)
72 {
73 	struct mca_device *	m_dev = device_get_ivars(dev);
74 	u_int8_t		slot = mca_get_slot(dev);
75 
76 	if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7))
77 		return;
78 
79 	/* Disable motherboard setup */
80 	outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
81 
82 	/* Select adapter setup regs */
83 	outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET));
84 
85 	/* Write the register */
86 	outb(MCA_POS_REG(reg), data);
87 
88 	/* Disable adapter setup */
89 	outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
90 
91 	/* Update the IVAR copy */
92 	m_dev->pos[reg] = data;
93 
94 	return;
95 }
96 
97 u_int8_t
mca_pos_get(device_t dev,u_int8_t reg)98 mca_pos_get (device_t dev, u_int8_t reg)
99 {
100 	u_int8_t	slot = mca_get_slot(dev);
101 	u_int8_t	data = 0;
102 
103 	if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7))
104 		return (0);
105 
106 	/* Disable motherboard setup */
107 	outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
108 
109 	switch (slot) {
110 		case MCA_MB_SCSI_SLOT:
111 
112 			/* Disable adapter setup */
113 			outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
114 
115 			/* Select motherboard video setup regs */
116 			outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_SCSI);
117 
118 			/* read the register */
119 			data = inb(MCA_POS_REG(reg));
120 
121 			/* Disable motherboard setup */
122 			outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
123 
124 			break;
125 		case MCA_MB_VIDEO_SLOT:
126 			/* Disable adapter setup */
127 			outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
128 
129 			/* Select motherboard scsi setup regs */
130 			outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_VIDEO);
131 
132 			/* read the register */
133 			data = inb(MCA_POS_REG(reg));
134 
135 			/* Disable motherboard setup */
136 			outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
137 			break;
138 		default:
139 
140 			/* Select adapter setup regs */
141 			outb(MCA_ADAP_SETUP_REG,
142 			     ((slot & 0x0f) | MCA_ADAP_SET));
143 
144 			/* read the register */
145 			data = inb(MCA_POS_REG(reg));
146 
147 			/* Disable adapter setup */
148 			outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
149 			break;
150 	}
151 
152 	return (data);
153 }
154 
155 const char *
mca_match_id(u_int16_t id,struct mca_ident * mca_devs)156 mca_match_id (u_int16_t id, struct mca_ident *mca_devs)
157 {
158 	struct mca_ident *	m = mca_devs;
159 	while(m->name != NULL) {
160 		if (id == m->id)
161 			return (m->name);
162 		m++;
163 	}
164 	return (NULL);
165 }
166 
167 u_int8_t
mca_pos_read(device_t dev,u_int8_t reg)168 mca_pos_read (device_t dev, u_int8_t reg)
169 {
170 	struct mca_device *	m_dev = device_get_ivars(dev);
171 
172 	if (reg > MCA_POS7)
173 		return (0);
174 
175 	return (m_dev->pos[reg]);
176 }
177 
178 void
mca_add_irq(dev,irq)179 mca_add_irq (dev, irq)
180 	device_t		dev;
181 	int			irq;
182 {
183 	struct mca_device *	m_dev = device_get_ivars(dev);
184 	int			rid = 0;
185 
186 	while (resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid)) rid++;
187 	resource_list_add(&(m_dev->rl), SYS_RES_IRQ, rid, irq, irq, 1);
188 
189 	return;
190 }
191 
192 void
mca_add_drq(dev,drq)193 mca_add_drq (dev, drq)
194 	device_t		dev;
195 	int			drq;
196 {
197 	struct mca_device *	m_dev = device_get_ivars(dev);
198 	int			rid = 0;
199 
200 	while (resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid)) rid++;
201 	resource_list_add(&(m_dev->rl), SYS_RES_DRQ, rid, drq, drq, 1);
202 
203 	return;
204 }
205 
206 void
mca_add_mspace(dev,mbase,msize)207 mca_add_mspace (dev, mbase, msize)
208 	device_t		dev;
209 	u_long			mbase;
210 	u_long			msize;
211 {
212 	struct mca_device *	m_dev = device_get_ivars(dev);
213 	int			rid = 0;
214 
215 	while (resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid)) rid++;
216 	resource_list_add(&(m_dev->rl), SYS_RES_MEMORY, rid,
217 		mbase, (mbase + msize), msize);
218 
219 	return;
220 }
221 
222 void
mca_add_iospace(dev,iobase,iosize)223 mca_add_iospace (dev, iobase, iosize)
224 	device_t		dev;
225 	u_long			iobase;
226 	u_long			iosize;
227 {
228 	struct mca_device *	m_dev = device_get_ivars(dev);
229 	int			rid = 0;
230 
231 	while (resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid)) rid++;
232 	resource_list_add(&(m_dev->rl), SYS_RES_IOPORT, rid,
233 		iobase, (iobase + iosize), iosize);
234 
235 	return;
236 }
237 
238 static int
mca_probe(device_t dev)239 mca_probe (device_t dev)
240 {
241 	device_t		child;
242 	struct mca_device *	m_dev = NULL;
243 	int			devices_found = 0;
244 	u_int8_t		slot;
245 	u_int8_t		reg;
246 
247 	device_set_desc(dev, "MCA bus");
248 
249 	/* Disable adapter setup */
250 	outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
251 	/* Disable motherboard setup */
252 	outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
253 
254 	if (bootverbose) {
255 		printf("POS REG     00 01 02 03 04 05 06 07\n");
256 		printf("-----------------------------------\n");
257 	}
258 
259 	for (slot = 0; slot < MCA_MAX_SLOTS; slot++) {
260 
261 		if (!m_dev) {
262 			m_dev = (struct mca_device *)malloc(sizeof(*m_dev),
263 		 					    M_DEVBUF, M_NOWAIT);
264 			if (!m_dev) {
265 				device_printf(dev, "cannot malloc mca_device");
266 				break;
267 			}
268 		}
269 		bzero(m_dev, sizeof(*m_dev));
270 
271 		/* Select adapter setup regs */
272 		outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET));
273 
274 		/* Read the POS registers */
275 		for (reg = MCA_POS0; reg <= MCA_POS7; reg++) {
276 			m_dev->pos[reg] = inb(MCA_POS_REG(reg));
277 		}
278 
279 		/* Disable adapter setup */
280 		outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
281 
282 		if (bootverbose) {
283 			printf("mca slot %d:", slot + 1);
284 			for (reg = MCA_POS0; reg <= MCA_POS7; reg++) {
285 				printf(" %02x", m_dev->pos[reg]);
286 			}
287 			printf("\n");
288 		}
289 
290 		m_dev->id = (u_int16_t)m_dev->pos[MCA_POS0] |
291 			    ((u_int16_t)m_dev->pos[MCA_POS1] << 8);
292 
293 		if (m_dev->id == 0xffff) {
294 			continue;
295 		}
296 
297 		devices_found++;
298 
299 		m_dev->enabled = (m_dev->pos[MCA_POS2] & MCA_POS2_ENABLE);
300 		m_dev->slot = slot;
301 
302 		resource_list_init(&(m_dev->rl));
303 
304 		child = device_add_child(dev, NULL, -1);
305 		device_set_ivars(child, m_dev);
306 
307 		m_dev = NULL;
308 	}
309 
310 	if (m_dev) {
311 		free(m_dev, M_DEVBUF);
312 	}
313 
314 	return (devices_found ? 0 : ENXIO);
315 }
316 
317 static void
mca_reg_print(dev,string,separator,column)318 mca_reg_print (dev, string, separator, column)
319 	device_t	dev;
320 	char *		string;
321 	char *		separator;
322 	int *		column;
323 {
324 	int		length = strlen(string);
325 
326 	length += (separator ? 2 : 1);
327 
328 	if (((*column) + length) >= MAX_COL) {
329 		printf("\n");
330 		(*column) = 0;
331 	} else if ((*column) != 0) {
332 		if (separator) {
333 			printf("%c", *separator);
334 			(*column)++;
335 		}
336 		printf(" ");
337 		(*column)++;
338 	}
339 
340 	if ((*column) == 0) {
341 		(*column) += device_printf(dev, "%s", string);
342 	} else {
343 		(*column) += printf("%s", string);
344 	}
345 
346 	return;
347 }
348 
349 static int
mca_print_child(device_t dev,device_t child)350 mca_print_child (device_t dev, device_t child)
351 {
352 	char				buf[MAX_COL+1];
353 	struct mca_device *		m_dev = device_get_ivars(child);
354 	int				rid;
355 	struct resource_list_entry *	rle;
356 	char				separator = ',';
357 	int				column = 0;
358 	int				retval = 0;
359 
360 	if (device_get_desc(child)) {
361 		snprintf(buf, sizeof(buf), "<%s>", device_get_desc(child));
362 		mca_reg_print(child, buf, NULL, &column);
363 	}
364 
365 	rid = 0;
366 	while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid++))) {
367 		if (rle->count == 1) {
368 			snprintf(buf, sizeof(buf), "%s%lx",
369 				((rid == 1) ? "io 0x" : "0x"),
370 				rle->start);
371 		} else {
372 			snprintf(buf, sizeof(buf), "%s%lx-0x%lx",
373 				((rid == 1) ? "io 0x" : "0x"),
374 				rle->start,
375 				(rle->start + rle->count));
376 		}
377 		mca_reg_print(child, buf,
378 			((rid == 2) ? &separator : NULL), &column);
379 	}
380 
381 	rid = 0;
382 	while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid++))) {
383 		if (rle->count == 1) {
384 			snprintf(buf, sizeof(buf), "%s%lx",
385 				((rid == 1) ? "mem 0x" : "0x"),
386 				rle->start);
387 		} else {
388 			snprintf(buf, sizeof(buf), "%s%lx-0x%lx",
389 				((rid == 1) ? "mem 0x" : "0x"),
390 				rle->start,
391 				(rle->start + rle->count));
392 		}
393 		mca_reg_print(child, buf,
394 			((rid == 2) ? &separator : NULL), &column);
395 	}
396 
397 	rid = 0;
398 	while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid++))) {
399 		snprintf(buf, sizeof(buf), "irq %ld", rle->start);
400 		mca_reg_print(child, buf,
401 			((rid == 1) ? &separator : NULL), &column);
402 	}
403 
404 	rid = 0;
405 	while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid++))) {
406 		snprintf(buf, sizeof(buf), "drq %lx", rle->start);
407 		mca_reg_print(child, buf,
408 			((rid == 1) ? &separator : NULL), &column);
409 	}
410 
411 	snprintf(buf, sizeof(buf), "on %s id %04x slot %d\n",
412 		device_get_nameunit(dev),
413 		mca_get_id(child), mca_get_slot(child)+1);
414 	mca_reg_print(child, buf, NULL, &column);
415 
416 	return (retval);
417 }
418 
419 static void
mca_probe_nomatch(device_t dev,device_t child)420 mca_probe_nomatch (device_t dev, device_t child)
421 {
422 	mca_id_t	mca_id = mca_get_id(child);
423 	u_int8_t	slot = mca_get_slot(child);
424 	u_int8_t	enabled = mca_get_enabled(child);
425 
426 	device_printf(dev, "unknown card (id 0x%04x, %s) at slot %d\n",
427 		mca_id,
428 		(enabled ? "enabled" : "disabled"),
429 		slot + 1);
430 
431 	return;
432 }
433 
434 static int
mca_read_ivar(device_t dev,device_t child,int which,uintptr_t * result)435 mca_read_ivar (device_t dev, device_t child, int which, uintptr_t * result)
436 {
437 	struct mca_device *		m_dev = device_get_ivars(child);
438 
439 	switch (which) {
440 		case MCA_IVAR_SLOT:
441 			*result = m_dev->slot;
442 			break;
443 		case MCA_IVAR_ID:
444 			*result = m_dev->id;
445 			break;
446 		case MCA_IVAR_ENABLED:
447 			*result = m_dev->enabled;
448 			break;
449 		default:
450 			return (ENOENT);
451 			break;
452 	}
453 
454 	return (0);
455 }
456 
457 static struct resource *
mca_alloc_resource(device_t dev,device_t child,int type,int * rid,u_long start,u_long end,u_long count,u_int flags)458 mca_alloc_resource (device_t dev, device_t child, int type, int *rid,
459 		    u_long start, u_long end, u_long count, u_int flags)
460 {
461 	struct mca_device *		m_dev = device_get_ivars(child);
462 	struct resource_list_entry *	rle;
463 	int				isdefault;
464 	int				passthrough;
465 
466 	isdefault = (start == 0UL && end == ~0UL);
467 	passthrough = (device_get_parent(child) != dev);
468 
469 	if (!passthrough && !isdefault) {
470 		rle = resource_list_find(&(m_dev->rl), type, *rid);
471 		if (!rle) {
472 			resource_list_add(&(m_dev->rl), type, *rid,
473 					  start, end, count);
474 		}
475 	}
476 
477 	if (type == SYS_RES_IRQ) {
478 		flags |= RF_SHAREABLE;
479 	}
480 
481 	return (resource_list_alloc(&(m_dev->rl), dev, child, type, rid,
482 				    start, end, count, flags));
483 }
484 
485 static struct resource_list *
mca_get_resource_list(device_t dev,device_t child)486 mca_get_resource_list (device_t dev, device_t child)
487 {
488 	struct mca_device *	m_dev = device_get_ivars(child);
489 	struct resource_list *	rl = &m_dev->rl;
490 
491  	if (!rl)
492 		return (NULL);
493 
494 	return (rl);
495 }
496 
497 static device_method_t mca_methods[] = {
498 	/* Device interface */
499 	DEVMETHOD(device_probe,		mca_probe),
500 	DEVMETHOD(device_attach,	bus_generic_attach),
501 	DEVMETHOD(device_shutdown,      bus_generic_shutdown),
502 	DEVMETHOD(device_suspend,       bus_generic_suspend),
503 	DEVMETHOD(device_resume,	bus_generic_resume),
504 
505 	/* Bus interface */
506 	DEVMETHOD(bus_print_child,	mca_print_child),
507 	DEVMETHOD(bus_probe_nomatch,	mca_probe_nomatch),
508 	DEVMETHOD(bus_read_ivar,	mca_read_ivar),
509 	DEVMETHOD(bus_write_ivar,	bus_generic_write_ivar),
510 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
511 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
512 
513 	DEVMETHOD(bus_get_resource_list,mca_get_resource_list),
514 	DEVMETHOD(bus_alloc_resource,	mca_alloc_resource),
515 	DEVMETHOD(bus_release_resource,	bus_generic_rl_release_resource),
516 	DEVMETHOD(bus_set_resource,     bus_generic_rl_set_resource),
517 	DEVMETHOD(bus_get_resource,     bus_generic_rl_get_resource),
518 	DEVMETHOD(bus_delete_resource,  bus_generic_rl_delete_resource),
519 	DEVMETHOD(bus_activate_resource,bus_generic_activate_resource),
520 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
521 
522 	DEVMETHOD_END
523 };
524 
525 static driver_t mca_driver = {
526 	"mca",
527 	mca_methods,
528 	1,		/* no softc */
529 };
530 
531 static devclass_t mca_devclass;
532 
533 DRIVER_MODULE(mca, legacy, mca_driver, mca_devclass, 0, 0);
534