diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/agp/nvidia-agp.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/agp/nvidia-agp.c')
-rw-r--r-- | drivers/char/agp/nvidia-agp.c | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c new file mode 100644 index 000000000000..4f7a3e8bc919 --- /dev/null +++ b/drivers/char/agp/nvidia-agp.c | |||
@@ -0,0 +1,424 @@ | |||
1 | /* | ||
2 | * Nvidia AGPGART routines. | ||
3 | * Based upon a 2.4 agpgart diff by the folks from NVIDIA, and hacked up | ||
4 | * to work in 2.5 by Dave Jones <davej@codemonkey.org.uk> | ||
5 | */ | ||
6 | |||
7 | #include <linux/module.h> | ||
8 | #include <linux/pci.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/agp_backend.h> | ||
11 | #include <linux/gfp.h> | ||
12 | #include <linux/page-flags.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include "agp.h" | ||
15 | |||
16 | /* NVIDIA registers */ | ||
17 | #define NVIDIA_0_APSIZE 0x80 | ||
18 | #define NVIDIA_1_WBC 0xf0 | ||
19 | #define NVIDIA_2_GARTCTRL 0xd0 | ||
20 | #define NVIDIA_2_APBASE 0xd8 | ||
21 | #define NVIDIA_2_APLIMIT 0xdc | ||
22 | #define NVIDIA_2_ATTBASE(i) (0xe0 + (i) * 4) | ||
23 | #define NVIDIA_3_APBASE 0x50 | ||
24 | #define NVIDIA_3_APLIMIT 0x54 | ||
25 | |||
26 | |||
27 | static struct _nvidia_private { | ||
28 | struct pci_dev *dev_1; | ||
29 | struct pci_dev *dev_2; | ||
30 | struct pci_dev *dev_3; | ||
31 | volatile u32 __iomem *aperture; | ||
32 | int num_active_entries; | ||
33 | off_t pg_offset; | ||
34 | u32 wbc_mask; | ||
35 | } nvidia_private; | ||
36 | |||
37 | |||
38 | static int nvidia_fetch_size(void) | ||
39 | { | ||
40 | int i; | ||
41 | u8 size_value; | ||
42 | struct aper_size_info_8 *values; | ||
43 | |||
44 | pci_read_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE, &size_value); | ||
45 | size_value &= 0x0f; | ||
46 | values = A_SIZE_8(agp_bridge->driver->aperture_sizes); | ||
47 | |||
48 | for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { | ||
49 | if (size_value == values[i].size_value) { | ||
50 | agp_bridge->previous_size = | ||
51 | agp_bridge->current_size = (void *) (values + i); | ||
52 | agp_bridge->aperture_size_idx = i; | ||
53 | return values[i].size; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | #define SYSCFG 0xC0010010 | ||
61 | #define IORR_BASE0 0xC0010016 | ||
62 | #define IORR_MASK0 0xC0010017 | ||
63 | #define AMD_K7_NUM_IORR 2 | ||
64 | |||
65 | static int nvidia_init_iorr(u32 base, u32 size) | ||
66 | { | ||
67 | u32 base_hi, base_lo; | ||
68 | u32 mask_hi, mask_lo; | ||
69 | u32 sys_hi, sys_lo; | ||
70 | u32 iorr_addr, free_iorr_addr; | ||
71 | |||
72 | /* Find the iorr that is already used for the base */ | ||
73 | /* If not found, determine the uppermost available iorr */ | ||
74 | free_iorr_addr = AMD_K7_NUM_IORR; | ||
75 | for(iorr_addr = 0; iorr_addr < AMD_K7_NUM_IORR; iorr_addr++) { | ||
76 | rdmsr(IORR_BASE0 + 2 * iorr_addr, base_lo, base_hi); | ||
77 | rdmsr(IORR_MASK0 + 2 * iorr_addr, mask_lo, mask_hi); | ||
78 | |||
79 | if ((base_lo & 0xfffff000) == (base & 0xfffff000)) | ||
80 | break; | ||
81 | |||
82 | if ((mask_lo & 0x00000800) == 0) | ||
83 | free_iorr_addr = iorr_addr; | ||
84 | } | ||
85 | |||
86 | if (iorr_addr >= AMD_K7_NUM_IORR) { | ||
87 | iorr_addr = free_iorr_addr; | ||
88 | if (iorr_addr >= AMD_K7_NUM_IORR) | ||
89 | return -EINVAL; | ||
90 | } | ||
91 | base_hi = 0x0; | ||
92 | base_lo = (base & ~0xfff) | 0x18; | ||
93 | mask_hi = 0xf; | ||
94 | mask_lo = ((~(size - 1)) & 0xfffff000) | 0x800; | ||
95 | wrmsr(IORR_BASE0 + 2 * iorr_addr, base_lo, base_hi); | ||
96 | wrmsr(IORR_MASK0 + 2 * iorr_addr, mask_lo, mask_hi); | ||
97 | |||
98 | rdmsr(SYSCFG, sys_lo, sys_hi); | ||
99 | sys_lo |= 0x00100000; | ||
100 | wrmsr(SYSCFG, sys_lo, sys_hi); | ||
101 | |||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static int nvidia_configure(void) | ||
106 | { | ||
107 | int i, rc, num_dirs; | ||
108 | u32 apbase, aplimit; | ||
109 | struct aper_size_info_8 *current_size; | ||
110 | u32 temp; | ||
111 | |||
112 | current_size = A_SIZE_8(agp_bridge->current_size); | ||
113 | |||
114 | /* aperture size */ | ||
115 | pci_write_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE, | ||
116 | current_size->size_value); | ||
117 | |||
118 | /* address to map to */ | ||
119 | pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &apbase); | ||
120 | apbase &= PCI_BASE_ADDRESS_MEM_MASK; | ||
121 | agp_bridge->gart_bus_addr = apbase; | ||
122 | aplimit = apbase + (current_size->size * 1024 * 1024) - 1; | ||
123 | pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APBASE, apbase); | ||
124 | pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APLIMIT, aplimit); | ||
125 | pci_write_config_dword(nvidia_private.dev_3, NVIDIA_3_APBASE, apbase); | ||
126 | pci_write_config_dword(nvidia_private.dev_3, NVIDIA_3_APLIMIT, aplimit); | ||
127 | if (0 != (rc = nvidia_init_iorr(apbase, current_size->size * 1024 * 1024))) | ||
128 | return rc; | ||
129 | |||
130 | /* directory size is 64k */ | ||
131 | num_dirs = current_size->size / 64; | ||
132 | nvidia_private.num_active_entries = current_size->num_entries; | ||
133 | nvidia_private.pg_offset = 0; | ||
134 | if (num_dirs == 0) { | ||
135 | num_dirs = 1; | ||
136 | nvidia_private.num_active_entries /= (64 / current_size->size); | ||
137 | nvidia_private.pg_offset = (apbase & (64 * 1024 * 1024 - 1) & | ||
138 | ~(current_size->size * 1024 * 1024 - 1)) / PAGE_SIZE; | ||
139 | } | ||
140 | |||
141 | /* attbase */ | ||
142 | for(i = 0; i < 8; i++) { | ||
143 | pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_ATTBASE(i), | ||
144 | (agp_bridge->gatt_bus_addr + (i % num_dirs) * 64 * 1024) | 1); | ||
145 | } | ||
146 | |||
147 | /* gtlb control */ | ||
148 | pci_read_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, &temp); | ||
149 | pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, temp | 0x11); | ||
150 | |||
151 | /* gart control */ | ||
152 | pci_read_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, &temp); | ||
153 | pci_write_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, temp | 0x100); | ||
154 | |||
155 | /* map aperture */ | ||
156 | nvidia_private.aperture = | ||
157 | (volatile u32 __iomem *) ioremap(apbase, 33 * PAGE_SIZE); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static void nvidia_cleanup(void) | ||
163 | { | ||
164 | struct aper_size_info_8 *previous_size; | ||
165 | u32 temp; | ||
166 | |||
167 | /* gart control */ | ||
168 | pci_read_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, &temp); | ||
169 | pci_write_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, temp & ~(0x100)); | ||
170 | |||
171 | /* gtlb control */ | ||
172 | pci_read_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, &temp); | ||
173 | pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, temp & ~(0x11)); | ||
174 | |||
175 | /* unmap aperture */ | ||
176 | iounmap((void __iomem *) nvidia_private.aperture); | ||
177 | |||
178 | /* restore previous aperture size */ | ||
179 | previous_size = A_SIZE_8(agp_bridge->previous_size); | ||
180 | pci_write_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE, | ||
181 | previous_size->size_value); | ||
182 | |||
183 | /* restore iorr for previous aperture size */ | ||
184 | nvidia_init_iorr(agp_bridge->gart_bus_addr, | ||
185 | previous_size->size * 1024 * 1024); | ||
186 | } | ||
187 | |||
188 | |||
189 | /* | ||
190 | * Note we can't use the generic routines, even though they are 99% the same. | ||
191 | * Aperture sizes <64M still requires a full 64k GART directory, but | ||
192 | * only use the portion of the TLB entries that correspond to the apertures | ||
193 | * alignment inside the surrounding 64M block. | ||
194 | */ | ||
195 | extern int agp_memory_reserved; | ||
196 | |||
197 | static int nvidia_insert_memory(struct agp_memory *mem, off_t pg_start, int type) | ||
198 | { | ||
199 | int i, j; | ||
200 | |||
201 | if ((type != 0) || (mem->type != 0)) | ||
202 | return -EINVAL; | ||
203 | |||
204 | if ((pg_start + mem->page_count) > | ||
205 | (nvidia_private.num_active_entries - agp_memory_reserved/PAGE_SIZE)) | ||
206 | return -EINVAL; | ||
207 | |||
208 | for(j = pg_start; j < (pg_start + mem->page_count); j++) { | ||
209 | if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+nvidia_private.pg_offset+j))) | ||
210 | return -EBUSY; | ||
211 | } | ||
212 | |||
213 | if (mem->is_flushed == FALSE) { | ||
214 | global_cache_flush(); | ||
215 | mem->is_flushed = TRUE; | ||
216 | } | ||
217 | for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { | ||
218 | writel(agp_bridge->driver->mask_memory(agp_bridge, | ||
219 | mem->memory[i], mem->type), | ||
220 | agp_bridge->gatt_table+nvidia_private.pg_offset+j); | ||
221 | readl(agp_bridge->gatt_table+nvidia_private.pg_offset+j); /* PCI Posting. */ | ||
222 | } | ||
223 | agp_bridge->driver->tlb_flush(mem); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | |||
228 | static int nvidia_remove_memory(struct agp_memory *mem, off_t pg_start, int type) | ||
229 | { | ||
230 | int i; | ||
231 | |||
232 | if ((type != 0) || (mem->type != 0)) | ||
233 | return -EINVAL; | ||
234 | |||
235 | for (i = pg_start; i < (mem->page_count + pg_start); i++) | ||
236 | writel(agp_bridge->scratch_page, agp_bridge->gatt_table+nvidia_private.pg_offset+i); | ||
237 | |||
238 | agp_bridge->driver->tlb_flush(mem); | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | |||
243 | static void nvidia_tlbflush(struct agp_memory *mem) | ||
244 | { | ||
245 | unsigned long end; | ||
246 | u32 wbc_reg, temp; | ||
247 | int i; | ||
248 | |||
249 | /* flush chipset */ | ||
250 | if (nvidia_private.wbc_mask) { | ||
251 | pci_read_config_dword(nvidia_private.dev_1, NVIDIA_1_WBC, &wbc_reg); | ||
252 | wbc_reg |= nvidia_private.wbc_mask; | ||
253 | pci_write_config_dword(nvidia_private.dev_1, NVIDIA_1_WBC, wbc_reg); | ||
254 | |||
255 | end = jiffies + 3*HZ; | ||
256 | do { | ||
257 | pci_read_config_dword(nvidia_private.dev_1, | ||
258 | NVIDIA_1_WBC, &wbc_reg); | ||
259 | if ((signed)(end - jiffies) <= 0) { | ||
260 | printk(KERN_ERR PFX | ||
261 | "TLB flush took more than 3 seconds.\n"); | ||
262 | } | ||
263 | } while (wbc_reg & nvidia_private.wbc_mask); | ||
264 | } | ||
265 | |||
266 | /* flush TLB entries */ | ||
267 | for(i = 0; i < 32 + 1; i++) | ||
268 | temp = readl(nvidia_private.aperture+(i * PAGE_SIZE / sizeof(u32))); | ||
269 | for(i = 0; i < 32 + 1; i++) | ||
270 | temp = readl(nvidia_private.aperture+(i * PAGE_SIZE / sizeof(u32))); | ||
271 | } | ||
272 | |||
273 | |||
274 | static struct aper_size_info_8 nvidia_generic_sizes[5] = | ||
275 | { | ||
276 | {512, 131072, 7, 0}, | ||
277 | {256, 65536, 6, 8}, | ||
278 | {128, 32768, 5, 12}, | ||
279 | {64, 16384, 4, 14}, | ||
280 | /* The 32M mode still requires a 64k gatt */ | ||
281 | {32, 16384, 4, 15} | ||
282 | }; | ||
283 | |||
284 | |||
285 | static struct gatt_mask nvidia_generic_masks[] = | ||
286 | { | ||
287 | { .mask = 1, .type = 0} | ||
288 | }; | ||
289 | |||
290 | |||
291 | struct agp_bridge_driver nvidia_driver = { | ||
292 | .owner = THIS_MODULE, | ||
293 | .aperture_sizes = nvidia_generic_sizes, | ||
294 | .size_type = U8_APER_SIZE, | ||
295 | .num_aperture_sizes = 5, | ||
296 | .configure = nvidia_configure, | ||
297 | .fetch_size = nvidia_fetch_size, | ||
298 | .cleanup = nvidia_cleanup, | ||
299 | .tlb_flush = nvidia_tlbflush, | ||
300 | .mask_memory = agp_generic_mask_memory, | ||
301 | .masks = nvidia_generic_masks, | ||
302 | .agp_enable = agp_generic_enable, | ||
303 | .cache_flush = global_cache_flush, | ||
304 | .create_gatt_table = agp_generic_create_gatt_table, | ||
305 | .free_gatt_table = agp_generic_free_gatt_table, | ||
306 | .insert_memory = nvidia_insert_memory, | ||
307 | .remove_memory = nvidia_remove_memory, | ||
308 | .alloc_by_type = agp_generic_alloc_by_type, | ||
309 | .free_by_type = agp_generic_free_by_type, | ||
310 | .agp_alloc_page = agp_generic_alloc_page, | ||
311 | .agp_destroy_page = agp_generic_destroy_page, | ||
312 | }; | ||
313 | |||
314 | static int __devinit agp_nvidia_probe(struct pci_dev *pdev, | ||
315 | const struct pci_device_id *ent) | ||
316 | { | ||
317 | struct agp_bridge_data *bridge; | ||
318 | u8 cap_ptr; | ||
319 | |||
320 | nvidia_private.dev_1 = | ||
321 | pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(0, 1)); | ||
322 | nvidia_private.dev_2 = | ||
323 | pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(0, 2)); | ||
324 | nvidia_private.dev_3 = | ||
325 | pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(30, 0)); | ||
326 | |||
327 | if (!nvidia_private.dev_1 || !nvidia_private.dev_2 || !nvidia_private.dev_3) { | ||
328 | printk(KERN_INFO PFX "Detected an NVIDIA nForce/nForce2 " | ||
329 | "chipset, but could not find the secondary devices.\n"); | ||
330 | return -ENODEV; | ||
331 | } | ||
332 | |||
333 | cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); | ||
334 | if (!cap_ptr) | ||
335 | return -ENODEV; | ||
336 | |||
337 | switch (pdev->device) { | ||
338 | case PCI_DEVICE_ID_NVIDIA_NFORCE: | ||
339 | printk(KERN_INFO PFX "Detected NVIDIA nForce chipset\n"); | ||
340 | nvidia_private.wbc_mask = 0x00010000; | ||
341 | break; | ||
342 | case PCI_DEVICE_ID_NVIDIA_NFORCE2: | ||
343 | printk(KERN_INFO PFX "Detected NVIDIA nForce2 chipset\n"); | ||
344 | nvidia_private.wbc_mask = 0x80000000; | ||
345 | break; | ||
346 | default: | ||
347 | printk(KERN_ERR PFX "Unsupported NVIDIA chipset (device id: %04x)\n", | ||
348 | pdev->device); | ||
349 | return -ENODEV; | ||
350 | } | ||
351 | |||
352 | bridge = agp_alloc_bridge(); | ||
353 | if (!bridge) | ||
354 | return -ENOMEM; | ||
355 | |||
356 | bridge->driver = &nvidia_driver; | ||
357 | bridge->dev_private_data = &nvidia_private, | ||
358 | bridge->dev = pdev; | ||
359 | bridge->capndx = cap_ptr; | ||
360 | |||
361 | /* Fill in the mode register */ | ||
362 | pci_read_config_dword(pdev, | ||
363 | bridge->capndx+PCI_AGP_STATUS, | ||
364 | &bridge->mode); | ||
365 | |||
366 | pci_set_drvdata(pdev, bridge); | ||
367 | return agp_add_bridge(bridge); | ||
368 | } | ||
369 | |||
370 | static void __devexit agp_nvidia_remove(struct pci_dev *pdev) | ||
371 | { | ||
372 | struct agp_bridge_data *bridge = pci_get_drvdata(pdev); | ||
373 | |||
374 | agp_remove_bridge(bridge); | ||
375 | agp_put_bridge(bridge); | ||
376 | } | ||
377 | |||
378 | static struct pci_device_id agp_nvidia_pci_table[] = { | ||
379 | { | ||
380 | .class = (PCI_CLASS_BRIDGE_HOST << 8), | ||
381 | .class_mask = ~0, | ||
382 | .vendor = PCI_VENDOR_ID_NVIDIA, | ||
383 | .device = PCI_DEVICE_ID_NVIDIA_NFORCE, | ||
384 | .subvendor = PCI_ANY_ID, | ||
385 | .subdevice = PCI_ANY_ID, | ||
386 | }, | ||
387 | { | ||
388 | .class = (PCI_CLASS_BRIDGE_HOST << 8), | ||
389 | .class_mask = ~0, | ||
390 | .vendor = PCI_VENDOR_ID_NVIDIA, | ||
391 | .device = PCI_DEVICE_ID_NVIDIA_NFORCE2, | ||
392 | .subvendor = PCI_ANY_ID, | ||
393 | .subdevice = PCI_ANY_ID, | ||
394 | }, | ||
395 | { } | ||
396 | }; | ||
397 | |||
398 | MODULE_DEVICE_TABLE(pci, agp_nvidia_pci_table); | ||
399 | |||
400 | static struct pci_driver agp_nvidia_pci_driver = { | ||
401 | .name = "agpgart-nvidia", | ||
402 | .id_table = agp_nvidia_pci_table, | ||
403 | .probe = agp_nvidia_probe, | ||
404 | .remove = agp_nvidia_remove, | ||
405 | }; | ||
406 | |||
407 | static int __init agp_nvidia_init(void) | ||
408 | { | ||
409 | if (agp_off) | ||
410 | return -EINVAL; | ||
411 | return pci_register_driver(&agp_nvidia_pci_driver); | ||
412 | } | ||
413 | |||
414 | static void __exit agp_nvidia_cleanup(void) | ||
415 | { | ||
416 | pci_unregister_driver(&agp_nvidia_pci_driver); | ||
417 | } | ||
418 | |||
419 | module_init(agp_nvidia_init); | ||
420 | module_exit(agp_nvidia_cleanup); | ||
421 | |||
422 | MODULE_LICENSE("GPL and additional rights"); | ||
423 | MODULE_AUTHOR("NVIDIA Corporation"); | ||
424 | |||