xref: /NextBSD/sys/dev/rp/rp_isa.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /*-
2  * Copyright (c) Comtrol Corporation <support@comtrol.com>
3  * All rights reserved.
4  *
5  * ISA-specific part separated from:
6  * sys/i386/isa/rp.c,v 1.33 1999/09/28 11:45:27 phk Exp
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted prodived that the follwoing conditions
10  * are met.
11  * 1. Redistributions of source code must retain the above copyright
12  *    notive, this list of conditions and the following disclainer.
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 prodided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *       This product includes software developed by Comtrol Corporation.
19  * 4. The name of Comtrol Corporation may not be used to endorse or
20  *    promote products derived from this software without specific
21  *    prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``AS IS'' AND ANY
24  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL COMTROL CORPORATION BE LIABLE FOR
27  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36 
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/fcntl.h>
43 #include <sys/malloc.h>
44 #include <sys/tty.h>
45 #include <sys/conf.h>
46 #include <sys/kernel.h>
47 #include <sys/module.h>
48 #include <machine/resource.h>
49 #include <machine/bus.h>
50 #include <sys/bus.h>
51 #include <sys/rman.h>
52 
53 #define ROCKET_C
54 #include <dev/rp/rpreg.h>
55 #include <dev/rp/rpvar.h>
56 
57 #include <isa/isavar.h>
58 
59 /* ISA-specific part of CONTROLLER_t */
60 struct ISACONTROLLER_T {
61 	int		MBaseIO;	/* rid of the Mudbac controller for this controller */
62 	int		MReg0IO;	/* offset0 of the Mudbac controller for this controller */
63 	int		MReg1IO;	/* offset1 of the Mudbac controller for this controller */
64 	int		MReg2IO;	/* offset2 of the Mudbac controller for this controller */
65 	int		MReg3IO;	/* offset3 of the Mudbac controller for this controller */
66 	Byte_t		MReg2;
67 	Byte_t		MReg3;
68 };
69 typedef struct ISACONTROLLER_T ISACONTROLLER_t;
70 
71 #define ISACTL(ctlp) ((ISACONTROLLER_t *)((ctlp)->bus_ctlp))
72 
73 /***************************************************************************
74 Function: sControllerEOI
75 Purpose:  Strobe the MUDBAC's End Of Interrupt bit.
76 Call:	  sControllerEOI(MudbacCtlP,CtlP)
77 	  CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
78 	  CONTROLLER_T *CtlP; Ptr to controller structure
79 */
80 #define sControllerEOI(MudbacCtlP,CtlP) \
81 	rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg2IO,ISACTL(CtlP)->MReg2 | INT_STROB)
82 
83 /***************************************************************************
84 Function: sDisAiop
85 Purpose:  Disable I/O access to an AIOP
86 Call:	  sDisAiop(MudbacCtlP,CtlP)
87 	  CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
88 	  CONTROLLER_T *CtlP; Ptr to controller structure
89 	  int AiopNum; Number of AIOP on controller
90 */
91 #define sDisAiop(MudbacCtlP,CtlP,AIOPNUM) \
92 { \
93    ISACTL(CtlP)->MReg3 &= rp_sBitMapClrTbl[AIOPNUM]; \
94    rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg3IO,ISACTL(CtlP)->MReg3); \
95 }
96 
97 /***************************************************************************
98 Function: sEnAiop
99 Purpose:  Enable I/O access to an AIOP
100 Call:	  sEnAiop(MudbacCtlP,CtlP)
101 	  CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
102 	  CONTROLLER_T *CtlP; Ptr to controller structure
103 	  int AiopNum; Number of AIOP on controller
104 */
105 #define sEnAiop(MudbacCtlP,CtlP,AIOPNUM) \
106 { \
107    ISACTL(CtlP)->MReg3 |= rp_sBitMapSetTbl[AIOPNUM]; \
108    rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg3IO,ISACTL(CtlP)->MReg3); \
109 }
110 
111 /***************************************************************************
112 Function: sGetControllerIntStatus
113 Purpose:  Get the controller interrupt status
114 Call:	  sGetControllerIntStatus(MudbacCtlP,CtlP)
115 	  CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
116 	  CONTROLLER_T *CtlP; Ptr to controller structure
117 Return:   Byte_t: The controller interrupt status in the lower 4
118 			 bits.	Bits 0 through 3 represent AIOP's 0
119 			 through 3 respectively.  If a bit is set that
120 			 AIOP is interrupting.	Bits 4 through 7 will
121 			 always be cleared.
122 */
123 #define sGetControllerIntStatus(MudbacCtlP,CtlP) \
124 	(rp_readio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg1IO) & 0x0f)
125 
126 static devclass_t rp_devclass;
127 static CONTROLLER_t *rp_controller;
128 static int rp_nisadevs;
129 
130 static int rp_probe(device_t dev);
131 static int rp_attach(device_t dev);
132 static void rp_isareleaseresource(CONTROLLER_t *ctlp);
133 static int sInitController(CONTROLLER_T *CtlP,
134 			   CONTROLLER_T *MudbacCtlP,
135 			   int AiopNum,
136 			   int IRQNum,
137 			   Byte_t Frequency,
138 			   int PeriodicOnly);
139 static rp_aiop2rid_t rp_isa_aiop2rid;
140 static rp_aiop2off_t rp_isa_aiop2off;
141 static rp_ctlmask_t rp_isa_ctlmask;
142 
143 static int
rp_probe(device_t dev)144 rp_probe(device_t dev)
145 {
146 	int unit;
147 	CONTROLLER_t *controller;
148 	int num_aiops;
149 	CONTROLLER_t *ctlp;
150 	int retval;
151 
152 	/*
153 	 * We have no PnP RocketPort cards.
154 	 * (At least according to LINT)
155 	 */
156 	if (isa_get_logicalid(dev) != 0)
157 		return (ENXIO);
158 
159 	/* We need IO port resource to configure an ISA device. */
160 	if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 0)
161 		return (ENXIO);
162 
163 	unit = device_get_unit(dev);
164 	if (unit >= 4) {
165 		device_printf(dev, "rpprobe: unit number %d invalid.\n", unit);
166 		return (ENXIO);
167 	}
168 	device_printf(dev, "probing for RocketPort(ISA) unit %d.\n", unit);
169 
170 	ctlp = device_get_softc(dev);
171 	bzero(ctlp, sizeof(*ctlp));
172 	ctlp->dev = dev;
173 	ctlp->aiop2rid = rp_isa_aiop2rid;
174 	ctlp->aiop2off = rp_isa_aiop2off;
175 	ctlp->ctlmask = rp_isa_ctlmask;
176 
177 	/* The IO ports of AIOPs for an ISA controller are discrete. */
178 	ctlp->io_num = 1;
179 	ctlp->io_rid = malloc(sizeof(*(ctlp->io_rid)) * MAX_AIOPS_PER_BOARD, M_DEVBUF, M_NOWAIT | M_ZERO);
180 	ctlp->io = malloc(sizeof(*(ctlp->io)) * MAX_AIOPS_PER_BOARD, M_DEVBUF, M_NOWAIT | M_ZERO);
181 	if (ctlp->io_rid == NULL || ctlp->io == NULL) {
182 		device_printf(dev, "rp_attach: Out of memory.\n");
183 		retval = ENOMEM;
184 		goto nogo;
185 	}
186 
187 	ctlp->bus_ctlp = malloc(sizeof(ISACONTROLLER_t) * 1, M_DEVBUF, M_NOWAIT | M_ZERO);
188 	if (ctlp->bus_ctlp == NULL) {
189 		device_printf(dev, "rp_attach: Out of memory.\n");
190 		retval = ENOMEM;
191 		goto nogo;
192 	}
193 
194 	ctlp->io_rid[0] = 0;
195 	if (rp_controller != NULL) {
196 		controller = rp_controller;
197 		ctlp->io[0] = bus_alloc_resource(dev, SYS_RES_IOPORT, &ctlp->io_rid[0], 0, ~0, 0x40, RF_ACTIVE);
198 	} else {
199 		controller = rp_controller = ctlp;
200 		ctlp->io[0] = bus_alloc_resource(dev, SYS_RES_IOPORT, &ctlp->io_rid[0], 0, ~0, 0x44, RF_ACTIVE);
201 	}
202 	if (ctlp->io[0] == NULL) {
203 		device_printf(dev, "rp_attach: Resource not available.\n");
204 		retval = ENXIO;
205 		goto nogo;
206 	}
207 
208 	num_aiops = sInitController(ctlp,
209 				controller,
210 				MAX_AIOPS_PER_BOARD, 0,
211 				FREQ_DIS, 0);
212 	if (num_aiops <= 0) {
213 		device_printf(dev, "board%d init failed.\n", unit);
214 		retval = ENXIO;
215 		goto nogo;
216 	}
217 
218 	if (rp_controller == NULL)
219 		rp_controller = controller;
220 	rp_nisadevs++;
221 
222 	device_set_desc(dev, "RocketPort ISA");
223 
224 	return (0);
225 
226 nogo:
227 	rp_isareleaseresource(ctlp);
228 
229 	return (retval);
230 }
231 
232 static int
rp_attach(device_t dev)233 rp_attach(device_t dev)
234 {
235 	int	unit;
236 	int	num_ports, num_aiops;
237 	int	aiop;
238 	CONTROLLER_t	*ctlp;
239 	int	retval;
240 
241 	unit = device_get_unit(dev);
242 
243 	ctlp = device_get_softc(dev);
244 
245 #ifdef notdef
246 	num_aiops = sInitController(ctlp,
247 				rp_controller,
248 				MAX_AIOPS_PER_BOARD, 0,
249 				FREQ_DIS, 0);
250 #else
251 	num_aiops = ctlp->NumAiop;
252 #endif /* notdef */
253 
254 	num_ports = 0;
255 	for(aiop=0; aiop < num_aiops; aiop++) {
256 		sResetAiopByNum(ctlp, aiop);
257 		sEnAiop(rp_controller, ctlp, aiop);
258 		num_ports += sGetAiopNumChan(ctlp, aiop);
259 	}
260 
261 	retval = rp_attachcommon(ctlp, num_aiops, num_ports);
262 	if (retval != 0)
263 		goto nogo;
264 
265 	return (0);
266 
267 nogo:
268 	rp_isareleaseresource(ctlp);
269 
270 	return (retval);
271 }
272 
273 static void
rp_isareleaseresource(CONTROLLER_t * ctlp)274 rp_isareleaseresource(CONTROLLER_t *ctlp)
275 {
276 	int i;
277 
278 	rp_releaseresource(ctlp);
279 
280 	if (ctlp == rp_controller)
281 		rp_controller = NULL;
282 	if (ctlp->io != NULL) {
283 		for (i = 0 ; i < MAX_AIOPS_PER_BOARD ; i++)
284 			if (ctlp->io[i] != NULL)
285 				bus_release_resource(ctlp->dev, SYS_RES_IOPORT, ctlp->io_rid[i], ctlp->io[i]);
286 		free(ctlp->io, M_DEVBUF);
287 	}
288 	if (ctlp->io_rid != NULL)
289 		free(ctlp->io_rid, M_DEVBUF);
290 	if (rp_controller != NULL && rp_controller->io[ISACTL(ctlp)->MBaseIO] != NULL) {
291 		bus_release_resource(rp_controller->dev, SYS_RES_IOPORT, rp_controller->io_rid[ISACTL(ctlp)->MBaseIO], rp_controller->io[ISACTL(ctlp)->MBaseIO]);
292 		rp_controller->io[ISACTL(ctlp)->MBaseIO] = NULL;
293 		rp_controller->io_rid[ISACTL(ctlp)->MBaseIO] = 0;
294 	}
295 	if (ctlp->bus_ctlp != NULL)
296 		free(ctlp->bus_ctlp, M_DEVBUF);
297 }
298 
299 /***************************************************************************
300 Function: sInitController
301 Purpose:  Initialization of controller global registers and controller
302 	  structure.
303 Call:	  sInitController(CtlP,MudbacCtlP,AiopNum,
304 			  IRQNum,Frequency,PeriodicOnly)
305 	  CONTROLLER_T *CtlP; Ptr to controller structure
306 	  CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
307 	  int AiopNum; Number of Aiops
308 	  int IRQNum; Interrupt Request number.  Can be any of the following:
309 			 0: Disable global interrupts
310 			 3: IRQ 3
311 			 4: IRQ 4
312 			 5: IRQ 5
313 			 9: IRQ 9
314 			 10: IRQ 10
315 			 11: IRQ 11
316 			 12: IRQ 12
317 			 15: IRQ 15
318 	  Byte_t Frequency: A flag identifying the frequency
319 		   of the periodic interrupt, can be any one of the following:
320 		      FREQ_DIS - periodic interrupt disabled
321 		      FREQ_137HZ - 137 Hertz
322 		      FREQ_69HZ - 69 Hertz
323 		      FREQ_34HZ - 34 Hertz
324 		      FREQ_17HZ - 17 Hertz
325 		      FREQ_9HZ - 9 Hertz
326 		      FREQ_4HZ - 4 Hertz
327 		   If IRQNum is set to 0 the Frequency parameter is
328 		   overidden, it is forced to a value of FREQ_DIS.
329 	  int PeriodicOnly: TRUE if all interrupts except the periodic
330 			       interrupt are to be blocked.
331 			    FALSE is both the periodic interrupt and
332 			       other channel interrupts are allowed.
333 			    If IRQNum is set to 0 the PeriodicOnly parameter is
334 			       overidden, it is forced to a value of FALSE.
335 Return:   int: Number of AIOPs on the controller, or CTLID_NULL if controller
336 	       initialization failed.
337 
338 Comments:
339 	  If periodic interrupts are to be disabled but AIOP interrupts
340 	  are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE.
341 
342 	  If interrupts are to be completely disabled set IRQNum to 0.
343 
344 	  Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an
345 	  invalid combination.
346 
347 	  This function performs initialization of global interrupt modes,
348 	  but it does not actually enable global interrupts.  To enable
349 	  and disable global interrupts use functions sEnGlobalInt() and
350 	  sDisGlobalInt().  Enabling of global interrupts is normally not
351 	  done until all other initializations are complete.
352 
353 	  Even if interrupts are globally enabled, they must also be
354 	  individually enabled for each channel that is to generate
355 	  interrupts.
356 
357 Warnings: No range checking on any of the parameters is done.
358 
359 	  No context switches are allowed while executing this function.
360 
361 	  After this function all AIOPs on the controller are disabled,
362 	  they can be enabled with sEnAiop().
363 */
364 static int
sInitController(CONTROLLER_T * CtlP,CONTROLLER_T * MudbacCtlP,int AiopNum,int IRQNum,Byte_t Frequency,int PeriodicOnly)365 sInitController(	CONTROLLER_T *CtlP,
366 			CONTROLLER_T *MudbacCtlP,
367 			int AiopNum,
368 			int IRQNum,
369 			Byte_t Frequency,
370 			int PeriodicOnly)
371 {
372 	int		i;
373 	int		ctl_base, aiop_base, aiop_size;
374 
375 	CtlP->CtlID = CTLID_0001;		/* controller release 1 */
376 
377 	ISACTL(CtlP)->MBaseIO = rp_nisadevs;
378 	if (MudbacCtlP->io[ISACTL(CtlP)->MBaseIO] != NULL) {
379 		ISACTL(CtlP)->MReg0IO = 0x40 + 0;
380 		ISACTL(CtlP)->MReg1IO = 0x40 + 1;
381 		ISACTL(CtlP)->MReg2IO = 0x40 + 2;
382 		ISACTL(CtlP)->MReg3IO = 0x40 + 3;
383 	} else {
384 		MudbacCtlP->io_rid[ISACTL(CtlP)->MBaseIO] = ISACTL(CtlP)->MBaseIO;
385 		ctl_base = rman_get_start(MudbacCtlP->io[0]) + 0x40 + 0x400 * rp_nisadevs;
386 		MudbacCtlP->io[ISACTL(CtlP)->MBaseIO] = bus_alloc_resource(MudbacCtlP->dev, SYS_RES_IOPORT, &CtlP->io_rid[ISACTL(CtlP)->MBaseIO], ctl_base, ctl_base + 3, 4, RF_ACTIVE);
387 		ISACTL(CtlP)->MReg0IO = 0;
388 		ISACTL(CtlP)->MReg1IO = 1;
389 		ISACTL(CtlP)->MReg2IO = 2;
390 		ISACTL(CtlP)->MReg3IO = 3;
391 	}
392 #if 1
393 	ISACTL(CtlP)->MReg2 = 0;			/* interrupt disable */
394 	ISACTL(CtlP)->MReg3 = 0;			/* no periodic interrupts */
395 #else
396 	if(sIRQMap[IRQNum] == 0)		/* interrupts globally disabled */
397 	{
398 		ISACTL(CtlP)->MReg2 = 0;		/* interrupt disable */
399 		ISACTL(CtlP)->MReg3 = 0;		/* no periodic interrupts */
400 	}
401 	else
402 	{
403 		ISACTL(CtlP)->MReg2 = sIRQMap[IRQNum];	/* set IRQ number */
404 		ISACTL(CtlP)->MReg3 = Frequency;	/* set frequency */
405 		if(PeriodicOnly)		/* periodic interrupt only */
406 		{
407 			ISACTL(CtlP)->MReg3 |= PERIODIC_ONLY;
408 		}
409 	}
410 #endif
411 	rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg2IO,ISACTL(CtlP)->MReg2);
412 	rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg3IO,ISACTL(CtlP)->MReg3);
413 	sControllerEOI(MudbacCtlP,CtlP);			/* clear EOI if warm init */
414 
415 	/* Init AIOPs */
416 	CtlP->NumAiop = 0;
417 	for(i=0; i < AiopNum; i++)
418 	{
419 		if (CtlP->io[i] == NULL) {
420 			CtlP->io_rid[i] = i;
421 			aiop_base = rman_get_start(CtlP->io[0]) + 0x400 * i;
422 			if (rp_nisadevs == 0)
423 				aiop_size = 0x44;
424 			else
425 				aiop_size = 0x40;
426 			CtlP->io[i] = bus_alloc_resource(CtlP->dev, SYS_RES_IOPORT, &CtlP->io_rid[i], aiop_base, aiop_base + aiop_size - 1, aiop_size, RF_ACTIVE);
427 		} else
428 			aiop_base = rman_get_start(CtlP->io[i]);
429 		rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,
430 			    ISACTL(CtlP)->MReg2IO,
431 			    ISACTL(CtlP)->MReg2 | (i & 0x03));	/* AIOP index */
432 		rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,
433 			    ISACTL(CtlP)->MReg0IO,
434 			    (Byte_t)(aiop_base >> 6));		/* set up AIOP I/O in MUDBAC */
435 		sEnAiop(MudbacCtlP,CtlP,i);			/* enable the AIOP */
436 
437 		CtlP->AiopID[i] = sReadAiopID(CtlP, i);		/* read AIOP ID */
438 		if(CtlP->AiopID[i] == AIOPID_NULL)		/* if AIOP does not exist */
439 		{
440 			sDisAiop(MudbacCtlP,CtlP,i);		/* disable AIOP */
441 			bus_release_resource(CtlP->dev, SYS_RES_IOPORT, CtlP->io_rid[i], CtlP->io[i]);
442 			CtlP->io[i] = NULL;
443 			break;					/* done looking for AIOPs */
444 		}
445 
446 		CtlP->AiopNumChan[i] = sReadAiopNumChan(CtlP, i);	/* num channels in AIOP */
447 		rp_writeaiop2(CtlP,i,_INDX_ADDR,_CLK_PRE);	/* clock prescaler */
448 		rp_writeaiop1(CtlP,i,_INDX_DATA,CLOCK_PRESC);
449 		CtlP->NumAiop++;				/* bump count of AIOPs */
450 		sDisAiop(MudbacCtlP,CtlP,i);			/* disable AIOP */
451 	}
452 
453 	if(CtlP->NumAiop == 0)
454 		return(-1);
455 	else
456 		return(CtlP->NumAiop);
457 }
458 
459 /*
460  * ARGSUSED
461  * Maps (aiop, offset) to rid.
462  */
463 static int
rp_isa_aiop2rid(int aiop,int offset)464 rp_isa_aiop2rid(int aiop, int offset)
465 {
466 	/* rid equals to aiop for an ISA controller. */
467 	return aiop;
468 }
469 
470 /*
471  * ARGSUSED
472  * Maps (aiop, offset) to the offset of resource.
473  */
474 static int
rp_isa_aiop2off(int aiop,int offset)475 rp_isa_aiop2off(int aiop, int offset)
476 {
477 	/* Each aiop has its own resource. */
478 	return offset;
479 }
480 
481 /* Read the int status for an ISA controller. */
482 static unsigned char
rp_isa_ctlmask(CONTROLLER_t * ctlp)483 rp_isa_ctlmask(CONTROLLER_t *ctlp)
484 {
485 	return sGetControllerIntStatus(rp_controller,ctlp);
486 }
487 
488 static device_method_t rp_methods[] = {
489 	/* Device interface */
490 	DEVMETHOD(device_probe,		rp_probe),
491 	DEVMETHOD(device_attach,	rp_attach),
492 
493 	{ 0, 0 }
494 };
495 
496 static driver_t rp_driver = {
497 	"rp",
498 	rp_methods,
499 	sizeof(CONTROLLER_t),
500 };
501 
502 /*
503  * rp can be attached to an isa bus.
504  */
505 DRIVER_MODULE(rp, isa, rp_driver, rp_devclass, 0, 0);
506