1 | #include <linux/module.h> |
2 | #include <linux/pci.h> |
3 | #include <linux/init.h> |
4 | #include <linux/agp_backend.h> |
5 | #include <linux/mm.h> |
6 | #include <linux/slab.h> |
7 | |
8 | #include <asm/machvec.h> |
9 | #include <asm/agp_backend.h> |
10 | #include "../../../arch/alpha/kernel/pci_impl.h" |
11 | |
12 | #include "agp.h" |
13 | |
14 | static vm_fault_t alpha_core_agp_vm_fault(struct vm_fault *vmf) |
15 | { |
16 | alpha_agp_info *agp = agp_bridge->dev_private_data; |
17 | dma_addr_t dma_addr; |
18 | unsigned long pa; |
19 | struct page *page; |
20 | |
21 | dma_addr = vmf->address - vmf->vma->vm_start + agp->aperture.bus_base; |
22 | pa = agp->ops->translate(agp, dma_addr); |
23 | |
24 | if (pa == (unsigned long)-EINVAL) |
25 | return VM_FAULT_SIGBUS; /* no translation */ |
26 | |
27 | /* |
28 | * Get the page, inc the use count, and return it |
29 | */ |
30 | page = virt_to_page(__va(pa)); |
31 | get_page(page); |
32 | vmf->page = page; |
33 | return 0; |
34 | } |
35 | |
36 | static struct aper_size_info_fixed alpha_core_agp_sizes[] = |
37 | { |
38 | { 0, 0, 0 }, /* filled in by alpha_core_agp_setup */ |
39 | }; |
40 | |
41 | static const struct vm_operations_struct alpha_core_agp_vm_ops = { |
42 | .fault = alpha_core_agp_vm_fault, |
43 | }; |
44 | |
45 | |
46 | static int alpha_core_agp_fetch_size(void) |
47 | { |
48 | return alpha_core_agp_sizes[0].size; |
49 | } |
50 | |
51 | static int alpha_core_agp_configure(void) |
52 | { |
53 | alpha_agp_info *agp = agp_bridge->dev_private_data; |
54 | agp_bridge->gart_bus_addr = agp->aperture.bus_base; |
55 | return 0; |
56 | } |
57 | |
58 | static void alpha_core_agp_cleanup(void) |
59 | { |
60 | alpha_agp_info *agp = agp_bridge->dev_private_data; |
61 | |
62 | agp->ops->cleanup(agp); |
63 | } |
64 | |
65 | static void alpha_core_agp_tlbflush(struct agp_memory *mem) |
66 | { |
67 | alpha_agp_info *agp = agp_bridge->dev_private_data; |
68 | alpha_mv.mv_pci_tbi(agp->hose, 0, -1); |
69 | } |
70 | |
71 | static void alpha_core_agp_enable(struct agp_bridge_data *bridge, u32 mode) |
72 | { |
73 | alpha_agp_info *agp = bridge->dev_private_data; |
74 | |
75 | agp->mode.lw = agp_collect_device_status(bridge, mode, |
76 | agp->capability.lw); |
77 | |
78 | agp->mode.bits.enable = 1; |
79 | agp->ops->configure(agp); |
80 | |
81 | agp_device_command(command: agp->mode.lw, agp_v3: false); |
82 | } |
83 | |
84 | static int alpha_core_agp_insert_memory(struct agp_memory *mem, off_t pg_start, |
85 | int type) |
86 | { |
87 | alpha_agp_info *agp = agp_bridge->dev_private_data; |
88 | int num_entries, status; |
89 | void *temp; |
90 | |
91 | if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES) |
92 | return -EINVAL; |
93 | |
94 | temp = agp_bridge->current_size; |
95 | num_entries = A_SIZE_FIX(temp)->num_entries; |
96 | if ((pg_start + mem->page_count) > num_entries) |
97 | return -EINVAL; |
98 | |
99 | status = agp->ops->bind(agp, pg_start, mem); |
100 | mb(); |
101 | alpha_core_agp_tlbflush(mem); |
102 | |
103 | return status; |
104 | } |
105 | |
106 | static int alpha_core_agp_remove_memory(struct agp_memory *mem, off_t pg_start, |
107 | int type) |
108 | { |
109 | alpha_agp_info *agp = agp_bridge->dev_private_data; |
110 | int status; |
111 | |
112 | status = agp->ops->unbind(agp, pg_start, mem); |
113 | alpha_core_agp_tlbflush(mem); |
114 | return status; |
115 | } |
116 | |
117 | static int alpha_core_agp_create_free_gatt_table(struct agp_bridge_data *a) |
118 | { |
119 | return 0; |
120 | } |
121 | |
122 | struct agp_bridge_driver alpha_core_agp_driver = { |
123 | .owner = THIS_MODULE, |
124 | .aperture_sizes = alpha_core_agp_sizes, |
125 | .num_aperture_sizes = 1, |
126 | .size_type = FIXED_APER_SIZE, |
127 | .cant_use_aperture = true, |
128 | .masks = NULL, |
129 | |
130 | .fetch_size = alpha_core_agp_fetch_size, |
131 | .configure = alpha_core_agp_configure, |
132 | .agp_enable = alpha_core_agp_enable, |
133 | .cleanup = alpha_core_agp_cleanup, |
134 | .tlb_flush = alpha_core_agp_tlbflush, |
135 | .mask_memory = agp_generic_mask_memory, |
136 | .cache_flush = global_cache_flush, |
137 | .create_gatt_table = alpha_core_agp_create_free_gatt_table, |
138 | .free_gatt_table = alpha_core_agp_create_free_gatt_table, |
139 | .insert_memory = alpha_core_agp_insert_memory, |
140 | .remove_memory = alpha_core_agp_remove_memory, |
141 | .alloc_by_type = agp_generic_alloc_by_type, |
142 | .free_by_type = agp_generic_free_by_type, |
143 | .agp_alloc_page = agp_generic_alloc_page, |
144 | .agp_alloc_pages = agp_generic_alloc_pages, |
145 | .agp_destroy_page = agp_generic_destroy_page, |
146 | .agp_destroy_pages = agp_generic_destroy_pages, |
147 | .agp_type_to_mask_type = agp_generic_type_to_mask_type, |
148 | }; |
149 | |
150 | struct agp_bridge_data *alpha_bridge; |
151 | |
152 | int __init |
153 | alpha_core_agp_setup(void) |
154 | { |
155 | alpha_agp_info *agp = alpha_mv.agp_info(); |
156 | struct pci_dev *pdev; /* faked */ |
157 | struct aper_size_info_fixed *aper_size; |
158 | |
159 | if (!agp) |
160 | return -ENODEV; |
161 | if (agp->ops->setup(agp)) |
162 | return -ENODEV; |
163 | |
164 | /* |
165 | * Build the aperture size descriptor |
166 | */ |
167 | aper_size = alpha_core_agp_sizes; |
168 | aper_size->size = agp->aperture.size / (1024 * 1024); |
169 | aper_size->num_entries = agp->aperture.size / PAGE_SIZE; |
170 | aper_size->page_order = __ffs(aper_size->num_entries / 1024); |
171 | |
172 | /* |
173 | * Build a fake pci_dev struct |
174 | */ |
175 | pdev = pci_alloc_dev(NULL); |
176 | if (!pdev) |
177 | return -ENOMEM; |
178 | pdev->vendor = 0xffff; |
179 | pdev->device = 0xffff; |
180 | pdev->sysdata = agp->hose; |
181 | |
182 | alpha_bridge = agp_alloc_bridge(); |
183 | if (!alpha_bridge) |
184 | goto fail; |
185 | |
186 | alpha_bridge->driver = &alpha_core_agp_driver; |
187 | alpha_bridge->vm_ops = &alpha_core_agp_vm_ops; |
188 | alpha_bridge->current_size = aper_size; /* only 1 size */ |
189 | alpha_bridge->dev_private_data = agp; |
190 | alpha_bridge->dev = pdev; |
191 | alpha_bridge->mode = agp->capability.lw; |
192 | |
193 | printk(KERN_INFO PFX "Detected AGP on hose %d\n" , agp->hose->index); |
194 | return agp_add_bridge(bridge: alpha_bridge); |
195 | |
196 | fail: |
197 | kfree(objp: pdev); |
198 | return -ENOMEM; |
199 | } |
200 | |
201 | static int __init agp_alpha_core_init(void) |
202 | { |
203 | if (agp_off) |
204 | return -EINVAL; |
205 | if (alpha_mv.agp_info) |
206 | return alpha_core_agp_setup(); |
207 | return -ENODEV; |
208 | } |
209 | |
210 | static void __exit agp_alpha_core_cleanup(void) |
211 | { |
212 | agp_remove_bridge(bridge: alpha_bridge); |
213 | agp_put_bridge(bridge: alpha_bridge); |
214 | } |
215 | |
216 | module_init(agp_alpha_core_init); |
217 | module_exit(agp_alpha_core_cleanup); |
218 | |
219 | MODULE_AUTHOR("Jeff Wiedemeier <Jeff.Wiedemeier@hp.com>" ); |
220 | MODULE_LICENSE("GPL and additional rights" ); |
221 | |