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