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