pci_subr.c revision 224069
169783Smsmith/*-
2223520Sjhb * Copyright (c) 2011 Advanced Computing Technologies LLC
3223520Sjhb * Written by: John H. Baldwin <jhb@FreeBSD.org>
469783Smsmith * All rights reserved.
569783Smsmith *
669783Smsmith * Redistribution and use in source and binary forms, with or without
769783Smsmith * modification, are permitted provided that the following conditions
869783Smsmith * are met:
969783Smsmith * 1. Redistributions of source code must retain the above copyright
1069783Smsmith *    notice, this list of conditions and the following disclaimer.
1169783Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1269783Smsmith *    notice, this list of conditions and the following disclaimer in the
1369783Smsmith *    documentation and/or other materials provided with the distribution.
1469783Smsmith *
1569783Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1669783Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1769783Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1869783Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1969783Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2069783Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2169783Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2269783Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2369783Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2469783Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2569783Smsmith * SUCH DAMAGE.
2669783Smsmith */
2769783Smsmith
28119418Sobrien#include <sys/cdefs.h>
29119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/pci/pci_subr.c 224069 2011-07-15 21:08:58Z jhb $");
30119418Sobrien
3169783Smsmith/*
32223520Sjhb * Support APIs for Host to PCI bridge drivers and drivers that
33223520Sjhb * provide PCI domains.
3469783Smsmith */
3569783Smsmith
36224069Sjhb#include <sys/param.h>
37221393Sjhb#include <sys/bus.h>
38107546Simp#include <sys/rman.h>
39224069Sjhb#include <sys/systm.h>
4069783Smsmith
41223520Sjhb#include <dev/pci/pcireg.h>
42119285Simp#include <dev/pci/pcivar.h>
43119285Simp#include <dev/pci/pcib_private.h>
4469783Smsmith
4569783Smsmith/*
46107172Sjhb * Try to read the bus number of a host-PCI bridge using appropriate config
47107172Sjhb * registers.
48107172Sjhb */
49107172Sjhbint
50107172Sjhbhost_pcib_get_busno(pci_read_config_fn read_config, int bus, int slot, int func,
51119266Simp    uint8_t *busnum)
52107172Sjhb{
53119266Simp	uint32_t id;
54107172Sjhb
55107172Sjhb	id = read_config(bus, slot, func, PCIR_DEVVENDOR, 4);
56107248Sjhb	if (id == 0xffffffff)
57107172Sjhb		return (0);
58107172Sjhb
59107172Sjhb	switch (id) {
60107172Sjhb	case 0x12258086:
61107172Sjhb		/* Intel 824?? */
62107172Sjhb		/* XXX This is a guess */
63107172Sjhb		/* *busnum = read_config(bus, slot, func, 0x41, 1); */
64107172Sjhb		*busnum = bus;
65107172Sjhb		break;
66107172Sjhb	case 0x84c48086:
67107172Sjhb		/* Intel 82454KX/GX (Orion) */
68107172Sjhb		*busnum = read_config(bus, slot, func, 0x4a, 1);
69107172Sjhb		break;
70107172Sjhb	case 0x84ca8086:
71107172Sjhb		/*
72107172Sjhb		 * For the 450nx chipset, there is a whole bundle of
73107172Sjhb		 * things pretending to be host bridges. The MIOC will
74107172Sjhb		 * be seen first and isn't really a pci bridge (the
75107172Sjhb		 * actual busses are attached to the PXB's). We need to
76107172Sjhb		 * read the registers of the MIOC to figure out the
77107172Sjhb		 * bus numbers for the PXB channels.
78107172Sjhb		 *
79107172Sjhb		 * Since the MIOC doesn't have a pci bus attached, we
80107172Sjhb		 * pretend it wasn't there.
81107172Sjhb		 */
82107172Sjhb		return (0);
83107172Sjhb	case 0x84cb8086:
84107172Sjhb		switch (slot) {
85107172Sjhb		case 0x12:
86107172Sjhb			/* Intel 82454NX PXB#0, Bus#A */
87107248Sjhb			*busnum = read_config(bus, 0x10, func, 0xd0, 1);
88107172Sjhb			break;
89107172Sjhb		case 0x13:
90107172Sjhb			/* Intel 82454NX PXB#0, Bus#B */
91107248Sjhb			*busnum = read_config(bus, 0x10, func, 0xd1, 1) + 1;
92107172Sjhb			break;
93107172Sjhb		case 0x14:
94107172Sjhb			/* Intel 82454NX PXB#1, Bus#A */
95107248Sjhb			*busnum = read_config(bus, 0x10, func, 0xd3, 1);
96107172Sjhb			break;
97107172Sjhb		case 0x15:
98107172Sjhb			/* Intel 82454NX PXB#1, Bus#B */
99107248Sjhb			*busnum = read_config(bus, 0x10, func, 0xd4, 1) + 1;
100107172Sjhb			break;
101107172Sjhb		}
102107172Sjhb		break;
103107172Sjhb
104107172Sjhb		/* ServerWorks -- vendor 0x1166 */
105107172Sjhb	case 0x00051166:
106107172Sjhb	case 0x00061166:
107107172Sjhb	case 0x00081166:
108107172Sjhb	case 0x00091166:
109107172Sjhb	case 0x00101166:
110107172Sjhb	case 0x00111166:
111107172Sjhb	case 0x00171166:
112107172Sjhb	case 0x01011166:
113107172Sjhb	case 0x010f1014:
114215820Sjhb	case 0x01101166:
115107172Sjhb	case 0x02011166:
116215820Sjhb	case 0x02251166:
117107172Sjhb	case 0x03021014:
118107172Sjhb		*busnum = read_config(bus, slot, func, 0x44, 1);
119107172Sjhb		break;
120144110Sjhb
121144110Sjhb		/* Compaq/HP -- vendor 0x0e11 */
122144110Sjhb	case 0x60100e11:
123144110Sjhb		*busnum = read_config(bus, slot, func, 0xc8, 1);
124144110Sjhb		break;
125107172Sjhb	default:
126107172Sjhb		/* Don't know how to read bus number. */
127107172Sjhb		return 0;
128107172Sjhb	}
129107172Sjhb
130107172Sjhb	return 1;
131107172Sjhb}
132224069Sjhb
133224069Sjhb#ifdef NEW_PCIB
134224069Sjhb/*
135224069Sjhb * Return a pointer to a pretty name for a PCI device.  If the device
136224069Sjhb * has a driver attached, the device's name is used, otherwise a name
137224069Sjhb * is generated from the device's PCI address.
138224069Sjhb */
139224069Sjhbconst char *
140224069Sjhbpcib_child_name(device_t child)
141224069Sjhb{
142224069Sjhb	static char buf[64];
143224069Sjhb
144224069Sjhb	if (device_get_nameunit(child) != NULL)
145224069Sjhb		return (device_get_nameunit(child));
146224069Sjhb	snprintf(buf, sizeof(buf), "pci%d:%d:%d:%d", pci_get_domain(child),
147224069Sjhb	    pci_get_bus(child), pci_get_slot(child), pci_get_function(child));
148224069Sjhb	return (buf);
149224069Sjhb}
150224069Sjhb
151224069Sjhb/*
152224069Sjhb * Some Host-PCI bridge drivers know which resource ranges they can
153224069Sjhb * decode and should only allocate subranges to child PCI devices.
154224069Sjhb * This API provides a way to manage this.  The bridge drive should
155224069Sjhb * initialize this structure during attach and call
156224069Sjhb * pcib_host_res_decodes() on each resource range it decodes.  It can
157224069Sjhb * then use pcib_host_res_alloc() and pcib_host_res_adjust() as helper
158224069Sjhb * routines for BUS_ALLOC_RESOURCE() and BUS_ADJUST_RESOURCE().  This
159224069Sjhb * API assumes that resources for any decoded ranges can be safely
160224069Sjhb * allocated from the parent via bus_generic_alloc_resource().
161224069Sjhb */
162224069Sjhbint
163224069Sjhbpcib_host_res_init(device_t pcib, struct pcib_host_resources *hr)
164224069Sjhb{
165224069Sjhb
166224069Sjhb	hr->hr_pcib = pcib;
167224069Sjhb	resource_list_init(&hr->hr_rl);
168224069Sjhb	return (0);
169224069Sjhb}
170224069Sjhb
171224069Sjhbint
172224069Sjhbpcib_host_res_free(device_t pcib, struct pcib_host_resources *hr)
173224069Sjhb{
174224069Sjhb
175224069Sjhb	resource_list_free(&hr->hr_rl);
176224069Sjhb	return (0);
177224069Sjhb}
178224069Sjhb
179224069Sjhbint
180224069Sjhbpcib_host_res_decodes(struct pcib_host_resources *hr, int type, u_long start,
181224069Sjhb    u_long end, u_int flags)
182224069Sjhb{
183224069Sjhb	struct resource_list_entry *rle;
184224069Sjhb	int rid;
185224069Sjhb
186224069Sjhb	if (bootverbose)
187224069Sjhb		device_printf(hr->hr_pcib, "decoding %d %srange %#lx-%#lx\n",
188224069Sjhb		    type, flags & RF_PREFETCHABLE ? "prefetchable ": "", start,
189224069Sjhb		    end);
190224069Sjhb	rid = resource_list_add_next(&hr->hr_rl, type, start, end,
191224069Sjhb	    end - start + 1);
192224069Sjhb	if (flags & RF_PREFETCHABLE) {
193224069Sjhb		KASSERT(type == SYS_RES_MEMORY,
194224069Sjhb		    ("only memory is prefetchable"));
195224069Sjhb		rle = resource_list_find(&hr->hr_rl, type, rid);
196224069Sjhb		rle->flags = RLE_PREFETCH;
197224069Sjhb	}
198224069Sjhb	return (0);
199224069Sjhb}
200224069Sjhb
201224069Sjhbstruct resource *
202224069Sjhbpcib_host_res_alloc(struct pcib_host_resources *hr, device_t dev, int type,
203224069Sjhb    int *rid, u_long start, u_long end, u_long count, u_int flags)
204224069Sjhb{
205224069Sjhb	struct resource_list_entry *rle;
206224069Sjhb	struct resource *r;
207224069Sjhb	u_long new_start, new_end;
208224069Sjhb
209224069Sjhb	if (flags & RF_PREFETCHABLE)
210224069Sjhb		KASSERT(type == SYS_RES_MEMORY,
211224069Sjhb		    ("only memory is prefetchable"));
212224069Sjhb
213224069Sjhb	rle = resource_list_find(&hr->hr_rl, type, 0);
214224069Sjhb	if (rle == NULL) {
215224069Sjhb		/*
216224069Sjhb		 * No decoding ranges for this resource type, just pass
217224069Sjhb		 * the request up to the parent.
218224069Sjhb		 */
219224069Sjhb		return (bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid,
220224069Sjhb		    start, end, count, flags));
221224069Sjhb	}
222224069Sjhb
223224069Sjhbrestart:
224224069Sjhb	/* Try to allocate from each decoded range. */
225224069Sjhb	for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) {
226224069Sjhb		if (rle->type != type)
227224069Sjhb			continue;
228224069Sjhb		if (((flags & RF_PREFETCHABLE) != 0) !=
229224069Sjhb		    ((rle->flags & RLE_PREFETCH) != 0))
230224069Sjhb			continue;
231224069Sjhb		new_start = ulmax(start, rle->start);
232224069Sjhb		new_end = ulmin(end, rle->end);
233224069Sjhb		if (new_start > new_end ||
234224069Sjhb		    new_start + count - 1 > new_end ||
235224069Sjhb		    new_start + count < new_start)
236224069Sjhb			continue;
237224069Sjhb		r = bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid,
238224069Sjhb		    new_start, new_end, count, flags);
239224069Sjhb		if (r != NULL) {
240224069Sjhb			if (bootverbose)
241224069Sjhb				device_printf(hr->hr_pcib,
242224069Sjhb			    "allocated type %d (%#lx-%#lx) for rid %x of %s\n",
243224069Sjhb				    type, rman_get_start(r), rman_get_end(r),
244224069Sjhb				    *rid, pcib_child_name(dev));
245224069Sjhb			return (r);
246224069Sjhb		}
247224069Sjhb	}
248224069Sjhb
249224069Sjhb	/*
250224069Sjhb	 * If we failed to find a prefetch range for a memory
251224069Sjhb	 * resource, try again without prefetch.
252224069Sjhb	 */
253224069Sjhb	if (flags & RF_PREFETCHABLE) {
254224069Sjhb		flags &= ~RF_PREFETCHABLE;
255224069Sjhb		rle = resource_list_find(&hr->hr_rl, type, 0);
256224069Sjhb		goto restart;
257224069Sjhb	}
258224069Sjhb	return (NULL);
259224069Sjhb}
260224069Sjhb
261224069Sjhbint
262224069Sjhbpcib_host_res_adjust(struct pcib_host_resources *hr, device_t dev, int type,
263224069Sjhb    struct resource *r, u_long start, u_long end)
264224069Sjhb{
265224069Sjhb	struct resource_list_entry *rle;
266224069Sjhb
267224069Sjhb	rle = resource_list_find(&hr->hr_rl, type, 0);
268224069Sjhb	if (rle == NULL) {
269224069Sjhb		/*
270224069Sjhb		 * No decoding ranges for this resource type, just pass
271224069Sjhb		 * the request up to the parent.
272224069Sjhb		 */
273224069Sjhb		return (bus_generic_adjust_resource(hr->hr_pcib, dev, type, r,
274224069Sjhb		    start, end));
275224069Sjhb	}
276224069Sjhb
277224069Sjhb	/* Only allow adjustments that stay within a decoded range. */
278224069Sjhb	for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) {
279224069Sjhb		if (rle->start <= start && rle->end >= end)
280224069Sjhb			return (bus_generic_adjust_resource(hr->hr_pcib, dev,
281224069Sjhb			    type, r, start, end));
282224069Sjhb	}
283224069Sjhb	return (ERANGE);
284224069Sjhb}
285224069Sjhb#endif /* NEW_PCIB */
286