1 /*        $NetBSD: opti82c700.c,v 1.10 2011/07/01 17:37:26 dyoung Exp $         */
2 
3 /*-
4  * Copyright (c) 1999 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Copyright (c) 1999, by UCHIYAMA Yasushi
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. The name of the developer may NOT be used to endorse or promote products
43  *    derived from this software without specific prior written permission.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  */
57 
58 /*
59  * Support for the Opti 82c700 FireStar PCI-ISA bridge interrupt controller.
60  */
61 
62 #include <sys/cdefs.h>
63 __KERNEL_RCSID(0, "$NetBSD: opti82c700.c,v 1.10 2011/07/01 17:37:26 dyoung Exp $");
64 
65 #include <sys/param.h>
66 #include <sys/systm.h>
67 #include <sys/device.h>
68 #include <sys/malloc.h>
69 
70 #include <machine/intr.h>
71 #include <sys/bus.h>
72 
73 #include <dev/pci/pcivar.h>
74 #include <dev/pci/pcireg.h>
75 #include <dev/pci/pcidevs.h>
76 
77 #include <i386/pci/pci_intr_fixup.h>
78 #include <i386/pci/opti82c700reg.h>
79 
80 #ifdef FIRESTARDEBUG
81 #define   DPRINTF(arg) printf arg
82 #else
83 #define   DPRINTF(arg)
84 #endif
85 
86 int       opti82c700_getclink(pciintr_icu_handle_t, int, int *);
87 int       opti82c700_get_intr(pciintr_icu_handle_t, int, int *);
88 int       opti82c700_set_intr(pciintr_icu_handle_t, int, int);
89 int       opti82c700_get_trigger(pciintr_icu_handle_t, int, int *);
90 int       opti82c700_set_trigger(pciintr_icu_handle_t, int, int);
91 
92 const struct pciintr_icu opti82c700_pci_icu = {
93           opti82c700_getclink,
94           opti82c700_get_intr,
95           opti82c700_set_intr,
96           opti82c700_get_trigger,
97           opti82c700_set_trigger,
98 };
99 
100 struct opti82c700_handle {
101           pci_chipset_tag_t ph_pc;
102           pcitag_t ph_tag;
103 };
104 
105 int       opti82c700_addr(int, int *, int *);
106 #ifdef FIRESTARDEBUG
107 void      opti82c700_pir_dump(struct opti82c700_handle *);
108 #endif
109 
110 int
opti82c700_init(pci_chipset_tag_t pc,bus_space_tag_t iot,pcitag_t tag,pciintr_icu_tag_t * ptagp,pciintr_icu_handle_t * phandp)111 opti82c700_init(pci_chipset_tag_t pc, bus_space_tag_t iot,
112     pcitag_t tag, pciintr_icu_tag_t *ptagp, pciintr_icu_handle_t *phandp)
113 {
114           struct opti82c700_handle *ph;
115 
116           ph = malloc(sizeof(*ph), M_DEVBUF, M_NOWAIT);
117           if (ph == NULL)
118                     return (1);
119 
120           ph->ph_pc = pc;
121           ph->ph_tag = tag;
122 #ifdef FIRESTARDEBUG
123           opti82c700_pir_dump(ph);
124 #endif
125           *ptagp = &opti82c700_pci_icu;
126           *phandp = ph;
127           return (0);
128 }
129 
130 int
opti82c700_addr(int link,int * addrofs,int * ofs)131 opti82c700_addr(int link, int *addrofs, int *ofs)
132 {
133           int regofs, src;
134 
135           regofs = FIRESTAR_PIR_REGOFS(link);
136           src = FIRESTAR_PIR_SELECTSRC(link);
137 
138           switch (src) {
139           case FIRESTAR_PIR_SELECT_NONE:
140                     return (1);
141 
142           case FIRESTAR_PIR_SELECT_IRQ:
143                     if (regofs < 0 || regofs > 7)
144                               return (1);
145                     *addrofs = FIRESTAR_CFG_INTR_IRQ + (regofs >> 2);
146                     *ofs = (regofs & 3) << 3;
147                     break;
148 
149           case FIRESTAR_PIR_SELECT_PIRQ:
150                     /* FALLTHROUGH */
151           case FIRESTAR_PIR_SELECT_BRIDGE:
152                     if (regofs < 0 || regofs > 3)
153                               return (1);
154                     *addrofs = FIRESTAR_CFG_INTR_PIRQ;
155                     *ofs = regofs << 2;
156                     break;
157 
158           default:
159                     return (1);
160           }
161 
162           return (0);
163 }
164 
165 int
opti82c700_getclink(pciintr_icu_handle_t v,int link,int * clinkp)166 opti82c700_getclink(pciintr_icu_handle_t v, int link, int *clinkp)
167 {
168           DPRINTF(("FireStar link value 0x%x: ", link));
169 
170           switch (FIRESTAR_PIR_SELECTSRC(link)) {
171           default:
172                     DPRINTF(("bogus IRQ selection source\n"));
173                     return (1);
174           case FIRESTAR_PIR_SELECT_NONE:
175                     DPRINTF(("No interrupt connection\n"));
176                     return (1);
177           case FIRESTAR_PIR_SELECT_IRQ:
178                     DPRINTF(("FireStar IRQ pin"));
179                     break;
180           case FIRESTAR_PIR_SELECT_PIRQ:
181                     DPRINTF(("FireStar PIO pin or Serial IRQ PIRQ#"));
182                     break;
183           case FIRESTAR_PIR_SELECT_BRIDGE:
184                     DPRINTF(("FireBridge 1 INTx# pin"));
185                     break;
186           }
187 
188           DPRINTF((" REGOFST:%#x\n", FIRESTAR_PIR_REGOFS(link)));
189           *clinkp = link;
190 
191           return (0);
192 }
193 
194 int
opti82c700_get_intr(pciintr_icu_handle_t v,int clink,int * irqp)195 opti82c700_get_intr(pciintr_icu_handle_t v, int clink, int *irqp)
196 {
197           struct opti82c700_handle *ph = v;
198           pcireg_t reg;
199           int val, addrofs, ofs;
200 
201           if (opti82c700_addr(clink, &addrofs, &ofs))
202                     return (1);
203 
204           reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
205           val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
206 
207           *irqp = (val == FIRESTAR_PIRQ_NONE) ?
208               X86_PCI_INTERRUPT_LINE_NO_CONNECTION : val;
209 
210           return (0);
211 }
212 
213 int
opti82c700_set_intr(pciintr_icu_handle_t v,int clink,int irq)214 opti82c700_set_intr(pciintr_icu_handle_t v, int clink, int irq)
215 {
216           struct opti82c700_handle *ph = v;
217           int addrofs, ofs;
218           pcireg_t reg;
219 
220           if (FIRESTAR_LEGAL_IRQ(irq) == 0)
221                     return (1);
222 
223           if (opti82c700_addr(clink, &addrofs, &ofs))
224                     return (1);
225 
226           reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
227           reg &= ~(FIRESTAR_CFG_PIRQ_MASK << ofs);
228           reg |= (irq << ofs);
229           pci_conf_write(ph->ph_pc, ph->ph_tag, addrofs, reg);
230 
231           return (0);
232 }
233 
234 int
opti82c700_get_trigger(pciintr_icu_handle_t v,int irq,int * triggerp)235 opti82c700_get_trigger(pciintr_icu_handle_t v, int irq, int *triggerp)
236 {
237           struct opti82c700_handle *ph = v;
238           int i, val, addrofs, ofs;
239           pcireg_t reg;
240 
241           if (FIRESTAR_LEGAL_IRQ(irq) == 0) {
242                     /* ISA IRQ? */
243                     *triggerp = IST_EDGE;
244                     return (0);
245           }
246 
247           /*
248            * Search PCIDV1 registers.
249            */
250           for (i = 0; i < 8; i++) {
251                     opti82c700_addr(FIRESTAR_PIR_MAKELINK(FIRESTAR_PIR_SELECT_IRQ,
252                         i), &addrofs, &ofs);
253                     reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
254                     val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
255                     if (val != irq)
256                               continue;
257                     val = ((reg >> ofs) >> FIRESTAR_TRIGGER_SHIFT) &
258                         FIRESTAR_TRIGGER_MASK;
259                     *triggerp = val ? IST_LEVEL : IST_EDGE;
260                     return (0);
261           }
262 
263           /*
264            * Search PIO PCIIRQ.
265            */
266           for (i = 0; i < 4; i++) {
267                     opti82c700_addr(FIRESTAR_PIR_MAKELINK(FIRESTAR_PIR_SELECT_PIRQ,
268                         i), &addrofs, &ofs);
269                     reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
270                     val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
271                     if (val != irq)
272                               continue;
273                     *triggerp = IST_LEVEL;
274                     return (0);
275           }
276 
277           return (1);
278 }
279 
280 int
opti82c700_set_trigger(pciintr_icu_handle_t v,int irq,int trigger)281 opti82c700_set_trigger(pciintr_icu_handle_t v, int irq, int trigger)
282 {
283           struct opti82c700_handle *ph = v;
284           int i, val, addrofs, ofs;
285           pcireg_t reg;
286 
287           if (FIRESTAR_LEGAL_IRQ(irq) == 0) {
288                     /* ISA IRQ? */
289                     return ((trigger != IST_LEVEL) ? 0 : 1);
290           }
291 
292           /*
293            * Search PCIDV1 registers.
294            */
295           for (i = 0; i < 8; i++) {
296                     opti82c700_addr(FIRESTAR_PIR_MAKELINK(FIRESTAR_PIR_SELECT_IRQ,
297                         i), &addrofs, &ofs);
298                     reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
299                     val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
300                     if (val != irq)
301                               continue;
302                     if (trigger == IST_LEVEL)
303                               reg |= (FIRESTAR_TRIGGER_MASK <<
304                                   (FIRESTAR_TRIGGER_SHIFT + ofs));
305                     else
306                               reg &= ~(FIRESTAR_TRIGGER_MASK <<
307                                   (FIRESTAR_TRIGGER_SHIFT + ofs));
308                     pci_conf_write(ph->ph_pc, ph->ph_tag, addrofs, reg);
309                     return (0);
310           }
311 
312           /*
313            * Search PIO PCIIRQ.
314            */
315           for (i = 0; i < 4; i++) {
316                     opti82c700_addr(FIRESTAR_PIR_MAKELINK(FIRESTAR_PIR_SELECT_PIRQ,
317                         i), &addrofs, &ofs);
318                     reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
319                     val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
320                     if (val != irq)
321                               continue;
322                     return (trigger == IST_LEVEL ? 0 : 1);
323           }
324 
325           return (1);
326 }
327 
328 #ifdef FIRESTARDEBUG
329 void
opti82c700_pir_dump(struct opti82c700_handle * ph)330 opti82c700_pir_dump(struct opti82c700_handle *ph)
331 {
332           pcireg_t r;
333           pcitag_t tag = ph->ph_tag;
334           pci_chipset_tag_t pc = ph->ph_pc;
335           int i, j, k;
336 
337           /* FireStar IRQ pin */
338           printf("-FireStar IRQ pin-\n");
339           for (i = j = k = 0; i < 8; i += 4) {
340                     r = pci_conf_read(pc, tag, 0xb0 + i);
341                     printf ("\t");
342                     for (j = 0; j < 4; j++, k++, r >>= 8) {
343                               printf("[%d:%s-IRQ%2d] ", k,
344                                      (r & (FIRESTAR_TRIGGER_MASK <<
345                                              FIRESTAR_TRIGGER_SHIFT)) ? "PCI" : "ISA",
346                                      r & FIRESTAR_CFG_PIRQ_MASK);
347                     }
348                     printf("\n");
349           }
350 
351           /* FireStar PIO pin or Serial IRQ PIRQ# */
352           r = pci_conf_read(pc, tag, 0xb8);
353           printf("-FireStar PIO pin or Serial IRQ PIRQ#-\n\t");
354           for (i = 0; i < 4; i++, r >>= 4) {
355                     printf("[PCIIRQ%d# %d] ", i, r & FIRESTAR_CFG_PIRQ_MASK);
356           }
357           printf("\n");
358 }
359 #endif /* FIRESTARDEBUG */
360