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