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