xref: /openbsd-src/sys/dev/pci/ppb.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: ppb.c,v 1.29 2009/04/01 19:51:10 kettenis Exp $	*/
2 /*	$NetBSD: ppb.c,v 1.16 1997/06/06 23:48:05 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Christopher G. Demetriou
18  *	for the NetBSD Project.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/device.h>
38 #include <sys/proc.h>
39 #include <sys/workq.h>
40 
41 #include <dev/pci/pcireg.h>
42 #include <dev/pci/pcivar.h>
43 #include <dev/pci/pcidevs.h>
44 #include <dev/pci/ppbreg.h>
45 
46 struct ppb_softc {
47 	struct device sc_dev;		/* generic device glue */
48 	pci_chipset_tag_t sc_pc;	/* our PCI chipset... */
49 	pcitag_t sc_tag;		/* ...and tag. */
50 	pci_intr_handle_t sc_ih[4];
51 	void *sc_intrhand;
52 	struct device *sc_psc;
53 	int sc_cap_off;
54 	struct timeout sc_to;
55 };
56 
57 int	ppbmatch(struct device *, void *, void *);
58 void	ppbattach(struct device *, struct device *, void *);
59 int	ppbdetach(struct device *self, int flags);
60 
61 struct cfattach ppb_ca = {
62 	sizeof(struct ppb_softc), ppbmatch, ppbattach, ppbdetach
63 };
64 
65 struct cfdriver ppb_cd = {
66 	NULL, "ppb", DV_DULL
67 };
68 
69 int	ppb_intr(void *);
70 void	ppb_hotplug_insert(void *, void *);
71 void	ppb_hotplug_insert_finish(void *);
72 int	ppb_hotplug_fixup(struct pci_attach_args *);
73 int	ppb_hotplug_fixup_type0(pci_chipset_tag_t, pcitag_t, pcitag_t);
74 int	ppb_hotplug_fixup_type1(pci_chipset_tag_t, pcitag_t, pcitag_t);
75 void	ppb_hotplug_rescan(void *, void *);
76 void	ppb_hotplug_remove(void *, void *);
77 int	ppbprint(void *, const char *pnp);
78 
79 int
80 ppbmatch(struct device *parent, void *match, void *aux)
81 {
82 	struct pci_attach_args *pa = aux;
83 
84 	/*
85 	 * This device is mislabeled.  It is not a PCI bridge.
86 	 */
87 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_VIATECH &&
88 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT82C586_PWR)
89 		return (0);
90 	/*
91 	 * Check the ID register to see that it's a PCI bridge.
92 	 * If it is, we assume that we can deal with it; it _should_
93 	 * work in a standardized way...
94 	 */
95 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
96 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_PCI)
97 		return (1);
98 
99 	return (0);
100 }
101 
102 void
103 ppbattach(struct device *parent, struct device *self, void *aux)
104 {
105 	struct ppb_softc *sc = (struct ppb_softc *)self;
106 	struct pci_attach_args *pa = aux;
107 	pci_chipset_tag_t pc = pa->pa_pc;
108 	struct pcibus_attach_args pba;
109 	pci_intr_handle_t ih;
110 	pcireg_t busdata, reg;
111 	int pin;
112 
113 	sc->sc_pc = pc;
114 	sc->sc_tag = pa->pa_tag;
115 
116 	busdata = pci_conf_read(pc, pa->pa_tag, PPB_REG_BUSINFO);
117 
118 	if (PPB_BUSINFO_SECONDARY(busdata) == 0) {
119 		printf(": not configured by system firmware\n");
120 		return;
121 	}
122 
123 #if 0
124 	/*
125 	 * XXX can't do this, because we're not given our bus number
126 	 * (we shouldn't need it), and because we've no way to
127 	 * decompose our tag.
128 	 */
129 	/* sanity check. */
130 	if (pa->pa_bus != PPB_BUSINFO_PRIMARY(busdata))
131 		panic("ppbattach: bus in tag (%d) != bus in reg (%d)",
132 		    pa->pa_bus, PPB_BUSINFO_PRIMARY(busdata));
133 #endif
134 
135 	/* Check for PCI Express capabilities and setup hotplug support. */
136 	if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
137 	    &sc->sc_cap_off, &reg) && (reg & PCI_PCIE_XCAP_SI)) {
138 		if (pci_intr_map(pa, &ih) == 0)
139 			sc->sc_intrhand = pci_intr_establish(pc, ih, IPL_TTY,
140 			    ppb_intr, sc, self->dv_xname);
141 
142 		if (sc->sc_intrhand) {
143 			printf(": %s", pci_intr_string(pc, ih));
144 
145 			/* Enable hotplug interrupt. */
146 			reg = pci_conf_read(pc, pa->pa_tag,
147 			    sc->sc_cap_off + PCI_PCIE_SLCSR);
148 			reg |= (PCI_PCIE_SLCSR_HPE | PCI_PCIE_SLCSR_PDE);
149 			pci_conf_write(pc, pa->pa_tag,
150 			    sc->sc_cap_off + PCI_PCIE_SLCSR, reg);
151 
152 			timeout_set(&sc->sc_to, ppb_hotplug_insert_finish, sc);
153 		}
154 	}
155 
156 	printf("\n");
157 
158 	for (pin = PCI_INTERRUPT_PIN_A; pin <= PCI_INTERRUPT_PIN_D; pin++) {
159 		pa->pa_intrpin = pa->pa_rawintrpin = pin;
160 		pa->pa_intrline = 0;
161 		pci_intr_map(pa, &sc->sc_ih[pin - PCI_INTERRUPT_PIN_A]);
162 	}
163 
164 	/*
165 	 * Attach the PCI bus that hangs off of it.
166 	 *
167 	 * XXX Don't pass-through Memory Read Multiple.  Should we?
168 	 * XXX Consult the spec...
169 	 */
170 	bzero(&pba, sizeof(pba));
171 	pba.pba_busname = "pci";
172 	pba.pba_iot = pa->pa_iot;
173 	pba.pba_memt = pa->pa_memt;
174 	pba.pba_dmat = pa->pa_dmat;
175 	pba.pba_pc = pc;
176 #if 0
177 	pba.pba_flags = pa->pa_flags & ~PCI_FLAGS_MRM_OKAY;
178 #endif
179 	pba.pba_domain = pa->pa_domain;
180 	pba.pba_bus = PPB_BUSINFO_SECONDARY(busdata);
181 	pba.pba_bridgeih = sc->sc_ih;
182 	pba.pba_bridgetag = &sc->sc_tag;
183 	pba.pba_intrswiz = pa->pa_intrswiz;
184 	pba.pba_intrtag = pa->pa_intrtag;
185 
186 	sc->sc_psc = config_found(self, &pba, ppbprint);
187 }
188 
189 int
190 ppbdetach(struct device *self, int flags)
191 {
192 	struct ppb_softc *sc = (struct ppb_softc *)self;
193 
194 	if (sc->sc_intrhand)
195 		pci_intr_disestablish(sc->sc_pc, sc->sc_intrhand);
196 
197 	return config_detach_children(self, flags);
198 }
199 
200 int
201 ppb_intr(void *arg)
202 {
203 	struct ppb_softc *sc = arg;
204 	pcireg_t reg;
205 
206 	/*
207 	 * XXX ignore hotplug events while in autoconf.  On some
208 	 * machines with onboard re(4), we gat a bogus hotplug remove
209 	 * event when we reset that device.  Ignoring that event makes
210 	 * sure we will not try to forcibly detach re(4) when it isn't
211 	 * ready to deal with that.
212 	 */
213 	if (cold)
214 		return (0);
215 
216 	reg = pci_conf_read(sc->sc_pc, sc->sc_tag,
217 	    sc->sc_cap_off + PCI_PCIE_SLCSR);
218 	if (reg & PCI_PCIE_SLCSR_PDC) {
219 		if (reg & PCI_PCIE_SLCSR_PDS)
220 			workq_add_task(NULL, 0, ppb_hotplug_insert, sc, NULL);
221 		else
222 			workq_add_task(NULL, 0, ppb_hotplug_remove, sc, NULL);
223 
224 		/* Clear interrupts. */
225 		pci_conf_write(sc->sc_pc, sc->sc_tag,
226 		    sc->sc_cap_off + PCI_PCIE_SLCSR, reg);
227 		return (1);
228 	}
229 
230 	return (0);
231 }
232 
233 #ifdef PCI_MACHDEP_ENUMERATE_BUS
234 #define pci_enumerate_bus PCI_MACHDEP_ENUMERATE_BUS
235 #else
236 extern int pci_enumerate_bus(struct pci_softc *,
237     int (*)(struct pci_attach_args *), struct pci_attach_args *);
238 #endif
239 
240 void
241 ppb_hotplug_insert(void *arg1, void *arg2)
242 {
243 	struct ppb_softc *sc = arg1;
244 	struct pci_softc *psc = (struct pci_softc *)sc->sc_psc;
245 
246 	if (!LIST_EMPTY(&psc->sc_devs))
247 		return;
248 
249 	/* XXX Powerup the card. */
250 
251 	/* XXX Turn on LEDs. */
252 
253 	/* Wait a second for things to settle. */
254 	timeout_add_sec(&sc->sc_to, 1);
255 }
256 
257 void
258 ppb_hotplug_insert_finish(void *arg)
259 {
260 	workq_add_task(NULL, 0, ppb_hotplug_rescan, arg, NULL);
261 }
262 
263 int
264 ppb_hotplug_fixup(struct pci_attach_args *pa)
265 {
266 	pcireg_t bhlcr;
267 
268 	bhlcr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
269 	switch (PCI_HDRTYPE_TYPE(bhlcr)) {
270 	case 0:
271 		return ppb_hotplug_fixup_type0(pa->pa_pc,
272 		    pa->pa_tag, *pa->pa_bridgetag);
273 	case 1:
274 		return ppb_hotplug_fixup_type1(pa->pa_pc,
275 		    pa->pa_tag, *pa->pa_bridgetag);
276 	default:
277 		return (0);
278 	}
279 }
280 
281 int
282 ppb_hotplug_fixup_type0(pci_chipset_tag_t pc, pcitag_t tag, pcitag_t bridgetag)
283 {
284 	pcireg_t blr, type, intr;
285 	int reg, line;
286 	bus_addr_t base, io_base, io_limit, mem_base, mem_limit;
287 	bus_size_t size, io_size, mem_size;
288 
289 	/*
290 	 * The code below assumes that the address ranges on our
291 	 * parent PCI Express bridge are really available and don't
292 	 * overlap with other devices in the system.
293 	 */
294 
295 	/* Figure out the I/O address range of the bridge. */
296 	blr = pci_conf_read(pc, bridgetag, PPB_REG_IOSTATUS);
297 	io_base = (blr & 0x000000f0) << 8;
298 	io_limit = (blr & 0x000f000) | 0x00000fff;
299 	if (io_limit > io_base)
300 		io_size = (io_limit - io_base + 1);
301 	else
302 		io_size = 0;
303 
304 	/* Figure out the memory mapped I/O address range of the bridge. */
305 	blr = pci_conf_read(pc, bridgetag, PPB_REG_MEM);
306 	mem_base = (blr & 0x0000fff0) << 16;
307 	mem_limit = (blr & 0xffff0000) | 0x000fffff;
308 	if (mem_limit > mem_base)
309 		mem_size = (mem_limit - mem_base + 1);
310 	else
311 		mem_size = 0;
312 
313 	/* Assign resources to the Base Address Registers. */
314 	for (reg = PCI_MAPREG_START; reg < PCI_MAPREG_END; reg += 4) {
315 		if (!pci_mapreg_probe(pc, tag, reg, &type))
316 			continue;
317 
318 		if (pci_mapreg_info(pc, tag, reg, type, &base, &size, NULL))
319 			continue;
320 
321 		if (base != 0)
322 			continue;
323 
324 		switch (type) {
325 		case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
326 		case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
327 			base = roundup(mem_base, size);
328 			size += base - mem_base;
329 			if (size > mem_size)
330 				continue;
331 			pci_conf_write(pc, tag, reg, base);
332 			mem_base += size;
333 			mem_size -= size;
334 			break;
335 		case PCI_MAPREG_TYPE_IO:
336 			base = roundup(io_base, size);
337 			size += base - io_base;
338 			if (size > io_size)
339 				continue;
340 			pci_conf_write(pc, tag, reg, base);
341 			io_base += size;
342 			io_size -= size;
343 			break;
344 		default:
345 			break;
346 		}
347 
348 		if (type & PCI_MAPREG_MEM_TYPE_64BIT)
349 			reg += 4;
350 	}
351 
352 	/*
353 	 * Fill in the interrupt line for platforms that need it.
354 	 *
355 	 * XXX We assume that the interrupt line matches the line used
356 	 * by the PCI Express bridge.  This may not be true.
357 	 */
358 	intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
359 	if (PCI_INTERRUPT_PIN(intr) != PCI_INTERRUPT_PIN_NONE &&
360 	    PCI_INTERRUPT_LINE(intr) == 0) {
361 		/* Get the interrupt line from our parent. */
362 		intr = pci_conf_read(pc, bridgetag, PCI_INTERRUPT_REG);
363 		line = PCI_INTERRUPT_LINE(intr);
364 
365 		intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
366 		intr &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
367 		intr |= line << PCI_INTERRUPT_LINE_SHIFT;
368 		pci_conf_write(pc, tag, PCI_INTERRUPT_REG, intr);
369 	}
370 
371 	return (0);
372 }
373 
374 int
375 ppb_hotplug_fixup_type1(pci_chipset_tag_t pc, pcitag_t tag, pcitag_t bridgetag)
376 {
377 	pcireg_t bhlcr, bir, csr, val;
378 	int bus, dev, reg;
379 
380 	bir = pci_conf_read(pc, bridgetag, PPB_REG_BUSINFO);
381 	if (PPB_BUSINFO_SUBORDINATE(bir) <= PPB_BUSINFO_SECONDARY(bir))
382 		return (0);
383 
384 	bus = PPB_BUSINFO_SECONDARY(bir);
385 	bir = pci_conf_read(pc, tag, PPB_REG_BUSINFO);
386 	bir &= (0xff << 24);
387 	bir |= bus++;
388 	bir |= (bus << 8);
389 	bir |= (bus << 16);
390 	pci_conf_write(pc, tag, PPB_REG_BUSINFO, bir);
391 
392 	for (reg = PPB_REG_IOSTATUS; reg < PPB_REG_BRIDGECONTROL; reg += 4) {
393 		val = pci_conf_read(pc, bridgetag, reg);
394 		pci_conf_write(pc, tag, reg, val);
395 	}
396 
397 	csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
398 	csr |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE;
399 	csr |= PCI_COMMAND_MASTER_ENABLE;
400 	csr |= PCI_COMMAND_INVALIDATE_ENABLE;
401 	csr |= PCI_COMMAND_SERR_ENABLE;
402 	pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
403 
404 	for (dev = 0; dev < pci_bus_maxdevs(pc, bus); dev++) {
405 		tag = pci_make_tag(pc, bus, dev, 0);
406 
407 		bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
408 		if (PCI_HDRTYPE_TYPE(bhlcr) != 0)
409 			continue;
410 
411 		ppb_hotplug_fixup_type0(pc, tag, bridgetag);
412 	}
413 
414 	return (0);
415 }
416 
417 void
418 ppb_hotplug_rescan(void *arg1, void *arg2)
419 {
420 	struct ppb_softc *sc = arg1;
421 	struct pci_softc *psc = (struct pci_softc *)sc->sc_psc;
422 
423 	if (psc) {
424 		/* Assign resources. */
425 		pci_enumerate_bus(psc, ppb_hotplug_fixup, NULL);
426 
427 		/* Attach devices. */
428 		pci_enumerate_bus(psc, NULL, NULL);
429 	}
430 }
431 
432 void
433 ppb_hotplug_remove(void *arg1, void *arg2)
434 {
435 	struct ppb_softc *sc = arg1;
436 	struct pci_softc *psc = (struct pci_softc *)sc->sc_psc;
437 
438 	if (psc)
439 		pci_detach_devices(psc, DETACH_FORCE);
440 }
441 
442 int
443 ppbprint(void *aux, const char *pnp)
444 {
445 	struct pcibus_attach_args *pba = aux;
446 
447 	/* only PCIs can attach to PPBs; easy. */
448 	if (pnp)
449 		printf("pci at %s", pnp);
450 	printf(" bus %d", pba->pba_bus);
451 	return (UNCONF);
452 }
453