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: releng/11.0/sys/dev/mca/mca_bus.c 297000 2016-03-18 01:28:41Z jhibbits $");
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
58static void	mca_reg_print	(device_t, char *, char *, int *);
59
60struct 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! */
70void
71mca_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
97u_int8_t
98mca_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
155const char *
156mca_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
167u_int8_t
168mca_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
178void
179mca_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
192void
193mca_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
206void
207mca_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
222void
223mca_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
238static int
239mca_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
317static void
318mca_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
349static int
350mca_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%jx",
369				((rid == 1) ? "io 0x" : "0x"),
370				rle->start);
371		} else {
372			snprintf(buf, sizeof(buf), "%s%jx-0x%jx",
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%jx",
385				((rid == 1) ? "mem 0x" : "0x"),
386				rle->start);
387		} else {
388			snprintf(buf, sizeof(buf), "%s%jx-0x%jx",
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 %jd", 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 %jx", 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
419static void
420mca_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
434static int
435mca_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
457static struct resource *
458mca_alloc_resource (device_t dev, device_t child, int type, int *rid,
459		    rman_res_t start, rman_res_t end, rman_res_t 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 = RMAN_IS_DEFAULT_RANGE(start, end);
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
485static struct resource_list *
486mca_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
497static 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
525static driver_t mca_driver = {
526	"mca",
527	mca_methods,
528	1,		/* no softc */
529};
530
531static devclass_t mca_devclass;
532
533DRIVER_MODULE(mca, legacy, mca_driver, mca_devclass, 0, 0);
534