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