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 */
60struct 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};
69typedef struct ISACONTROLLER_T ISACONTROLLER_t;
70
71#define ISACTL(ctlp) ((ISACONTROLLER_t *)((ctlp)->bus_ctlp))
72
73/***************************************************************************
74Function: sControllerEOI
75Purpose:  Strobe the MUDBAC's End Of Interrupt bit.
76Call:	  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/***************************************************************************
84Function: sDisAiop
85Purpose:  Disable I/O access to an AIOP
86Call:	  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/***************************************************************************
98Function: sEnAiop
99Purpose:  Enable I/O access to an AIOP
100Call:	  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/***************************************************************************
112Function: sGetControllerIntStatus
113Purpose:  Get the controller interrupt status
114Call:	  sGetControllerIntStatus(MudbacCtlP,CtlP)
115	  CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
116	  CONTROLLER_T *CtlP; Ptr to controller structure
117Return:   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
126static devclass_t rp_devclass;
127static CONTROLLER_t *rp_controller;
128static int rp_nisadevs;
129
130static int rp_probe(device_t dev);
131static int rp_attach(device_t dev);
132static void rp_isareleaseresource(CONTROLLER_t *ctlp);
133static int sInitController(CONTROLLER_T *CtlP,
134			   CONTROLLER_T *MudbacCtlP,
135			   int AiopNum,
136			   int IRQNum,
137			   Byte_t Frequency,
138			   int PeriodicOnly);
139static rp_aiop2rid_t rp_isa_aiop2rid;
140static rp_aiop2off_t rp_isa_aiop2off;
141static rp_ctlmask_t rp_isa_ctlmask;
142
143static int
144rp_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
226nogo:
227	rp_isareleaseresource(ctlp);
228
229	return (retval);
230}
231
232static int
233rp_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
267nogo:
268	rp_isareleaseresource(ctlp);
269
270	return (retval);
271}
272
273static void
274rp_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/***************************************************************************
300Function: sInitController
301Purpose:  Initialization of controller global registers and controller
302	  structure.
303Call:	  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.
335Return:   int: Number of AIOPs on the controller, or CTLID_NULL if controller
336	       initialization failed.
337
338Comments:
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
357Warnings: 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*/
364static int
365sInitController(	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 */
463static int
464rp_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 */
474static int
475rp_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. */
482static unsigned char
483rp_isa_ctlmask(CONTROLLER_t *ctlp)
484{
485	return sGetControllerIntStatus(rp_controller,ctlp);
486}
487
488static 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
496static 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 */
505DRIVER_MODULE(rp, isa, rp_driver, rp_devclass, 0, 0);
506