xref: /openbsd/sys/arch/macppc/pci/hpb.c (revision ba99a0ed)
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