xref: /openbsd/sys/dev/pci/pci_map.c (revision 7b44a193)
1 /*      $OpenBSD: pci_map.c,v 1.33 2023/04/13 15:07:43 miod Exp $     */
2 /*	$NetBSD: pci_map.c,v 1.7 2000/05/10 16:58:42 thorpej Exp $	*/
3 
4 /*-
5  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Charles M. Hannum; by William R. Studenmund; by Jason R. Thorpe.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * PCI device mapping.
35  */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 
40 #include <dev/pci/pcireg.h>
41 #include <dev/pci/pcivar.h>
42 
43 #ifndef PCI_IO_START
44 #define PCI_IO_START	0
45 #endif
46 
47 #ifndef PCI_IO_END
48 #define PCI_IO_END	0xffffffff
49 #endif
50 
51 #ifndef PCI_MEM_START
52 #define PCI_MEM_START	0
53 #endif
54 
55 #ifndef PCI_MEM_END
56 #define PCI_MEM_END	0xffffffff
57 #endif
58 
59 
60 int obsd_pci_io_find(pci_chipset_tag_t, pcitag_t, int, pcireg_t,
61     bus_addr_t *, bus_size_t *, int *);
62 int obsd_pci_mem_find(pci_chipset_tag_t, pcitag_t, int, pcireg_t,
63     bus_addr_t *, bus_size_t *, int *);
64 
65 int
obsd_pci_io_find(pci_chipset_tag_t pc,pcitag_t tag,int reg,pcireg_t type,bus_addr_t * basep,bus_size_t * sizep,int * flagsp)66 obsd_pci_io_find(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t type,
67     bus_addr_t *basep, bus_size_t *sizep, int *flagsp)
68 {
69 	pcireg_t address, mask, csr;
70 	int s;
71 
72 	if (reg < PCI_MAPREG_START ||
73 #if 0
74 	    /*
75 	     * Can't do this check; some devices have mapping registers
76 	     * way out in left field.
77 	     */
78 	    reg >= PCI_MAPREG_END ||
79 #endif
80 	    (reg & 3))
81 		panic("pci_io_find: bad request");
82 
83 	/*
84 	 * Section 6.2.5.1, `Address Maps', tells us that:
85 	 *
86 	 * 1) The builtin software should have already mapped the device in a
87 	 * reasonable way.
88 	 *
89 	 * 2) A device which wants 2^n bytes of memory will hardwire the bottom
90 	 * n bits of the address to 0.  As recommended, we write all 1s while
91 	 * the device is disabled and see what we get back.
92 	 */
93 	s = splhigh();
94 	csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
95 	if (csr & PCI_COMMAND_IO_ENABLE)
96 		pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG,
97 		    csr & ~PCI_COMMAND_IO_ENABLE);
98 	address = pci_conf_read(pc, tag, reg);
99 	pci_conf_write(pc, tag, reg, 0xffffffff);
100 	mask = pci_conf_read(pc, tag, reg);
101 	pci_conf_write(pc, tag, reg, address);
102 	if (csr & PCI_COMMAND_IO_ENABLE)
103 		pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
104 	splx(s);
105 
106 	if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_IO) {
107 #ifdef DEBUG
108 		printf("pci_io_find: expected type i/o, found mem\n");
109 #endif
110 		return (EINVAL);
111 	}
112 
113 	if (PCI_MAPREG_IO_SIZE(mask) == 0) {
114 #ifdef DEBUG
115 		printf("pci_io_find: void region\n");
116 #endif
117 		return (ENOENT);
118 	}
119 
120 	if (basep != 0)
121 		*basep = PCI_MAPREG_IO_ADDR(address);
122 	if (sizep != 0)
123 		*sizep = PCI_MAPREG_IO_SIZE(mask);
124 	if (flagsp != 0)
125 		*flagsp = 0;
126 
127 	return (0);
128 }
129 
130 int
obsd_pci_mem_find(pci_chipset_tag_t pc,pcitag_t tag,int reg,pcireg_t type,bus_addr_t * basep,bus_size_t * sizep,int * flagsp)131 obsd_pci_mem_find(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t type,
132     bus_addr_t *basep, bus_size_t *sizep, int *flagsp)
133 {
134 	pcireg_t address, mask, address1 = 0, mask1 = 0xffffffff, csr;
135 	u_int64_t waddress, wmask;
136 	int s, is64bit;
137 
138 	is64bit = (PCI_MAPREG_MEM_TYPE(type) == PCI_MAPREG_MEM_TYPE_64BIT);
139 
140 	if (reg < PCI_MAPREG_START ||
141 #if 0
142 	    /*
143 	     * Can't do this check; some devices have mapping registers
144 	     * way out in left field.
145 	     */
146 	    reg >= PCI_MAPREG_END ||
147 #endif
148 	    (reg & 3))
149 		panic("pci_mem_find: bad request");
150 
151 	if (is64bit && (reg + 4) >= PCI_MAPREG_END)
152 		panic("pci_mem_find: bad 64-bit request");
153 
154 	/*
155 	 * Section 6.2.5.1, `Address Maps', tells us that:
156 	 *
157 	 * 1) The builtin software should have already mapped the device in a
158 	 * reasonable way.
159 	 *
160 	 * 2) A device which wants 2^n bytes of memory will hardwire the bottom
161 	 * n bits of the address to 0.  As recommended, we write all 1s while
162 	 * the device is disabled and see what we get back.
163 	 */
164 	s = splhigh();
165 	csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
166 	if (csr & PCI_COMMAND_MEM_ENABLE)
167 		pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG,
168 		    csr & ~PCI_COMMAND_MEM_ENABLE);
169 	address = pci_conf_read(pc, tag, reg);
170 	pci_conf_write(pc, tag, reg, PCI_MAPREG_MEM_ADDR_MASK);
171 	mask = pci_conf_read(pc, tag, reg);
172 	pci_conf_write(pc, tag, reg, address);
173 	if (is64bit) {
174 		address1 = pci_conf_read(pc, tag, reg + 4);
175 		pci_conf_write(pc, tag, reg + 4, 0xffffffff);
176 		mask1 = pci_conf_read(pc, tag, reg + 4);
177 		pci_conf_write(pc, tag, reg + 4, address1);
178 	}
179 	if (csr & PCI_COMMAND_MEM_ENABLE)
180 		pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
181 	splx(s);
182 
183 	if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_MEM) {
184 #ifdef DEBUG
185 		printf("pci_mem_find: expected type mem, found i/o\n");
186 #endif
187 		return (EINVAL);
188 	}
189 	if (type != -1 &&
190 	    PCI_MAPREG_MEM_TYPE(address) != PCI_MAPREG_MEM_TYPE(type)) {
191 #ifdef DEBUG
192 		printf("pci_mem_find: expected mem type %08x, found %08x\n",
193 		    PCI_MAPREG_MEM_TYPE(type),
194 		    PCI_MAPREG_MEM_TYPE(address));
195 #endif
196 		return (EINVAL);
197 	}
198 
199 	waddress = (u_int64_t)address1 << 32UL | address;
200 	wmask = (u_int64_t)mask1 << 32UL | mask;
201 
202 	if ((is64bit && PCI_MAPREG_MEM64_SIZE(wmask) == 0) ||
203 	    (!is64bit && PCI_MAPREG_MEM_SIZE(mask) == 0)) {
204 #ifdef DEBUG
205 		printf("pci_mem_find: void region\n");
206 #endif
207 		return (ENOENT);
208 	}
209 
210 	switch (PCI_MAPREG_MEM_TYPE(address)) {
211 	case PCI_MAPREG_MEM_TYPE_32BIT:
212 	case PCI_MAPREG_MEM_TYPE_32BIT_1M:
213 		break;
214 	case PCI_MAPREG_MEM_TYPE_64BIT:
215 		/*
216 		 * Handle the case of a 64-bit memory register on a
217 		 * platform with 32-bit addressing.  Make sure that
218 		 * the address assigned and the device's memory size
219 		 * fit in 32 bits.  We implicitly assume that if
220 		 * bus_addr_t is 64-bit, then so is bus_size_t.
221 		 */
222 		if (sizeof(u_int64_t) > sizeof(bus_addr_t) &&
223 		    (address1 != 0 || mask1 != 0xffffffff)) {
224 #ifdef DEBUG
225 			printf("pci_mem_find: 64-bit memory map which is "
226 			    "inaccessible on a 32-bit platform\n");
227 #endif
228 			return (EINVAL);
229 		}
230 		break;
231 	default:
232 #ifdef DEBUG
233 		printf("pci_mem_find: reserved mapping register type\n");
234 #endif
235 		return (EINVAL);
236 	}
237 
238 	if (sizeof(u_int64_t) > sizeof(bus_addr_t)) {
239 		if (basep != 0)
240 			*basep = PCI_MAPREG_MEM_ADDR(address);
241 		if (sizep != 0)
242 			*sizep = PCI_MAPREG_MEM_SIZE(mask);
243 	} else {
244 		if (basep != 0)
245 			*basep = PCI_MAPREG_MEM64_ADDR(waddress);
246 		if (sizep != 0)
247 			*sizep = PCI_MAPREG_MEM64_SIZE(wmask);
248 	}
249 	if (flagsp != 0)
250 		*flagsp =
251 		    PCI_MAPREG_MEM_PREFETCHABLE(address) ?
252 		      BUS_SPACE_MAP_PREFETCHABLE : 0;
253 
254 	return (0);
255 }
256 
257 pcireg_t
pci_mapreg_type(pci_chipset_tag_t pc,pcitag_t tag,int reg)258 pci_mapreg_type(pci_chipset_tag_t pc, pcitag_t tag, int reg)
259 {
260 	return (_PCI_MAPREG_TYPEBITS(pci_conf_read(pc, tag, reg)));
261 }
262 
263 int
pci_mapreg_probe(pci_chipset_tag_t pc,pcitag_t tag,int reg,pcireg_t * typep)264 pci_mapreg_probe(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t *typep)
265 {
266 	pcireg_t address, mask, csr;
267 	int s;
268 
269 	s = splhigh();
270 	csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
271 	if (csr & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
272 		pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr &
273 		    ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE));
274 	address = pci_conf_read(pc, tag, reg);
275 	pci_conf_write(pc, tag, reg, 0xffffffff);
276 	mask = pci_conf_read(pc, tag, reg);
277 	pci_conf_write(pc, tag, reg, address);
278 	if (csr & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
279 		pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
280 	splx(s);
281 
282 	if (mask == 0) /* unimplemented mapping register */
283 		return (0);
284 
285 	if (typep)
286 		*typep = _PCI_MAPREG_TYPEBITS(address);
287 	return (1);
288 }
289 
290 int
pci_mapreg_info(pci_chipset_tag_t pc,pcitag_t tag,int reg,pcireg_t type,bus_addr_t * basep,bus_size_t * sizep,int * flagsp)291 pci_mapreg_info(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t type,
292     bus_addr_t *basep, bus_size_t *sizep, int *flagsp)
293 {
294 
295 	if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO)
296 		return (obsd_pci_io_find(pc, tag, reg, type, basep, sizep,
297 		    flagsp));
298 	else
299 		return (obsd_pci_mem_find(pc, tag, reg, type, basep, sizep,
300 		    flagsp));
301 }
302 
303 int
pci_mapreg_assign(struct pci_attach_args * pa,int reg,pcireg_t type,bus_addr_t * basep,bus_size_t * sizep)304 pci_mapreg_assign(struct pci_attach_args *pa, int reg, pcireg_t type,
305     bus_addr_t *basep, bus_size_t *sizep)
306 {
307 	bus_addr_t base;
308 	bus_size_t size;
309 	pcireg_t csr;
310 	int rv;
311 
312 	if ((rv = pci_mapreg_info(pa->pa_pc, pa->pa_tag, reg, type,
313 	    &base, &size, NULL)) != 0)
314 		return (rv);
315 #if !defined(__sparc64__)
316 	if (base == 0) {
317 		struct extent *ex;
318 		bus_addr_t start, end;
319 
320 		if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) {
321 			ex = pa->pa_ioex;
322 			if (ex != NULL) {
323 				start = max(PCI_IO_START, ex->ex_start);
324 				end = min(PCI_IO_END, ex->ex_end);
325 			}
326 		} else {
327 			ex = pa->pa_memex;
328 			if (ex != NULL) {
329 				start = max(PCI_MEM_START, ex->ex_start);
330 				end = min(PCI_MEM_END, ex->ex_end);
331 			}
332 		}
333 
334 		if (ex == NULL || extent_alloc_subregion(ex, start, end,
335 		    size, size, 0, 0, 0, &base))
336 			return (EINVAL); /* disabled because of invalid BAR */
337 
338 		pci_conf_write(pa->pa_pc, pa->pa_tag, reg, base);
339 		if (PCI_MAPREG_MEM_TYPE(type) == PCI_MAPREG_MEM_TYPE_64BIT)
340 			pci_conf_write(pa->pa_pc, pa->pa_tag, reg + 4,
341 			    (u_int64_t)base >> 32);
342 	}
343 #endif
344 
345 	csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
346 	if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO)
347 		csr |= PCI_COMMAND_IO_ENABLE;
348 	else
349 		csr |= PCI_COMMAND_MEM_ENABLE;
350 	/* XXX Should this only be done for devices that do DMA?  */
351 	csr |= PCI_COMMAND_MASTER_ENABLE;
352 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, csr);
353 
354 	if (basep != NULL)
355 		*basep = base;
356 	if (sizep != NULL)
357 		*sizep = size;
358 
359 	return (0);
360 }
361 
362 int
pci_mapreg_map(struct pci_attach_args * pa,int reg,pcireg_t type,int flags,bus_space_tag_t * tagp,bus_space_handle_t * handlep,bus_addr_t * basep,bus_size_t * sizep,bus_size_t maxsize)363 pci_mapreg_map(struct pci_attach_args *pa, int reg, pcireg_t type, int flags,
364     bus_space_tag_t *tagp, bus_space_handle_t *handlep, bus_addr_t *basep,
365     bus_size_t *sizep, bus_size_t maxsize)
366 {
367 	bus_space_tag_t tag;
368 	bus_space_handle_t handle;
369 	bus_addr_t base;
370 	bus_size_t size;
371 	int rv;
372 
373 	if ((rv = pci_mapreg_assign(pa, reg, type, &base, &size)) != 0)
374 		return (rv);
375 
376 	if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) {
377 		if ((pa->pa_flags & PCI_FLAGS_IO_ENABLED) == 0)
378 			return (EINVAL);
379 		tag = pa->pa_iot;
380 	} else {
381 		if ((pa->pa_flags & PCI_FLAGS_MEM_ENABLED) == 0)
382 			return (EINVAL);
383 		tag = pa->pa_memt;
384 	}
385 
386 	/* The caller can request limitation of the mapping's size. */
387 	if (maxsize != 0 && size > maxsize) {
388 #ifdef DEBUG
389 		printf("pci_mapreg_map: limited PCI mapping from %lx to %lx\n",
390 		    (u_long)size, (u_long)maxsize);
391 #endif
392 		size = maxsize;
393 	}
394 
395 	if (bus_space_map(tag, base, size, flags, &handle))
396 		return (1);
397 
398 	if (tagp != NULL)
399 		*tagp = tag;
400 	if (handlep != NULL)
401 		*handlep = handle;
402 	if (basep != NULL)
403 		*basep = base;
404 	if (sizep != NULL)
405 		*sizep = size;
406 
407 	return (0);
408 }
409