xref: /openbsd-src/sys/arch/macppc/pci/hpb.c (revision ba99a0ed9a190d694621c8af1f241c30ba0378b3)
1*ba99a0edSmpi /*	$OpenBSD: hpb.c,v 1.1 2015/06/02 13:53:43 mpi Exp $	*/
2*ba99a0edSmpi 
3*ba99a0edSmpi /*
4*ba99a0edSmpi  * Copyright (c) 2015 Martin Pieuchot
5*ba99a0edSmpi  *
6*ba99a0edSmpi  * Permission to use, copy, modify, and distribute this software for any
7*ba99a0edSmpi  * purpose with or without fee is hereby granted, provided that the above
8*ba99a0edSmpi  * copyright notice and this permission notice appear in all copies.
9*ba99a0edSmpi  *
10*ba99a0edSmpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*ba99a0edSmpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*ba99a0edSmpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*ba99a0edSmpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*ba99a0edSmpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*ba99a0edSmpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*ba99a0edSmpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*ba99a0edSmpi  */
18*ba99a0edSmpi 
19*ba99a0edSmpi #include <sys/param.h>
20*ba99a0edSmpi #include <sys/systm.h>
21*ba99a0edSmpi #include <sys/device.h>
22*ba99a0edSmpi 
23*ba99a0edSmpi #include <machine/intr.h>
24*ba99a0edSmpi 
25*ba99a0edSmpi #include <dev/pci/pcireg.h>
26*ba99a0edSmpi #include <dev/pci/pcivar.h>
27*ba99a0edSmpi #include <dev/pci/pcidevs.h>
28*ba99a0edSmpi #include <dev/pci/ppbreg.h>
29*ba99a0edSmpi 
30*ba99a0edSmpi /*
31*ba99a0edSmpi  * Section 7.6.2  Interrupt Message Definition Register
32*ba99a0edSmpi  */
33*ba99a0edSmpi #define PCI_HT_LAST_ISMG		(0x01 << 16)
34*ba99a0edSmpi #define PCI_HT_IMSG_LO(idx)		(((2 * (idx)) + 0x10) << 16)
35*ba99a0edSmpi #define  HT_MASK	(1 << 0)
36*ba99a0edSmpi #define  HT_ACTIVELOW	(1 << 1)
37*ba99a0edSmpi #define  HT_EOI		(1 << 5)
38*ba99a0edSmpi 
39*ba99a0edSmpi #define PCI_HT_IMSG_HI(idx)		(PCI_HT_IMSG_LO(idx) + 1)
40*ba99a0edSmpi #define  HT_PASSPW	(1U << 30)
41*ba99a0edSmpi #define  HT_WAITEOI	(1U << 31)	/* Waiting for EOI */
42*ba99a0edSmpi 
43*ba99a0edSmpi 
44*ba99a0edSmpi /* Apple hardware is special... */
45*ba99a0edSmpi #define HT_APPL_EOIOFF(idx)		(0x60 + (((idx) >> 3) & ~3))
46*ba99a0edSmpi #define	HT_APPL_WEOI(idx)		(1 << ((idx) & 0x1f))
47*ba99a0edSmpi 
48*ba99a0edSmpi struct ht_intr_msg {
49*ba99a0edSmpi 	unsigned int	him_idx;	/* Index */
50*ba99a0edSmpi 	int		him_ist;	/* Share type */
51*ba99a0edSmpi 	pcireg_t	him_weoi;	/* Cached wait for interrupt data */
52*ba99a0edSmpi };
53*ba99a0edSmpi 
54*ba99a0edSmpi #define	hpb_MAX_IMSG	128
55*ba99a0edSmpi 
56*ba99a0edSmpi struct hpb_softc {
57*ba99a0edSmpi 	struct device		sc_dev;
58*ba99a0edSmpi 	pci_chipset_tag_t	sc_pc;
59*ba99a0edSmpi 	pcitag_t		sc_tag;
60*ba99a0edSmpi 	pcireg_t		sc_id;		/* Needed for Apple hardware */
61*ba99a0edSmpi 	unsigned int		sc_off;		/* Interrupt cap. offset */
62*ba99a0edSmpi 	unsigned int		sc_nirq;
63*ba99a0edSmpi 	struct ht_intr_msg	sc_imap[hpb_MAX_IMSG];
64*ba99a0edSmpi };
65*ba99a0edSmpi 
66*ba99a0edSmpi int	hpb_match(struct device *, void *, void *);
67*ba99a0edSmpi void	hpb_attach(struct device *, struct device *, void *);
68*ba99a0edSmpi 
69*ba99a0edSmpi int	hpb_print(void *, const char *);
70*ba99a0edSmpi 
71*ba99a0edSmpi void	hpb_eoi(int);
72*ba99a0edSmpi int	hpb_enable_irq(int, int);
73*ba99a0edSmpi int	hpb_disable_irq(int, int);
74*ba99a0edSmpi 
75*ba99a0edSmpi const struct cfattach hpb_ca = {
76*ba99a0edSmpi 	sizeof(struct hpb_softc), hpb_match, hpb_attach
77*ba99a0edSmpi };
78*ba99a0edSmpi 
79*ba99a0edSmpi struct cfdriver hpb_cd = {
80*ba99a0edSmpi 	NULL, "hpb", DV_DULL,
81*ba99a0edSmpi };
82*ba99a0edSmpi 
83*ba99a0edSmpi int
hpb_match(struct device * parent,void * match,void * aux)84*ba99a0edSmpi hpb_match(struct device *parent, void *match, void *aux)
85*ba99a0edSmpi {
86*ba99a0edSmpi 	struct pci_attach_args *pa = aux;
87*ba99a0edSmpi 	pci_chipset_tag_t pc = pa->pa_pc;
88*ba99a0edSmpi 	pcitag_t tag = pa->pa_tag;
89*ba99a0edSmpi 
90*ba99a0edSmpi 	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE ||
91*ba99a0edSmpi 	    PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_PCI)
92*ba99a0edSmpi 		return (0);
93*ba99a0edSmpi 
94*ba99a0edSmpi 	if (!pci_get_ht_capability(pc, tag, PCI_HT_CAP_INTR, NULL, NULL))
95*ba99a0edSmpi 		return (0);
96*ba99a0edSmpi 
97*ba99a0edSmpi 	return (10);
98*ba99a0edSmpi }
99*ba99a0edSmpi 
100*ba99a0edSmpi void
hpb_attach(struct device * parent,struct device * self,void * aux)101*ba99a0edSmpi hpb_attach(struct device *parent, struct device *self, void *aux)
102*ba99a0edSmpi {
103*ba99a0edSmpi 	struct hpb_softc *sc = (struct hpb_softc *)self;
104*ba99a0edSmpi 	struct pci_attach_args *pa = aux;
105*ba99a0edSmpi 	struct pcibus_attach_args pba;
106*ba99a0edSmpi 	pci_chipset_tag_t pc = pa->pa_pc;
107*ba99a0edSmpi 	pcitag_t tag = pa->pa_tag;
108*ba99a0edSmpi 	int idx, irq, off;
109*ba99a0edSmpi 	pcireg_t busdata, reg;
110*ba99a0edSmpi 
111*ba99a0edSmpi 	if (!pci_get_ht_capability(pc, tag, PCI_HT_CAP_INTR, &off, NULL))
112*ba99a0edSmpi 		panic("A clown ate your HT capability");
113*ba99a0edSmpi 
114*ba99a0edSmpi 	/* Interrupt definitions are numbered beginning with 0. */
115*ba99a0edSmpi 	pci_conf_write(pc, tag, off, PCI_HT_LAST_ISMG);
116*ba99a0edSmpi 	reg = pci_conf_read(pc, tag, off + PCI_HT_INTR_DATA);
117*ba99a0edSmpi 
118*ba99a0edSmpi 	sc->sc_pc = pc;
119*ba99a0edSmpi 	sc->sc_tag = tag;
120*ba99a0edSmpi 	sc->sc_id = pa->pa_id;
121*ba99a0edSmpi 	sc->sc_off = off;
122*ba99a0edSmpi 	sc->sc_nirq = ((reg >> 16) & 0xff);
123*ba99a0edSmpi 
124*ba99a0edSmpi 	if (sc->sc_nirq == 0 || sc->sc_nirq > hpb_MAX_IMSG)
125*ba99a0edSmpi 		return;
126*ba99a0edSmpi 
127*ba99a0edSmpi 	printf(": %u sources\n", sc->sc_nirq);
128*ba99a0edSmpi 
129*ba99a0edSmpi 	for (idx = 0; idx < sc->sc_nirq; idx++) {
130*ba99a0edSmpi 		pci_conf_write(pc, tag, off, PCI_HT_IMSG_LO(idx));
131*ba99a0edSmpi 		reg = pci_conf_read(pc, tag, off + PCI_HT_INTR_DATA);
132*ba99a0edSmpi 
133*ba99a0edSmpi 		pci_conf_write(pc, tag, off + PCI_HT_INTR_DATA, reg | HT_MASK);
134*ba99a0edSmpi 		irq = (reg >> 16) & 0xff;
135*ba99a0edSmpi 
136*ba99a0edSmpi #ifdef DIAGNOSTIC
137*ba99a0edSmpi 		if (sc->sc_imap[irq].him_idx != 0) {
138*ba99a0edSmpi 			printf("%s: multiple definition for irq %d\n",
139*ba99a0edSmpi 			    sc->sc_dev.dv_xname, irq);
140*ba99a0edSmpi 			continue;
141*ba99a0edSmpi 		}
142*ba99a0edSmpi #endif
143*ba99a0edSmpi 		pci_conf_write(pc, tag, off, PCI_HT_IMSG_HI(idx));
144*ba99a0edSmpi 		reg = pci_conf_read(pc, tag, off + PCI_HT_INTR_DATA);
145*ba99a0edSmpi 
146*ba99a0edSmpi 		sc->sc_imap[irq].him_idx = idx;
147*ba99a0edSmpi 		sc->sc_imap[irq].him_weoi = reg | HT_WAITEOI;
148*ba99a0edSmpi 	}
149*ba99a0edSmpi 
150*ba99a0edSmpi 	busdata = pci_conf_read(pc, pa->pa_tag, PPB_REG_BUSINFO);
151*ba99a0edSmpi 
152*ba99a0edSmpi 	memset(&pba, 0, sizeof(pba));
153*ba99a0edSmpi 	pba.pba_busname = "pci";
154*ba99a0edSmpi 	pba.pba_iot = pa->pa_iot;
155*ba99a0edSmpi 	pba.pba_memt = pa->pa_memt;
156*ba99a0edSmpi 	pba.pba_dmat = pa->pa_dmat;
157*ba99a0edSmpi 	pba.pba_pc = pc;
158*ba99a0edSmpi 	pba.pba_domain = pa->pa_domain;
159*ba99a0edSmpi 	pba.pba_bus = PPB_BUSINFO_SECONDARY(busdata);
160*ba99a0edSmpi 	pba.pba_bridgetag = &sc->sc_tag;
161*ba99a0edSmpi 
162*ba99a0edSmpi 	config_found(self, &pba, hpb_print);
163*ba99a0edSmpi }
164*ba99a0edSmpi 
165*ba99a0edSmpi int
hpb_print(void * aux,const char * pnp)166*ba99a0edSmpi hpb_print(void *aux, const char *pnp)
167*ba99a0edSmpi {
168*ba99a0edSmpi 	struct pcibus_attach_args *pba = aux;
169*ba99a0edSmpi 
170*ba99a0edSmpi 	if (pnp)
171*ba99a0edSmpi 		printf("%s at %s", pba->pba_busname, pnp);
172*ba99a0edSmpi 	printf(" bus %d", pba->pba_bus);
173*ba99a0edSmpi 	return (UNCONF);
174*ba99a0edSmpi }
175*ba99a0edSmpi 
176*ba99a0edSmpi void
hpb_eoi(int irq)177*ba99a0edSmpi hpb_eoi(int irq)
178*ba99a0edSmpi {
179*ba99a0edSmpi 	struct hpb_softc *sc = hpb_cd.cd_devs[0];
180*ba99a0edSmpi 	pci_chipset_tag_t pc = sc->sc_pc;
181*ba99a0edSmpi 	pcitag_t tag = sc->sc_tag;
182*ba99a0edSmpi 	int idx;
183*ba99a0edSmpi 
184*ba99a0edSmpi 	if (irq >= sc->sc_nirq || sc->sc_imap[irq].him_weoi == 0 ||
185*ba99a0edSmpi 	    sc->sc_imap[irq].him_ist != IST_LEVEL)
186*ba99a0edSmpi 		return;
187*ba99a0edSmpi 
188*ba99a0edSmpi 	idx = sc->sc_imap[irq].him_idx;
189*ba99a0edSmpi 
190*ba99a0edSmpi 	if (PCI_VENDOR(sc->sc_id) == PCI_VENDOR_APPLE) {
191*ba99a0edSmpi 		pci_conf_write(pc, tag, HT_APPL_EOIOFF(idx), HT_APPL_WEOI(idx));
192*ba99a0edSmpi 	} else {
193*ba99a0edSmpi 		pci_conf_write(pc, tag, sc->sc_off, PCI_HT_IMSG_HI(idx));
194*ba99a0edSmpi 		pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA,
195*ba99a0edSmpi 		    sc->sc_imap[irq].him_weoi);
196*ba99a0edSmpi 	}
197*ba99a0edSmpi }
198*ba99a0edSmpi 
199*ba99a0edSmpi int
hpb_enable_irq(int irq,int ist)200*ba99a0edSmpi hpb_enable_irq(int irq, int ist)
201*ba99a0edSmpi {
202*ba99a0edSmpi 	struct hpb_softc *sc = hpb_cd.cd_devs[0];
203*ba99a0edSmpi 	pci_chipset_tag_t pc = sc->sc_pc;
204*ba99a0edSmpi 	pcitag_t tag = sc->sc_tag;
205*ba99a0edSmpi 	pcireg_t reg;
206*ba99a0edSmpi 	int idx;
207*ba99a0edSmpi 
208*ba99a0edSmpi 	if (irq >= sc->sc_nirq || sc->sc_imap[irq].him_weoi == 0)
209*ba99a0edSmpi 		return (0);
210*ba99a0edSmpi 
211*ba99a0edSmpi 	idx = sc->sc_imap[irq].him_idx;
212*ba99a0edSmpi 	sc->sc_imap[irq].him_ist = ist;
213*ba99a0edSmpi 
214*ba99a0edSmpi 	pci_conf_write(pc, tag, sc->sc_off, PCI_HT_IMSG_LO(idx));
215*ba99a0edSmpi 	reg = pci_conf_read(pc, tag, sc->sc_off + PCI_HT_INTR_DATA);
216*ba99a0edSmpi 
217*ba99a0edSmpi 	pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA, reg | HT_MASK);
218*ba99a0edSmpi 
219*ba99a0edSmpi 	reg &= ~(HT_ACTIVELOW | HT_EOI | HT_MASK);
220*ba99a0edSmpi 	if (ist == IST_LEVEL)
221*ba99a0edSmpi 		reg |= HT_ACTIVELOW | HT_EOI;
222*ba99a0edSmpi 
223*ba99a0edSmpi 	pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA, reg);
224*ba99a0edSmpi 
225*ba99a0edSmpi 	return (1);
226*ba99a0edSmpi }
227*ba99a0edSmpi 
228*ba99a0edSmpi int
hpb_disable_irq(int irq,int ist)229*ba99a0edSmpi hpb_disable_irq(int irq, int ist)
230*ba99a0edSmpi {
231*ba99a0edSmpi 	struct hpb_softc *sc = hpb_cd.cd_devs[0];
232*ba99a0edSmpi 	pci_chipset_tag_t pc = sc->sc_pc;
233*ba99a0edSmpi 	pcitag_t tag = sc->sc_tag;
234*ba99a0edSmpi 	pcireg_t reg;
235*ba99a0edSmpi 	int idx;
236*ba99a0edSmpi 
237*ba99a0edSmpi 	if (irq > sc->sc_nirq || sc->sc_imap[irq].him_weoi == 0)
238*ba99a0edSmpi 		return (0);
239*ba99a0edSmpi 
240*ba99a0edSmpi 	idx = sc->sc_imap[irq].him_idx;
241*ba99a0edSmpi 
242*ba99a0edSmpi 	pci_conf_write(pc, tag, sc->sc_off, PCI_HT_IMSG_LO(idx));
243*ba99a0edSmpi 	reg = pci_conf_read(pc, tag, sc->sc_off + PCI_HT_INTR_DATA);
244*ba99a0edSmpi 
245*ba99a0edSmpi 	pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA, reg | HT_MASK);
246*ba99a0edSmpi 
247*ba99a0edSmpi 	return (1);
248*ba99a0edSmpi }
249