1 | /* |
2 | * SiS AGPGART routines. |
3 | */ |
4 | |
5 | #include <linux/module.h> |
6 | #include <linux/pci.h> |
7 | #include <linux/init.h> |
8 | #include <linux/agp_backend.h> |
9 | #include <linux/delay.h> |
10 | #include "agp.h" |
11 | |
12 | #define SIS_ATTBASE 0x90 |
13 | #define SIS_APSIZE 0x94 |
14 | #define SIS_TLBCNTRL 0x97 |
15 | #define SIS_TLBFLUSH 0x98 |
16 | |
17 | #define PCI_DEVICE_ID_SI_662 0x0662 |
18 | #define PCI_DEVICE_ID_SI_671 0x0671 |
19 | |
20 | static bool agp_sis_force_delay = 0; |
21 | static int agp_sis_agp_spec = -1; |
22 | |
23 | static int sis_fetch_size(void) |
24 | { |
25 | u8 temp_size; |
26 | int i; |
27 | struct aper_size_info_8 *values; |
28 | |
29 | pci_read_config_byte(dev: agp_bridge->dev, SIS_APSIZE, val: &temp_size); |
30 | values = A_SIZE_8(agp_bridge->driver->aperture_sizes); |
31 | for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { |
32 | if ((temp_size == values[i].size_value) || |
33 | ((temp_size & ~(0x07)) == |
34 | (values[i].size_value & ~(0x07)))) { |
35 | agp_bridge->previous_size = |
36 | agp_bridge->current_size = (void *) (values + i); |
37 | |
38 | agp_bridge->aperture_size_idx = i; |
39 | return values[i].size; |
40 | } |
41 | } |
42 | |
43 | return 0; |
44 | } |
45 | |
46 | static void sis_tlbflush(struct agp_memory *mem) |
47 | { |
48 | pci_write_config_byte(dev: agp_bridge->dev, SIS_TLBFLUSH, val: 0x02); |
49 | } |
50 | |
51 | static int sis_configure(void) |
52 | { |
53 | struct aper_size_info_8 *current_size; |
54 | |
55 | current_size = A_SIZE_8(agp_bridge->current_size); |
56 | pci_write_config_byte(dev: agp_bridge->dev, SIS_TLBCNTRL, val: 0x05); |
57 | agp_bridge->gart_bus_addr = pci_bus_address(pdev: agp_bridge->dev, |
58 | AGP_APERTURE_BAR); |
59 | pci_write_config_dword(dev: agp_bridge->dev, SIS_ATTBASE, |
60 | val: agp_bridge->gatt_bus_addr); |
61 | pci_write_config_byte(dev: agp_bridge->dev, SIS_APSIZE, |
62 | val: current_size->size_value); |
63 | return 0; |
64 | } |
65 | |
66 | static void sis_cleanup(void) |
67 | { |
68 | struct aper_size_info_8 *previous_size; |
69 | |
70 | previous_size = A_SIZE_8(agp_bridge->previous_size); |
71 | pci_write_config_byte(dev: agp_bridge->dev, SIS_APSIZE, |
72 | val: (previous_size->size_value & ~(0x03))); |
73 | } |
74 | |
75 | static void sis_delayed_enable(struct agp_bridge_data *bridge, u32 mode) |
76 | { |
77 | struct pci_dev *device = NULL; |
78 | u32 command; |
79 | int rate; |
80 | |
81 | dev_info(&agp_bridge->dev->dev, "AGP %d.%d bridge\n" , |
82 | agp_bridge->major_version, agp_bridge->minor_version); |
83 | |
84 | pci_read_config_dword(dev: agp_bridge->dev, where: agp_bridge->capndx + PCI_AGP_STATUS, val: &command); |
85 | command = agp_collect_device_status(bridge, mode, command); |
86 | command |= AGPSTAT_AGP_ENABLE; |
87 | rate = (command & 0x7) << 2; |
88 | |
89 | for_each_pci_dev(device) { |
90 | u8 agp = pci_find_capability(dev: device, PCI_CAP_ID_AGP); |
91 | if (!agp) |
92 | continue; |
93 | |
94 | dev_info(&agp_bridge->dev->dev, "putting AGP V3 device at %s into %dx mode\n" , |
95 | pci_name(device), rate); |
96 | |
97 | pci_write_config_dword(dev: device, where: agp + PCI_AGP_COMMAND, val: command); |
98 | |
99 | /* |
100 | * Weird: on some sis chipsets any rate change in the target |
101 | * command register triggers a 5ms screwup during which the master |
102 | * cannot be configured |
103 | */ |
104 | if (device->device == bridge->dev->device) { |
105 | dev_info(&agp_bridge->dev->dev, "SiS delay workaround: giving bridge time to recover\n" ); |
106 | msleep(msecs: 10); |
107 | } |
108 | } |
109 | } |
110 | |
111 | static const struct aper_size_info_8 sis_generic_sizes[7] = |
112 | { |
113 | {256, 65536, 6, 99}, |
114 | {128, 32768, 5, 83}, |
115 | {64, 16384, 4, 67}, |
116 | {32, 8192, 3, 51}, |
117 | {16, 4096, 2, 35}, |
118 | {8, 2048, 1, 19}, |
119 | {4, 1024, 0, 3} |
120 | }; |
121 | |
122 | static struct agp_bridge_driver sis_driver = { |
123 | .owner = THIS_MODULE, |
124 | .aperture_sizes = sis_generic_sizes, |
125 | .size_type = U8_APER_SIZE, |
126 | .num_aperture_sizes = 7, |
127 | .needs_scratch_page = true, |
128 | .configure = sis_configure, |
129 | .fetch_size = sis_fetch_size, |
130 | .cleanup = sis_cleanup, |
131 | .tlb_flush = sis_tlbflush, |
132 | .mask_memory = agp_generic_mask_memory, |
133 | .masks = NULL, |
134 | .agp_enable = agp_generic_enable, |
135 | .cache_flush = global_cache_flush, |
136 | .create_gatt_table = agp_generic_create_gatt_table, |
137 | .free_gatt_table = agp_generic_free_gatt_table, |
138 | .insert_memory = agp_generic_insert_memory, |
139 | .remove_memory = agp_generic_remove_memory, |
140 | .alloc_by_type = agp_generic_alloc_by_type, |
141 | .free_by_type = agp_generic_free_by_type, |
142 | .agp_alloc_page = agp_generic_alloc_page, |
143 | .agp_alloc_pages = agp_generic_alloc_pages, |
144 | .agp_destroy_page = agp_generic_destroy_page, |
145 | .agp_destroy_pages = agp_generic_destroy_pages, |
146 | .agp_type_to_mask_type = agp_generic_type_to_mask_type, |
147 | }; |
148 | |
149 | // chipsets that require the 'delay hack' |
150 | static int sis_broken_chipsets[] = { |
151 | PCI_DEVICE_ID_SI_648, |
152 | PCI_DEVICE_ID_SI_746, |
153 | 0 // terminator |
154 | }; |
155 | |
156 | static void sis_get_driver(struct agp_bridge_data *bridge) |
157 | { |
158 | int i; |
159 | |
160 | for (i=0; sis_broken_chipsets[i]!=0; ++i) |
161 | if (bridge->dev->device==sis_broken_chipsets[i]) |
162 | break; |
163 | |
164 | if (sis_broken_chipsets[i] || agp_sis_force_delay) |
165 | sis_driver.agp_enable=sis_delayed_enable; |
166 | |
167 | // sis chipsets that indicate less than agp3.5 |
168 | // are not actually fully agp3 compliant |
169 | if ((agp_bridge->major_version == 3 && agp_bridge->minor_version >= 5 |
170 | && agp_sis_agp_spec!=0) || agp_sis_agp_spec==1) { |
171 | sis_driver.aperture_sizes = agp3_generic_sizes; |
172 | sis_driver.size_type = U16_APER_SIZE; |
173 | sis_driver.num_aperture_sizes = AGP_GENERIC_SIZES_ENTRIES; |
174 | sis_driver.configure = agp3_generic_configure; |
175 | sis_driver.fetch_size = agp3_generic_fetch_size; |
176 | sis_driver.cleanup = agp3_generic_cleanup; |
177 | sis_driver.tlb_flush = agp3_generic_tlbflush; |
178 | } |
179 | } |
180 | |
181 | |
182 | static int agp_sis_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
183 | { |
184 | struct agp_bridge_data *bridge; |
185 | u8 cap_ptr; |
186 | |
187 | cap_ptr = pci_find_capability(dev: pdev, PCI_CAP_ID_AGP); |
188 | if (!cap_ptr) |
189 | return -ENODEV; |
190 | |
191 | |
192 | dev_info(&pdev->dev, "SiS chipset [%04x/%04x]\n" , |
193 | pdev->vendor, pdev->device); |
194 | bridge = agp_alloc_bridge(); |
195 | if (!bridge) |
196 | return -ENOMEM; |
197 | |
198 | bridge->driver = &sis_driver; |
199 | bridge->dev = pdev; |
200 | bridge->capndx = cap_ptr; |
201 | |
202 | get_agp_version(bridge); |
203 | |
204 | /* Fill in the mode register */ |
205 | pci_read_config_dword(dev: pdev, where: bridge->capndx+PCI_AGP_STATUS, val: &bridge->mode); |
206 | sis_get_driver(bridge); |
207 | |
208 | pci_set_drvdata(pdev, data: bridge); |
209 | return agp_add_bridge(bridge); |
210 | } |
211 | |
212 | static void agp_sis_remove(struct pci_dev *pdev) |
213 | { |
214 | struct agp_bridge_data *bridge = pci_get_drvdata(pdev); |
215 | |
216 | agp_remove_bridge(bridge); |
217 | agp_put_bridge(bridge); |
218 | } |
219 | |
220 | static int agp_sis_resume(__attribute__((unused)) struct device *dev) |
221 | { |
222 | return sis_driver.configure(); |
223 | } |
224 | |
225 | static const struct pci_device_id agp_sis_pci_table[] = { |
226 | { |
227 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
228 | .class_mask = ~0, |
229 | .vendor = PCI_VENDOR_ID_SI, |
230 | .device = PCI_DEVICE_ID_SI_5591, |
231 | .subvendor = PCI_ANY_ID, |
232 | .subdevice = PCI_ANY_ID, |
233 | }, |
234 | { |
235 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
236 | .class_mask = ~0, |
237 | .vendor = PCI_VENDOR_ID_SI, |
238 | .device = PCI_DEVICE_ID_SI_530, |
239 | .subvendor = PCI_ANY_ID, |
240 | .subdevice = PCI_ANY_ID, |
241 | }, |
242 | { |
243 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
244 | .class_mask = ~0, |
245 | .vendor = PCI_VENDOR_ID_SI, |
246 | .device = PCI_DEVICE_ID_SI_540, |
247 | .subvendor = PCI_ANY_ID, |
248 | .subdevice = PCI_ANY_ID, |
249 | }, |
250 | { |
251 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
252 | .class_mask = ~0, |
253 | .vendor = PCI_VENDOR_ID_SI, |
254 | .device = PCI_DEVICE_ID_SI_550, |
255 | .subvendor = PCI_ANY_ID, |
256 | .subdevice = PCI_ANY_ID, |
257 | }, |
258 | { |
259 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
260 | .class_mask = ~0, |
261 | .vendor = PCI_VENDOR_ID_SI, |
262 | .device = PCI_DEVICE_ID_SI_620, |
263 | .subvendor = PCI_ANY_ID, |
264 | .subdevice = PCI_ANY_ID, |
265 | }, |
266 | { |
267 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
268 | .class_mask = ~0, |
269 | .vendor = PCI_VENDOR_ID_SI, |
270 | .device = PCI_DEVICE_ID_SI_630, |
271 | .subvendor = PCI_ANY_ID, |
272 | .subdevice = PCI_ANY_ID, |
273 | }, |
274 | { |
275 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
276 | .class_mask = ~0, |
277 | .vendor = PCI_VENDOR_ID_SI, |
278 | .device = PCI_DEVICE_ID_SI_635, |
279 | .subvendor = PCI_ANY_ID, |
280 | .subdevice = PCI_ANY_ID, |
281 | }, |
282 | { |
283 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
284 | .class_mask = ~0, |
285 | .vendor = PCI_VENDOR_ID_SI, |
286 | .device = PCI_DEVICE_ID_SI_645, |
287 | .subvendor = PCI_ANY_ID, |
288 | .subdevice = PCI_ANY_ID, |
289 | }, |
290 | { |
291 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
292 | .class_mask = ~0, |
293 | .vendor = PCI_VENDOR_ID_SI, |
294 | .device = PCI_DEVICE_ID_SI_646, |
295 | .subvendor = PCI_ANY_ID, |
296 | .subdevice = PCI_ANY_ID, |
297 | }, |
298 | { |
299 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
300 | .class_mask = ~0, |
301 | .vendor = PCI_VENDOR_ID_SI, |
302 | .device = PCI_DEVICE_ID_SI_648, |
303 | .subvendor = PCI_ANY_ID, |
304 | .subdevice = PCI_ANY_ID, |
305 | }, |
306 | { |
307 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
308 | .class_mask = ~0, |
309 | .vendor = PCI_VENDOR_ID_SI, |
310 | .device = PCI_DEVICE_ID_SI_650, |
311 | .subvendor = PCI_ANY_ID, |
312 | .subdevice = PCI_ANY_ID, |
313 | }, |
314 | { |
315 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
316 | .class_mask = ~0, |
317 | .vendor = PCI_VENDOR_ID_SI, |
318 | .device = PCI_DEVICE_ID_SI_651, |
319 | .subvendor = PCI_ANY_ID, |
320 | .subdevice = PCI_ANY_ID, |
321 | }, |
322 | { |
323 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
324 | .class_mask = ~0, |
325 | .vendor = PCI_VENDOR_ID_SI, |
326 | .device = PCI_DEVICE_ID_SI_655, |
327 | .subvendor = PCI_ANY_ID, |
328 | .subdevice = PCI_ANY_ID, |
329 | }, |
330 | { |
331 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
332 | .class_mask = ~0, |
333 | .vendor = PCI_VENDOR_ID_SI, |
334 | .device = PCI_DEVICE_ID_SI_661, |
335 | .subvendor = PCI_ANY_ID, |
336 | .subdevice = PCI_ANY_ID, |
337 | }, |
338 | { |
339 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
340 | .class_mask = ~0, |
341 | .vendor = PCI_VENDOR_ID_SI, |
342 | .device = PCI_DEVICE_ID_SI_662, |
343 | .subvendor = PCI_ANY_ID, |
344 | .subdevice = PCI_ANY_ID, |
345 | }, |
346 | { |
347 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
348 | .class_mask = ~0, |
349 | .vendor = PCI_VENDOR_ID_SI, |
350 | .device = PCI_DEVICE_ID_SI_671, |
351 | .subvendor = PCI_ANY_ID, |
352 | .subdevice = PCI_ANY_ID, |
353 | }, |
354 | { |
355 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
356 | .class_mask = ~0, |
357 | .vendor = PCI_VENDOR_ID_SI, |
358 | .device = PCI_DEVICE_ID_SI_730, |
359 | .subvendor = PCI_ANY_ID, |
360 | .subdevice = PCI_ANY_ID, |
361 | }, |
362 | { |
363 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
364 | .class_mask = ~0, |
365 | .vendor = PCI_VENDOR_ID_SI, |
366 | .device = PCI_DEVICE_ID_SI_735, |
367 | .subvendor = PCI_ANY_ID, |
368 | .subdevice = PCI_ANY_ID, |
369 | }, |
370 | { |
371 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
372 | .class_mask = ~0, |
373 | .vendor = PCI_VENDOR_ID_SI, |
374 | .device = PCI_DEVICE_ID_SI_740, |
375 | .subvendor = PCI_ANY_ID, |
376 | .subdevice = PCI_ANY_ID, |
377 | }, |
378 | { |
379 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
380 | .class_mask = ~0, |
381 | .vendor = PCI_VENDOR_ID_SI, |
382 | .device = PCI_DEVICE_ID_SI_741, |
383 | .subvendor = PCI_ANY_ID, |
384 | .subdevice = PCI_ANY_ID, |
385 | }, |
386 | { |
387 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
388 | .class_mask = ~0, |
389 | .vendor = PCI_VENDOR_ID_SI, |
390 | .device = PCI_DEVICE_ID_SI_745, |
391 | .subvendor = PCI_ANY_ID, |
392 | .subdevice = PCI_ANY_ID, |
393 | }, |
394 | { |
395 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
396 | .class_mask = ~0, |
397 | .vendor = PCI_VENDOR_ID_SI, |
398 | .device = PCI_DEVICE_ID_SI_746, |
399 | .subvendor = PCI_ANY_ID, |
400 | .subdevice = PCI_ANY_ID, |
401 | }, |
402 | { } |
403 | }; |
404 | |
405 | MODULE_DEVICE_TABLE(pci, agp_sis_pci_table); |
406 | |
407 | static DEFINE_SIMPLE_DEV_PM_OPS(agp_sis_pm_ops, NULL, agp_sis_resume); |
408 | |
409 | static struct pci_driver agp_sis_pci_driver = { |
410 | .name = "agpgart-sis" , |
411 | .id_table = agp_sis_pci_table, |
412 | .probe = agp_sis_probe, |
413 | .remove = agp_sis_remove, |
414 | .driver.pm = &agp_sis_pm_ops, |
415 | }; |
416 | |
417 | static int __init agp_sis_init(void) |
418 | { |
419 | if (agp_off) |
420 | return -EINVAL; |
421 | return pci_register_driver(&agp_sis_pci_driver); |
422 | } |
423 | |
424 | static void __exit agp_sis_cleanup(void) |
425 | { |
426 | pci_unregister_driver(dev: &agp_sis_pci_driver); |
427 | } |
428 | |
429 | module_init(agp_sis_init); |
430 | module_exit(agp_sis_cleanup); |
431 | |
432 | module_param(agp_sis_force_delay, bool, 0); |
433 | MODULE_PARM_DESC(agp_sis_force_delay,"forces sis delay hack" ); |
434 | module_param(agp_sis_agp_spec, int, 0); |
435 | MODULE_PARM_DESC(agp_sis_agp_spec,"0=force sis init, 1=force generic agp3 init, default: autodetect" ); |
436 | MODULE_LICENSE("GPL and additional rights" ); |
437 | |