1/*-
2 * Copyright (c) 2011 Advanced Computing Technologies LLC
3 * Written by: John H. Baldwin <jhb@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31/*
32 * Support APIs for Host to PCI bridge drivers and drivers that
33 * provide PCI domains.
34 */
35
36#include <sys/param.h>
37#include <sys/bus.h>
38#include <sys/rman.h>
39#include <sys/systm.h>
40
41#include <dev/pci/pcireg.h>
42#include <dev/pci/pcivar.h>
43#include <dev/pci/pcib_private.h>
44
45/*
46 * Try to read the bus number of a host-PCI bridge using appropriate config
47 * registers.
48 */
49int
50host_pcib_get_busno(pci_read_config_fn read_config, int bus, int slot, int func,
51    uint8_t *busnum)
52{
53	uint32_t id;
54
55	id = read_config(bus, slot, func, PCIR_DEVVENDOR, 4);
56	if (id == 0xffffffff)
57		return (0);
58
59	switch (id) {
60	case 0x12258086:
61		/* Intel 824?? */
62		/* XXX This is a guess */
63		/* *busnum = read_config(bus, slot, func, 0x41, 1); */
64		*busnum = bus;
65		break;
66	case 0x84c48086:
67		/* Intel 82454KX/GX (Orion) */
68		*busnum = read_config(bus, slot, func, 0x4a, 1);
69		break;
70	case 0x84ca8086:
71		/*
72		 * For the 450nx chipset, there is a whole bundle of
73		 * things pretending to be host bridges. The MIOC will
74		 * be seen first and isn't really a pci bridge (the
75		 * actual busses are attached to the PXB's). We need to
76		 * read the registers of the MIOC to figure out the
77		 * bus numbers for the PXB channels.
78		 *
79		 * Since the MIOC doesn't have a pci bus attached, we
80		 * pretend it wasn't there.
81		 */
82		return (0);
83	case 0x84cb8086:
84		switch (slot) {
85		case 0x12:
86			/* Intel 82454NX PXB#0, Bus#A */
87			*busnum = read_config(bus, 0x10, func, 0xd0, 1);
88			break;
89		case 0x13:
90			/* Intel 82454NX PXB#0, Bus#B */
91			*busnum = read_config(bus, 0x10, func, 0xd1, 1) + 1;
92			break;
93		case 0x14:
94			/* Intel 82454NX PXB#1, Bus#A */
95			*busnum = read_config(bus, 0x10, func, 0xd3, 1);
96			break;
97		case 0x15:
98			/* Intel 82454NX PXB#1, Bus#B */
99			*busnum = read_config(bus, 0x10, func, 0xd4, 1) + 1;
100			break;
101		}
102		break;
103
104		/* ServerWorks -- vendor 0x1166 */
105	case 0x00051166:
106	case 0x00061166:
107	case 0x00081166:
108	case 0x00091166:
109	case 0x00101166:
110	case 0x00111166:
111	case 0x00171166:
112	case 0x01011166:
113	case 0x010f1014:
114	case 0x01101166:
115	case 0x02011166:
116	case 0x02251166:
117	case 0x03021014:
118		*busnum = read_config(bus, slot, func, 0x44, 1);
119		break;
120
121		/* Compaq/HP -- vendor 0x0e11 */
122	case 0x60100e11:
123		*busnum = read_config(bus, slot, func, 0xc8, 1);
124		break;
125	default:
126		/* Don't know how to read bus number. */
127		return 0;
128	}
129
130	return 1;
131}
132
133#ifdef NEW_PCIB
134/*
135 * Return a pointer to a pretty name for a PCI device.  If the device
136 * has a driver attached, the device's name is used, otherwise a name
137 * is generated from the device's PCI address.
138 */
139const char *
140pcib_child_name(device_t child)
141{
142	static char buf[64];
143
144	if (device_get_nameunit(child) != NULL)
145		return (device_get_nameunit(child));
146	snprintf(buf, sizeof(buf), "pci%d:%d:%d:%d", pci_get_domain(child),
147	    pci_get_bus(child), pci_get_slot(child), pci_get_function(child));
148	return (buf);
149}
150
151/*
152 * Some Host-PCI bridge drivers know which resource ranges they can
153 * decode and should only allocate subranges to child PCI devices.
154 * This API provides a way to manage this.  The bridge drive should
155 * initialize this structure during attach and call
156 * pcib_host_res_decodes() on each resource range it decodes.  It can
157 * then use pcib_host_res_alloc() and pcib_host_res_adjust() as helper
158 * routines for BUS_ALLOC_RESOURCE() and BUS_ADJUST_RESOURCE().  This
159 * API assumes that resources for any decoded ranges can be safely
160 * allocated from the parent via bus_generic_alloc_resource().
161 */
162int
163pcib_host_res_init(device_t pcib, struct pcib_host_resources *hr)
164{
165
166	hr->hr_pcib = pcib;
167	resource_list_init(&hr->hr_rl);
168	return (0);
169}
170
171int
172pcib_host_res_free(device_t pcib, struct pcib_host_resources *hr)
173{
174
175	resource_list_free(&hr->hr_rl);
176	return (0);
177}
178
179int
180pcib_host_res_decodes(struct pcib_host_resources *hr, int type, u_long start,
181    u_long end, u_int flags)
182{
183	struct resource_list_entry *rle;
184	int rid;
185
186	if (bootverbose)
187		device_printf(hr->hr_pcib, "decoding %d %srange %#lx-%#lx\n",
188		    type, flags & RF_PREFETCHABLE ? "prefetchable ": "", start,
189		    end);
190	rid = resource_list_add_next(&hr->hr_rl, type, start, end,
191	    end - start + 1);
192	if (flags & RF_PREFETCHABLE) {
193		KASSERT(type == SYS_RES_MEMORY,
194		    ("only memory is prefetchable"));
195		rle = resource_list_find(&hr->hr_rl, type, rid);
196		rle->flags = RLE_PREFETCH;
197	}
198	return (0);
199}
200
201struct resource *
202pcib_host_res_alloc(struct pcib_host_resources *hr, device_t dev, int type,
203    int *rid, u_long start, u_long end, u_long count, u_int flags)
204{
205	struct resource_list_entry *rle;
206	struct resource *r;
207	u_long new_start, new_end;
208
209	if (flags & RF_PREFETCHABLE)
210		KASSERT(type == SYS_RES_MEMORY,
211		    ("only memory is prefetchable"));
212
213	rle = resource_list_find(&hr->hr_rl, type, 0);
214	if (rle == NULL) {
215		/*
216		 * No decoding ranges for this resource type, just pass
217		 * the request up to the parent.
218		 */
219		return (bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid,
220		    start, end, count, flags));
221	}
222
223restart:
224	/* Try to allocate from each decoded range. */
225	for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) {
226		if (rle->type != type)
227			continue;
228		if (((flags & RF_PREFETCHABLE) != 0) !=
229		    ((rle->flags & RLE_PREFETCH) != 0))
230			continue;
231		new_start = ulmax(start, rle->start);
232		new_end = ulmin(end, rle->end);
233		if (new_start > new_end ||
234		    new_start + count - 1 > new_end ||
235		    new_start + count < new_start)
236			continue;
237		r = bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid,
238		    new_start, new_end, count, flags);
239		if (r != NULL) {
240			if (bootverbose)
241				device_printf(hr->hr_pcib,
242			    "allocated type %d (%#lx-%#lx) for rid %x of %s\n",
243				    type, rman_get_start(r), rman_get_end(r),
244				    *rid, pcib_child_name(dev));
245			return (r);
246		}
247	}
248
249	/*
250	 * If we failed to find a prefetch range for a memory
251	 * resource, try again without prefetch.
252	 */
253	if (flags & RF_PREFETCHABLE) {
254		flags &= ~RF_PREFETCHABLE;
255		rle = resource_list_find(&hr->hr_rl, type, 0);
256		goto restart;
257	}
258	return (NULL);
259}
260
261int
262pcib_host_res_adjust(struct pcib_host_resources *hr, device_t dev, int type,
263    struct resource *r, u_long start, u_long end)
264{
265	struct resource_list_entry *rle;
266
267	rle = resource_list_find(&hr->hr_rl, type, 0);
268	if (rle == NULL) {
269		/*
270		 * No decoding ranges for this resource type, just pass
271		 * the request up to the parent.
272		 */
273		return (bus_generic_adjust_resource(hr->hr_pcib, dev, type, r,
274		    start, end));
275	}
276
277	/* Only allow adjustments that stay within a decoded range. */
278	for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) {
279		if (rle->start <= start && rle->end >= end)
280			return (bus_generic_adjust_resource(hr->hr_pcib, dev,
281			    type, r, start, end));
282	}
283	return (ERANGE);
284}
285#endif /* NEW_PCIB */
286