xref: /openbsd/sys/arch/amd64/pci/agp_machdep.c (revision 891d7ab6)
1 /*	$OpenBSD: agp_machdep.c,v 1.6 2010/05/10 22:06:04 oga Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 - 2009 Owain G. Ainsworth <oga@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 /*
19  * Copyright (c) 2002 Michael Shalayeff
20  * All rights reserved.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  * 1. Redistributions of source code must retain the above copyright
26  *    notice, this list of conditions and the following disclaimer.
27  * 2. Redistributions in binary form must reproduce the above copyright
28  *    notice, this list of conditions and the following disclaimer in the
29  *    documentation and/or other materials provided with the distribution.
30  *
31  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
32  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
34  * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
35  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
37  * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
41  * THE POSSIBILITY OF SUCH DAMAGE.
42  */
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/device.h>
47 #include <sys/malloc.h>
48 
49 #include <dev/pci/pcivar.h>
50 #include <dev/pci/pcireg.h>
51 #include <dev/pci/pcidevs.h>
52 #include <dev/pci/agpvar.h>
53 
54 #include <machine/cpufunc.h>
55 #include <machine/bus.h>
56 
57 #include <uvm/uvm.h>
58 
59 #include "intagp.h"
60 
61 /* bus_dma functions */
62 
63 #if NINTAGP > 0
64 void	intagp_dma_sync(bus_dma_tag_t, bus_dmamap_t, bus_addr_t,
65 	    bus_size_t, int);
66 #endif
67 
68 void
69 agp_flush_cache(void)
70 {
71 	wbinvd();
72 }
73 
74 void
75 agp_flush_cache_range(vaddr_t va, vsize_t sz)
76 {
77 	pmap_flush_cache(va, sz);
78 }
79 
80 /*
81  * functions for bus_dma used by drm for GEM
82  *
83  * We use the sg_dma backend (also used by iommu) to provide the actual
84  * implementation, so all we need provide is the magic to create the tag, and
85  * the appropriate callbacks.
86  *
87  * We give the backend drivers a chance to honour the bus_dma flags, some of
88  * these may be used, for example to provide snooped mappings (intagp).
89  * For intagp at least, we honour the BUS_DMA_COHERENT flag, though it is not
90  * used often, and is * technically to be used for dmamem_map, we use it for
91  * dmamap_load since adding coherency involes flags to the gtt pagetables.
92  * We only use it for very special circumstances since when a GTT mapping is
93  * set to coherent, the cpu can't read or write through the gtt aperture.
94  *
95  * Currently, since the userland agp driver still needs to access the gart, we
96  * only do bus_dma for a section that we've been told is ours, hence the need
97  * for the init function at present.
98  */
99 
100 int
101 agp_bus_dma_init(struct agp_softc *sc, bus_addr_t start, bus_addr_t end,
102     bus_dma_tag_t *dmat)
103 {
104 	struct bus_dma_tag	*tag;
105 	struct sg_cookie	*cookie;
106 
107 	/*
108 	 * XXX add agp map into the main queue that takes up our chunk of
109 	 * GTT space to prevent the userland api stealing any of it.
110 	 */
111 	if ((tag = malloc(sizeof(*tag), M_DEVBUF,
112 	    M_WAITOK | M_CANFAIL)) == NULL)
113 		return (ENOMEM);
114 
115 	if ((cookie = sg_dmatag_init("agpgtt", sc->sc_chipc, start, end - start,
116 	    sc->sc_methods->bind_page, sc->sc_methods->unbind_page,
117 	    sc->sc_methods->flush_tlb)) == NULL) {
118 		free(tag, M_DEVBUF);
119 		return (ENOMEM);
120 	}
121 
122 	tag->_cookie = cookie;
123 	tag->_dmamap_create = sg_dmamap_create;
124 	tag->_dmamap_destroy = sg_dmamap_destroy;
125 	tag->_dmamap_load = sg_dmamap_load;
126 	tag->_dmamap_load_mbuf = sg_dmamap_load_mbuf;
127 	tag->_dmamap_load_uio = sg_dmamap_load_uio;
128 	tag->_dmamap_load_raw = sg_dmamap_load_raw;
129 	tag->_dmamap_unload = sg_dmamap_unload;
130 	tag->_dmamem_alloc = sg_dmamem_alloc;
131 	tag->_dmamem_free = _bus_dmamem_free;
132 	tag->_dmamem_map = _bus_dmamem_map;
133 	tag->_dmamem_unmap = _bus_dmamem_unmap;
134 	tag->_dmamem_mmap = _bus_dmamem_mmap;
135 
136 	/* Driver may need special sync handling */
137 	if (sc->sc_methods->dma_sync != NULL) {
138 		tag->_dmamap_sync = sc->sc_methods->dma_sync;
139 	} else {
140 		tag->_dmamap_sync = _bus_dmamap_sync;
141 	}
142 
143 	*dmat = tag;
144 	return (0);
145 }
146 
147 void
148 agp_bus_dma_destroy(struct agp_softc *sc, bus_dma_tag_t dmat)
149 {
150 	struct sg_cookie	*cookie = dmat->_cookie;
151 	bus_addr_t		 offset;
152 
153 
154 	/*
155 	 * XXX clear up blocker queue
156 	 */
157 
158 	/*
159 	 * some backends use a dummy page to avoid errors on prefetching, etc.
160 	 * make sure that all of them are clean.
161 	 */
162 	for (offset = cookie->sg_ex->ex_start;
163 	    offset < cookie->sg_ex->ex_end; offset += PAGE_SIZE)
164 		sc->sc_methods->unbind_page(sc->sc_chipc, offset);
165 
166 	sg_dmatag_destroy(cookie);
167 	free(dmat, M_DEVBUF);
168 }
169 
170 void
171 agp_bus_dma_set_alignment(bus_dma_tag_t tag, bus_dmamap_t dmam,
172     u_long alignment)
173 {
174 	sg_dmamap_set_alignment(tag, dmam, alignment);
175 }
176 
177 struct agp_map {
178 	bus_space_tag_t		bst;
179 	bus_space_handle_t	bsh;
180 	bus_size_t		size;
181 };
182 
183 int
184 agp_init_map(bus_space_tag_t tag, bus_addr_t address, bus_size_t size,
185     int flags, struct agp_map **mapp)
186 {
187 	struct agp_map	*map;
188 	int		 err;
189 
190 	map = malloc(sizeof(*map), M_AGP, M_WAITOK | M_CANFAIL);
191 	if (map == NULL)
192 		return (ENOMEM);
193 
194 	map->bst = tag;
195 	map->size = size;
196 
197 	if ((err = bus_space_map(tag, address, size, flags, &map->bsh)) != 0) {
198 		free(map, M_AGP);
199 		return (err);
200 	}
201 	*mapp = map;
202 	return (0);
203 }
204 
205 void
206 agp_destroy_map(struct agp_map *map)
207 {
208 	bus_space_unmap(map->bst, map->bsh, map->size);
209 	free(map, M_AGP);
210 }
211 
212 
213 int
214 agp_map_subregion(struct agp_map *map, bus_size_t offset, bus_size_t size,
215     bus_space_handle_t *bshp)
216 {
217 	if (offset > map->size || size > map->size || offset + size > map->size)
218 		return (EINVAL);
219 	return (bus_space_subregion(map->bst, map->bsh, offset, size, bshp));
220 
221 }
222 
223 void
224 agp_unmap_subregion(struct agp_map *map, bus_space_handle_t bsh,
225     bus_size_t size)
226 {
227 	/* subregion doesn't need unmapping, do nothing */
228 }
229 
230 /*
231  * ick ick ick. However, the rest of this driver is supposedly MI (though
232  * they only exist on x86), so this can't be in dev/pci.
233  */
234 
235 #if NINTAGP > 0
236 
237 /*
238  * bus_dmamap_sync routine for intagp.
239  *
240  * This is tailored to the usage that drm with the GEM memory manager
241  * will be using, since intagp is for intel IGD, and thus shouldn't be
242  * used for anything other than gpu-based work. Essentially for the intel GEM
243  * driver we use bus_dma as an abstraction to convert our memory into a gtt
244  * address and deal with any cache incoherencies that we create.
245  *
246  * We use the cflush instruction to deal with clearing the caches, since our
247  * cache is physically indexed, we can even map then clear the page and it'll
248  * work. on i386 we need to check for the presence of cflush() in cpuid,
249  * however, all cpus that have a new enough intel GMCH should be suitable.
250  */
251 void
252 intagp_dma_sync(bus_dma_tag_t tag, bus_dmamap_t dmam,
253     bus_addr_t offset, bus_size_t size, int ops)
254 {
255 	bus_dma_segment_t	*segp;
256 	struct sg_page_map	*spm;
257 	void			*addr;
258 	paddr_t	 		 pa;
259 	bus_addr_t		 poff, endoff, soff;
260 
261 #ifdef DIAGNOSTIC
262 	if ((ops & (BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE)) != 0 &&
263 	    (ops & (BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE)) != 0)
264 		panic("agp_dmamap_sync: mix PRE and POST");
265 	if (offset >= dmam->dm_mapsize)
266 		panic("_intagp_dma_sync: bad offset %lu (size = %lu)",
267 		    offset, dmam->dm_mapsize);
268 	if (size == 0 || (offset + size) > dmam->dm_mapsize)
269 		panic("intagp_dma_sync: bad length");
270 #endif /* DIAGNOSTIC */
271 
272 	/* Coherent mappings need no sync. */
273 	if (dmam->_dm_flags & BUS_DMA_COHERENT)
274 		return;
275 
276 	/*
277 	 * We need to clflush the object cache in all cases but postwrite.
278 	 *
279 	 * - Due to gpu incoherency, postread we need to flush speculative
280 	 * reads (which are not written back on intel cpus).
281 	 *
282 	 * - preread we need to flush data which will very soon be stale from
283 	 * the caches
284 	 *
285 	 * - prewrite we need to make sure our data hits the memory before the
286 	 * gpu hoovers it up.
287 	 *
288 	 * The chipset also may need flushing, but that fits badly into
289 	 * bus_dma and it done in the driver.
290 	 */
291 	soff = trunc_page(offset);
292 	endoff = round_page(offset + size);
293 	if (ops & BUS_DMASYNC_POSTREAD || ops & BUS_DMASYNC_PREREAD ||
294 	    ops & BUS_DMASYNC_PREWRITE) {
295 		if (curcpu()->ci_cflushsz == 0) {
296 			/* save some wbinvd()s. we're MD anyway so it's ok */
297 			wbinvd();
298 			return;
299 		}
300 
301 		mfence();
302 		spm = dmam->_dm_cookie;
303 		switch (spm->spm_buftype) {
304 		case BUS_BUFTYPE_LINEAR:
305 			addr = spm->spm_origbuf + soff;
306 			while (soff < endoff) {
307 				pmap_flush_cache((vaddr_t)addr, PAGE_SIZE);
308 				soff += PAGE_SIZE;
309 				addr += PAGE_SIZE;
310 			} break;
311 		case BUS_BUFTYPE_RAW:
312 			segp = (bus_dma_segment_t *)spm->spm_origbuf;
313 			poff = 0;
314 
315 			while (poff < soff) {
316 				if (poff + segp->ds_len > soff)
317 					break;
318 				poff += segp->ds_len;
319 				segp++;
320 			}
321 			/* first time round may not start at seg beginning */
322 			pa = segp->ds_addr + (soff - poff);
323 			while (poff < endoff) {
324 				for (; pa < segp->ds_addr + segp->ds_len &&
325 				    poff < endoff; pa += PAGE_SIZE) {
326 					pmap_flush_page(pa);
327 					poff += PAGE_SIZE;
328 				}
329 				segp++;
330 				if (poff < endoff)
331 					pa = segp->ds_addr;
332 			}
333 			break;
334 		/* You do not want to load mbufs or uios onto a graphics card */
335 		case BUS_BUFTYPE_MBUF:
336 			/* FALLTHROUGH */
337 		case BUS_BUFTYPE_UIO:
338 			/* FALLTHROUGH */
339 		default:
340 			panic("intagp_dmamap_sync: bad buftype %d",
341 			    spm->spm_buftype);
342 
343 		}
344 		mfence();
345 	}
346 }
347 #endif /* NINTAGP > 0 */
348