1 | /* $NetBSD: agp_via.c,v 1.21 2011/02/19 20:07:02 jmcneill Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2000 Doug Rabson |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. |
27 | * |
28 | * $FreeBSD: src/sys/pci/agp_via.c,v 1.3 2001/07/05 21:28:47 jhb Exp $ |
29 | */ |
30 | |
31 | #include <sys/cdefs.h> |
32 | __KERNEL_RCSID(0, "$NetBSD: agp_via.c,v 1.21 2011/02/19 20:07:02 jmcneill Exp $" ); |
33 | |
34 | #include <sys/param.h> |
35 | #include <sys/systm.h> |
36 | #include <sys/malloc.h> |
37 | #include <sys/kernel.h> |
38 | #include <sys/proc.h> |
39 | #include <sys/conf.h> |
40 | #include <sys/device.h> |
41 | #include <sys/agpio.h> |
42 | |
43 | #include <dev/pci/pcivar.h> |
44 | #include <dev/pci/pcireg.h> |
45 | #include <dev/pci/agpvar.h> |
46 | #include <dev/pci/agpreg.h> |
47 | #include <dev/pci/pcidevs.h> |
48 | |
49 | #include <sys/bus.h> |
50 | |
51 | static u_int32_t agp_via_get_aperture(struct agp_softc *); |
52 | static int agp_via_set_aperture(struct agp_softc *, u_int32_t); |
53 | static int agp_via_bind_page(struct agp_softc *, off_t, bus_addr_t); |
54 | static int agp_via_unbind_page(struct agp_softc *, off_t); |
55 | static void agp_via_flush_tlb(struct agp_softc *); |
56 | |
57 | static struct agp_methods agp_via_methods = { |
58 | agp_via_get_aperture, |
59 | agp_via_set_aperture, |
60 | agp_via_bind_page, |
61 | agp_via_unbind_page, |
62 | agp_via_flush_tlb, |
63 | agp_generic_enable, |
64 | agp_generic_alloc_memory, |
65 | agp_generic_free_memory, |
66 | agp_generic_bind_memory, |
67 | agp_generic_unbind_memory, |
68 | }; |
69 | |
70 | struct agp_via_softc { |
71 | u_int32_t initial_aperture; /* aperture size at startup */ |
72 | struct agp_gatt *gatt; |
73 | int *regs; |
74 | }; |
75 | |
76 | #define REG_GARTCTRL 0 |
77 | #define REG_APSIZE 1 |
78 | #define REG_ATTBASE 2 |
79 | |
80 | static int via_v2_regs[] = |
81 | { AGP_VIA_GARTCTRL, AGP_VIA_APSIZE, AGP_VIA_ATTBASE }; |
82 | static int via_v3_regs[] = |
83 | { AGP3_VIA_GARTCTRL, AGP3_VIA_APSIZE, AGP3_VIA_ATTBASE }; |
84 | |
85 | int |
86 | agp_via_attach(device_t parent, device_t self, void *aux) |
87 | { |
88 | struct pci_attach_args *pa = aux; |
89 | struct agp_softc *sc = device_private(self); |
90 | struct agp_via_softc *asc; |
91 | struct agp_gatt *gatt; |
92 | pcireg_t agpsel, capval; |
93 | |
94 | asc = malloc(sizeof *asc, M_AGP, M_NOWAIT|M_ZERO); |
95 | if (asc == NULL) { |
96 | aprint_error(": can't allocate chipset-specific softc\n" ); |
97 | return ENOMEM; |
98 | } |
99 | sc->as_chipc = asc; |
100 | sc->as_methods = &agp_via_methods; |
101 | pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_AGP, &sc->as_capoff, |
102 | &capval); |
103 | |
104 | if (PCI_CAP_AGP_MAJOR(capval) >= 3) { |
105 | agpsel = pci_conf_read(pa->pa_pc, pa->pa_tag, AGP_VIA_AGPSEL); |
106 | if ((agpsel & (1 << 9)) == 0) { |
107 | asc->regs = via_v3_regs; |
108 | aprint_debug(" (v3)" ); |
109 | } else { |
110 | asc->regs = via_v2_regs; |
111 | aprint_debug(" (v2 compat mode)" ); |
112 | } |
113 | } else { |
114 | asc->regs = via_v2_regs; |
115 | aprint_debug(" (v2)" ); |
116 | } |
117 | |
118 | if (agp_map_aperture(pa, sc, AGP_APBASE) != 0) { |
119 | aprint_error(": can't map aperture\n" ); |
120 | free(asc, M_AGP); |
121 | return ENXIO; |
122 | } |
123 | |
124 | asc->initial_aperture = AGP_GET_APERTURE(sc); |
125 | |
126 | for (;;) { |
127 | gatt = agp_alloc_gatt(sc); |
128 | if (gatt) |
129 | break; |
130 | |
131 | /* |
132 | * Probably contigmalloc failure. Try reducing the |
133 | * aperture so that the gatt size reduces. |
134 | */ |
135 | if (AGP_SET_APERTURE(sc, AGP_GET_APERTURE(sc) / 2)) { |
136 | agp_generic_detach(sc); |
137 | aprint_error(": can't set aperture size\n" ); |
138 | return ENOMEM; |
139 | } |
140 | } |
141 | asc->gatt = gatt; |
142 | |
143 | if (asc->regs == via_v2_regs) { |
144 | /* Install the gatt. */ |
145 | pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_ATTBASE], |
146 | gatt->ag_physical | 3); |
147 | /* Enable the aperture. */ |
148 | pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_GARTCTRL], |
149 | 0x0000000f); |
150 | } else { |
151 | pcireg_t gartctrl; |
152 | /* Install the gatt. */ |
153 | pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_ATTBASE], |
154 | gatt->ag_physical); |
155 | /* Enable the aperture. */ |
156 | gartctrl = pci_conf_read(pa->pa_pc, pa->pa_tag, |
157 | asc->regs[REG_GARTCTRL]); |
158 | pci_conf_write(pa->pa_pc, pa->pa_tag, asc->regs[REG_GARTCTRL], |
159 | gartctrl | (3 << 7)); |
160 | } |
161 | |
162 | return 0; |
163 | } |
164 | |
165 | #if 0 |
166 | static int |
167 | agp_via_detach(struct agp_softc *sc) |
168 | { |
169 | struct agp_via_softc *asc = sc->as_chipc; |
170 | int error; |
171 | |
172 | error = agp_generic_detach(sc); |
173 | if (error) |
174 | return error; |
175 | |
176 | pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], 0); |
177 | pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_ATTBASE], 0); |
178 | AGP_SET_APERTURE(sc, asc->initial_aperture); |
179 | agp_free_gatt(sc, asc->gatt); |
180 | |
181 | return 0; |
182 | } |
183 | #endif |
184 | |
185 | static u_int32_t |
186 | agp_via_get_aperture(struct agp_softc *sc) |
187 | { |
188 | struct agp_via_softc *asc = sc->as_chipc; |
189 | u_int32_t apsize; |
190 | |
191 | if (asc->regs == via_v2_regs) { |
192 | apsize = pci_conf_read(sc->as_pc, sc->as_tag, |
193 | asc->regs[REG_APSIZE]) & 0xff; |
194 | |
195 | /* |
196 | * The size is determined by the number of low bits of |
197 | * register APBASE which are forced to zero. The low 20 bits |
198 | * are always forced to zero and each zero bit in the apsize |
199 | * field just read forces the corresponding bit in the 27:20 |
200 | * to be zero. We calculate the aperture size accordingly. |
201 | */ |
202 | return (((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1; |
203 | } else { |
204 | apsize = pci_conf_read(sc->as_pc, sc->as_tag, |
205 | asc->regs[REG_APSIZE]) & 0xfff; |
206 | switch (apsize) { |
207 | case 0x800: |
208 | return 0x80000000; |
209 | case 0xc00: |
210 | return 0x40000000; |
211 | case 0xe00: |
212 | return 0x20000000; |
213 | case 0xf00: |
214 | return 0x10000000; |
215 | case 0xf20: |
216 | return 0x08000000; |
217 | case 0xf30: |
218 | return 0x04000000; |
219 | case 0xf38: |
220 | return 0x02000000; |
221 | case 0xf3c: |
222 | return 0x01000000; |
223 | case 0xf3e: |
224 | return 0x00800000; |
225 | case 0xf3f: |
226 | return 0x00400000; |
227 | default: |
228 | aprint_error_dev(sc->as_dev, |
229 | "invalid aperture setting 0x%x\n" , apsize); |
230 | return 0; |
231 | } |
232 | } |
233 | } |
234 | |
235 | static int |
236 | agp_via_set_aperture(struct agp_softc *sc, u_int32_t aperture) |
237 | { |
238 | struct agp_via_softc *asc = sc->as_chipc; |
239 | u_int32_t apsize, key; |
240 | pcireg_t reg; |
241 | |
242 | if (asc->regs == via_v2_regs) { |
243 | /* |
244 | * Reverse the magic from get_aperture. |
245 | */ |
246 | apsize = ((aperture - 1) >> 20) ^ 0xff; |
247 | |
248 | /* |
249 | * Double check for sanity. |
250 | */ |
251 | if ((((apsize ^ 0xff) << 20) | ((1 << 20) - 1)) + 1 != aperture) |
252 | return EINVAL; |
253 | |
254 | reg = pci_conf_read(sc->as_pc, sc->as_tag, |
255 | asc->regs[REG_APSIZE]); |
256 | reg &= ~0xff; |
257 | reg |= apsize; |
258 | pci_conf_write(sc->as_pc, sc->as_tag, |
259 | asc->regs[REG_APSIZE], reg); |
260 | } else { |
261 | switch (aperture) { |
262 | case 0x80000000: |
263 | key = 0x800; |
264 | break; |
265 | case 0x40000000: |
266 | key = 0xc00; |
267 | break; |
268 | case 0x20000000: |
269 | key = 0xe00; |
270 | break; |
271 | case 0x10000000: |
272 | key = 0xf00; |
273 | break; |
274 | case 0x08000000: |
275 | key = 0xf20; |
276 | break; |
277 | case 0x04000000: |
278 | key = 0xf30; |
279 | break; |
280 | case 0x02000000: |
281 | key = 0xf38; |
282 | break; |
283 | case 0x01000000: |
284 | key = 0xf3c; |
285 | break; |
286 | case 0x00800000: |
287 | key = 0xf3e; |
288 | break; |
289 | case 0x00400000: |
290 | key = 0xf3f; |
291 | break; |
292 | default: |
293 | aprint_error_dev(sc->as_dev, |
294 | "invalid aperture size (%dMB)\n" , |
295 | aperture / 1024 / 1024); |
296 | return EINVAL; |
297 | } |
298 | reg = pci_conf_read(sc->as_pc, sc->as_tag, asc->regs[REG_APSIZE]); |
299 | reg &= ~0xfff; |
300 | reg |= key; |
301 | pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_APSIZE], reg); |
302 | } |
303 | |
304 | return 0; |
305 | } |
306 | |
307 | static int |
308 | agp_via_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) |
309 | { |
310 | struct agp_via_softc *asc = sc->as_chipc; |
311 | |
312 | if (offset < 0 || offset >= (asc->gatt->ag_entries << AGP_PAGE_SHIFT)) |
313 | return EINVAL; |
314 | |
315 | asc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical; |
316 | return 0; |
317 | } |
318 | |
319 | static int |
320 | agp_via_unbind_page(struct agp_softc *sc, off_t offset) |
321 | { |
322 | struct agp_via_softc *asc = sc->as_chipc; |
323 | |
324 | if (offset < 0 || offset >= (asc->gatt->ag_entries << AGP_PAGE_SHIFT)) |
325 | return EINVAL; |
326 | |
327 | asc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; |
328 | return 0; |
329 | } |
330 | |
331 | static void |
332 | agp_via_flush_tlb(struct agp_softc *sc) |
333 | { |
334 | struct agp_via_softc *asc = sc->as_chipc; |
335 | pcireg_t gartctrl; |
336 | |
337 | if (asc->regs == via_v2_regs) { |
338 | pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], |
339 | 0x8f); |
340 | pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], |
341 | 0x0f); |
342 | } else { |
343 | gartctrl = pci_conf_read(sc->as_pc, sc->as_tag, |
344 | asc->regs[REG_GARTCTRL]); |
345 | pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], |
346 | gartctrl & ~(1 << 7)); |
347 | pci_conf_write(sc->as_pc, sc->as_tag, asc->regs[REG_GARTCTRL], |
348 | gartctrl); |
349 | } |
350 | } |
351 | |