1209908Sraj/*-
2209908Sraj * Copyright 2006-2007 by Juniper Networks.
3209908Sraj * Copyright 2008 Semihalf.
4209908Sraj * Copyright 2010 The FreeBSD Foundation
5209908Sraj * All rights reserved.
6209908Sraj *
7209908Sraj * Portions of this software were developed by Semihalf
8209908Sraj * under sponsorship from the FreeBSD Foundation.
9209908Sraj *
10209908Sraj * Redistribution and use in source and binary forms, with or without
11209908Sraj * modification, are permitted provided that the following conditions
12209908Sraj * are met:
13209908Sraj * 1. Redistributions of source code must retain the above copyright
14209908Sraj *    notice, this list of conditions and the following disclaimer.
15209908Sraj * 2. Redistributions in binary form must reproduce the above copyright
16209908Sraj *    notice, this list of conditions and the following disclaimer in the
17209908Sraj *    documentation and/or other materials provided with the distribution.
18209908Sraj * 3. The name of the author may not be used to endorse or promote products
19209908Sraj *    derived from this software without specific prior written permission.
20209908Sraj *
21209908Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22209908Sraj * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23209908Sraj * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24209908Sraj * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25209908Sraj * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26209908Sraj * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27209908Sraj * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28209908Sraj * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29209908Sraj * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30209908Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31209908Sraj * SUCH DAMAGE.
32209908Sraj *
33209908Sraj * From: FreeBSD: src/sys/powerpc/mpc85xx/pci_ocp.c,v 1.9 2010/03/23 23:46:28 marcel
34209908Sraj */
35209908Sraj
36209908Sraj#include <sys/cdefs.h>
37209908Sraj__FBSDID("$FreeBSD$");
38209908Sraj
39209908Sraj#include <sys/param.h>
40209908Sraj#include <sys/systm.h>
41209908Sraj#include <sys/ktr.h>
42209908Sraj#include <sys/sockio.h>
43209908Sraj#include <sys/mbuf.h>
44209908Sraj#include <sys/malloc.h>
45209908Sraj#include <sys/kernel.h>
46209908Sraj#include <sys/module.h>
47209908Sraj#include <sys/socket.h>
48209908Sraj#include <sys/queue.h>
49209908Sraj#include <sys/bus.h>
50209908Sraj#include <sys/lock.h>
51209908Sraj#include <sys/mutex.h>
52209908Sraj#include <sys/rman.h>
53209908Sraj#include <sys/endian.h>
54209908Sraj
55209908Sraj#include <vm/vm.h>
56209908Sraj#include <vm/pmap.h>
57209908Sraj
58257117Snwhitehorn#include <dev/ofw/ofw_pci.h>
59209908Sraj#include <dev/ofw/ofw_bus.h>
60209908Sraj#include <dev/ofw/ofw_bus_subr.h>
61209908Sraj#include <dev/pci/pcivar.h>
62209908Sraj#include <dev/pci/pcireg.h>
63209908Sraj#include <dev/pci/pcib_private.h>
64209908Sraj
65257117Snwhitehorn#include <powerpc/ofw/ofw_pci.h>
66257117Snwhitehorn
67209908Sraj#include "ofw_bus_if.h"
68209908Sraj#include "pcib_if.h"
69209908Sraj
70209908Sraj#include <machine/resource.h>
71209908Sraj#include <machine/bus.h>
72209908Sraj#include <machine/intr_machdep.h>
73209908Sraj
74209908Sraj#include <powerpc/mpc85xx/mpc85xx.h>
75209908Sraj
76209908Sraj#define	REG_CFG_ADDR	0x0000
77209908Sraj#define	CONFIG_ACCESS_ENABLE	0x80000000
78209908Sraj
79209908Sraj#define	REG_CFG_DATA	0x0004
80209908Sraj#define	REG_INT_ACK	0x0008
81209908Sraj
82209908Sraj#define	REG_POTAR(n)	(0x0c00 + 0x20 * (n))
83209908Sraj#define	REG_POTEAR(n)	(0x0c04 + 0x20 * (n))
84209908Sraj#define	REG_POWBAR(n)	(0x0c08 + 0x20 * (n))
85209908Sraj#define	REG_POWAR(n)	(0x0c10 + 0x20 * (n))
86209908Sraj
87209908Sraj#define	REG_PITAR(n)	(0x0e00 - 0x20 * (n))
88209908Sraj#define	REG_PIWBAR(n)	(0x0e08 - 0x20 * (n))
89209908Sraj#define	REG_PIWBEAR(n)	(0x0e0c - 0x20 * (n))
90209908Sraj#define	REG_PIWAR(n)	(0x0e10 - 0x20 * (n))
91209908Sraj
92209908Sraj#define	REG_PEX_MES_DR	0x0020
93209908Sraj#define	REG_PEX_MES_IER	0x0028
94209908Sraj#define	REG_PEX_ERR_DR	0x0e00
95209908Sraj#define	REG_PEX_ERR_EN	0x0e08
96209908Sraj
97209908Sraj#define PCIR_LTSSM	0x404
98209908Sraj#define LTSSM_STAT_L0	0x16
99209908Sraj
100209908Sraj#define	DEVFN(b, s, f)	((b << 16) | (s << 8) | f)
101209908Sraj
102209908Srajstruct fsl_pcib_softc {
103257117Snwhitehorn	struct ofw_pci_softc pci_sc;
104209908Sraj	device_t	sc_dev;
105209908Sraj
106209908Sraj	int		sc_iomem_target;
107257117Snwhitehorn	bus_addr_t	sc_iomem_alloc, sc_iomem_start, sc_iomem_end;
108209908Sraj	int		sc_ioport_target;
109257117Snwhitehorn	bus_addr_t	sc_ioport_alloc, sc_ioport_start, sc_ioport_end;
110209908Sraj
111257117Snwhitehorn	struct resource *sc_res;
112209908Sraj	bus_space_handle_t sc_bsh;
113209908Sraj	bus_space_tag_t	sc_bst;
114209908Sraj	int		sc_rid;
115209908Sraj
116209908Sraj	int		sc_busnr;
117209908Sraj	int		sc_pcie;
118209908Sraj	uint8_t		sc_pcie_capreg;		/* PCI-E Capability Reg Set */
119209908Sraj
120209908Sraj	/* Devices that need special attention. */
121209908Sraj	int		sc_devfn_tundra;
122209908Sraj	int		sc_devfn_via_ide;
123209908Sraj};
124209908Sraj
125209908Sraj/* Local forward declerations. */
126209908Srajstatic uint32_t fsl_pcib_cfgread(struct fsl_pcib_softc *, u_int, u_int, u_int,
127209908Sraj    u_int, int);
128209908Srajstatic void fsl_pcib_cfgwrite(struct fsl_pcib_softc *, u_int, u_int, u_int,
129209908Sraj    u_int, uint32_t, int);
130209908Srajstatic int fsl_pcib_decode_win(phandle_t, struct fsl_pcib_softc *);
131209908Srajstatic void fsl_pcib_err_init(device_t);
132209908Srajstatic void fsl_pcib_inbound(struct fsl_pcib_softc *, int, int, u_long,
133209908Sraj    u_long, u_long);
134218075Smarcelstatic int fsl_pcib_init(struct fsl_pcib_softc *, int, int);
135209908Srajstatic void fsl_pcib_outbound(struct fsl_pcib_softc *, int, int, u_long,
136209908Sraj    u_long, u_long);
137209908Sraj
138209908Sraj/* Forward declerations. */
139209908Srajstatic int fsl_pcib_attach(device_t);
140209908Srajstatic int fsl_pcib_detach(device_t);
141209908Srajstatic int fsl_pcib_probe(device_t);
142209908Sraj
143209908Srajstatic int fsl_pcib_maxslots(device_t);
144209908Srajstatic uint32_t fsl_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int);
145209908Srajstatic void fsl_pcib_write_config(device_t, u_int, u_int, u_int, u_int,
146209908Sraj    uint32_t, int);
147209908Sraj
148209908Sraj/* Configuration r/w mutex. */
149209908Srajstruct mtx pcicfg_mtx;
150209908Srajstatic int mtx_initialized = 0;
151209908Sraj
152209908Sraj/*
153209908Sraj * Bus interface definitions.
154209908Sraj */
155209908Srajstatic device_method_t fsl_pcib_methods[] = {
156209908Sraj	/* Device interface */
157209908Sraj	DEVMETHOD(device_probe,		fsl_pcib_probe),
158209908Sraj	DEVMETHOD(device_attach,	fsl_pcib_attach),
159209908Sraj	DEVMETHOD(device_detach,	fsl_pcib_detach),
160209908Sraj
161209908Sraj	/* pcib interface */
162209908Sraj	DEVMETHOD(pcib_maxslots,	fsl_pcib_maxslots),
163209908Sraj	DEVMETHOD(pcib_read_config,	fsl_pcib_read_config),
164209908Sraj	DEVMETHOD(pcib_write_config,	fsl_pcib_write_config),
165209908Sraj
166227843Smarius	DEVMETHOD_END
167209908Sraj};
168209908Sraj
169257117Snwhitehornstatic devclass_t fsl_pcib_devclass;
170209908Sraj
171257117SnwhitehornDEFINE_CLASS_1(pcib, fsl_pcib_driver, fsl_pcib_methods,
172257117Snwhitehorn    sizeof(struct fsl_pcib_softc), ofw_pci_driver);
173266160SianDRIVER_MODULE(pcib, ofwbus, fsl_pcib_driver, fsl_pcib_devclass, 0, 0);
174209908Sraj
175209908Srajstatic int
176209908Srajfsl_pcib_probe(device_t dev)
177209908Sraj{
178209908Sraj
179257117Snwhitehorn	if (ofw_bus_get_type(dev) == NULL ||
180257117Snwhitehorn	    strcmp(ofw_bus_get_type(dev), "pci") != 0)
181209908Sraj		return (ENXIO);
182218075Smarcel
183257117Snwhitehorn	if (!(ofw_bus_is_compatible(dev, "fsl,mpc8540-pci") ||
184257117Snwhitehorn	    ofw_bus_is_compatible(dev, "fsl,mpc8548-pcie")))
185209908Sraj		return (ENXIO);
186209908Sraj
187209908Sraj	device_set_desc(dev, "Freescale Integrated PCI/PCI-E Controller");
188209908Sraj	return (BUS_PROBE_DEFAULT);
189209908Sraj}
190209908Sraj
191209908Srajstatic int
192209908Srajfsl_pcib_attach(device_t dev)
193209908Sraj{
194209908Sraj	struct fsl_pcib_softc *sc;
195209908Sraj	phandle_t node;
196209908Sraj	uint32_t cfgreg;
197257117Snwhitehorn	int maxslot, error;
198209908Sraj	uint8_t ltssm, capptr;
199209908Sraj
200209908Sraj	sc = device_get_softc(dev);
201209908Sraj	sc->sc_dev = dev;
202209908Sraj
203209908Sraj	sc->sc_rid = 0;
204209908Sraj	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
205209908Sraj	    RF_ACTIVE);
206209908Sraj	if (sc->sc_res == NULL) {
207209908Sraj		device_printf(dev, "could not map I/O memory\n");
208209908Sraj		return (ENXIO);
209209908Sraj	}
210209908Sraj	sc->sc_bst = rman_get_bustag(sc->sc_res);
211209908Sraj	sc->sc_bsh = rman_get_bushandle(sc->sc_res);
212209908Sraj	sc->sc_busnr = 0;
213209908Sraj
214209908Sraj	if (!mtx_initialized) {
215209908Sraj		mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
216209908Sraj		mtx_initialized = 1;
217209908Sraj	}
218209908Sraj
219209908Sraj	cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_VENDOR, 2);
220209908Sraj	if (cfgreg != 0x1057 && cfgreg != 0x1957)
221209908Sraj		goto err;
222209908Sraj
223209908Sraj	capptr = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_CAP_PTR, 1);
224209908Sraj	while (capptr != 0) {
225209908Sraj		cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, capptr, 2);
226209908Sraj		switch (cfgreg & 0xff) {
227209908Sraj		case PCIY_PCIX:
228209908Sraj			break;
229209908Sraj		case PCIY_EXPRESS:
230209908Sraj			sc->sc_pcie = 1;
231209908Sraj			sc->sc_pcie_capreg = capptr;
232209908Sraj			break;
233209908Sraj		}
234209908Sraj		capptr = (cfgreg >> 8) & 0xff;
235209908Sraj	}
236209908Sraj
237209908Sraj	node = ofw_bus_get_node(dev);
238257117Snwhitehorn
239209908Sraj	/*
240257117Snwhitehorn	 * Initialize generic OF PCI interface (ranges, etc.)
241209908Sraj	 */
242209908Sraj
243257117Snwhitehorn	error = ofw_pci_init(dev);
244257117Snwhitehorn	if (error)
245257117Snwhitehorn		return (error);
246257117Snwhitehorn
247209908Sraj	/*
248209908Sraj	 * Configure decode windows for PCI(E) access.
249209908Sraj	 */
250209908Sraj	if (fsl_pcib_decode_win(node, sc) != 0)
251209908Sraj		goto err;
252209908Sraj
253209908Sraj	cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_COMMAND, 2);
254209908Sraj	cfgreg |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN |
255209908Sraj	    PCIM_CMD_PORTEN;
256209908Sraj	fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_COMMAND, cfgreg, 2);
257209908Sraj
258209908Sraj	sc->sc_devfn_tundra = -1;
259209908Sraj	sc->sc_devfn_via_ide = -1;
260209908Sraj
261209908Sraj
262209908Sraj	/*
263218075Smarcel	 * Scan bus using firmware configured, 0 based bus numbering.
264209908Sraj	 */
265218075Smarcel	sc->sc_busnr = 0;
266218075Smarcel	maxslot = (sc->sc_pcie) ? 0 : PCI_SLOTMAX;
267218075Smarcel	fsl_pcib_init(sc, sc->sc_busnr, maxslot);
268209908Sraj
269209908Sraj	if (sc->sc_pcie) {
270209908Sraj		ltssm = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_LTSSM, 1);
271209908Sraj		if (ltssm < LTSSM_STAT_L0) {
272209908Sraj			if (bootverbose)
273209908Sraj				printf("PCI %d: no PCIE link, skipping\n",
274209908Sraj				    device_get_unit(dev));
275209908Sraj			return (0);
276209908Sraj		}
277209908Sraj	}
278209908Sraj
279209908Sraj	fsl_pcib_err_init(dev);
280209908Sraj
281257117Snwhitehorn	return (ofw_pci_attach(dev));
282209908Sraj
283209908Srajerr:
284209908Sraj	return (ENXIO);
285209908Sraj}
286209908Sraj
287209908Srajstatic uint32_t
288209908Srajfsl_pcib_cfgread(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func,
289209908Sraj    u_int reg, int bytes)
290209908Sraj{
291209908Sraj	uint32_t addr, data;
292209908Sraj
293209908Sraj	if (bus == sc->sc_busnr - 1)
294209908Sraj		bus = 0;
295209908Sraj
296209908Sraj	addr = CONFIG_ACCESS_ENABLE;
297209908Sraj	addr |= (bus & 0xff) << 16;
298209908Sraj	addr |= (slot & 0x1f) << 11;
299209908Sraj	addr |= (func & 0x7) << 8;
300209908Sraj	addr |= reg & 0xfc;
301209908Sraj	if (sc->sc_pcie)
302209908Sraj		addr |= (reg & 0xf00) << 16;
303209908Sraj
304209908Sraj	mtx_lock_spin(&pcicfg_mtx);
305209908Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr);
306209908Sraj
307209908Sraj	switch (bytes) {
308209908Sraj	case 1:
309209908Sraj		data = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
310209908Sraj		    REG_CFG_DATA + (reg & 3));
311209908Sraj		break;
312209908Sraj	case 2:
313209908Sraj		data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh,
314209908Sraj		    REG_CFG_DATA + (reg & 2)));
315209908Sraj		break;
316209908Sraj	case 4:
317209908Sraj		data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh,
318209908Sraj		    REG_CFG_DATA));
319209908Sraj		break;
320209908Sraj	default:
321209908Sraj		data = ~0;
322209908Sraj		break;
323209908Sraj	}
324209908Sraj	mtx_unlock_spin(&pcicfg_mtx);
325209908Sraj	return (data);
326209908Sraj}
327209908Sraj
328209908Srajstatic void
329209908Srajfsl_pcib_cfgwrite(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func,
330209908Sraj    u_int reg, uint32_t data, int bytes)
331209908Sraj{
332209908Sraj	uint32_t addr;
333209908Sraj
334209908Sraj	if (bus == sc->sc_busnr - 1)
335209908Sraj		bus = 0;
336209908Sraj
337209908Sraj	addr = CONFIG_ACCESS_ENABLE;
338209908Sraj	addr |= (bus & 0xff) << 16;
339209908Sraj	addr |= (slot & 0x1f) << 11;
340209908Sraj	addr |= (func & 0x7) << 8;
341209908Sraj	addr |= reg & 0xfc;
342209908Sraj	if (sc->sc_pcie)
343209908Sraj		addr |= (reg & 0xf00) << 16;
344209908Sraj
345209908Sraj	mtx_lock_spin(&pcicfg_mtx);
346209908Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr);
347209908Sraj
348209908Sraj	switch (bytes) {
349209908Sraj	case 1:
350209908Sraj		bus_space_write_1(sc->sc_bst, sc->sc_bsh,
351209908Sraj		    REG_CFG_DATA + (reg & 3), data);
352209908Sraj		break;
353209908Sraj	case 2:
354209908Sraj		bus_space_write_2(sc->sc_bst, sc->sc_bsh,
355209908Sraj		    REG_CFG_DATA + (reg & 2), htole16(data));
356209908Sraj		break;
357209908Sraj	case 4:
358209908Sraj		bus_space_write_4(sc->sc_bst, sc->sc_bsh,
359209908Sraj		    REG_CFG_DATA, htole32(data));
360209908Sraj		break;
361209908Sraj	}
362209908Sraj	mtx_unlock_spin(&pcicfg_mtx);
363209908Sraj}
364209908Sraj
365209908Sraj#if 0
366209908Srajstatic void
367209908Srajdump(struct fsl_pcib_softc *sc)
368209908Sraj{
369209908Sraj	unsigned int i;
370209908Sraj
371209908Sraj#define RD(o)	bus_space_read_4(sc->sc_bst, sc->sc_bsh, o)
372209908Sraj	for (i = 0; i < 5; i++) {
373209908Sraj		printf("POTAR%u  =0x%08x\n", i, RD(REG_POTAR(i)));
374209908Sraj		printf("POTEAR%u =0x%08x\n", i, RD(REG_POTEAR(i)));
375209908Sraj		printf("POWBAR%u =0x%08x\n", i, RD(REG_POWBAR(i)));
376209908Sraj		printf("POWAR%u  =0x%08x\n", i, RD(REG_POWAR(i)));
377209908Sraj	}
378209908Sraj	printf("\n");
379209908Sraj	for (i = 1; i < 4; i++) {
380209908Sraj		printf("PITAR%u  =0x%08x\n", i, RD(REG_PITAR(i)));
381209908Sraj		printf("PIWBAR%u =0x%08x\n", i, RD(REG_PIWBAR(i)));
382209908Sraj		printf("PIWBEAR%u=0x%08x\n", i, RD(REG_PIWBEAR(i)));
383209908Sraj		printf("PIWAR%u  =0x%08x\n", i, RD(REG_PIWAR(i)));
384209908Sraj	}
385209908Sraj	printf("\n");
386209908Sraj#undef RD
387209908Sraj
388209908Sraj	for (i = 0; i < 0x48; i += 4) {
389209908Sraj		printf("cfg%02x=0x%08x\n", i, fsl_pcib_cfgread(sc, 0, 0, 0,
390209908Sraj		    i, 4));
391209908Sraj	}
392209908Sraj}
393209908Sraj#endif
394209908Sraj
395209908Srajstatic int
396209908Srajfsl_pcib_maxslots(device_t dev)
397209908Sraj{
398209908Sraj	struct fsl_pcib_softc *sc = device_get_softc(dev);
399209908Sraj
400218075Smarcel	return ((sc->sc_pcie) ? 0 : PCI_SLOTMAX);
401209908Sraj}
402209908Sraj
403209908Srajstatic uint32_t
404209908Srajfsl_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
405209908Sraj    u_int reg, int bytes)
406209908Sraj{
407209908Sraj	struct fsl_pcib_softc *sc = device_get_softc(dev);
408209908Sraj	u_int devfn;
409209908Sraj
410209908Sraj	if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10)
411209908Sraj		return (~0);
412209908Sraj	devfn = DEVFN(bus, slot, func);
413209908Sraj	if (devfn == sc->sc_devfn_tundra)
414209908Sraj		return (~0);
415209908Sraj	if (devfn == sc->sc_devfn_via_ide && reg == PCIR_INTPIN)
416209908Sraj		return (1);
417209908Sraj	return (fsl_pcib_cfgread(sc, bus, slot, func, reg, bytes));
418209908Sraj}
419209908Sraj
420209908Srajstatic void
421209908Srajfsl_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
422209908Sraj    u_int reg, uint32_t val, int bytes)
423209908Sraj{
424209908Sraj	struct fsl_pcib_softc *sc = device_get_softc(dev);
425209908Sraj
426209908Sraj	if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10)
427209908Sraj		return;
428209908Sraj	fsl_pcib_cfgwrite(sc, bus, slot, func, reg, val, bytes);
429209908Sraj}
430209908Sraj
431209908Srajstatic void
432209908Srajfsl_pcib_init_via(struct fsl_pcib_softc *sc, uint16_t device, int bus,
433209908Sraj    int slot, int fn)
434209908Sraj{
435209908Sraj
436209908Sraj	if (device == 0x0686) {
437209908Sraj		fsl_pcib_write_config(sc->sc_dev, bus, slot, fn, 0x52, 0x34, 1);
438209908Sraj		fsl_pcib_write_config(sc->sc_dev, bus, slot, fn, 0x77, 0x00, 1);
439209908Sraj		fsl_pcib_write_config(sc->sc_dev, bus, slot, fn, 0x83, 0x98, 1);
440209908Sraj		fsl_pcib_write_config(sc->sc_dev, bus, slot, fn, 0x85, 0x03, 1);
441209908Sraj	} else if (device == 0x0571) {
442209908Sraj		sc->sc_devfn_via_ide = DEVFN(bus, slot, fn);
443209908Sraj		fsl_pcib_write_config(sc->sc_dev, bus, slot, fn, 0x40, 0x0b, 1);
444209908Sraj	}
445209908Sraj}
446209908Sraj
447209908Srajstatic int
448209908Srajfsl_pcib_init_bar(struct fsl_pcib_softc *sc, int bus, int slot, int func,
449209908Sraj    int barno)
450209908Sraj{
451209908Sraj	bus_addr_t *allocp;
452209908Sraj	uint32_t addr, mask, size;
453209908Sraj	int reg, width;
454209908Sraj
455209908Sraj	reg = PCIR_BAR(barno);
456209908Sraj
457209908Sraj	if (DEVFN(bus, slot, func) == sc->sc_devfn_via_ide) {
458209908Sraj		switch (barno) {
459209908Sraj		case 0:	addr = 0x1f0; break;
460209908Sraj		case 1: addr = 0x3f4; break;
461209908Sraj		case 2: addr = 0x170; break;
462209908Sraj		case 3: addr = 0x374; break;
463209908Sraj		case 4: addr = 0xcc0; break;
464209908Sraj		default: return (1);
465209908Sraj		}
466209908Sraj		fsl_pcib_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4);
467209908Sraj		return (1);
468209908Sraj	}
469209908Sraj
470209908Sraj	fsl_pcib_write_config(sc->sc_dev, bus, slot, func, reg, ~0, 4);
471209908Sraj	size = fsl_pcib_read_config(sc->sc_dev, bus, slot, func, reg, 4);
472209908Sraj	if (size == 0)
473209908Sraj		return (1);
474209908Sraj	width = ((size & 7) == 4) ? 2 : 1;
475209908Sraj
476209908Sraj	if (size & 1) {		/* I/O port */
477209908Sraj		allocp = &sc->sc_ioport_alloc;
478209908Sraj		size &= ~3;
479209908Sraj		if ((size & 0xffff0000) == 0)
480209908Sraj			size |= 0xffff0000;
481209908Sraj	} else {		/* memory */
482209908Sraj		allocp = &sc->sc_iomem_alloc;
483209908Sraj		size &= ~15;
484209908Sraj	}
485209908Sraj	mask = ~size;
486209908Sraj	size = mask + 1;
487209908Sraj	/* Sanity check (must be a power of 2). */
488209908Sraj	if (size & mask)
489209908Sraj		return (width);
490209908Sraj
491209908Sraj	addr = (*allocp + mask) & ~mask;
492209908Sraj	*allocp = addr + size;
493209908Sraj
494209908Sraj	if (bootverbose)
495209908Sraj		printf("PCI %u:%u:%u:%u: reg %x: size=%08x: addr=%08x\n",
496209908Sraj		    device_get_unit(sc->sc_dev), bus, slot, func, reg,
497209908Sraj		    size, addr);
498209908Sraj
499209908Sraj	fsl_pcib_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4);
500209908Sraj	if (width == 2)
501209908Sraj		fsl_pcib_write_config(sc->sc_dev, bus, slot, func, reg + 4,
502209908Sraj		    0, 4);
503209908Sraj	return (width);
504209908Sraj}
505209908Sraj
506209908Srajstatic int
507218075Smarcelfsl_pcib_init(struct fsl_pcib_softc *sc, int bus, int maxslot)
508209908Sraj{
509218075Smarcel	int secbus;
510209908Sraj	int old_pribus, old_secbus, old_subbus;
511209908Sraj	int new_pribus, new_secbus, new_subbus;
512209908Sraj	int slot, func, maxfunc;
513209908Sraj	int bar, maxbar;
514209908Sraj	uint16_t vendor, device;
515209908Sraj	uint8_t command, hdrtype, class, subclass;
516209908Sraj
517218075Smarcel	secbus = bus;
518209908Sraj	for (slot = 0; slot <= maxslot; slot++) {
519209908Sraj		maxfunc = 0;
520209908Sraj		for (func = 0; func <= maxfunc; func++) {
521209908Sraj			hdrtype = fsl_pcib_read_config(sc->sc_dev, bus, slot,
522209908Sraj			    func, PCIR_HDRTYPE, 1);
523209908Sraj
524209908Sraj			if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
525209908Sraj				continue;
526209908Sraj
527209908Sraj			if (func == 0 && (hdrtype & PCIM_MFDEV))
528209908Sraj				maxfunc = PCI_FUNCMAX;
529209908Sraj
530209908Sraj			vendor = fsl_pcib_read_config(sc->sc_dev, bus, slot,
531209908Sraj			    func, PCIR_VENDOR, 2);
532209908Sraj			device = fsl_pcib_read_config(sc->sc_dev, bus, slot,
533209908Sraj			    func, PCIR_DEVICE, 2);
534209908Sraj
535209908Sraj			if (vendor == 0x1957 && device == 0x3fff) {
536209908Sraj				sc->sc_devfn_tundra = DEVFN(bus, slot, func);
537209908Sraj				continue;
538209908Sraj			}
539209908Sraj
540209908Sraj			command = fsl_pcib_read_config(sc->sc_dev, bus, slot,
541209908Sraj			    func, PCIR_COMMAND, 1);
542209908Sraj			command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN);
543209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
544209908Sraj			    PCIR_COMMAND, command, 1);
545209908Sraj
546209908Sraj			if (vendor == 0x1106)
547209908Sraj				fsl_pcib_init_via(sc, device, bus, slot, func);
548209908Sraj
549209908Sraj			/* Program the base address registers. */
550209908Sraj			maxbar = (hdrtype & PCIM_HDRTYPE) ? 1 : 6;
551209908Sraj			bar = 0;
552209908Sraj			while (bar < maxbar)
553209908Sraj				bar += fsl_pcib_init_bar(sc, bus, slot, func,
554209908Sraj				    bar);
555209908Sraj
556257117Snwhitehorn			/* Put a placeholder interrupt value */
557209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
558257117Snwhitehorn			    PCIR_INTLINE, PCI_INVALID_IRQ, 1);
559209908Sraj
560209908Sraj			command |= PCIM_CMD_MEMEN | PCIM_CMD_PORTEN;
561209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
562209908Sraj			    PCIR_COMMAND, command, 1);
563209908Sraj
564209908Sraj			/*
565209908Sraj			 * Handle PCI-PCI bridges
566209908Sraj			 */
567209908Sraj			class = fsl_pcib_read_config(sc->sc_dev, bus, slot,
568209908Sraj			    func, PCIR_CLASS, 1);
569209908Sraj			subclass = fsl_pcib_read_config(sc->sc_dev, bus, slot,
570209908Sraj			    func, PCIR_SUBCLASS, 1);
571218075Smarcel
572209908Sraj			/* Allow only proper PCI-PCI briges */
573209908Sraj			if (class != PCIC_BRIDGE)
574209908Sraj				continue;
575209908Sraj			if (subclass != PCIS_BRIDGE_PCI)
576209908Sraj				continue;
577209908Sraj
578218075Smarcel			secbus++;
579209908Sraj
580209908Sraj			/* Program I/O decoder. */
581209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
582257117Snwhitehorn			    PCIR_IOBASEL_1, sc->sc_ioport_start >> 8, 1);
583209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
584257117Snwhitehorn			    PCIR_IOLIMITL_1, sc->sc_ioport_end >> 8, 1);
585209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
586257117Snwhitehorn			    PCIR_IOBASEH_1, sc->sc_ioport_start >> 16, 2);
587209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
588257117Snwhitehorn			    PCIR_IOLIMITH_1, sc->sc_ioport_end >> 16, 2);
589209908Sraj
590209908Sraj			/* Program (non-prefetchable) memory decoder. */
591209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
592257117Snwhitehorn			    PCIR_MEMBASE_1, sc->sc_iomem_start >> 16, 2);
593209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
594257117Snwhitehorn			    PCIR_MEMLIMIT_1, sc->sc_iomem_end >> 16, 2);
595209908Sraj
596209908Sraj			/* Program prefetchable memory decoder. */
597209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
598209908Sraj			    PCIR_PMBASEL_1, 0x0010, 2);
599209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
600209908Sraj			    PCIR_PMLIMITL_1, 0x000f, 2);
601209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
602209908Sraj			    PCIR_PMBASEH_1, 0x00000000, 4);
603209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
604209908Sraj			    PCIR_PMLIMITH_1, 0x00000000, 4);
605209908Sraj
606209908Sraj			/* Read currect bus register configuration */
607209908Sraj			old_pribus = fsl_pcib_read_config(sc->sc_dev, bus,
608209908Sraj			    slot, func, PCIR_PRIBUS_1, 1);
609209908Sraj			old_secbus = fsl_pcib_read_config(sc->sc_dev, bus,
610209908Sraj			    slot, func, PCIR_SECBUS_1, 1);
611209908Sraj			old_subbus = fsl_pcib_read_config(sc->sc_dev, bus,
612209908Sraj			    slot, func, PCIR_SUBBUS_1, 1);
613209908Sraj
614209908Sraj			if (bootverbose)
615209908Sraj				printf("PCI: reading firmware bus numbers for "
616209908Sraj				    "secbus = %d (bus/sec/sub) = (%d/%d/%d)\n",
617218075Smarcel				    secbus, old_pribus, old_secbus, old_subbus);
618209908Sraj
619218075Smarcel			new_pribus = bus;
620218075Smarcel			new_secbus = secbus;
621209908Sraj
622218075Smarcel			secbus = fsl_pcib_init(sc, secbus,
623218075Smarcel			    (subclass == PCIS_BRIDGE_PCI) ? PCI_SLOTMAX : 0);
624209908Sraj
625218075Smarcel			new_subbus = secbus;
626209908Sraj
627209908Sraj			if (bootverbose)
628218075Smarcel				printf("PCI: translate firmware bus numbers "
629218075Smarcel				    "for secbus %d (%d/%d/%d) -> (%d/%d/%d)\n",
630218075Smarcel				    secbus, old_pribus, old_secbus, old_subbus,
631209908Sraj				    new_pribus, new_secbus, new_subbus);
632209908Sraj
633209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
634209908Sraj			    PCIR_PRIBUS_1, new_pribus, 1);
635209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
636209908Sraj			    PCIR_SECBUS_1, new_secbus, 1);
637209908Sraj			fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
638209908Sraj			    PCIR_SUBBUS_1, new_subbus, 1);
639209908Sraj		}
640209908Sraj	}
641209908Sraj
642218075Smarcel	return (secbus);
643209908Sraj}
644209908Sraj
645209908Srajstatic void
646209908Srajfsl_pcib_inbound(struct fsl_pcib_softc *sc, int wnd, int tgt, u_long start,
647209908Sraj    u_long size, u_long pci_start)
648209908Sraj{
649209908Sraj	uint32_t attr, bar, tar;
650209908Sraj
651209908Sraj	KASSERT(wnd > 0, ("%s: inbound window 0 is invalid", __func__));
652209908Sraj
653209908Sraj	switch (tgt) {
654209908Sraj	/* XXX OCP85XX_TGTIF_RAM2, OCP85XX_TGTIF_RAM_INTL should be handled */
655209908Sraj	case OCP85XX_TGTIF_RAM1:
656209908Sraj		attr = 0xa0f55000 | (ffsl(size) - 2);
657209908Sraj		break;
658209908Sraj	default:
659209908Sraj		attr = 0;
660209908Sraj		break;
661209908Sraj	}
662209908Sraj	tar = start >> 12;
663209908Sraj	bar = pci_start >> 12;
664209908Sraj
665209908Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PITAR(wnd), tar);
666209908Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBEAR(wnd), 0);
667209908Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBAR(wnd), bar);
668209908Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWAR(wnd), attr);
669209908Sraj}
670209908Sraj
671209908Srajstatic void
672209908Srajfsl_pcib_outbound(struct fsl_pcib_softc *sc, int wnd, int res, u_long start,
673209908Sraj    u_long size, u_long pci_start)
674209908Sraj{
675209908Sraj	uint32_t attr, bar, tar;
676209908Sraj
677209908Sraj	switch (res) {
678209908Sraj	case SYS_RES_MEMORY:
679209908Sraj		attr = 0x80044000 | (ffsl(size) - 2);
680209908Sraj		break;
681209908Sraj	case SYS_RES_IOPORT:
682209908Sraj		attr = 0x80088000 | (ffsl(size) - 2);
683209908Sraj		break;
684209908Sraj	default:
685209908Sraj		attr = 0x0004401f;
686209908Sraj		break;
687209908Sraj	}
688209908Sraj	bar = start >> 12;
689209908Sraj	tar = pci_start >> 12;
690209908Sraj
691209908Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTAR(wnd), tar);
692209908Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTEAR(wnd), 0);
693209908Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWBAR(wnd), bar);
694209908Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWAR(wnd), attr);
695209908Sraj}
696209908Sraj
697209908Sraj
698209908Srajstatic void
699209908Srajfsl_pcib_err_init(device_t dev)
700209908Sraj{
701209908Sraj	struct fsl_pcib_softc *sc;
702209908Sraj	uint16_t sec_stat, dsr;
703209908Sraj	uint32_t dcr, err_en;
704209908Sraj
705209908Sraj	sc = device_get_softc(dev);
706209908Sraj
707209908Sraj	sec_stat = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_SECSTAT_1, 2);
708209908Sraj	if (sec_stat)
709209908Sraj		fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_SECSTAT_1, 0xffff, 2);
710209908Sraj	if (sc->sc_pcie) {
711209908Sraj		/* Clear error bits */
712209908Sraj		bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_IER,
713209908Sraj		    0xffffffff);
714209908Sraj		bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_DR,
715209908Sraj		    0xffffffff);
716209908Sraj		bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR,
717209908Sraj		    0xffffffff);
718209908Sraj
719209908Sraj		dsr = fsl_pcib_cfgread(sc, 0, 0, 0,
720240680Sgavin		    sc->sc_pcie_capreg + PCIER_DEVICE_STA, 2);
721209908Sraj		if (dsr)
722209908Sraj			fsl_pcib_cfgwrite(sc, 0, 0, 0,
723240680Sgavin			    sc->sc_pcie_capreg + PCIER_DEVICE_STA,
724209908Sraj			    0xffff, 2);
725209908Sraj
726209908Sraj		/* Enable all errors reporting */
727209908Sraj		err_en = 0x00bfff00;
728209908Sraj		bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_EN,
729209908Sraj		    err_en);
730209908Sraj
731209908Sraj		/* Enable error reporting: URR, FER, NFER */
732209908Sraj		dcr = fsl_pcib_cfgread(sc, 0, 0, 0,
733240680Sgavin		    sc->sc_pcie_capreg + PCIER_DEVICE_CTL, 4);
734240680Sgavin		dcr |= PCIEM_CTL_URR_ENABLE | PCIEM_CTL_FER_ENABLE |
735240680Sgavin		    PCIEM_CTL_NFER_ENABLE;
736209908Sraj		fsl_pcib_cfgwrite(sc, 0, 0, 0,
737240680Sgavin		    sc->sc_pcie_capreg + PCIER_DEVICE_CTL, dcr, 4);
738209908Sraj	}
739209908Sraj}
740209908Sraj
741209908Srajstatic int
742209908Srajfsl_pcib_detach(device_t dev)
743209908Sraj{
744209908Sraj
745209908Sraj	if (mtx_initialized) {
746209908Sraj		mtx_destroy(&pcicfg_mtx);
747209908Sraj		mtx_initialized = 0;
748209908Sraj	}
749209908Sraj	return (bus_generic_detach(dev));
750209908Sraj}
751209908Sraj
752209908Srajstatic int
753209908Srajfsl_pcib_decode_win(phandle_t node, struct fsl_pcib_softc *sc)
754209908Sraj{
755209908Sraj	device_t dev;
756257117Snwhitehorn	int error, i, trgt;
757209908Sraj
758209908Sraj	dev = sc->sc_dev;
759209908Sraj
760257117Snwhitehorn	fsl_pcib_outbound(sc, 0, -1, 0, 0, 0);
761209908Sraj
762209908Sraj	/*
763209908Sraj	 * Configure LAW decode windows.
764209908Sraj	 */
765209908Sraj	error = law_pci_target(sc->sc_res, &sc->sc_iomem_target,
766209908Sraj	    &sc->sc_ioport_target);
767209908Sraj	if (error != 0) {
768209908Sraj		device_printf(dev, "could not retrieve PCI LAW target info\n");
769209908Sraj		return (error);
770209908Sraj	}
771257117Snwhitehorn
772257117Snwhitehorn	for (i = 0; i < sc->pci_sc.sc_nrange; i++) {
773257117Snwhitehorn		switch (sc->pci_sc.sc_range[i].pci_hi &
774257117Snwhitehorn		    OFW_PCI_PHYS_HI_SPACEMASK) {
775257117Snwhitehorn		case OFW_PCI_PHYS_HI_SPACE_CONFIG:
776257117Snwhitehorn			continue;
777257117Snwhitehorn		case OFW_PCI_PHYS_HI_SPACE_IO:
778257117Snwhitehorn			trgt = sc->sc_ioport_target;
779257117Snwhitehorn			fsl_pcib_outbound(sc, 2, SYS_RES_IOPORT,
780257117Snwhitehorn			    sc->pci_sc.sc_range[i].host,
781257117Snwhitehorn			    sc->pci_sc.sc_range[i].size,
782257117Snwhitehorn			    sc->pci_sc.sc_range[i].pci);
783257117Snwhitehorn			sc->sc_ioport_start = sc->pci_sc.sc_range[i].host;
784257117Snwhitehorn			sc->sc_ioport_end = sc->pci_sc.sc_range[i].host +
785257117Snwhitehorn			    sc->pci_sc.sc_range[i].size;
786257117Snwhitehorn			sc->sc_ioport_alloc = 0x1000 + sc->pci_sc.sc_range[i].pci;
787257117Snwhitehorn			break;
788257117Snwhitehorn		case OFW_PCI_PHYS_HI_SPACE_MEM32:
789257117Snwhitehorn		case OFW_PCI_PHYS_HI_SPACE_MEM64:
790257117Snwhitehorn			trgt = sc->sc_iomem_target;
791257117Snwhitehorn			fsl_pcib_outbound(sc, 1, SYS_RES_MEMORY,
792257117Snwhitehorn			    sc->pci_sc.sc_range[i].host,
793257117Snwhitehorn			    sc->pci_sc.sc_range[i].size,
794257117Snwhitehorn			    sc->pci_sc.sc_range[i].pci);
795257117Snwhitehorn			sc->sc_iomem_start = sc->pci_sc.sc_range[i].host;
796257117Snwhitehorn			sc->sc_iomem_end = sc->pci_sc.sc_range[i].host +
797257117Snwhitehorn			    sc->pci_sc.sc_range[i].size;
798257117Snwhitehorn			sc->sc_iomem_alloc = sc->pci_sc.sc_range[i].pci;
799257117Snwhitehorn			break;
800257117Snwhitehorn		default:
801257117Snwhitehorn			panic("Unknown range type %#x\n",
802257117Snwhitehorn			    sc->pci_sc.sc_range[i].pci_hi &
803257117Snwhitehorn			    OFW_PCI_PHYS_HI_SPACEMASK);
804257117Snwhitehorn		}
805257117Snwhitehorn		error = law_enable(trgt, sc->pci_sc.sc_range[i].host,
806257117Snwhitehorn		    sc->pci_sc.sc_range[i].size);
807257117Snwhitehorn		if (error != 0) {
808257117Snwhitehorn			device_printf(dev, "could not program LAW for range "
809257117Snwhitehorn			    "%d\n", i);
810257117Snwhitehorn			return (error);
811257117Snwhitehorn		}
812209908Sraj	}
813209908Sraj
814209908Sraj	/*
815209908Sraj	 * Set outbout and inbound windows.
816209908Sraj	 */
817209908Sraj	fsl_pcib_outbound(sc, 3, -1, 0, 0, 0);
818209908Sraj	fsl_pcib_outbound(sc, 4, -1, 0, 0, 0);
819209908Sraj
820209908Sraj	fsl_pcib_inbound(sc, 1, -1, 0, 0, 0);
821209908Sraj	fsl_pcib_inbound(sc, 2, -1, 0, 0, 0);
822209908Sraj	fsl_pcib_inbound(sc, 3, OCP85XX_TGTIF_RAM1, 0,
823209908Sraj	    2U * 1024U * 1024U * 1024U, 0);
824209908Sraj
825209908Sraj	return (0);
826209908Sraj}
827257117Snwhitehorn
828