xref: /openbsd/sys/dev/pci/agp.c (revision 0f9891f1)
1 /* $OpenBSD: agp.c,v 1.51 2024/05/24 06:02:53 jsg Exp $ */
2 /*-
3  * Copyright (c) 2000 Doug Rabson
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  *	$FreeBSD: src/sys/pci/agp.c,v 1.12 2001/05/19 01:28:07 alfred Exp $
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/malloc.h>
33 
34 #include <uvm/uvm_extern.h>
35 
36 #include <dev/pci/pcivar.h>
37 
38 #include <dev/pci/agpvar.h>
39 #include <dev/pci/agpreg.h>
40 
41 void	agp_attach(struct device *, struct device *, void *);
42 int	agp_probe(struct device *, void *, void *);
43 
44 int	agpvga_match(struct pci_attach_args *);
45 
46 int
agpdev_print(void * aux,const char * pnp)47 agpdev_print(void *aux, const char *pnp)
48 {
49 	if (pnp) {
50 		printf("agp at %s", pnp);
51 	}
52 	return (UNCONF);
53 }
54 
55 int
agpbus_probe(struct agp_attach_args * aa)56 agpbus_probe(struct agp_attach_args *aa)
57 {
58 	struct pci_attach_args	*pa = aa->aa_pa;
59 
60 	if (strncmp(aa->aa_busname, "agp", 3) == 0 &&
61 	    PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
62 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_HOST)
63 		return (1);
64 	return (0);
65 }
66 
67 /*
68  * Find the video card hanging off the agp bus XXX assumes only one bus
69  */
70 int
agpvga_match(struct pci_attach_args * pa)71 agpvga_match(struct pci_attach_args *pa)
72 {
73 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY &&
74 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA) {
75 		if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_AGP,
76 		    NULL, NULL))
77 			return (1);
78 	}
79 	return (0);
80 }
81 
82 struct device *
agp_attach_bus(struct pci_attach_args * pa,const struct agp_methods * methods,bus_addr_t apaddr,bus_size_t apsize,struct device * dev)83 agp_attach_bus(struct pci_attach_args *pa, const struct agp_methods *methods,
84     bus_addr_t apaddr, bus_size_t apsize, struct device *dev)
85 {
86 	struct agpbus_attach_args arg;
87 
88 	arg.aa_methods = methods;
89 	arg.aa_pa = pa;
90 	arg.aa_apaddr = apaddr;
91 	arg.aa_apsize = apsize;
92 
93 	printf("\n"); /* newline from the driver that called us */
94 	return (config_found(dev, &arg, agpdev_print));
95 }
96 
97 int
agp_probe(struct device * parent,void * match,void * aux)98 agp_probe(struct device *parent, void *match, void *aux)
99 {
100 	/*
101 	 * we don't do any checking here, driver we're attaching this
102 	 * interface to should have already done it.
103 	 */
104 	return (1);
105 }
106 
107 void
agp_attach(struct device * parent,struct device * self,void * aux)108 agp_attach(struct device *parent, struct device *self, void *aux)
109 {
110 	struct agpbus_attach_args *aa = aux;
111 	struct pci_attach_args *pa = aa->aa_pa;
112 	struct agp_softc *sc = (struct agp_softc *)self;
113 	u_int memsize;
114 	int i;
115 
116 	sc->sc_chipc = parent;
117 	sc->sc_methods = aa->aa_methods;
118 	sc->sc_apaddr = aa->aa_apaddr;
119 	sc->sc_apsize = aa->aa_apsize;
120 
121 	static const int agp_max[][2] = {
122 		{0,		0},
123 		{32,		4},
124 		{64,		28},
125 		{128,		96},
126 		{256,		204},
127 		{512,		440},
128 		{1024,		942},
129 		{2048,		1920},
130 		{4096,		3932}
131 	};
132 
133 	/*
134 	 * Work out an upper bound for agp memory allocation. This
135 	 * uses a heuristic table from the Linux driver.
136 	 */
137 	memsize = ptoa(physmem) >> 20;
138 
139 	for (i = 0; i < nitems(agp_max) && memsize > agp_max[i][0]; i++)
140 		;
141 	if (i == nitems(agp_max))
142 		i = nitems(agp_max) - 1;
143 	sc->sc_maxmem = agp_max[i][1] << 20;
144 
145 	sc->sc_pcitag = pa->pa_tag;
146 	sc->sc_pc = pa->pa_pc;
147 	sc->sc_id = pa->pa_id;
148 	sc->sc_dmat = pa->pa_dmat;
149 	sc->sc_memt = pa->pa_memt;
150 
151 	pci_get_capability(sc->sc_pc, sc->sc_pcitag, PCI_CAP_AGP,
152 	    &sc->sc_capoff, NULL);
153 
154 	printf(": aperture at 0x%lx, size 0x%lx\n", (u_long)sc->sc_apaddr,
155 	    (u_long)sc->sc_apsize);
156 }
157 
158 const struct cfattach agp_ca = {
159 	sizeof(struct agp_softc), agp_probe, agp_attach,
160 	NULL, NULL
161 };
162 
163 struct cfdriver agp_cd = {
164 	NULL, "agp", DV_DULL
165 };
166 
167 struct agp_gatt *
agp_alloc_gatt(bus_dma_tag_t dmat,u_int32_t apsize)168 agp_alloc_gatt(bus_dma_tag_t dmat, u_int32_t apsize)
169 {
170 	struct agp_gatt		*gatt;
171 	u_int32_t	 	 entries = apsize >> AGP_PAGE_SHIFT;
172 
173 	gatt = malloc(sizeof(*gatt), M_AGP, M_NOWAIT | M_ZERO);
174 	if (!gatt)
175 		return (NULL);
176 	gatt->ag_entries = entries;
177 	gatt->ag_size = entries * sizeof(u_int32_t);
178 
179 	if (agp_alloc_dmamem(dmat, gatt->ag_size, &gatt->ag_dmamap,
180 	    &gatt->ag_physical, &gatt->ag_dmaseg) != 0) {
181 		free(gatt, M_AGP, sizeof *gatt);
182 		return (NULL);
183 	}
184 
185 	if (bus_dmamem_map(dmat, &gatt->ag_dmaseg, 1, gatt->ag_size,
186 	    (caddr_t *)&gatt->ag_virtual, BUS_DMA_NOWAIT) != 0) {
187 		agp_free_dmamem(dmat, gatt->ag_size, gatt->ag_dmamap,
188 		    &gatt->ag_dmaseg);
189 		free(gatt, M_AGP, sizeof *gatt);
190 		return (NULL);
191 	}
192 
193 	agp_flush_cache();
194 
195 	return (gatt);
196 }
197 
198 void
agp_free_gatt(bus_dma_tag_t dmat,struct agp_gatt * gatt)199 agp_free_gatt(bus_dma_tag_t dmat, struct agp_gatt *gatt)
200 {
201 	bus_dmamem_unmap(dmat, (caddr_t)gatt->ag_virtual, gatt->ag_size);
202 	agp_free_dmamem(dmat, gatt->ag_size, gatt->ag_dmamap, &gatt->ag_dmaseg);
203 	free(gatt, M_AGP, sizeof *gatt);
204 }
205 
206 int
agp_generic_enable(struct agp_softc * sc,u_int32_t mode)207 agp_generic_enable(struct agp_softc *sc, u_int32_t mode)
208 {
209 	struct pci_attach_args	pa;
210 	pcireg_t		tstatus, mstatus, command;
211 	int			rq, sba, fw, rate, capoff;
212 
213 	if (pci_find_device(&pa, agpvga_match) == 0 ||
214 	    pci_get_capability(pa.pa_pc, pa.pa_tag, PCI_CAP_AGP,
215 	    &capoff, NULL) == 0) {
216 		printf("agp_generic_enable: not an AGP capable device\n");
217 		return (-1);
218 	}
219 
220 	tstatus = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
221 	    sc->sc_capoff + AGP_STATUS);
222 	/* display agp mode */
223 	mstatus = pci_conf_read(pa.pa_pc, pa.pa_tag,
224 	    capoff + AGP_STATUS);
225 
226 	/* Set RQ to the min of mode, tstatus and mstatus */
227 	rq = AGP_MODE_GET_RQ(mode);
228 	if (AGP_MODE_GET_RQ(tstatus) < rq)
229 		rq = AGP_MODE_GET_RQ(tstatus);
230 	if (AGP_MODE_GET_RQ(mstatus) < rq)
231 		rq = AGP_MODE_GET_RQ(mstatus);
232 
233 	/* Set SBA if all three can deal with SBA */
234 	sba = (AGP_MODE_GET_SBA(tstatus)
235 	    & AGP_MODE_GET_SBA(mstatus)
236 	    & AGP_MODE_GET_SBA(mode));
237 
238 	/* Similar for FW */
239 	fw = (AGP_MODE_GET_FW(tstatus)
240 	    & AGP_MODE_GET_FW(mstatus)
241 	    & AGP_MODE_GET_FW(mode));
242 
243 	/* Figure out the max rate */
244 	rate = (AGP_MODE_GET_RATE(tstatus)
245 	    & AGP_MODE_GET_RATE(mstatus)
246 	    & AGP_MODE_GET_RATE(mode));
247 	if (rate & AGP_MODE_RATE_4x)
248 		rate = AGP_MODE_RATE_4x;
249 	else if (rate & AGP_MODE_RATE_2x)
250 		rate = AGP_MODE_RATE_2x;
251 	else
252 		rate = AGP_MODE_RATE_1x;
253 
254 	/* Construct the new mode word and tell the hardware  */
255 	command = AGP_MODE_SET_RQ(0, rq);
256 	command = AGP_MODE_SET_SBA(command, sba);
257 	command = AGP_MODE_SET_FW(command, fw);
258 	command = AGP_MODE_SET_RATE(command, rate);
259 	command = AGP_MODE_SET_AGP(command, 1);
260 
261 	pci_conf_write(sc->sc_pc, sc->sc_pcitag,
262 	    sc->sc_capoff + AGP_COMMAND, command);
263 	pci_conf_write(pa.pa_pc, pa.pa_tag, capoff + AGP_COMMAND, command);
264 	return (0);
265 }
266 
267 /*
268  * Allocates a single-segment block of zeroed, wired dma memory.
269  */
270 int
agp_alloc_dmamem(bus_dma_tag_t tag,size_t size,bus_dmamap_t * mapp,bus_addr_t * baddr,bus_dma_segment_t * seg)271 agp_alloc_dmamem(bus_dma_tag_t tag, size_t size, bus_dmamap_t *mapp,
272     bus_addr_t *baddr, bus_dma_segment_t *seg)
273 {
274 	int error, level = 0, nseg;
275 
276 	if ((error = bus_dmamem_alloc(tag, size, PAGE_SIZE, 0,
277 	    seg, 1, &nseg, BUS_DMA_NOWAIT | BUS_DMA_ZERO)) != 0)
278 		goto out;
279 	level++;
280 
281 	if ((error = bus_dmamap_create(tag, size, nseg, size, 0,
282 	    BUS_DMA_NOWAIT, mapp)) != 0)
283 		goto out;
284 	level++;
285 
286 	if ((error = bus_dmamap_load_raw(tag, *mapp, seg, nseg, size,
287 	    BUS_DMA_NOWAIT)) != 0)
288 		goto out;
289 
290 	*baddr = (*mapp)->dm_segs[0].ds_addr;
291 
292 	return (0);
293 out:
294 	switch (level) {
295 	case 2:
296 		bus_dmamap_destroy(tag, *mapp);
297 		/* FALLTHROUGH */
298 	case 1:
299 		bus_dmamem_free(tag, seg, nseg);
300 		break;
301 	default:
302 		break;
303 	}
304 
305 	return (error);
306 }
307 
308 void
agp_free_dmamem(bus_dma_tag_t tag,size_t size,bus_dmamap_t map,bus_dma_segment_t * seg)309 agp_free_dmamem(bus_dma_tag_t tag, size_t size, bus_dmamap_t map,
310     bus_dma_segment_t *seg)
311 {
312 	bus_dmamap_unload(tag, map);
313 	bus_dmamap_destroy(tag, map);
314 	bus_dmamem_free(tag, seg, 1);
315 }
316 
317 /* Implementation of the kernel api */
318 
319 void *
agp_find_device(int unit)320 agp_find_device(int unit)
321 {
322 	if (unit >= agp_cd.cd_ndevs || unit < 0)
323 		return (NULL);
324 	return (agp_cd.cd_devs[unit]);
325 }
326 
327 enum agp_acquire_state
agp_state(void * dev)328 agp_state(void *dev)
329 {
330 	struct agp_softc *sc = (struct agp_softc *) dev;
331         return (sc->sc_state);
332 }
333 
334 void
agp_get_info(void * dev,struct agp_info * info)335 agp_get_info(void *dev, struct agp_info *info)
336 {
337 	struct agp_softc *sc = (struct agp_softc *)dev;
338 
339 	if (sc->sc_capoff != 0)
340 		info->ai_mode = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
341 		    AGP_STATUS + sc->sc_capoff);
342 	else
343 		info->ai_mode = 0; /* i810 doesn't have real AGP */
344 	info->ai_aperture_base = sc->sc_apaddr;
345 	info->ai_aperture_size = sc->sc_apsize;
346 	info->ai_memory_allowed = sc->sc_maxmem;
347 	info->ai_memory_used = sc->sc_allocated;
348 	info->ai_devid = sc->sc_id;
349 }
350 
351 int
agp_acquire(void * dev)352 agp_acquire(void *dev)
353 {
354 	struct agp_softc *sc = (struct agp_softc *)dev;
355 
356 	if (sc->sc_chipc == NULL)
357 		return (EINVAL);
358 
359 	if (sc->sc_state != AGP_ACQUIRE_FREE)
360 		return (EBUSY);
361 	sc->sc_state = AGP_ACQUIRE_KERNEL;
362 
363 	return (0);
364 }
365 
366 int
agp_release(void * dev)367 agp_release(void *dev)
368 {
369 	struct agp_softc *sc = (struct agp_softc *)dev;
370 
371 	if (sc->sc_state == AGP_ACQUIRE_FREE)
372 		return (0);
373 
374 	if (sc->sc_state != AGP_ACQUIRE_KERNEL)
375 		return (EBUSY);
376 
377 	sc->sc_state = AGP_ACQUIRE_FREE;
378 	return (0);
379 }
380 
381 int
agp_enable(void * dev,u_int32_t mode)382 agp_enable(void *dev, u_int32_t mode)
383 {
384 	struct agp_softc	*sc = dev;
385 	int			 ret;
386 
387 	if (sc->sc_methods->enable != NULL) {
388 		ret = sc->sc_methods->enable(sc->sc_chipc, mode);
389 	} else {
390 		ret = agp_generic_enable(sc, mode);
391 	}
392 	return (ret);
393 }
394 
395 paddr_t
agp_mmap(struct agp_softc * sc,off_t off,int prot)396 agp_mmap(struct agp_softc *sc, off_t off, int prot)
397 {
398 	if (sc->sc_chipc == NULL)
399 		return (-1);
400 
401 	if (off >= sc->sc_apsize)
402 		return (-1);
403 
404 	if (sc->sc_apaddr == 0)
405 		return (-1);
406 
407 	return bus_space_mmap(sc->sc_memt, sc->sc_apaddr, off, prot, 0);
408 }
409