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