mca_bus.c revision 55890
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 * $FreeBSD: head/sys/dev/mca/mca_bus.c 55890 2000-01-13 09:01:46Z mdodd $
27 */
28
29/*
30 * References:
31 *		The CMU Mach3 microkernel
32 *		NetBSD MCA patches by Scott Telford
33 *		Linux MCA code.
34 */
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/queue.h>
39#include <sys/malloc.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42#include <sys/bus.h>
43
44#include <machine/limits.h>
45#include <machine/bus.h>
46#include <machine/resource.h>
47#include <sys/rman.h>
48
49#include <dev/mca/mca_busreg.h>
50#include <dev/mca/mca_busvar.h>
51
52#include <sys/interrupt.h>
53
54#define MAX_COL	 79
55
56static void	mca_reg_print	(device_t, char *, char *, int *);
57
58struct mca_device {
59	struct resource_list rl;	/* Resources */
60
61	mca_id_t	id;
62	u_int8_t	slot;
63	u_int8_t	enabled;
64	u_int8_t	pos[8];		/* Programable Option Select Regs. */
65};
66
67/* Not supposed to use this function! */
68void
69mca_pos_set (dev, reg, data)
70	device_t	dev;
71	u_int8_t	reg;
72	u_int8_t	data;
73{
74	struct mca_device *	m_dev = device_get_ivars(dev);
75	u_int8_t		slot = mca_get_slot(dev);
76
77	if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7))
78		return;
79
80	/* Disable motherboard setup */
81	outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
82
83	/* Select adapter setup regs */
84	outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET));
85
86	/* Write the register */
87	outb(MCA_POS_REG(reg), data);
88
89	/* Disable adapter setup */
90	outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
91
92	/* Update the IVAR copy */
93	m_dev->pos[reg] = data;
94
95	return;
96}
97
98u_int8_t
99mca_pos_get (dev, reg)
100	device_t	dev;
101	u_int8_t	reg;
102{
103	u_int8_t	slot = mca_get_slot(dev);
104	u_int8_t	data = 0;
105
106	if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7))
107		return (0);
108
109	/* Disable motherboard setup */
110	outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
111
112	switch (slot) {
113		case MCA_MB_SCSI_SLOT:
114
115			/* Disable adapter setup */
116			outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
117
118			/* Select motherboard video setup regs */
119			outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_SCSI);
120
121			/* read the register */
122			data = inb(MCA_POS_REG(reg));
123
124			/* Disable motherboard setup */
125			outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
126
127			break;
128		case MCA_MB_VIDEO_SLOT:
129			/* Disable adapter setup */
130			outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
131
132			/* Select motherboard scsi setup regs */
133			outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_VIDEO);
134
135			/* read the register */
136			data = inb(MCA_POS_REG(reg));
137
138			/* Disable motherboard setup */
139			outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
140			break;
141		default:
142
143			/* Select adapter setup regs */
144			outb(MCA_ADAP_SETUP_REG,
145			     ((slot & 0x0f) | MCA_ADAP_SET));
146
147			/* read the register */
148			data = inb(MCA_POS_REG(reg));
149
150			/* Disable adapter setup */
151			outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
152			break;
153	}
154
155	return (data);
156}
157
158const char *
159mca_match_id (id, mca_devs)
160	u_int16_t		id;
161	struct mca_ident *	mca_devs;
162{
163	struct mca_ident *	m = mca_devs;
164	while(m->name != NULL) {
165		if (id == m->id)
166			return (m->name);
167		m++;
168	}
169	return (NULL);
170}
171
172u_int8_t
173mca_pos_read (dev, reg)
174	device_t		dev;
175	u_int8_t		reg;
176{
177	struct mca_device *	m_dev = device_get_ivars(dev);
178
179	if (reg > MCA_POS7)
180		return (0);
181
182	return (m_dev->pos[reg]);
183}
184
185void
186mca_add_irq (dev, irq)
187	device_t		dev;
188	int			irq;
189{
190	struct mca_device *	m_dev = device_get_ivars(dev);
191	int			rid = 0;
192
193	while (resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid)) rid++;
194	resource_list_add(&(m_dev->rl), SYS_RES_IRQ, rid, irq, irq, 1);
195
196	return;
197}
198
199void
200mca_add_drq (dev, drq)
201	device_t		dev;
202	int			drq;
203{
204	struct mca_device *	m_dev = device_get_ivars(dev);
205	int			rid = 0;
206
207	while (resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid)) rid++;
208	resource_list_add(&(m_dev->rl), SYS_RES_DRQ, rid, drq, drq, 1);
209
210	return;
211}
212
213void
214mca_add_mspace (dev, mbase, msize)
215	device_t		dev;
216	u_long			mbase;
217	u_long			msize;
218{
219	struct mca_device *	m_dev = device_get_ivars(dev);
220	int			rid = 0;
221
222	while (resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid)) rid++;
223	resource_list_add(&(m_dev->rl), SYS_RES_MEMORY, rid,
224		mbase, (mbase + msize), msize);
225
226	return;
227}
228
229void
230mca_add_iospace (dev, iobase, iosize)
231	device_t		dev;
232	u_long			iobase;
233	u_long			iosize;
234{
235	struct mca_device *	m_dev = device_get_ivars(dev);
236	int			rid = 0;
237
238	while (resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid)) rid++;
239	resource_list_add(&(m_dev->rl), SYS_RES_IOPORT, rid,
240		iobase, (iobase + iosize), iosize);
241
242	return;
243}
244
245static int
246mca_probe (device_t dev)
247{
248	device_t		child;
249	struct mca_device *	m_dev = NULL;
250	int			devices_found = 0;
251	u_int8_t		slot;
252	u_int8_t		reg;
253
254	device_set_desc(dev, "MCA bus");
255
256	/* Disable adapter setup */
257	outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
258	/* Disable motherboard setup */
259	outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
260
261	if (bootverbose) {
262		printf("POS REG     00 01 02 03 04 05 06 07\n");
263		printf("-----------------------------------\n");
264	}
265
266	for (slot = 0; slot < MCA_MAX_SLOTS; slot++) {
267
268		if (!m_dev) {
269			m_dev = (struct mca_device *)malloc(sizeof(*m_dev),
270		 					    M_DEVBUF, M_NOWAIT);
271			if (!m_dev) {
272				device_printf(dev, "cannot malloc mca_device");
273				break;
274			}
275		}
276		bzero(m_dev, sizeof(*m_dev));
277
278		/* Select adapter setup regs */
279		outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET));
280
281		/* Read the POS registers */
282		for (reg = MCA_POS0; reg <= MCA_POS7; reg++) {
283			m_dev->pos[reg] = inb(MCA_POS_REG(reg));
284		}
285
286		/* Disable adapter setup */
287		outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
288
289		if (bootverbose) {
290			printf("mca slot %d:", slot + 1);
291			for (reg = MCA_POS0; reg <= MCA_POS7; reg++) {
292				printf(" %02x", m_dev->pos[reg]);
293			}
294			printf("\n");
295		}
296
297		m_dev->id = (u_int16_t)m_dev->pos[MCA_POS0] |
298			    ((u_int16_t)m_dev->pos[MCA_POS1] << 8);
299
300		if (m_dev->id == 0xffff) {
301			continue;
302		}
303
304		devices_found++;
305
306		m_dev->enabled = (m_dev->pos[MCA_POS2] & MCA_POS2_ENABLE);
307		m_dev->slot = slot;
308
309		resource_list_init(&(m_dev->rl));
310
311		child = device_add_child(dev, NULL, -1);
312		device_set_ivars(child, m_dev);
313
314		m_dev = NULL;
315	}
316
317	if (m_dev) {
318		free(m_dev, M_DEVBUF);
319	}
320
321	return (devices_found ? 0 : ENXIO);
322}
323
324static void
325mca_reg_print (dev, string, separator, column)
326	device_t	dev;
327	char *		string;
328	char *		separator;
329	int *		column;
330{
331	int		length = strlen(string);
332
333	length += (separator ? 2 : 1);
334
335	if (((*column) + length) >= MAX_COL) {
336		printf("\n");
337		(*column) = 0;
338	} else if ((*column) != 0) {
339		if (separator) {
340			printf("%c", *separator);
341			(*column)++;
342		}
343		printf(" ");
344		(*column)++;
345	}
346
347	if ((*column) == 0) {
348		(*column) += device_printf(dev, "%s", string);
349	} else {
350		(*column) += printf("%s", string);
351	}
352
353	return;
354}
355
356static int
357mca_print_child (device_t dev, device_t child)
358{
359	char				buf[MAX_COL+1];
360	struct mca_device *		m_dev = device_get_ivars(child);
361	int				rid;
362	struct resource_list_entry *	rle;
363	char				separator = ',';
364	int				column = 0;
365	int				retval = 0;
366
367	if (device_get_desc(child)) {
368		snprintf(buf, sizeof(buf), "<%s>", device_get_desc(child));
369		mca_reg_print(child, buf, NULL, &column);
370	}
371
372	rid = 0;
373	while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid++))) {
374		if (rle->count == 1) {
375			snprintf(buf, sizeof(buf), "%s%lx",
376				((rid == 1) ? "io 0x" : "0x"),
377				rle->start);
378		} else {
379			snprintf(buf, sizeof(buf), "%s%lx-0x%lx",
380				((rid == 1) ? "io 0x" : "0x"),
381				rle->start,
382				(rle->start + rle->count));
383		}
384		mca_reg_print(child, buf,
385			((rid == 2) ? &separator : NULL), &column);
386	}
387
388	rid = 0;
389	while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid++))) {
390		if (rle->count == 1) {
391			snprintf(buf, sizeof(buf), "%s%lx",
392				((rid == 1) ? "mem 0x" : "0x"),
393				rle->start);
394		} else {
395			snprintf(buf, sizeof(buf), "%s%lx-0x%lx",
396				((rid == 1) ? "mem 0x" : "0x"),
397				rle->start,
398				(rle->start + rle->count));
399		}
400		mca_reg_print(child, buf,
401			((rid == 2) ? &separator : NULL), &column);
402	}
403
404	rid = 0;
405	while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid++))) {
406		snprintf(buf, sizeof(buf), "irq %ld", rle->start);
407		mca_reg_print(child, buf,
408			((rid == 1) ? &separator : NULL), &column);
409	}
410
411	rid = 0;
412	while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid++))) {
413		snprintf(buf, sizeof(buf), "drq %lx", rle->start);
414		mca_reg_print(child, buf,
415			((rid == 1) ? &separator : NULL), &column);
416	}
417
418	snprintf(buf, sizeof(buf), "on %s id %04x slot %d\n",
419		device_get_nameunit(dev),
420		mca_get_id(child), mca_get_slot(child)+1);
421	mca_reg_print(child, buf, NULL, &column);
422
423	return (retval);
424}
425
426static void
427mca_probe_nomatch (device_t dev, device_t child)
428{
429	mca_id_t	mca_id = mca_get_id(child);
430	u_int8_t	slot = mca_get_slot(child);
431	u_int8_t	enabled = mca_get_enabled(child);
432
433	device_printf(dev, "unknown card (id 0x%04x, %s) at slot %d\n",
434		mca_id,
435		(enabled ? "enabled" : "disabled"),
436		slot + 1);
437
438	return;
439}
440
441static int
442mca_read_ivar (device_t dev, device_t child, int which, u_long * result)
443{
444	struct mca_device *		m_dev = device_get_ivars(child);
445
446	switch (which) {
447		case MCA_IVAR_SLOT:
448			*result = m_dev->slot;
449			break;
450		case MCA_IVAR_ID:
451			*result = m_dev->id;
452			break;
453		case MCA_IVAR_ENABLED:
454			*result = m_dev->enabled;
455			break;
456		default:
457			return (ENOENT);
458			break;
459	}
460
461	return (0);
462}
463
464static int
465mca_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
466{
467	return (EINVAL);
468}
469
470static struct resource *
471mca_alloc_resource (device_t dev, device_t child, int type, int *rid,
472		    u_long start, u_long end, u_long count, u_int flags)
473{
474	struct mca_device *		m_dev = device_get_ivars(child);
475	struct resource_list_entry *	rle;
476	int				isdefault;
477	int				passthrough;
478
479	isdefault = (start == 0UL && end == ~0UL);
480	passthrough = (device_get_parent(child) != dev);
481
482	if (!passthrough && !isdefault) {
483		rle = resource_list_find(&(m_dev->rl), type, *rid);
484		if (!rle) {
485			resource_list_add(&(m_dev->rl), type, *rid,
486					  start, end, count);
487		}
488	}
489
490	return (resource_list_alloc(&(m_dev->rl), dev, child, type, rid,
491				    start, end, count, flags));
492}
493
494static int
495mca_release_resource (device_t dev, device_t child, int type, int rid,
496		      struct resource * r)
497{
498	struct mca_device *		m_dev = device_get_ivars(child);
499
500	return (resource_list_release(&(m_dev->rl), dev, child, type, rid, r));
501}
502
503static int
504mca_get_resource(device_t dev, device_t child, int type, int rid,
505		 u_long *startp, u_long *countp)
506{
507	struct mca_device *		m_dev = device_get_ivars(child);
508	struct resource_list *		rl = &(m_dev->rl);
509	struct resource_list_entry *	rle;
510
511	rle = resource_list_find(rl, type, rid);
512	if (!rle)
513		return ENOENT;
514
515	*startp = rle->start;
516	*countp = rle->count;
517
518	return (0);
519}
520
521static int
522mca_set_resource(device_t dev, device_t child, int type, int rid,
523		 u_long start, u_long count)
524{
525	struct mca_device *		m_dev = device_get_ivars(child);
526	struct resource_list *		rl = &(m_dev->rl);
527
528	resource_list_add(rl, type, rid, start, start + count - 1, count);
529	return (0);
530}
531
532static void
533mca_delete_resource(device_t dev, device_t child, int type, int rid)
534{
535	struct mca_device *		m_dev = device_get_ivars(child);
536	struct resource_list *		rl = &(m_dev->rl);
537
538	resource_list_delete(rl, type, rid);
539}
540
541static device_method_t mca_methods[] = {
542	/* Device interface */
543	DEVMETHOD(device_probe,		mca_probe),
544	DEVMETHOD(device_attach,	bus_generic_attach),
545	DEVMETHOD(device_shutdown,      bus_generic_shutdown),
546	DEVMETHOD(device_suspend,       bus_generic_suspend),
547	DEVMETHOD(device_resume,	bus_generic_resume),
548
549	/* Bus interface */
550	DEVMETHOD(bus_print_child,	mca_print_child),
551	DEVMETHOD(bus_probe_nomatch,	mca_probe_nomatch),
552	DEVMETHOD(bus_read_ivar,	mca_read_ivar),
553	DEVMETHOD(bus_write_ivar,	mca_write_ivar),
554	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
555	DEVMETHOD(bus_alloc_resource,	mca_alloc_resource),
556	DEVMETHOD(bus_release_resource,	mca_release_resource),
557	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
558	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
559	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
560	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
561
562	DEVMETHOD(bus_set_resource,     mca_set_resource),
563	DEVMETHOD(bus_get_resource,     mca_get_resource),
564	DEVMETHOD(bus_delete_resource,  mca_delete_resource),
565
566	{ 0, 0 }
567};
568
569static driver_t mca_driver = {
570	"mca",
571	mca_methods,
572	1,		/* no softc */
573};
574
575static devclass_t mca_devclass;
576
577DRIVER_MODULE(mca, nexus, mca_driver, mca_devclass, 0, 0);
578