patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / char / agp / sworks-agp.c
1 /*
2  * Serverworks 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 "agp.h"
10
11 #define SVWRKS_COMMAND          0x04
12 #define SVWRKS_APSIZE           0x10
13 #define SVWRKS_MMBASE           0x14
14 #define SVWRKS_CACHING          0x4b
15 #define SVWRKS_AGP_ENABLE       0x60
16 #define SVWRKS_FEATURE          0x68
17
18 #define SVWRKS_SIZE_MASK        0xfe000000
19
20 /* Memory mapped registers */
21 #define SVWRKS_GART_CACHE       0x02
22 #define SVWRKS_GATTBASE         0x04
23 #define SVWRKS_TLBFLUSH         0x10
24 #define SVWRKS_POSTFLUSH        0x14
25 #define SVWRKS_DIRFLUSH         0x0c
26
27
28 struct serverworks_page_map {
29         unsigned long *real;
30         unsigned long *remapped;
31 };
32
33 static struct _serverworks_private {
34         struct pci_dev *svrwrks_dev;    /* device one */
35         volatile u8 *registers;
36         struct serverworks_page_map **gatt_pages;
37         int num_tables;
38         struct serverworks_page_map scratch_dir;
39
40         int gart_addr_ofs;
41         int mm_addr_ofs;
42 } serverworks_private;
43
44 static int serverworks_create_page_map(struct serverworks_page_map *page_map)
45 {
46         int i;
47
48         page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL);
49         if (page_map->real == NULL) {
50                 return -ENOMEM;
51         }
52         SetPageReserved(virt_to_page(page_map->real));
53         global_cache_flush();
54         page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), 
55                                             PAGE_SIZE);
56         if (page_map->remapped == NULL) {
57                 ClearPageReserved(virt_to_page(page_map->real));
58                 free_page((unsigned long) page_map->real);
59                 page_map->real = NULL;
60                 return -ENOMEM;
61         }
62         global_cache_flush();
63
64         for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) {
65                 page_map->remapped[i] = agp_bridge->scratch_page;
66         }
67
68         return 0;
69 }
70
71 static void serverworks_free_page_map(struct serverworks_page_map *page_map)
72 {
73         iounmap(page_map->remapped);
74         ClearPageReserved(virt_to_page(page_map->real));
75         free_page((unsigned long) page_map->real);
76 }
77
78 static void serverworks_free_gatt_pages(void)
79 {
80         int i;
81         struct serverworks_page_map **tables;
82         struct serverworks_page_map *entry;
83
84         tables = serverworks_private.gatt_pages;
85         for(i = 0; i < serverworks_private.num_tables; i++) {
86                 entry = tables[i];
87                 if (entry != NULL) {
88                         if (entry->real != NULL) {
89                                 serverworks_free_page_map(entry);
90                         }
91                         kfree(entry);
92                 }
93         }
94         kfree(tables);
95 }
96
97 static int serverworks_create_gatt_pages(int nr_tables)
98 {
99         struct serverworks_page_map **tables;
100         struct serverworks_page_map *entry;
101         int retval = 0;
102         int i;
103
104         tables = kmalloc((nr_tables + 1) * sizeof(struct serverworks_page_map *), 
105                          GFP_KERNEL);
106         if (tables == NULL) {
107                 return -ENOMEM;
108         }
109         memset(tables, 0, sizeof(struct serverworks_page_map *) * (nr_tables + 1));
110         for (i = 0; i < nr_tables; i++) {
111                 entry = kmalloc(sizeof(struct serverworks_page_map), GFP_KERNEL);
112                 if (entry == NULL) {
113                         retval = -ENOMEM;
114                         break;
115                 }
116                 memset(entry, 0, sizeof(struct serverworks_page_map));
117                 tables[i] = entry;
118                 retval = serverworks_create_page_map(entry);
119                 if (retval != 0) break;
120         }
121         serverworks_private.num_tables = nr_tables;
122         serverworks_private.gatt_pages = tables;
123
124         if (retval != 0) serverworks_free_gatt_pages();
125
126         return retval;
127 }
128
129 #define SVRWRKS_GET_GATT(addr) (serverworks_private.gatt_pages[\
130         GET_PAGE_DIR_IDX(addr)]->remapped)
131
132 #ifndef GET_PAGE_DIR_OFF
133 #define GET_PAGE_DIR_OFF(addr) (addr >> 22)
134 #endif
135
136 #ifndef GET_PAGE_DIR_IDX
137 #define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \
138         GET_PAGE_DIR_OFF(agp_bridge->gart_bus_addr))
139 #endif
140
141 #ifndef GET_GATT_OFF
142 #define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12)
143 #endif
144
145 static int serverworks_create_gatt_table(void)
146 {
147         struct aper_size_info_lvl2 *value;
148         struct serverworks_page_map page_dir;
149         int retval;
150         u32 temp;
151         int i;
152
153         value = A_SIZE_LVL2(agp_bridge->current_size);
154         retval = serverworks_create_page_map(&page_dir);
155         if (retval != 0) {
156                 return retval;
157         }
158         retval = serverworks_create_page_map(&serverworks_private.scratch_dir);
159         if (retval != 0) {
160                 serverworks_free_page_map(&page_dir);
161                 return retval;
162         }
163         /* Create a fake scratch directory */
164         for(i = 0; i < 1024; i++) {
165                 serverworks_private.scratch_dir.remapped[i] = (unsigned long) agp_bridge->scratch_page;
166                 page_dir.remapped[i] =
167                         virt_to_phys(serverworks_private.scratch_dir.real);
168                 page_dir.remapped[i] |= 0x00000001;
169         }
170
171         retval = serverworks_create_gatt_pages(value->num_entries / 1024);
172         if (retval != 0) {
173                 serverworks_free_page_map(&page_dir);
174                 serverworks_free_page_map(&serverworks_private.scratch_dir);
175                 return retval;
176         }
177
178         agp_bridge->gatt_table_real = (u32 *)page_dir.real;
179         agp_bridge->gatt_table = (u32 *)page_dir.remapped;
180         agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real);
181
182         /* Get the address for the gart region.
183          * This is a bus address even on the alpha, b/c its
184          * used to program the agp master not the cpu
185          */
186
187         pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp);
188         agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
189
190         /* Calculate the agp offset */  
191
192         for(i = 0; i < value->num_entries / 1024; i++) {
193                 page_dir.remapped[i] =
194                         virt_to_phys(serverworks_private.gatt_pages[i]->real);
195                 page_dir.remapped[i] |= 0x00000001;
196         }
197
198         return 0;
199 }
200
201 static int serverworks_free_gatt_table(void)
202 {
203         struct serverworks_page_map page_dir;
204    
205         page_dir.real = (unsigned long *)agp_bridge->gatt_table_real;
206         page_dir.remapped = (unsigned long *)agp_bridge->gatt_table;
207
208         serverworks_free_gatt_pages();
209         serverworks_free_page_map(&page_dir);
210         serverworks_free_page_map(&serverworks_private.scratch_dir);
211         return 0;
212 }
213
214 static int serverworks_fetch_size(void)
215 {
216         int i;
217         u32 temp;
218         u32 temp2;
219         struct aper_size_info_lvl2 *values;
220
221         values = A_SIZE_LVL2(agp_bridge->driver->aperture_sizes);
222         pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp);
223         pci_write_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,
224                                         SVWRKS_SIZE_MASK);
225         pci_read_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,&temp2);
226         pci_write_config_dword(agp_bridge->dev,serverworks_private.gart_addr_ofs,temp);
227         temp2 &= SVWRKS_SIZE_MASK;
228
229         for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
230                 if (temp2 == values[i].size_value) {
231                         agp_bridge->previous_size =
232                             agp_bridge->current_size = (void *) (values + i);
233
234                         agp_bridge->aperture_size_idx = i;
235                         return values[i].size;
236                 }
237         }
238
239         return 0;
240 }
241
242 /*
243  * This routine could be implemented by taking the addresses
244  * written to the GATT, and flushing them individually.  However
245  * currently it just flushes the whole table.  Which is probably
246  * more efficent, since agp_memory blocks can be a large number of
247  * entries.
248  */
249 static void serverworks_tlbflush(struct agp_memory *temp)
250 {
251         OUTREG8(serverworks_private.registers, SVWRKS_POSTFLUSH, 1);
252         while(INREG8(serverworks_private.registers, SVWRKS_POSTFLUSH) == 1)
253                 cpu_relax();
254
255         OUTREG32(serverworks_private.registers, SVWRKS_DIRFLUSH, 1);
256         while(INREG32(serverworks_private.registers, SVWRKS_DIRFLUSH) == 1)
257                 cpu_relax();
258 }
259
260 static int serverworks_configure(void)
261 {
262         struct aper_size_info_lvl2 *current_size;
263         u32 temp;
264         u8 enable_reg;
265         u16 cap_reg;
266
267         current_size = A_SIZE_LVL2(agp_bridge->current_size);
268
269         /* Get the memory mapped registers */
270         pci_read_config_dword(agp_bridge->dev, serverworks_private.mm_addr_ofs, &temp);
271         temp = (temp & PCI_BASE_ADDRESS_MEM_MASK);
272         serverworks_private.registers = (volatile u8 *) ioremap(temp, 4096);
273         if (!serverworks_private.registers) {
274                 printk (KERN_ERR PFX "Unable to ioremap() memory.\n");
275                 return -ENOMEM;
276         }
277
278         OUTREG8(serverworks_private.registers, SVWRKS_GART_CACHE, 0x0a);
279
280         OUTREG32(serverworks_private.registers, SVWRKS_GATTBASE, 
281                  agp_bridge->gatt_bus_addr);
282
283         cap_reg = INREG16(serverworks_private.registers, SVWRKS_COMMAND);
284         cap_reg &= ~0x0007;
285         cap_reg |= 0x4;
286         OUTREG16(serverworks_private.registers, SVWRKS_COMMAND, cap_reg);
287
288         pci_read_config_byte(serverworks_private.svrwrks_dev,
289                              SVWRKS_AGP_ENABLE, &enable_reg);
290         enable_reg |= 0x1; /* Agp Enable bit */
291         pci_write_config_byte(serverworks_private.svrwrks_dev,
292                               SVWRKS_AGP_ENABLE, enable_reg);
293         serverworks_tlbflush(NULL);
294
295         agp_bridge->capndx = pci_find_capability(serverworks_private.svrwrks_dev, PCI_CAP_ID_AGP);
296
297         /* Fill in the mode register */
298         pci_read_config_dword(serverworks_private.svrwrks_dev,
299                               agp_bridge->capndx+PCI_AGP_STATUS, &agp_bridge->mode);
300
301         pci_read_config_byte(agp_bridge->dev, SVWRKS_CACHING, &enable_reg);
302         enable_reg &= ~0x3;
303         pci_write_config_byte(agp_bridge->dev, SVWRKS_CACHING, enable_reg);
304
305         pci_read_config_byte(agp_bridge->dev, SVWRKS_FEATURE, &enable_reg);
306         enable_reg |= (1<<6);
307         pci_write_config_byte(agp_bridge->dev,SVWRKS_FEATURE, enable_reg);
308
309         return 0;
310 }
311
312 static void serverworks_cleanup(void)
313 {
314         iounmap((void *) serverworks_private.registers);
315 }
316
317 static int serverworks_insert_memory(struct agp_memory *mem,
318                              off_t pg_start, int type)
319 {
320         int i, j, num_entries;
321         unsigned long *cur_gatt;
322         unsigned long addr;
323
324         num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
325
326         if (type != 0 || mem->type != 0) {
327                 return -EINVAL;
328         }
329         if ((pg_start + mem->page_count) > num_entries) {
330                 return -EINVAL;
331         }
332
333         j = pg_start;
334         while (j < (pg_start + mem->page_count)) {
335                 addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
336                 cur_gatt = SVRWRKS_GET_GATT(addr);
337                 if (!PGE_EMPTY(agp_bridge, cur_gatt[GET_GATT_OFF(addr)])) {
338                         return -EBUSY;
339                 }
340                 j++;
341         }
342
343         if (mem->is_flushed == FALSE) {
344                 global_cache_flush();
345                 mem->is_flushed = TRUE;
346         }
347
348         for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
349                 addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
350                 cur_gatt = SVRWRKS_GET_GATT(addr);
351                 cur_gatt[GET_GATT_OFF(addr)] =
352                         agp_bridge->driver->mask_memory(mem->memory[i], mem->type);
353         }
354         serverworks_tlbflush(mem);
355         return 0;
356 }
357
358 static int serverworks_remove_memory(struct agp_memory *mem, off_t pg_start,
359                              int type)
360 {
361         int i;
362         unsigned long *cur_gatt;
363         unsigned long addr;
364
365         if (type != 0 || mem->type != 0) {
366                 return -EINVAL;
367         }
368
369         global_cache_flush();
370         serverworks_tlbflush(mem);
371
372         for (i = pg_start; i < (mem->page_count + pg_start); i++) {
373                 addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr;
374                 cur_gatt = SVRWRKS_GET_GATT(addr);
375                 cur_gatt[GET_GATT_OFF(addr)] = 
376                         (unsigned long) agp_bridge->scratch_page;
377         }
378
379         serverworks_tlbflush(mem);
380         return 0;
381 }
382
383 static struct gatt_mask serverworks_masks[] =
384 {
385         {.mask = 1, .type = 0}
386 };
387
388 static struct aper_size_info_lvl2 serverworks_sizes[7] =
389 {
390         {2048, 524288, 0x80000000},
391         {1024, 262144, 0xc0000000},
392         {512, 131072, 0xe0000000},
393         {256, 65536, 0xf0000000},
394         {128, 32768, 0xf8000000},
395         {64, 16384, 0xfc000000},
396         {32, 8192, 0xfe000000}
397 };
398
399 static void serverworks_agp_enable(u32 mode)
400 {
401         u32 command;
402
403         pci_read_config_dword(serverworks_private.svrwrks_dev,
404                               agp_bridge->capndx + PCI_AGP_STATUS,
405                               &command);
406
407         command = agp_collect_device_status(mode, command);
408
409         command &= ~0x10;       /* disable FW */
410         command &= ~0x08;
411
412         command |= 0x100;
413
414         pci_write_config_dword(serverworks_private.svrwrks_dev,
415                                agp_bridge->capndx + PCI_AGP_COMMAND,
416                                command);
417
418         agp_device_command(command, 0);
419 }
420
421 struct agp_bridge_driver sworks_driver = {
422         .owner                  = THIS_MODULE,
423         .aperture_sizes         = serverworks_sizes,
424         .size_type              = LVL2_APER_SIZE,
425         .num_aperture_sizes     = 7,
426         .configure              = serverworks_configure,
427         .fetch_size             = serverworks_fetch_size,
428         .cleanup                = serverworks_cleanup,
429         .tlb_flush              = serverworks_tlbflush,
430         .mask_memory            = agp_generic_mask_memory,
431         .masks                  = serverworks_masks,
432         .agp_enable             = serverworks_agp_enable,
433         .cache_flush            = global_cache_flush,
434         .create_gatt_table      = serverworks_create_gatt_table,
435         .free_gatt_table        = serverworks_free_gatt_table,
436         .insert_memory          = serverworks_insert_memory,
437         .remove_memory          = serverworks_remove_memory,
438         .alloc_by_type          = agp_generic_alloc_by_type,
439         .free_by_type           = agp_generic_free_by_type,
440         .agp_alloc_page         = agp_generic_alloc_page,
441         .agp_destroy_page       = agp_generic_destroy_page,
442 };
443
444 static int __devinit agp_serverworks_probe(struct pci_dev *pdev,
445                                            const struct pci_device_id *ent)
446 {
447         struct agp_bridge_data *bridge;
448         struct pci_dev *bridge_dev;
449         u32 temp, temp2;
450
451         /* Everything is on func 1 here so we are hardcoding function one */
452         bridge_dev = pci_find_slot((unsigned int)pdev->bus->number,
453                         PCI_DEVFN(0, 1));
454         if (!bridge_dev) {
455                 printk(KERN_INFO PFX "Detected a Serverworks chipset "
456                        "but could not find the secondary device.\n");
457                 return -ENODEV;
458         }
459
460         switch (pdev->device) {
461         case 0x0006:
462                 /* ServerWorks CNB20HE
463                 Fail silently.*/
464                 printk (KERN_ERR PFX "Detected ServerWorks CNB20HE chipset: No AGP present.\n");
465                 return -ENODEV;
466
467         case PCI_DEVICE_ID_SERVERWORKS_HE:
468         case PCI_DEVICE_ID_SERVERWORKS_LE:
469         case 0x0007:
470                 break;
471
472         default:
473                 printk(KERN_ERR PFX "Unsupported Serverworks chipset "
474                                 "(device id: %04x)\n", pdev->device);
475                 return -ENODEV;
476         }
477
478         serverworks_private.svrwrks_dev = bridge_dev;
479         serverworks_private.gart_addr_ofs = 0x10;
480         
481         pci_read_config_dword(pdev, SVWRKS_APSIZE, &temp);
482         if (temp & PCI_BASE_ADDRESS_MEM_TYPE_64) {
483                 pci_read_config_dword(pdev, SVWRKS_APSIZE + 4, &temp2);
484                 if (temp2 != 0) {
485                         printk(KERN_INFO PFX "Detected 64 bit aperture address, "
486                                "but top bits are not zero.  Disabling agp\n");
487                         return -ENODEV;
488                 }
489                 serverworks_private.mm_addr_ofs = 0x18;
490         } else
491                 serverworks_private.mm_addr_ofs = 0x14;
492
493         pci_read_config_dword(pdev, serverworks_private.mm_addr_ofs, &temp);
494         if (temp & PCI_BASE_ADDRESS_MEM_TYPE_64) {
495                 pci_read_config_dword(pdev,
496                                 serverworks_private.mm_addr_ofs + 4, &temp2);
497                 if (temp2 != 0) {
498                         printk(KERN_INFO PFX "Detected 64 bit MMIO address, "
499                                "but top bits are not zero.  Disabling agp\n");
500                         return -ENODEV;
501                 }
502         }
503
504         bridge = agp_alloc_bridge();
505         if (!bridge)
506                 return -ENOMEM;
507
508         bridge->driver = &sworks_driver;
509         bridge->dev_private_data = &serverworks_private,
510         bridge->dev = pdev;
511
512         pci_set_drvdata(pdev, bridge);
513         return agp_add_bridge(bridge);
514 }
515
516 static void __devexit agp_serverworks_remove(struct pci_dev *pdev)
517 {
518         struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
519
520         agp_remove_bridge(bridge);
521         agp_put_bridge(bridge);
522 }
523
524 static struct pci_device_id agp_serverworks_pci_table[] = {
525         {
526         .class          = (PCI_CLASS_BRIDGE_HOST << 8),
527         .class_mask     = ~0,
528         .vendor         = PCI_VENDOR_ID_SERVERWORKS,
529         .device         = PCI_ANY_ID,
530         .subvendor      = PCI_ANY_ID,
531         .subdevice      = PCI_ANY_ID,
532         },
533         { }
534 };
535
536 MODULE_DEVICE_TABLE(pci, agp_serverworks_pci_table);
537
538 static struct pci_driver agp_serverworks_pci_driver = {
539         .name           = "agpgart-serverworks",
540         .id_table       = agp_serverworks_pci_table,
541         .probe          = agp_serverworks_probe,
542         .remove         = agp_serverworks_remove,
543 };
544
545 static int __init agp_serverworks_init(void)
546 {
547         return pci_module_init(&agp_serverworks_pci_driver);
548 }
549
550 static void __exit agp_serverworks_cleanup(void)
551 {
552         pci_unregister_driver(&agp_serverworks_pci_driver);
553 }
554
555 module_init(agp_serverworks_init);
556 module_exit(agp_serverworks_cleanup);
557
558 MODULE_LICENSE("GPL and additional rights");
559