1/*	$NetBSD: opti82c700.c,v 1.9 2008/04/28 20:23:25 martin 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.9 2008/04/28 20:23:25 martin 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
86int	opti82c700_getclink(pciintr_icu_handle_t, int, int *);
87int	opti82c700_get_intr(pciintr_icu_handle_t, int, int *);
88int	opti82c700_set_intr(pciintr_icu_handle_t, int, int);
89int	opti82c700_get_trigger(pciintr_icu_handle_t, int, int *);
90int	opti82c700_set_trigger(pciintr_icu_handle_t, int, int);
91
92const 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
100struct opti82c700_handle {
101	pci_chipset_tag_t ph_pc;
102	pcitag_t ph_tag;
103};
104
105int	opti82c700_addr(int, int *, int *);
106#ifdef FIRESTARDEBUG
107void	opti82c700_pir_dump(struct opti82c700_handle *);
108#endif
109
110int
111opti82c700_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
130int
131opti82c700_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
165int
166opti82c700_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
194int
195opti82c700_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
213int
214opti82c700_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
234int
235opti82c700_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
280int
281opti82c700_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
329void
330opti82c700_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