1 | /* |
2 | * ALi AGPGART routines. |
3 | */ |
4 | |
5 | #include <linux/types.h> |
6 | #include <linux/module.h> |
7 | #include <linux/pci.h> |
8 | #include <linux/init.h> |
9 | #include <linux/agp_backend.h> |
10 | #include <asm/page.h> /* PAGE_SIZE */ |
11 | #include "agp.h" |
12 | |
13 | #define ALI_AGPCTRL 0xb8 |
14 | #define ALI_ATTBASE 0xbc |
15 | #define ALI_TLBCTRL 0xc0 |
16 | #define ALI_TAGCTRL 0xc4 |
17 | #define ALI_CACHE_FLUSH_CTRL 0xD0 |
18 | #define ALI_CACHE_FLUSH_ADDR_MASK 0xFFFFF000 |
19 | #define ALI_CACHE_FLUSH_EN 0x100 |
20 | |
21 | static int ali_fetch_size(void) |
22 | { |
23 | int i; |
24 | u32 temp; |
25 | struct aper_size_info_32 *values; |
26 | |
27 | pci_read_config_dword(dev: agp_bridge->dev, ALI_ATTBASE, val: &temp); |
28 | temp &= ~(0xfffffff0); |
29 | values = A_SIZE_32(agp_bridge->driver->aperture_sizes); |
30 | |
31 | for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) { |
32 | if (temp == values[i].size_value) { |
33 | agp_bridge->previous_size = |
34 | agp_bridge->current_size = (void *) (values + i); |
35 | agp_bridge->aperture_size_idx = i; |
36 | return values[i].size; |
37 | } |
38 | } |
39 | |
40 | return 0; |
41 | } |
42 | |
43 | static void ali_tlbflush(struct agp_memory *mem) |
44 | { |
45 | u32 temp; |
46 | |
47 | pci_read_config_dword(dev: agp_bridge->dev, ALI_TLBCTRL, val: &temp); |
48 | temp &= 0xfffffff0; |
49 | temp |= (1<<0 | 1<<1); |
50 | pci_write_config_dword(dev: agp_bridge->dev, ALI_TAGCTRL, val: temp); |
51 | } |
52 | |
53 | static void ali_cleanup(void) |
54 | { |
55 | struct aper_size_info_32 *previous_size; |
56 | u32 temp; |
57 | |
58 | previous_size = A_SIZE_32(agp_bridge->previous_size); |
59 | |
60 | pci_read_config_dword(dev: agp_bridge->dev, ALI_TLBCTRL, val: &temp); |
61 | // clear tag |
62 | pci_write_config_dword(dev: agp_bridge->dev, ALI_TAGCTRL, |
63 | val: ((temp & 0xffffff00) | 0x00000001|0x00000002)); |
64 | |
65 | pci_read_config_dword(dev: agp_bridge->dev, ALI_ATTBASE, val: &temp); |
66 | pci_write_config_dword(dev: agp_bridge->dev, ALI_ATTBASE, |
67 | val: ((temp & 0x00000ff0) | previous_size->size_value)); |
68 | } |
69 | |
70 | static int ali_configure(void) |
71 | { |
72 | u32 temp; |
73 | struct aper_size_info_32 *current_size; |
74 | |
75 | current_size = A_SIZE_32(agp_bridge->current_size); |
76 | |
77 | /* aperture size and gatt addr */ |
78 | pci_read_config_dword(dev: agp_bridge->dev, ALI_ATTBASE, val: &temp); |
79 | temp = (((temp & 0x00000ff0) | (agp_bridge->gatt_bus_addr & 0xfffff000)) |
80 | | (current_size->size_value & 0xf)); |
81 | pci_write_config_dword(dev: agp_bridge->dev, ALI_ATTBASE, val: temp); |
82 | |
83 | /* tlb control */ |
84 | pci_read_config_dword(dev: agp_bridge->dev, ALI_TLBCTRL, val: &temp); |
85 | pci_write_config_dword(dev: agp_bridge->dev, ALI_TLBCTRL, val: ((temp & 0xffffff00) | 0x00000010)); |
86 | |
87 | /* address to map to */ |
88 | agp_bridge->gart_bus_addr = pci_bus_address(pdev: agp_bridge->dev, |
89 | AGP_APERTURE_BAR); |
90 | |
91 | #if 0 |
92 | if (agp_bridge->type == ALI_M1541) { |
93 | u32 nlvm_addr = 0; |
94 | |
95 | switch (current_size->size_value) { |
96 | case 0: break; |
97 | case 1: nlvm_addr = 0x100000;break; |
98 | case 2: nlvm_addr = 0x200000;break; |
99 | case 3: nlvm_addr = 0x400000;break; |
100 | case 4: nlvm_addr = 0x800000;break; |
101 | case 6: nlvm_addr = 0x1000000;break; |
102 | case 7: nlvm_addr = 0x2000000;break; |
103 | case 8: nlvm_addr = 0x4000000;break; |
104 | case 9: nlvm_addr = 0x8000000;break; |
105 | case 10: nlvm_addr = 0x10000000;break; |
106 | default: break; |
107 | } |
108 | nlvm_addr--; |
109 | nlvm_addr&=0xfff00000; |
110 | |
111 | nlvm_addr+= agp_bridge->gart_bus_addr; |
112 | nlvm_addr|=(agp_bridge->gart_bus_addr>>12); |
113 | dev_info(&agp_bridge->dev->dev, "nlvm top &base = %8x\n" , |
114 | nlvm_addr); |
115 | } |
116 | #endif |
117 | |
118 | pci_read_config_dword(dev: agp_bridge->dev, ALI_TLBCTRL, val: &temp); |
119 | temp &= 0xffffff7f; //enable TLB |
120 | pci_write_config_dword(dev: agp_bridge->dev, ALI_TLBCTRL, val: temp); |
121 | |
122 | return 0; |
123 | } |
124 | |
125 | |
126 | static void m1541_cache_flush(void) |
127 | { |
128 | int i, page_count; |
129 | u32 temp; |
130 | |
131 | global_cache_flush(); |
132 | |
133 | page_count = 1 << A_SIZE_32(agp_bridge->current_size)->page_order; |
134 | for (i = 0; i < PAGE_SIZE * page_count; i += PAGE_SIZE) { |
135 | pci_read_config_dword(dev: agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, |
136 | val: &temp); |
137 | pci_write_config_dword(dev: agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, |
138 | val: (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | |
139 | (agp_bridge->gatt_bus_addr + i)) | |
140 | ALI_CACHE_FLUSH_EN)); |
141 | } |
142 | } |
143 | |
144 | static struct page *m1541_alloc_page(struct agp_bridge_data *bridge) |
145 | { |
146 | struct page *page = agp_generic_alloc_page(bridge: agp_bridge); |
147 | u32 temp; |
148 | |
149 | if (!page) |
150 | return NULL; |
151 | |
152 | pci_read_config_dword(dev: agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, val: &temp); |
153 | pci_write_config_dword(dev: agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, |
154 | val: (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | |
155 | page_to_phys(page)) | ALI_CACHE_FLUSH_EN )); |
156 | return page; |
157 | } |
158 | |
159 | static void ali_destroy_page(struct page *page, int flags) |
160 | { |
161 | if (page) { |
162 | if (flags & AGP_PAGE_DESTROY_UNMAP) { |
163 | global_cache_flush(); /* is this really needed? --hch */ |
164 | agp_generic_destroy_page(page, flags); |
165 | } else |
166 | agp_generic_destroy_page(page, flags); |
167 | } |
168 | } |
169 | |
170 | static void m1541_destroy_page(struct page *page, int flags) |
171 | { |
172 | u32 temp; |
173 | |
174 | if (page == NULL) |
175 | return; |
176 | |
177 | if (flags & AGP_PAGE_DESTROY_UNMAP) { |
178 | global_cache_flush(); |
179 | |
180 | pci_read_config_dword(dev: agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, val: &temp); |
181 | pci_write_config_dword(dev: agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, |
182 | val: (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | |
183 | page_to_phys(page)) | ALI_CACHE_FLUSH_EN)); |
184 | } |
185 | agp_generic_destroy_page(page, flags); |
186 | } |
187 | |
188 | |
189 | /* Setup function */ |
190 | |
191 | static const struct aper_size_info_32 ali_generic_sizes[7] = |
192 | { |
193 | {256, 65536, 6, 10}, |
194 | {128, 32768, 5, 9}, |
195 | {64, 16384, 4, 8}, |
196 | {32, 8192, 3, 7}, |
197 | {16, 4096, 2, 6}, |
198 | {8, 2048, 1, 4}, |
199 | {4, 1024, 0, 3} |
200 | }; |
201 | |
202 | static const struct agp_bridge_driver ali_generic_bridge = { |
203 | .owner = THIS_MODULE, |
204 | .aperture_sizes = ali_generic_sizes, |
205 | .size_type = U32_APER_SIZE, |
206 | .num_aperture_sizes = 7, |
207 | .needs_scratch_page = true, |
208 | .configure = ali_configure, |
209 | .fetch_size = ali_fetch_size, |
210 | .cleanup = ali_cleanup, |
211 | .tlb_flush = ali_tlbflush, |
212 | .mask_memory = agp_generic_mask_memory, |
213 | .masks = NULL, |
214 | .agp_enable = agp_generic_enable, |
215 | .cache_flush = global_cache_flush, |
216 | .create_gatt_table = agp_generic_create_gatt_table, |
217 | .free_gatt_table = agp_generic_free_gatt_table, |
218 | .insert_memory = agp_generic_insert_memory, |
219 | .remove_memory = agp_generic_remove_memory, |
220 | .alloc_by_type = agp_generic_alloc_by_type, |
221 | .free_by_type = agp_generic_free_by_type, |
222 | .agp_alloc_page = agp_generic_alloc_page, |
223 | .agp_destroy_page = ali_destroy_page, |
224 | .agp_type_to_mask_type = agp_generic_type_to_mask_type, |
225 | }; |
226 | |
227 | static const struct agp_bridge_driver ali_m1541_bridge = { |
228 | .owner = THIS_MODULE, |
229 | .aperture_sizes = ali_generic_sizes, |
230 | .size_type = U32_APER_SIZE, |
231 | .num_aperture_sizes = 7, |
232 | .configure = ali_configure, |
233 | .fetch_size = ali_fetch_size, |
234 | .cleanup = ali_cleanup, |
235 | .tlb_flush = ali_tlbflush, |
236 | .mask_memory = agp_generic_mask_memory, |
237 | .masks = NULL, |
238 | .agp_enable = agp_generic_enable, |
239 | .cache_flush = m1541_cache_flush, |
240 | .create_gatt_table = agp_generic_create_gatt_table, |
241 | .free_gatt_table = agp_generic_free_gatt_table, |
242 | .insert_memory = agp_generic_insert_memory, |
243 | .remove_memory = agp_generic_remove_memory, |
244 | .alloc_by_type = agp_generic_alloc_by_type, |
245 | .free_by_type = agp_generic_free_by_type, |
246 | .agp_alloc_page = m1541_alloc_page, |
247 | .agp_destroy_page = m1541_destroy_page, |
248 | .agp_type_to_mask_type = agp_generic_type_to_mask_type, |
249 | }; |
250 | |
251 | |
252 | static struct agp_device_ids ali_agp_device_ids[] = |
253 | { |
254 | { |
255 | .device_id = PCI_DEVICE_ID_AL_M1541, |
256 | .chipset_name = "M1541" , |
257 | }, |
258 | { |
259 | .device_id = PCI_DEVICE_ID_AL_M1621, |
260 | .chipset_name = "M1621" , |
261 | }, |
262 | { |
263 | .device_id = PCI_DEVICE_ID_AL_M1631, |
264 | .chipset_name = "M1631" , |
265 | }, |
266 | { |
267 | .device_id = PCI_DEVICE_ID_AL_M1632, |
268 | .chipset_name = "M1632" , |
269 | }, |
270 | { |
271 | .device_id = PCI_DEVICE_ID_AL_M1641, |
272 | .chipset_name = "M1641" , |
273 | }, |
274 | { |
275 | .device_id = PCI_DEVICE_ID_AL_M1644, |
276 | .chipset_name = "M1644" , |
277 | }, |
278 | { |
279 | .device_id = PCI_DEVICE_ID_AL_M1647, |
280 | .chipset_name = "M1647" , |
281 | }, |
282 | { |
283 | .device_id = PCI_DEVICE_ID_AL_M1651, |
284 | .chipset_name = "M1651" , |
285 | }, |
286 | { |
287 | .device_id = PCI_DEVICE_ID_AL_M1671, |
288 | .chipset_name = "M1671" , |
289 | }, |
290 | { |
291 | .device_id = PCI_DEVICE_ID_AL_M1681, |
292 | .chipset_name = "M1681" , |
293 | }, |
294 | { |
295 | .device_id = PCI_DEVICE_ID_AL_M1683, |
296 | .chipset_name = "M1683" , |
297 | }, |
298 | |
299 | { }, /* dummy final entry, always present */ |
300 | }; |
301 | |
302 | static int agp_ali_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
303 | { |
304 | struct agp_device_ids *devs = ali_agp_device_ids; |
305 | struct agp_bridge_data *bridge; |
306 | u8 hidden_1621_id, cap_ptr; |
307 | int j; |
308 | |
309 | cap_ptr = pci_find_capability(dev: pdev, PCI_CAP_ID_AGP); |
310 | if (!cap_ptr) |
311 | return -ENODEV; |
312 | |
313 | /* probe for known chipsets */ |
314 | for (j = 0; devs[j].chipset_name; j++) { |
315 | if (pdev->device == devs[j].device_id) |
316 | goto found; |
317 | } |
318 | |
319 | dev_err(&pdev->dev, "unsupported ALi chipset [%04x/%04x])\n" , |
320 | pdev->vendor, pdev->device); |
321 | return -ENODEV; |
322 | |
323 | |
324 | found: |
325 | bridge = agp_alloc_bridge(); |
326 | if (!bridge) |
327 | return -ENOMEM; |
328 | |
329 | bridge->dev = pdev; |
330 | bridge->capndx = cap_ptr; |
331 | |
332 | switch (pdev->device) { |
333 | case PCI_DEVICE_ID_AL_M1541: |
334 | bridge->driver = &ali_m1541_bridge; |
335 | break; |
336 | case PCI_DEVICE_ID_AL_M1621: |
337 | pci_read_config_byte(dev: pdev, where: 0xFB, val: &hidden_1621_id); |
338 | switch (hidden_1621_id) { |
339 | case 0x31: |
340 | devs[j].chipset_name = "M1631" ; |
341 | break; |
342 | case 0x32: |
343 | devs[j].chipset_name = "M1632" ; |
344 | break; |
345 | case 0x41: |
346 | devs[j].chipset_name = "M1641" ; |
347 | break; |
348 | case 0x43: |
349 | devs[j].chipset_name = "M1621" ; |
350 | break; |
351 | case 0x47: |
352 | devs[j].chipset_name = "M1647" ; |
353 | break; |
354 | case 0x51: |
355 | devs[j].chipset_name = "M1651" ; |
356 | break; |
357 | default: |
358 | break; |
359 | } |
360 | fallthrough; |
361 | default: |
362 | bridge->driver = &ali_generic_bridge; |
363 | } |
364 | |
365 | dev_info(&pdev->dev, "ALi %s chipset\n" , devs[j].chipset_name); |
366 | |
367 | /* Fill in the mode register */ |
368 | pci_read_config_dword(dev: pdev, |
369 | where: bridge->capndx+PCI_AGP_STATUS, |
370 | val: &bridge->mode); |
371 | |
372 | pci_set_drvdata(pdev, data: bridge); |
373 | return agp_add_bridge(bridge); |
374 | } |
375 | |
376 | static void agp_ali_remove(struct pci_dev *pdev) |
377 | { |
378 | struct agp_bridge_data *bridge = pci_get_drvdata(pdev); |
379 | |
380 | agp_remove_bridge(bridge); |
381 | agp_put_bridge(bridge); |
382 | } |
383 | |
384 | static const struct pci_device_id agp_ali_pci_table[] = { |
385 | { |
386 | .class = (PCI_CLASS_BRIDGE_HOST << 8), |
387 | .class_mask = ~0, |
388 | .vendor = PCI_VENDOR_ID_AL, |
389 | .device = PCI_ANY_ID, |
390 | .subvendor = PCI_ANY_ID, |
391 | .subdevice = PCI_ANY_ID, |
392 | }, |
393 | { } |
394 | }; |
395 | |
396 | MODULE_DEVICE_TABLE(pci, agp_ali_pci_table); |
397 | |
398 | static struct pci_driver agp_ali_pci_driver = { |
399 | .name = "agpgart-ali" , |
400 | .id_table = agp_ali_pci_table, |
401 | .probe = agp_ali_probe, |
402 | .remove = agp_ali_remove, |
403 | }; |
404 | |
405 | static int __init agp_ali_init(void) |
406 | { |
407 | if (agp_off) |
408 | return -EINVAL; |
409 | return pci_register_driver(&agp_ali_pci_driver); |
410 | } |
411 | |
412 | static void __exit agp_ali_cleanup(void) |
413 | { |
414 | pci_unregister_driver(dev: &agp_ali_pci_driver); |
415 | } |
416 | |
417 | module_init(agp_ali_init); |
418 | module_exit(agp_ali_cleanup); |
419 | |
420 | MODULE_AUTHOR("Dave Jones" ); |
421 | MODULE_LICENSE("GPL and additional rights" ); |
422 | |
423 | |