ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / char / agp / uninorth-agp.c
1 /*
2  * UniNorth AGPGART routines.
3  */
4 #include <linux/module.h>
5 #include <linux/pci.h>
6 #include <linux/init.h>
7 #include <linux/pagemap.h>
8 #include <linux/agp_backend.h>
9 #include <asm/uninorth.h>
10 #include <asm/pci-bridge.h>
11 #include "agp.h"
12
13 static int uninorth_fetch_size(void)
14 {
15         int i;
16         u32 temp;
17         struct aper_size_info_32 *values;
18
19         pci_read_config_dword(agp_bridge->dev, UNI_N_CFG_GART_BASE, &temp);
20         temp &= ~(0xfffff000);
21         values = A_SIZE_32(agp_bridge->driver->aperture_sizes);
22
23         for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
24                 if (temp == values[i].size_value) {
25                         agp_bridge->previous_size =
26                             agp_bridge->current_size = (void *) (values + i);
27                         agp_bridge->aperture_size_idx = i;
28                         return values[i].size;
29                 }
30         }
31
32         agp_bridge->previous_size =
33             agp_bridge->current_size = (void *) (values + 1);
34         agp_bridge->aperture_size_idx = 1;
35         return values[1].size;
36
37         return 0;
38 }
39
40 static void uninorth_tlbflush(struct agp_memory *mem)
41 {
42         pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
43                         UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_INVAL);
44         pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
45                         UNI_N_CFG_GART_ENABLE);
46         pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
47                         UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_2xRESET);
48         pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
49                         UNI_N_CFG_GART_ENABLE);
50 }
51
52 static void uninorth_cleanup(void)
53 {
54         pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
55                         UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_INVAL);
56         pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
57                         0);
58         pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
59                         UNI_N_CFG_GART_2xRESET);
60         pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
61                         0);
62 }
63
64 static int uninorth_configure(void)
65 {
66         struct aper_size_info_32 *current_size;
67         
68         current_size = A_SIZE_32(agp_bridge->current_size);
69
70         printk(KERN_INFO PFX "configuring for size idx: %d\n",
71                current_size->size_value);
72         
73         /* aperture size and gatt addr */
74         pci_write_config_dword(agp_bridge->dev,
75                 UNI_N_CFG_GART_BASE,
76                 (agp_bridge->gatt_bus_addr & 0xfffff000)
77                         | current_size->size_value);
78
79         /* HACK ALERT
80          * UniNorth seem to be buggy enough not to handle properly when
81          * the AGP aperture isn't mapped at bus physical address 0
82          */
83         agp_bridge->gart_bus_addr = 0;
84         pci_write_config_dword(agp_bridge->dev,
85                 UNI_N_CFG_AGP_BASE, agp_bridge->gart_bus_addr);
86         
87         return 0;
88 }
89
90 static int uninorth_insert_memory(struct agp_memory *mem, off_t pg_start,
91                                 int type)
92 {
93         int i, j, num_entries;
94         void *temp;
95
96         temp = agp_bridge->current_size;
97         num_entries = A_SIZE_32(temp)->num_entries;
98
99         if (type != 0 || mem->type != 0)
100                 /* We know nothing of memory types */
101                 return -EINVAL;
102         if ((pg_start + mem->page_count) > num_entries)
103                 return -EINVAL;
104
105         j = pg_start;
106
107         while (j < (pg_start + mem->page_count)) {
108                 if (!PGE_EMPTY(agp_bridge, agp_bridge->gatt_table[j]))
109                         return -EBUSY;
110                 j++;
111         }
112
113         for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
114                 agp_bridge->gatt_table[j] = cpu_to_le32((mem->memory[i] & 0xfffff000) | 0x00000001UL);
115                 flush_dcache_range((unsigned long)__va(mem->memory[i]),
116                                    (unsigned long)__va(mem->memory[i])+0x1000);
117         }
118         (void)in_le32((volatile u32*)&agp_bridge->gatt_table[pg_start]);
119         mb();
120         flush_dcache_range((unsigned long)&agp_bridge->gatt_table[pg_start], 
121                 (unsigned long)&agp_bridge->gatt_table[pg_start + mem->page_count]);
122
123         uninorth_tlbflush(mem);
124         return 0;
125 }
126
127 static void uninorth_agp_enable(u32 mode)
128 {
129         u32 command, scratch;
130         int timeout;
131
132         pci_read_config_dword(agp_bridge->dev,
133                               agp_bridge->capndx + PCI_AGP_STATUS,
134                               &command);
135
136         command = agp_collect_device_status(mode, command);
137         command |= 0x100;
138
139         uninorth_tlbflush(NULL);
140
141         timeout = 0;
142         do {
143                 pci_write_config_dword(agp_bridge->dev,
144                                        agp_bridge->capndx + PCI_AGP_COMMAND,
145                                        command);
146                 pci_read_config_dword(agp_bridge->dev,
147                                        agp_bridge->capndx + PCI_AGP_COMMAND,
148                                        &scratch);
149         } while ((scratch & 0x100) == 0 && ++timeout < 1000);
150         if ((scratch & 0x100) == 0)
151                 printk(KERN_ERR PFX "failed to write UniNorth AGP command reg\n");
152
153         agp_device_command(command, 0);
154
155         uninorth_tlbflush(NULL);
156 }
157
158 static int uninorth_create_gatt_table(void)
159 {
160         char *table;
161         char *table_end;
162         int size;
163         int page_order;
164         int num_entries;
165         int i;
166         void *temp;
167         struct page *page;
168
169         /* We can't handle 2 level gatt's */
170         if (agp_bridge->driver->size_type == LVL2_APER_SIZE)
171                 return -EINVAL;
172
173         table = NULL;
174         i = agp_bridge->aperture_size_idx;
175         temp = agp_bridge->current_size;
176         size = page_order = num_entries = 0;
177
178         do {
179                 size = A_SIZE_32(temp)->size;
180                 page_order = A_SIZE_32(temp)->page_order;
181                 num_entries = A_SIZE_32(temp)->num_entries;
182
183                 table = (char *) __get_free_pages(GFP_KERNEL, page_order);
184
185                 if (table == NULL) {
186                         i++;
187                         agp_bridge->current_size = A_IDX32(agp_bridge);
188                 } else {
189                         agp_bridge->aperture_size_idx = i;
190                 }
191         } while (!table && (i < agp_bridge->driver->num_aperture_sizes));
192
193         if (table == NULL)
194                 return -ENOMEM;
195
196         table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
197
198         for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
199                 SetPageReserved(page);
200
201         agp_bridge->gatt_table_real = (u32 *) table;
202         agp_bridge->gatt_table = (u32 *)table;
203         agp_bridge->gatt_bus_addr = virt_to_phys(table);
204
205         for (i = 0; i < num_entries; i++) {
206                 agp_bridge->gatt_table[i] =
207                     (unsigned long) agp_bridge->scratch_page;
208         }
209
210         flush_dcache_range((unsigned long)table, (unsigned long)table_end);
211
212         return 0;
213 }
214
215 static int uninorth_free_gatt_table(void)
216 {
217         int page_order;
218         char *table, *table_end;
219         void *temp;
220         struct page *page;
221
222         temp = agp_bridge->current_size;
223         page_order = A_SIZE_32(temp)->page_order;
224
225         /* Do not worry about freeing memory, because if this is
226          * called, then all agp memory is deallocated and removed
227          * from the table.
228          */
229
230         table = (char *) agp_bridge->gatt_table_real;
231         table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
232
233         for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
234                 ClearPageReserved(page);
235
236         free_pages((unsigned long) agp_bridge->gatt_table_real, page_order);
237
238         return 0;
239 }
240
241 void null_cache_flush(void)
242 {
243         mb();
244 }
245
246 /* Setup function */
247
248 static struct aper_size_info_32 uninorth_sizes[7] =
249 {
250 #if 0 /* Not sure uninorth supports that high aperture sizes */
251         {256, 65536, 6, 64},
252         {128, 32768, 5, 32},
253         {64, 16384, 4, 16},
254 #endif  
255         {32, 8192, 3, 8},
256         {16, 4096, 2, 4},
257         {8, 2048, 1, 2},
258         {4, 1024, 0, 1}
259 };
260
261 struct agp_bridge_driver uninorth_agp_driver = {
262         .owner                  = THIS_MODULE,
263         .aperture_sizes         = (void *)uninorth_sizes,
264         .size_type              = U32_APER_SIZE,
265         .num_aperture_sizes     = 4,
266         .configure              = uninorth_configure,
267         .fetch_size             = uninorth_fetch_size,
268         .cleanup                = uninorth_cleanup,
269         .tlb_flush              = uninorth_tlbflush,
270         .mask_memory            = agp_generic_mask_memory,
271         .masks                  = NULL,
272         .cache_flush            = null_cache_flush,
273         .agp_enable             = uninorth_agp_enable,
274         .create_gatt_table      = uninorth_create_gatt_table,
275         .free_gatt_table        = uninorth_free_gatt_table,
276         .insert_memory          = uninorth_insert_memory,
277         .remove_memory          = agp_generic_remove_memory,
278         .alloc_by_type          = agp_generic_alloc_by_type,
279         .free_by_type           = agp_generic_free_by_type,
280         .agp_alloc_page         = agp_generic_alloc_page,
281         .agp_destroy_page       = agp_generic_destroy_page,
282         .cant_use_aperture      = 1,
283 };
284
285 static struct agp_device_ids uninorth_agp_device_ids[] __devinitdata = {
286         {
287                 .device_id      = PCI_DEVICE_ID_APPLE_UNI_N_AGP,
288                 .chipset_name   = "UniNorth",
289         },
290         {
291                 .device_id      = PCI_DEVICE_ID_APPLE_UNI_N_AGP_P,
292                 .chipset_name   = "UniNorth/Pangea",
293         },
294         {
295                 .device_id      = PCI_DEVICE_ID_APPLE_UNI_N_AGP15,
296                 .chipset_name   = "UniNorth 1.5",
297         },
298         {
299                 .device_id      = PCI_DEVICE_ID_APPLE_UNI_N_AGP2,
300                 .chipset_name   = "UniNorth 2",
301         },
302 };
303
304 static int __devinit agp_uninorth_probe(struct pci_dev *pdev,
305                                         const struct pci_device_id *ent)
306 {
307         struct agp_device_ids *devs = uninorth_agp_device_ids;
308         struct agp_bridge_data *bridge;
309         u8 cap_ptr;
310         int j;
311
312         cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
313         if (cap_ptr == 0)
314                 return -ENODEV;
315
316         /* probe for known chipsets */
317         for (j = 0; devs[j].chipset_name != NULL; ++j) {
318                 if (pdev->device == devs[j].device_id) {
319                         printk(KERN_INFO PFX "Detected Apple %s chipset\n",
320                                devs[j].chipset_name);
321                         goto found;
322                 }
323         }
324
325         printk(KERN_ERR PFX "Unsupported Apple chipset (device id: %04x).\n",
326                 pdev->device);
327         return -ENODEV;
328
329  found:
330         bridge = agp_alloc_bridge();
331         if (!bridge)
332                 return -ENOMEM;
333
334         bridge->driver = &uninorth_agp_driver;
335         bridge->dev = pdev;
336         bridge->capndx = cap_ptr;
337
338         /* Fill in the mode register */
339         pci_read_config_dword(pdev, cap_ptr+PCI_AGP_STATUS, &bridge->mode);
340
341         pci_set_drvdata(pdev, bridge);
342         return agp_add_bridge(bridge);
343 }
344
345 static void __devexit agp_uninorth_remove(struct pci_dev *pdev)
346 {
347         struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
348
349         agp_remove_bridge(bridge);
350         agp_put_bridge(bridge);
351 }
352
353 static struct pci_device_id agp_uninorth_pci_table[] = {
354         {
355         .class          = (PCI_CLASS_BRIDGE_HOST << 8),
356         .class_mask     = ~0,
357         .vendor         = PCI_VENDOR_ID_APPLE,
358         .device         = PCI_ANY_ID,
359         .subvendor      = PCI_ANY_ID,
360         .subdevice      = PCI_ANY_ID,
361         },
362         { }
363 };
364
365 MODULE_DEVICE_TABLE(pci, agp_uninorth_pci_table);
366
367 static struct pci_driver agp_uninorth_pci_driver = {
368         .name           = "agpgart-uninorth",
369         .id_table       = agp_uninorth_pci_table,
370         .probe          = agp_uninorth_probe,
371         .remove         = agp_uninorth_remove,
372 };
373
374 static int __init agp_uninorth_init(void)
375 {
376         return pci_module_init(&agp_uninorth_pci_driver);
377 }
378
379 static void __exit agp_uninorth_cleanup(void)
380 {
381         pci_unregister_driver(&agp_uninorth_pci_driver);
382 }
383
384 module_init(agp_uninorth_init);
385 module_exit(agp_uninorth_cleanup);
386
387 MODULE_AUTHOR("Ben Herrenschmidt & Paul Mackerras");
388 MODULE_LICENSE("GPL");