1208747Sraj/*-
2208747Sraj * Copyright (c) 2010 The FreeBSD Foundation
3208747Sraj * All rights reserved.
4208747Sraj *
5208747Sraj * This software was developed by Semihalf under sponsorship from
6208747Sraj * the FreeBSD Foundation.
7208747Sraj *
8208747Sraj * Redistribution and use in source and binary forms, with or without
9208747Sraj * modification, are permitted provided that the following conditions
10208747Sraj * are met:
11208747Sraj * 1. Redistributions of source code must retain the above copyright
12208747Sraj *    notice, this list of conditions and the following disclaimer.
13208747Sraj * 2. Redistributions in binary form must reproduce the above copyright
14208747Sraj *    notice, this list of conditions and the following disclaimer in the
15208747Sraj *    documentation and/or other materials provided with the distribution.
16208747Sraj *
17208747Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18208747Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19208747Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20208747Sraj * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21208747Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22208747Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23208747Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24208747Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25208747Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26208747Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27208747Sraj * SUCH DAMAGE.
28208747Sraj */
29208747Sraj
30208747Sraj#include <sys/cdefs.h>
31208747Sraj__FBSDID("$FreeBSD$");
32208747Sraj
33208747Sraj#include <sys/param.h>
34208747Sraj#include <sys/systm.h>
35208747Sraj#include <sys/ktr.h>
36208747Sraj#include <sys/kernel.h>
37208747Sraj#include <sys/bus.h>
38208747Sraj#include <sys/rman.h>
39208747Sraj#include <sys/malloc.h>
40208747Sraj
41208747Sraj#include <dev/fdt/fdt_common.h>
42218077Smarcel#include <dev/pci/pcireg.h>
43208747Sraj
44208747Sraj#include <machine/fdt.h>
45208747Sraj
46208747Sraj#include "ofw_bus_if.h"
47218077Smarcel#include "pcib_if.h"
48208747Sraj
49208747Sraj#ifdef DEBUG
50208747Sraj#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
51208747Sraj    printf(fmt,##args); } while (0)
52208747Sraj#else
53208747Sraj#define debugf(fmt, args...)
54208747Sraj#endif
55208747Sraj
56208747Sraj#define FDT_RANGES_CELLS	((3 + 3 + 2) * 2)
57208747Sraj
58208747Srajstatic void
59208747Srajfdt_pci_range_dump(struct fdt_pci_range *range)
60208747Sraj{
61208747Sraj#ifdef DEBUG
62208747Sraj	printf("\n");
63208747Sraj	printf("  base_pci = 0x%08lx\n", range->base_pci);
64208747Sraj	printf("  base_par = 0x%08lx\n", range->base_parent);
65208747Sraj	printf("  len      = 0x%08lx\n", range->len);
66208747Sraj#endif
67208747Sraj}
68208747Sraj
69208747Srajint
70208747Srajfdt_pci_ranges_decode(phandle_t node, struct fdt_pci_range *io_space,
71208747Sraj    struct fdt_pci_range *mem_space)
72208747Sraj{
73208747Sraj	pcell_t ranges[FDT_RANGES_CELLS];
74208747Sraj	struct fdt_pci_range *pci_space;
75208747Sraj	pcell_t addr_cells, size_cells, par_addr_cells;
76208747Sraj	pcell_t *rangesptr;
77208747Sraj	pcell_t cell0, cell1, cell2;
78208747Sraj	int tuple_size, tuples, i, rv, offset_cells, len;
79208747Sraj
80208747Sraj	/*
81208747Sraj	 * Retrieve 'ranges' property.
82208747Sraj	 */
83208747Sraj	if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0)
84208747Sraj		return (EINVAL);
85208747Sraj	if (addr_cells != 3 || size_cells != 2)
86208747Sraj		return (ERANGE);
87208747Sraj
88208747Sraj	par_addr_cells = fdt_parent_addr_cells(node);
89208747Sraj	if (par_addr_cells > 3)
90208747Sraj		return (ERANGE);
91208747Sraj
92208747Sraj	len = OF_getproplen(node, "ranges");
93208747Sraj	if (len > sizeof(ranges))
94208747Sraj		return (ENOMEM);
95208747Sraj
96208747Sraj	if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0)
97208747Sraj		return (EINVAL);
98208747Sraj
99208747Sraj	tuple_size = sizeof(pcell_t) * (addr_cells + par_addr_cells +
100208747Sraj	    size_cells);
101208747Sraj	tuples = len / tuple_size;
102208747Sraj
103235930Smarcel	/*
104235930Smarcel	 * Initialize the ranges so that we don't have to worry about
105235930Smarcel	 * having them all defined in the FDT. In particular, it is
106235930Smarcel	 * perfectly fine not to want I/O space on PCI busses.
107235930Smarcel	 */
108235930Smarcel	bzero(io_space, sizeof(*io_space));
109235930Smarcel	bzero(mem_space, sizeof(*mem_space));
110235930Smarcel
111208747Sraj	rangesptr = &ranges[0];
112208747Sraj	offset_cells = 0;
113208747Sraj	for (i = 0; i < tuples; i++) {
114208747Sraj		cell0 = fdt_data_get((void *)rangesptr, 1);
115208747Sraj		rangesptr++;
116208747Sraj		cell1 = fdt_data_get((void *)rangesptr, 1);
117208747Sraj		rangesptr++;
118208747Sraj		cell2 = fdt_data_get((void *)rangesptr, 1);
119208747Sraj		rangesptr++;
120208747Sraj
121208747Sraj		if (cell0 & 0x02000000) {
122208747Sraj			pci_space = mem_space;
123208747Sraj		} else if (cell0 & 0x01000000) {
124208747Sraj			pci_space = io_space;
125208747Sraj		} else {
126208747Sraj			rv = ERANGE;
127208747Sraj			goto out;
128208747Sraj		}
129208747Sraj
130208747Sraj		if (par_addr_cells == 3) {
131208747Sraj			/*
132208747Sraj			 * This is a PCI subnode 'ranges'. Skip cell0 and
133208747Sraj			 * cell1 of this entry and only use cell2.
134208747Sraj			 */
135208747Sraj			offset_cells = 2;
136208747Sraj			rangesptr += offset_cells;
137208747Sraj		}
138208747Sraj
139208747Sraj		if (fdt_data_verify((void *)rangesptr, par_addr_cells -
140208747Sraj		    offset_cells)) {
141208747Sraj			rv = ERANGE;
142208747Sraj			goto out;
143208747Sraj		}
144208747Sraj		pci_space->base_parent = fdt_data_get((void *)rangesptr,
145208747Sraj		    par_addr_cells - offset_cells);
146208747Sraj		rangesptr += par_addr_cells - offset_cells;
147208747Sraj
148208747Sraj		if (fdt_data_verify((void *)rangesptr, size_cells)) {
149208747Sraj			rv = ERANGE;
150208747Sraj			goto out;
151208747Sraj		}
152208747Sraj		pci_space->len = fdt_data_get((void *)rangesptr, size_cells);
153208747Sraj		rangesptr += size_cells;
154208747Sraj
155208747Sraj		pci_space->base_pci = cell2;
156208747Sraj	}
157208747Sraj	rv = 0;
158208747Srajout:
159208747Sraj	return (rv);
160208747Sraj}
161208747Sraj
162208747Srajint
163208747Srajfdt_pci_ranges(phandle_t node, struct fdt_pci_range *io_space,
164208747Sraj    struct fdt_pci_range *mem_space)
165208747Sraj{
166208747Sraj	int err;
167208747Sraj
168218077Smarcel	debugf("Processing PCI node: %x\n", node);
169218077Smarcel	if ((err = fdt_pci_ranges_decode(node, io_space, mem_space)) != 0) {
170208747Sraj		debugf("could not decode parent PCI node 'ranges'\n");
171208747Sraj		return (err);
172208747Sraj	}
173208747Sraj
174208747Sraj	debugf("Post fixup dump:\n");
175208747Sraj	fdt_pci_range_dump(io_space);
176208747Sraj	fdt_pci_range_dump(mem_space);
177208747Sraj	return (0);
178208747Sraj}
179208747Sraj
180208747Srajstatic int
181208747Srajfdt_addr_cells(phandle_t node, int *addr_cells)
182208747Sraj{
183208747Sraj	pcell_t cell;
184208747Sraj	int cell_size;
185208747Sraj
186208747Sraj	cell_size = sizeof(cell);
187208747Sraj	if (OF_getprop(node, "#address-cells", &cell, cell_size) < cell_size)
188208747Sraj		return (EINVAL);
189208747Sraj	*addr_cells = fdt32_to_cpu((int)cell);
190208747Sraj
191208747Sraj	if (*addr_cells > 3)
192208747Sraj		return (ERANGE);
193208747Sraj	return (0);
194208747Sraj}
195208747Sraj
196208747Srajstatic int
197208747Srajfdt_interrupt_cells(phandle_t node)
198208747Sraj{
199208747Sraj	pcell_t intr_cells;
200208747Sraj
201208747Sraj	if (OF_getprop(node, "#interrupt-cells", &intr_cells,
202208747Sraj	    sizeof(intr_cells)) <= 0) {
203208747Sraj		debugf("no intr-cells defined, defaulting to 1\n");
204208747Sraj		intr_cells = 1;
205208747Sraj	}
206208747Sraj	intr_cells = fdt32_to_cpu(intr_cells);
207208747Sraj
208208747Sraj	return ((int)intr_cells);
209208747Sraj}
210208747Sraj
211208747Srajint
212208747Srajfdt_pci_intr_info(phandle_t node, struct fdt_pci_intr *intr_info)
213208747Sraj{
214208747Sraj	void *map, *mask;
215218077Smarcel	int acells, icells;
216218077Smarcel	int error, len;
217208747Sraj
218218077Smarcel	error = fdt_addr_cells(node, &acells);
219218077Smarcel	if (error)
220218077Smarcel		return (error);
221208747Sraj
222218077Smarcel	icells = fdt_interrupt_cells(node);
223208747Sraj
224208747Sraj	/*
225208747Sraj	 * Retrieve the interrupt map and mask properties.
226208747Sraj	 */
227218077Smarcel	len = OF_getprop_alloc(node, "interrupt-map-mask", 1, &mask);
228218077Smarcel	if (len / sizeof(pcell_t) != (acells + icells)) {
229208747Sraj		debugf("bad mask len = %d\n", len);
230208747Sraj		goto err;
231208747Sraj	}
232208747Sraj
233218077Smarcel	len = OF_getprop_alloc(node, "interrupt-map", 1, &map);
234208747Sraj	if (len <= 0) {
235208747Sraj		debugf("bad map len = %d\n", len);
236208747Sraj		goto err;
237208747Sraj	}
238208747Sraj
239208747Sraj	intr_info->map_len = len;
240208747Sraj	intr_info->map = map;
241208747Sraj	intr_info->mask = mask;
242218077Smarcel	intr_info->addr_cells = acells;
243218077Smarcel	intr_info->intr_cells = icells;
244218077Smarcel
245218077Smarcel	debugf("acells=%u, icells=%u, map_len=%u\n", acells, icells, len);
246208747Sraj	return (0);
247208747Sraj
248208747Srajerr:
249208747Sraj	free(mask, M_OFWPROP);
250208747Sraj	return (ENXIO);
251208747Sraj}
252208747Sraj
253208747Srajint
254208747Srajfdt_pci_route_intr(int bus, int slot, int func, int pin,
255208747Sraj    struct fdt_pci_intr *intr_info, int *interrupt)
256208747Sraj{
257208747Sraj	pcell_t child_spec[4], masked[4];
258208747Sraj	ihandle_t iph;
259208747Sraj	pcell_t intr_par;
260208747Sraj	pcell_t *map_ptr;
261208747Sraj	uint32_t addr;
262208747Sraj	int i, j, map_len;
263208747Sraj	int par_intr_cells, par_addr_cells, child_spec_cells, row_cells;
264208747Sraj	int par_idx, spec_idx, err, trig, pol;
265208747Sraj
266208747Sraj	child_spec_cells = intr_info->addr_cells + intr_info->intr_cells;
267208747Sraj	if (child_spec_cells > sizeof(child_spec) / sizeof(pcell_t))
268208747Sraj		return (ENOMEM);
269208747Sraj
270208747Sraj	addr = (bus << 16) | (slot << 11) | (func << 8);
271208747Sraj	child_spec[0] = addr;
272208747Sraj	child_spec[1] = 0;
273208747Sraj	child_spec[2] = 0;
274208747Sraj	child_spec[3] = pin;
275208747Sraj
276208747Sraj	map_len = intr_info->map_len;
277208747Sraj	map_ptr = intr_info->map;
278208747Sraj
279208747Sraj	par_idx = child_spec_cells;
280208747Sraj	i = 0;
281208747Sraj	while (i < map_len) {
282208747Sraj		iph = fdt32_to_cpu(map_ptr[par_idx]);
283208747Sraj		intr_par = OF_instance_to_package(iph);
284208747Sraj
285208747Sraj		err = fdt_addr_cells(intr_par, &par_addr_cells);
286208747Sraj		if (err != 0) {
287208747Sraj			debugf("could not retrieve intr parent #addr-cells\n");
288208747Sraj			return (err);
289208747Sraj		}
290208747Sraj		par_intr_cells = fdt_interrupt_cells(intr_par);
291208747Sraj
292208747Sraj		row_cells = child_spec_cells + 1 + par_addr_cells +
293208747Sraj		    par_intr_cells;
294208747Sraj
295208747Sraj		/*
296208747Sraj		 * Apply mask and look up the entry in interrupt map.
297208747Sraj		 */
298208747Sraj		for (j = 0; j < child_spec_cells; j++) {
299208747Sraj			masked[j] = child_spec[j] &
300208747Sraj			    fdt32_to_cpu(intr_info->mask[j]);
301208747Sraj
302208747Sraj			if (masked[j] != fdt32_to_cpu(map_ptr[j]))
303208747Sraj				goto next;
304208747Sraj		}
305208747Sraj
306208747Sraj		/*
307208747Sraj		 * Decode interrupt of the parent intr controller.
308208747Sraj		 */
309208747Sraj		spec_idx = child_spec_cells + 1 + par_addr_cells;
310208747Sraj		err = fdt_intr_decode(intr_par, &map_ptr[spec_idx],
311208747Sraj		    interrupt, &trig, &pol);
312208747Sraj		if (err != 0) {
313208747Sraj			debugf("could not decode interrupt\n");
314208747Sraj			return (err);
315208747Sraj		}
316208747Sraj		debugf("decoded intr = %d, trig = %d, pol = %d\n", *interrupt,
317208747Sraj		    trig, pol);
318208747Sraj
319209908Sraj#if defined(__powerpc__)
320218077Smarcel		powerpc_config_intr(FDT_MAP_IRQ(intr_par, *interrupt), trig,
321218077Smarcel		    pol);
322209908Sraj#endif
323208747Sraj		return (0);
324208747Sraj
325208747Srajnext:
326208747Sraj		map_ptr += row_cells;
327208747Sraj		i += (row_cells * sizeof(pcell_t));
328208747Sraj	}
329208747Sraj
330208747Sraj	return (ENXIO);
331208747Sraj}
332208747Sraj
333208747Sraj#if defined(__arm__)
334208747Srajint
335208747Srajfdt_pci_devmap(phandle_t node, struct pmap_devmap *devmap, vm_offset_t io_va,
336208747Sraj    vm_offset_t mem_va)
337208747Sraj{
338208747Sraj	struct fdt_pci_range io_space, mem_space;
339208747Sraj	int error;
340208747Sraj
341208747Sraj	if ((error = fdt_pci_ranges_decode(node, &io_space, &mem_space)) != 0)
342208747Sraj		return (error);
343208747Sraj
344240487Sgber	devmap->pd_va = (io_va ? io_va : io_space.base_parent);
345208747Sraj	devmap->pd_pa = io_space.base_parent;
346208747Sraj	devmap->pd_size = io_space.len;
347208747Sraj	devmap->pd_prot = VM_PROT_READ | VM_PROT_WRITE;
348208747Sraj	devmap->pd_cache = PTE_NOCACHE;
349208747Sraj	devmap++;
350208747Sraj
351240487Sgber	devmap->pd_va = (mem_va ? mem_va : mem_space.base_parent);
352208747Sraj	devmap->pd_pa = mem_space.base_parent;
353208747Sraj	devmap->pd_size = mem_space.len;
354208747Sraj	devmap->pd_prot = VM_PROT_READ | VM_PROT_WRITE;
355208747Sraj	devmap->pd_cache = PTE_NOCACHE;
356208747Sraj	return (0);
357208747Sraj}
358208747Sraj#endif
359218077Smarcel
360218077Smarcel#if 0
361218077Smarcelstatic int
362218077Smarcelfdt_pci_config_bar(device_t dev, int bus, int slot, int func, int bar)
363218077Smarcel{
364218077Smarcel}
365218077Smarcel
366218077Smarcelstatic int
367218077Smarcelfdt_pci_config_normal(device_t dev, int bus, int slot, int func)
368218077Smarcel{
369218077Smarcel	int bar;
370218077Smarcel	uint8_t command, intline, intpin;
371218077Smarcel
372218077Smarcel	command = PCIB_READ_CONFIG(dev, bus, slot, func, PCIR_COMMAND, 1);
373218077Smarcel	command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN);
374218077Smarcel	PCIB_WRITE_CONFIG(dev, bus, slot, func, PCIR_COMMAND, command, 1);
375218077Smarcel
376218077Smarcel	/* Program the base address registers. */
377218077Smarcel	bar = 0;
378218077Smarcel	while (bar <= PCIR_MAX_BAR_0)
379218077Smarcel		bar += fdt_pci_config_bar(dev, bus, slot, func, bar);
380218077Smarcel
381218077Smarcel	/* Perform interrupt routing. */
382218077Smarcel	intpin = PCIB_READ_CONFIG(dev, bus, slot, func, PCIR_INTPIN, 1);
383218077Smarcel	intline = fsl_pcib_route_int(dev, bus, slot, func, intpin);
384218077Smarcel	PCIB_WRITE_CONFIG(dev, bus, slot, func, PCIR_INTLINE, intline, 1);
385218077Smarcel
386218077Smarcel	command |= PCIM_CMD_MEMEN | PCIM_CMD_PORTEN;
387218077Smarcel	PCIB_WRITE_CONFIG(dev, bus, slot, func, PCIR_COMMAND, command, 1);
388218077Smarcel}
389218077Smarcel
390218077Smarcelstatic int
391218077Smarcelfdt_pci_config_bridge(device_t dev, int bus, int secbus, int slot, int func)
392218077Smarcel{
393218077Smarcel	int maxbar;
394218077Smarcel	uint8_t command;
395218077Smarcel
396218077Smarcel	command = PCIB_READ_CONFIG(dev, bus, slot, func, PCIR_COMMAND, 1);
397218077Smarcel	command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN);
398218077Smarcel	PCIB_WRITE_CONFIG(dev, bus, slot, func, PCIR_COMMAND, command, 1);
399218077Smarcel
400218077Smarcel	/* Program the base address registers. */
401218077Smarcel                        maxbar = (hdrtype & PCIM_HDRTYPE) ? 1 : 6;
402218077Smarcel                        bar = 0;
403218077Smarcel                        while (bar < maxbar)
404218077Smarcel                                bar += fsl_pcib_init_bar(sc, bus, slot, func,
405218077Smarcel                                    bar);
406218077Smarcel
407218077Smarcel                        /* Perform interrupt routing. */
408218077Smarcel                        intpin = fsl_pcib_read_config(sc->sc_dev, bus, slot,
409218077Smarcel                            func, PCIR_INTPIN, 1);
410218077Smarcel                        intline = fsl_pcib_route_int(sc, bus, slot, func,
411218077Smarcel                            intpin);
412218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
413218077Smarcel                            PCIR_INTLINE, intline, 1);
414218077Smarcel
415218077Smarcel                        command |= PCIM_CMD_MEMEN | PCIM_CMD_PORTEN;
416218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
417218077Smarcel                            PCIR_COMMAND, command, 1);
418218077Smarcel
419218077Smarcel                        /*
420218077Smarcel                         * Handle PCI-PCI bridges
421218077Smarcel                         */
422218077Smarcel                        class = fsl_pcib_read_config(sc->sc_dev, bus, slot,
423218077Smarcel                            func, PCIR_CLASS, 1);
424218077Smarcel                        subclass = fsl_pcib_read_config(sc->sc_dev, bus, slot,
425218077Smarcel                            func, PCIR_SUBCLASS, 1);
426218077Smarcel
427218077Smarcel                        /* Allow only proper PCI-PCI briges */
428218077Smarcel                        if (class != PCIC_BRIDGE)
429218077Smarcel                                continue;
430218077Smarcel                        if (subclass != PCIS_BRIDGE_PCI)
431218077Smarcel                                continue;
432218077Smarcel
433218077Smarcel                        secbus++;
434218077Smarcel
435218077Smarcel                        /* Program I/O decoder. */
436218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
437218077Smarcel                            PCIR_IOBASEL_1, sc->sc_ioport.rm_start >> 8, 1);
438218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
439218077Smarcel                            PCIR_IOLIMITL_1, sc->sc_ioport.rm_end >> 8, 1);
440218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
441218077Smarcel                            PCIR_IOBASEH_1, sc->sc_ioport.rm_start >> 16, 2);
442218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
443218077Smarcel                            PCIR_IOLIMITH_1, sc->sc_ioport.rm_end >> 16, 2);
444218077Smarcel
445218077Smarcel                        /* Program (non-prefetchable) memory decoder. */
446218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
447218077Smarcel                            PCIR_MEMBASE_1, sc->sc_iomem.rm_start >> 16, 2);
448218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
449218077Smarcel                            PCIR_MEMLIMIT_1, sc->sc_iomem.rm_end >> 16, 2);
450218077Smarcel
451218077Smarcel                        /* Program prefetchable memory decoder. */
452218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
453218077Smarcel                            PCIR_PMBASEL_1, 0x0010, 2);
454218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
455218077Smarcel                            PCIR_PMLIMITL_1, 0x000f, 2);
456218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
457218077Smarcel                            PCIR_PMBASEH_1, 0x00000000, 4);
458218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
459218077Smarcel                            PCIR_PMLIMITH_1, 0x00000000, 4);
460218077Smarcel
461218077Smarcel                        /* Read currect bus register configuration */
462218077Smarcel                        old_pribus = fsl_pcib_read_config(sc->sc_dev, bus,
463218077Smarcel                            slot, func, PCIR_PRIBUS_1, 1);
464218077Smarcel                        old_secbus = fsl_pcib_read_config(sc->sc_dev, bus,
465218077Smarcel                            slot, func, PCIR_SECBUS_1, 1);
466218077Smarcel                        old_subbus = fsl_pcib_read_config(sc->sc_dev, bus,
467218077Smarcel                            slot, func, PCIR_SUBBUS_1, 1);
468218077Smarcel
469218077Smarcel                        if (bootverbose)
470218077Smarcel                                printf("PCI: reading firmware bus numbers for "
471218077Smarcel                                    "secbus = %d (bus/sec/sub) = (%d/%d/%d)\n",
472218077Smarcel                                    secbus, old_pribus, old_secbus, old_subbus);
473218077Smarcel
474218077Smarcel                        new_pribus = bus;
475218077Smarcel                        new_secbus = secbus;
476218077Smarcel
477218077Smarcel                        secbus = fsl_pcib_init(sc, secbus,
478218077Smarcel                            (subclass == PCIS_BRIDGE_PCI) ? PCI_SLOTMAX : 0);
479218077Smarcel
480218077Smarcel                        new_subbus = secbus;
481218077Smarcel
482218077Smarcel                        if (bootverbose)
483218077Smarcel                                printf("PCI: translate firmware bus numbers "
484218077Smarcel                                    "for secbus %d (%d/%d/%d) -> (%d/%d/%d)\n",
485218077Smarcel                                    secbus, old_pribus, old_secbus, old_subbus,
486218077Smarcel                                    new_pribus, new_secbus, new_subbus);
487218077Smarcel
488218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
489218077Smarcel                            PCIR_PRIBUS_1, new_pribus, 1);
490218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
491218077Smarcel                            PCIR_SECBUS_1, new_secbus, 1);
492218077Smarcel                        fsl_pcib_write_config(sc->sc_dev, bus, slot, func,
493218077Smarcel                            PCIR_SUBBUS_1, new_subbus, 1);
494218077Smarcel
495218077Smarcel}
496218077Smarcel
497218077Smarcelstatic int
498218077Smarcelfdt_pci_config_slot(device_t dev, int bus, int secbus, int slot)
499218077Smarcel{
500218077Smarcel	int func, maxfunc;
501218077Smarcel	uint16_t vendor;
502218077Smarcel	uint8_t hdrtype;
503218077Smarcel
504218077Smarcel	maxfunc = 0;
505218077Smarcel	for (func = 0; func <= maxfunc; func++) {
506218077Smarcel		hdrtype = PCIB_READ_CONFIG(dev, bus, slot, func,
507218077Smarcel		    PCIR_HDRTYPE, 1);
508218077Smarcel		if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
509218077Smarcel			continue;
510218077Smarcel		if (func == 0 && (hdrtype & PCIM_MFDEV))
511218077Smarcel			maxfunc = PCI_FUNCMAX;
512218077Smarcel
513218077Smarcel		vendor = PCIB_READ_CONFIG(dev, bus, slot, func,
514218077Smarcel		    PCIR_VENDOR, 2);
515218077Smarcel		if (vendor == 0xffff)
516218077Smarcel			continue;
517218077Smarcel
518218077Smarcel		if ((hdrtype & PCIM_HDRTYPE) == PCIM_HDRTYPE_NORMAL)
519218077Smarcel			fdt_pci_config_normal(dev, bus, slot, func);
520218077Smarcel		else
521218077Smarcel			secbus = fdt_pci_config_bridge(dev, bus, secbus,
522218077Smarcel			    slot, func);
523218077Smarcel	}
524218077Smarcel
525218077Smarcel	return (secbus);
526218077Smarcel}
527218077Smarcel
528218077Smarcelstatic int
529218077Smarcelfdt_pci_config_bus(device_t dev, int bus, int maxslot)
530218077Smarcel{
531218077Smarcel	int func, maxfunc, secbus, slot;
532218077Smarcel
533218077Smarcel	secbus = bus;
534218077Smarcel	for (slot = 0; slot <= maxslot; slot++)
535218077Smarcel		secbus = fdt_pci_config_slot(dev, bus, secbus, slot);
536218077Smarcel
537218077Smarcel	return (secbus);
538218077Smarcel}
539218077Smarcel
540218077Smarcelint
541218077Smarcelfdt_pci_config_domain(device_t dev)
542218077Smarcel{
543218077Smarcel	pcell_t bus_range[2];
544218077Smarcel	phandle_t root;
545218077Smarcel	int bus, error, maxslot;
546218077Smarcel
547218077Smarcel	root = ofw_bus_get_node(dev);
548218077Smarcel	if (root == 0)
549218077Smarcel		return (EINVAL);
550218077Smarcel	if (!fdt_is_type(root, "pci"))
551218077Smarcel		return (EINVAL);
552218077Smarcel
553218077Smarcel	/*
554218077Smarcel	 * Determine the bus number of the root in this domain.
555218077Smarcel	 * Lacking any information, this will be bus 0.
556218077Smarcel	 * Write the bus number to the bus device, using the IVAR.
557218077Smarcel	 */
558218077Smarcel	if ((OF_getprop(root, "bus-range", bus_range, sizeof(bus_range)) <= 0)
559218077Smarcel		bus = 0;
560218077Smarcel	else
561218077Smarcel		bus = fdt32_to_cpu(bus_range[0]);
562218077Smarcel
563218077Smarcel	error = BUS_WRITE_IVAR(dev, NULL, PCIB_IVAR_BUS, bus);
564218077Smarcel	if (error)
565218077Smarcel		return (error);
566218077Smarcel
567218077Smarcel	/* Get the maximum slot number for bus-enumeration. */
568218077Smarcel	maxslot = PCIB_MAXSLOTS(dev);
569218077Smarcel
570218077Smarcel	bus = fdt_pci_config_bus(dev, bus, maxslot);
571218077Smarcel	return (0);
572218077Smarcel}
573218077Smarcel#endif
574