1 | /* $NetBSD: agp_intel.c,v 1.37 2011/04/04 20:37:56 dyoung 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_intel.c,v 1.4 2001/07/05 21:28:47 jhb Exp $ |
29 | */ |
30 | |
31 | #include <sys/cdefs.h> |
32 | __KERNEL_RCSID(0, "$NetBSD: agp_intel.c,v 1.37 2011/04/04 20:37:56 dyoung 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/agpio.h> |
40 | #include <sys/device.h> |
41 | |
42 | #include <dev/pci/pcivar.h> |
43 | #include <dev/pci/pcireg.h> |
44 | #include <dev/pci/pcidevs.h> |
45 | #include <dev/pci/agpvar.h> |
46 | #include <dev/pci/agpreg.h> |
47 | |
48 | #include <sys/bus.h> |
49 | |
50 | struct agp_intel_softc { |
51 | u_int32_t initial_aperture; |
52 | /* aperture size at startup */ |
53 | struct agp_gatt *gatt; |
54 | struct pci_attach_args vga_pa; |
55 | u_int aperture_mask; |
56 | int chiptype; /* Chip type */ |
57 | #define CHIP_INTEL 0x0 |
58 | #define CHIP_I443 0x1 |
59 | #define CHIP_I840 0x2 |
60 | #define CHIP_I845 0x3 |
61 | #define CHIP_I850 0x4 |
62 | #define CHIP_I865 0x5 |
63 | |
64 | }; |
65 | |
66 | static u_int32_t agp_intel_get_aperture(struct agp_softc *); |
67 | static int agp_intel_set_aperture(struct agp_softc *, u_int32_t); |
68 | static int agp_intel_bind_page(struct agp_softc *, off_t, bus_addr_t); |
69 | static int agp_intel_unbind_page(struct agp_softc *, off_t); |
70 | static void agp_intel_flush_tlb(struct agp_softc *); |
71 | static int agp_intel_init(struct agp_softc *); |
72 | static bool agp_intel_resume(device_t, const pmf_qual_t *); |
73 | |
74 | static struct agp_methods agp_intel_methods = { |
75 | agp_intel_get_aperture, |
76 | agp_intel_set_aperture, |
77 | agp_intel_bind_page, |
78 | agp_intel_unbind_page, |
79 | agp_intel_flush_tlb, |
80 | agp_generic_enable, |
81 | agp_generic_alloc_memory, |
82 | agp_generic_free_memory, |
83 | agp_generic_bind_memory, |
84 | agp_generic_unbind_memory, |
85 | }; |
86 | |
87 | static int |
88 | agp_intel_vgamatch(const struct pci_attach_args *pa) |
89 | { |
90 | switch (PCI_PRODUCT(pa->pa_id)) { |
91 | case PCI_PRODUCT_INTEL_82855GM_AGP: |
92 | case PCI_PRODUCT_INTEL_82855PM_AGP: |
93 | case PCI_PRODUCT_INTEL_82443LX_AGP: |
94 | case PCI_PRODUCT_INTEL_82443BX_AGP: |
95 | case PCI_PRODUCT_INTEL_82443GX_AGP: |
96 | case PCI_PRODUCT_INTEL_82850_AGP: /* i850/i860 */ |
97 | case PCI_PRODUCT_INTEL_82845_AGP: |
98 | case PCI_PRODUCT_INTEL_82840_AGP: |
99 | case PCI_PRODUCT_INTEL_82865_AGP: |
100 | case PCI_PRODUCT_INTEL_82875P_AGP: |
101 | return (1); |
102 | } |
103 | |
104 | return (0); |
105 | } |
106 | |
107 | int |
108 | agp_intel_attach(device_t parent, device_t self, void *aux) |
109 | { |
110 | struct agp_softc *sc = device_private(self); |
111 | struct pci_attach_args *pa = aux; |
112 | struct agp_intel_softc *isc; |
113 | struct agp_gatt *gatt; |
114 | u_int32_t value; |
115 | |
116 | isc = malloc(sizeof *isc, M_AGP, M_NOWAIT|M_ZERO); |
117 | if (isc == NULL) { |
118 | aprint_error(": can't allocate chipset-specific softc\n" ); |
119 | return ENOMEM; |
120 | } |
121 | |
122 | sc->as_methods = &agp_intel_methods; |
123 | sc->as_chipc = isc; |
124 | |
125 | if (pci_find_device(&isc->vga_pa, agp_intel_vgamatch) == 0) { |
126 | aprint_normal(": using generic initialization for Intel AGP\n" ); |
127 | aprint_normal_dev(sc->as_dev, "" ); |
128 | isc->chiptype = CHIP_INTEL; |
129 | } |
130 | |
131 | pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_AGP, &sc->as_capoff, |
132 | NULL); |
133 | |
134 | if (agp_map_aperture(pa, sc, AGP_APBASE) != 0) { |
135 | aprint_error(": can't map aperture\n" ); |
136 | free(isc, M_AGP); |
137 | sc->as_chipc = NULL; |
138 | return ENXIO; |
139 | } |
140 | |
141 | switch (PCI_PRODUCT(isc->vga_pa.pa_id)) { |
142 | case PCI_PRODUCT_INTEL_82443LX_AGP: |
143 | case PCI_PRODUCT_INTEL_82443BX_AGP: |
144 | case PCI_PRODUCT_INTEL_82443GX_AGP: |
145 | isc->chiptype = CHIP_I443; |
146 | break; |
147 | case PCI_PRODUCT_INTEL_82840_AGP: |
148 | isc->chiptype = CHIP_I840; |
149 | break; |
150 | case PCI_PRODUCT_INTEL_82855GM_AGP: |
151 | case PCI_PRODUCT_INTEL_82855PM_AGP: |
152 | case PCI_PRODUCT_INTEL_82845_AGP: |
153 | isc->chiptype = CHIP_I845; |
154 | break; |
155 | case PCI_PRODUCT_INTEL_82850_AGP: |
156 | isc->chiptype = CHIP_I850; |
157 | break; |
158 | case PCI_PRODUCT_INTEL_82865_AGP: |
159 | case PCI_PRODUCT_INTEL_82875P_AGP: |
160 | isc->chiptype = CHIP_I865; |
161 | break; |
162 | } |
163 | |
164 | /* Determine maximum supported aperture size. */ |
165 | value = pci_conf_read(sc->as_pc, sc->as_tag, AGP_INTEL_APSIZE); |
166 | pci_conf_write(sc->as_pc, sc->as_tag, |
167 | AGP_INTEL_APSIZE, APSIZE_MASK); |
168 | isc->aperture_mask = pci_conf_read(sc->as_pc, sc->as_tag, |
169 | AGP_INTEL_APSIZE) & APSIZE_MASK; |
170 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_APSIZE, value); |
171 | isc->initial_aperture = AGP_GET_APERTURE(sc); |
172 | |
173 | for (;;) { |
174 | gatt = agp_alloc_gatt(sc); |
175 | if (gatt) |
176 | break; |
177 | |
178 | /* |
179 | * Probably contigmalloc failure. Try reducing the |
180 | * aperture so that the gatt size reduces. |
181 | */ |
182 | if (AGP_SET_APERTURE(sc, AGP_GET_APERTURE(sc) / 2)) { |
183 | agp_generic_detach(sc); |
184 | aprint_error(": failed to set aperture\n" ); |
185 | return ENOMEM; |
186 | } |
187 | } |
188 | isc->gatt = gatt; |
189 | |
190 | if (!pmf_device_register(self, NULL, agp_intel_resume)) |
191 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
192 | |
193 | return agp_intel_init(sc); |
194 | } |
195 | |
196 | static int |
197 | agp_intel_init(struct agp_softc *sc) |
198 | { |
199 | struct agp_intel_softc *isc = sc->as_chipc; |
200 | struct agp_gatt *gatt = isc->gatt; |
201 | pcireg_t reg; |
202 | |
203 | /* Install the gatt. */ |
204 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_ATTBASE, |
205 | gatt->ag_physical); |
206 | |
207 | /* Enable the GLTB and setup the control register. */ |
208 | switch (isc->chiptype) { |
209 | case CHIP_I443: |
210 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_AGPCTRL, |
211 | AGPCTRL_AGPRSE | AGPCTRL_GTLB); |
212 | |
213 | default: |
214 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_AGPCTRL, |
215 | pci_conf_read(sc->as_pc, sc->as_tag, AGP_INTEL_AGPCTRL) |
216 | | AGPCTRL_GTLB); |
217 | } |
218 | |
219 | /* Enable things, clear errors etc. */ |
220 | switch (isc->chiptype) { |
221 | case CHIP_I845: |
222 | case CHIP_I865: |
223 | { |
224 | reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I840_MCHCFG); |
225 | reg |= MCHCFG_AAGN; |
226 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_I840_MCHCFG, reg); |
227 | break; |
228 | } |
229 | case CHIP_I840: |
230 | case CHIP_I850: |
231 | { |
232 | reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_INTEL_AGPCMD); |
233 | reg |= AGPCMD_AGPEN; |
234 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_AGPCMD, |
235 | reg); |
236 | reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I840_MCHCFG); |
237 | reg |= MCHCFG_AAGN; |
238 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_I840_MCHCFG, |
239 | reg); |
240 | break; |
241 | } |
242 | default: |
243 | { |
244 | reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_INTEL_NBXCFG); |
245 | reg &= ~NBXCFG_APAE; |
246 | reg |= NBXCFG_AAGN; |
247 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_NBXCFG, reg); |
248 | } |
249 | } |
250 | |
251 | /* Clear Error status */ |
252 | switch (isc->chiptype) { |
253 | case CHIP_I840: |
254 | pci_conf_write(sc->as_pc, sc->as_tag, |
255 | AGP_INTEL_I8XX_ERRSTS, 0xc000); |
256 | break; |
257 | |
258 | case CHIP_I845: |
259 | case CHIP_I850: |
260 | case CHIP_I865: |
261 | pci_conf_write(sc->as_pc, sc->as_tag, |
262 | AGP_INTEL_I8XX_ERRSTS, 0x00ff); |
263 | break; |
264 | |
265 | default: |
266 | { |
267 | reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_INTEL_ERRSTS); |
268 | /* clear error bits (write-one-to-clear) - just write back */ |
269 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_ERRSTS, reg); |
270 | } |
271 | } |
272 | |
273 | return (0); |
274 | } |
275 | |
276 | #if 0 |
277 | static int |
278 | agp_intel_detach(struct agp_softc *sc) |
279 | { |
280 | int error; |
281 | pcireg_t reg; |
282 | struct agp_intel_softc *isc = sc->as_chipc; |
283 | |
284 | error = agp_generic_detach(sc); |
285 | if (error) |
286 | return error; |
287 | |
288 | /* XXX i845/i855PM/i840/i850E */ |
289 | reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_INTEL_NBXCFG); |
290 | reg &= ~(1 << 9); |
291 | printf("%s: set NBXCFG to %x\n" , __func__, reg); |
292 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_NBXCFG, reg); |
293 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_ATTBASE, 0); |
294 | AGP_SET_APERTURE(sc, isc->initial_aperture); |
295 | agp_free_gatt(sc, isc->gatt); |
296 | |
297 | return 0; |
298 | } |
299 | #endif |
300 | |
301 | static u_int32_t |
302 | agp_intel_get_aperture(struct agp_softc *sc) |
303 | { |
304 | struct agp_intel_softc *isc = sc->as_chipc; |
305 | u_int32_t apsize; |
306 | |
307 | apsize = pci_conf_read(sc->as_pc, sc->as_tag, |
308 | AGP_INTEL_APSIZE) & isc->aperture_mask; |
309 | |
310 | /* |
311 | * The size is determined by the number of low bits of |
312 | * register APBASE which are forced to zero. The low 22 bits |
313 | * are always forced to zero and each zero bit in the apsize |
314 | * field just read forces the corresponding bit in the 27:22 |
315 | * to be zero. We calculate the aperture size accordingly. |
316 | */ |
317 | return (((apsize ^ isc->aperture_mask) << 22) | ((1 << 22) - 1)) + 1; |
318 | } |
319 | |
320 | static int |
321 | agp_intel_set_aperture(struct agp_softc *sc, u_int32_t aperture) |
322 | { |
323 | struct agp_intel_softc *isc = sc->as_chipc; |
324 | u_int32_t apsize; |
325 | |
326 | /* |
327 | * Reverse the magic from get_aperture. |
328 | */ |
329 | apsize = ((aperture - 1) >> 22) ^ isc->aperture_mask; |
330 | |
331 | /* |
332 | * Double check for sanity. |
333 | */ |
334 | if ((((apsize ^ isc->aperture_mask) << 22) | |
335 | ((1 << 22) - 1)) + 1 != aperture) |
336 | return EINVAL; |
337 | |
338 | pci_conf_write(sc->as_pc, sc->as_tag, |
339 | AGP_INTEL_APSIZE, apsize); |
340 | |
341 | return 0; |
342 | } |
343 | |
344 | static int |
345 | agp_intel_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) |
346 | { |
347 | struct agp_intel_softc *isc = sc->as_chipc; |
348 | |
349 | if (offset < 0 || offset >= (isc->gatt->ag_entries << AGP_PAGE_SHIFT)) |
350 | return EINVAL; |
351 | |
352 | isc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = physical | 0x17; |
353 | return 0; |
354 | } |
355 | |
356 | static int |
357 | agp_intel_unbind_page(struct agp_softc *sc, off_t offset) |
358 | { |
359 | struct agp_intel_softc *isc = sc->as_chipc; |
360 | |
361 | if (offset < 0 || offset >= (isc->gatt->ag_entries << AGP_PAGE_SHIFT)) |
362 | return EINVAL; |
363 | |
364 | isc->gatt->ag_virtual[offset >> AGP_PAGE_SHIFT] = 0; |
365 | return 0; |
366 | } |
367 | |
368 | static void |
369 | agp_intel_flush_tlb(struct agp_softc *sc) |
370 | { |
371 | struct agp_intel_softc *isc = sc->as_chipc; |
372 | pcireg_t reg; |
373 | |
374 | switch (isc->chiptype) { |
375 | case CHIP_I865: |
376 | case CHIP_I850: |
377 | case CHIP_I845: |
378 | case CHIP_I840: |
379 | case CHIP_I443: |
380 | { |
381 | reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_INTEL_AGPCTRL); |
382 | reg &= ~AGPCTRL_GTLB; |
383 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_AGPCTRL, |
384 | reg); |
385 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_AGPCTRL, |
386 | reg | AGPCTRL_GTLB); |
387 | break; |
388 | } |
389 | default: /* XXX */ |
390 | { |
391 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_AGPCTRL, |
392 | 0x2200); |
393 | pci_conf_write(sc->as_pc, sc->as_tag, AGP_INTEL_AGPCTRL, |
394 | 0x2280); |
395 | } |
396 | } |
397 | } |
398 | |
399 | static bool |
400 | agp_intel_resume(device_t dv, const pmf_qual_t *qual) |
401 | { |
402 | struct agp_softc *sc = device_private(dv); |
403 | |
404 | agp_intel_init(sc); |
405 | agp_flush_cache(); |
406 | |
407 | return true; |
408 | } |
409 | |