diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/agp/hp-agp.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/agp/hp-agp.c')
-rw-r--r-- | drivers/char/agp/hp-agp.c | 552 |
1 files changed, 552 insertions, 0 deletions
diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c new file mode 100644 index 000000000000..6052bfa04c72 --- /dev/null +++ b/drivers/char/agp/hp-agp.c | |||
@@ -0,0 +1,552 @@ | |||
1 | /* | ||
2 | * HP zx1 AGPGART routines. | ||
3 | * | ||
4 | * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P. | ||
5 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/acpi.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/pci.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/agp_backend.h> | ||
17 | |||
18 | #include <asm/acpi-ext.h> | ||
19 | |||
20 | #include "agp.h" | ||
21 | |||
22 | #ifndef log2 | ||
23 | #define log2(x) ffz(~(x)) | ||
24 | #endif | ||
25 | |||
26 | #define HP_ZX1_IOC_OFFSET 0x1000 /* ACPI reports SBA, we want IOC */ | ||
27 | |||
28 | /* HP ZX1 IOC registers */ | ||
29 | #define HP_ZX1_IBASE 0x300 | ||
30 | #define HP_ZX1_IMASK 0x308 | ||
31 | #define HP_ZX1_PCOM 0x310 | ||
32 | #define HP_ZX1_TCNFG 0x318 | ||
33 | #define HP_ZX1_PDIR_BASE 0x320 | ||
34 | |||
35 | #define HP_ZX1_IOVA_BASE GB(1UL) | ||
36 | #define HP_ZX1_IOVA_SIZE GB(1UL) | ||
37 | #define HP_ZX1_GART_SIZE (HP_ZX1_IOVA_SIZE / 2) | ||
38 | #define HP_ZX1_SBA_IOMMU_COOKIE 0x0000badbadc0ffeeUL | ||
39 | |||
40 | #define HP_ZX1_PDIR_VALID_BIT 0x8000000000000000UL | ||
41 | #define HP_ZX1_IOVA_TO_PDIR(va) ((va - hp_private.iova_base) >> hp_private.io_tlb_shift) | ||
42 | |||
43 | #define AGP8X_MODE_BIT 3 | ||
44 | #define AGP8X_MODE (1 << AGP8X_MODE_BIT) | ||
45 | |||
46 | /* AGP bridge need not be PCI device, but DRM thinks it is. */ | ||
47 | static struct pci_dev fake_bridge_dev; | ||
48 | |||
49 | static int hp_zx1_gart_found; | ||
50 | |||
51 | static struct aper_size_info_fixed hp_zx1_sizes[] = | ||
52 | { | ||
53 | {0, 0, 0}, /* filled in by hp_zx1_fetch_size() */ | ||
54 | }; | ||
55 | |||
56 | static struct gatt_mask hp_zx1_masks[] = | ||
57 | { | ||
58 | {.mask = HP_ZX1_PDIR_VALID_BIT, .type = 0} | ||
59 | }; | ||
60 | |||
61 | static struct _hp_private { | ||
62 | volatile u8 __iomem *ioc_regs; | ||
63 | volatile u8 __iomem *lba_regs; | ||
64 | int lba_cap_offset; | ||
65 | u64 *io_pdir; // PDIR for entire IOVA | ||
66 | u64 *gatt; // PDIR just for GART (subset of above) | ||
67 | u64 gatt_entries; | ||
68 | u64 iova_base; | ||
69 | u64 gart_base; | ||
70 | u64 gart_size; | ||
71 | u64 io_pdir_size; | ||
72 | int io_pdir_owner; // do we own it, or share it with sba_iommu? | ||
73 | int io_page_size; | ||
74 | int io_tlb_shift; | ||
75 | int io_tlb_ps; // IOC ps config | ||
76 | int io_pages_per_kpage; | ||
77 | } hp_private; | ||
78 | |||
79 | static int __init hp_zx1_ioc_shared(void) | ||
80 | { | ||
81 | struct _hp_private *hp = &hp_private; | ||
82 | |||
83 | printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR shared with sba_iommu\n"); | ||
84 | |||
85 | /* | ||
86 | * IOC already configured by sba_iommu module; just use | ||
87 | * its setup. We assume: | ||
88 | * - IOVA space is 1Gb in size | ||
89 | * - first 512Mb is IOMMU, second 512Mb is GART | ||
90 | */ | ||
91 | hp->io_tlb_ps = readq(hp->ioc_regs+HP_ZX1_TCNFG); | ||
92 | switch (hp->io_tlb_ps) { | ||
93 | case 0: hp->io_tlb_shift = 12; break; | ||
94 | case 1: hp->io_tlb_shift = 13; break; | ||
95 | case 2: hp->io_tlb_shift = 14; break; | ||
96 | case 3: hp->io_tlb_shift = 16; break; | ||
97 | default: | ||
98 | printk(KERN_ERR PFX "Invalid IOTLB page size " | ||
99 | "configuration 0x%x\n", hp->io_tlb_ps); | ||
100 | hp->gatt = NULL; | ||
101 | hp->gatt_entries = 0; | ||
102 | return -ENODEV; | ||
103 | } | ||
104 | hp->io_page_size = 1 << hp->io_tlb_shift; | ||
105 | hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size; | ||
106 | |||
107 | hp->iova_base = readq(hp->ioc_regs+HP_ZX1_IBASE) & ~0x1; | ||
108 | hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - HP_ZX1_GART_SIZE; | ||
109 | |||
110 | hp->gart_size = HP_ZX1_GART_SIZE; | ||
111 | hp->gatt_entries = hp->gart_size / hp->io_page_size; | ||
112 | |||
113 | hp->io_pdir = phys_to_virt(readq(hp->ioc_regs+HP_ZX1_PDIR_BASE)); | ||
114 | hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; | ||
115 | |||
116 | if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) { | ||
117 | /* Normal case when no AGP device in system */ | ||
118 | hp->gatt = NULL; | ||
119 | hp->gatt_entries = 0; | ||
120 | printk(KERN_ERR PFX "No reserved IO PDIR entry found; " | ||
121 | "GART disabled\n"); | ||
122 | return -ENODEV; | ||
123 | } | ||
124 | |||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int __init | ||
129 | hp_zx1_ioc_owner (void) | ||
130 | { | ||
131 | struct _hp_private *hp = &hp_private; | ||
132 | |||
133 | printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR dedicated to GART\n"); | ||
134 | |||
135 | /* | ||
136 | * Select an IOV page size no larger than system page size. | ||
137 | */ | ||
138 | if (PAGE_SIZE >= KB(64)) { | ||
139 | hp->io_tlb_shift = 16; | ||
140 | hp->io_tlb_ps = 3; | ||
141 | } else if (PAGE_SIZE >= KB(16)) { | ||
142 | hp->io_tlb_shift = 14; | ||
143 | hp->io_tlb_ps = 2; | ||
144 | } else if (PAGE_SIZE >= KB(8)) { | ||
145 | hp->io_tlb_shift = 13; | ||
146 | hp->io_tlb_ps = 1; | ||
147 | } else { | ||
148 | hp->io_tlb_shift = 12; | ||
149 | hp->io_tlb_ps = 0; | ||
150 | } | ||
151 | hp->io_page_size = 1 << hp->io_tlb_shift; | ||
152 | hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size; | ||
153 | |||
154 | hp->iova_base = HP_ZX1_IOVA_BASE; | ||
155 | hp->gart_size = HP_ZX1_GART_SIZE; | ||
156 | hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - hp->gart_size; | ||
157 | |||
158 | hp->gatt_entries = hp->gart_size / hp->io_page_size; | ||
159 | hp->io_pdir_size = (HP_ZX1_IOVA_SIZE / hp->io_page_size) * sizeof(u64); | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int __init | ||
165 | hp_zx1_ioc_init (u64 hpa) | ||
166 | { | ||
167 | struct _hp_private *hp = &hp_private; | ||
168 | |||
169 | hp->ioc_regs = ioremap(hpa, 1024); | ||
170 | if (!hp->ioc_regs) | ||
171 | return -ENOMEM; | ||
172 | |||
173 | /* | ||
174 | * If the IOTLB is currently disabled, we can take it over. | ||
175 | * Otherwise, we have to share with sba_iommu. | ||
176 | */ | ||
177 | hp->io_pdir_owner = (readq(hp->ioc_regs+HP_ZX1_IBASE) & 0x1) == 0; | ||
178 | |||
179 | if (hp->io_pdir_owner) | ||
180 | return hp_zx1_ioc_owner(); | ||
181 | |||
182 | return hp_zx1_ioc_shared(); | ||
183 | } | ||
184 | |||
185 | static int | ||
186 | hp_zx1_lba_find_capability (volatile u8 __iomem *hpa, int cap) | ||
187 | { | ||
188 | u16 status; | ||
189 | u8 pos, id; | ||
190 | int ttl = 48; | ||
191 | |||
192 | status = readw(hpa+PCI_STATUS); | ||
193 | if (!(status & PCI_STATUS_CAP_LIST)) | ||
194 | return 0; | ||
195 | pos = readb(hpa+PCI_CAPABILITY_LIST); | ||
196 | while (ttl-- && pos >= 0x40) { | ||
197 | pos &= ~3; | ||
198 | id = readb(hpa+pos+PCI_CAP_LIST_ID); | ||
199 | if (id == 0xff) | ||
200 | break; | ||
201 | if (id == cap) | ||
202 | return pos; | ||
203 | pos = readb(hpa+pos+PCI_CAP_LIST_NEXT); | ||
204 | } | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int __init | ||
209 | hp_zx1_lba_init (u64 hpa) | ||
210 | { | ||
211 | struct _hp_private *hp = &hp_private; | ||
212 | int cap; | ||
213 | |||
214 | hp->lba_regs = ioremap(hpa, 256); | ||
215 | if (!hp->lba_regs) | ||
216 | return -ENOMEM; | ||
217 | |||
218 | hp->lba_cap_offset = hp_zx1_lba_find_capability(hp->lba_regs, PCI_CAP_ID_AGP); | ||
219 | |||
220 | cap = readl(hp->lba_regs+hp->lba_cap_offset) & 0xff; | ||
221 | if (cap != PCI_CAP_ID_AGP) { | ||
222 | printk(KERN_ERR PFX "Invalid capability ID 0x%02x at 0x%x\n", | ||
223 | cap, hp->lba_cap_offset); | ||
224 | return -ENODEV; | ||
225 | } | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static int | ||
231 | hp_zx1_fetch_size(void) | ||
232 | { | ||
233 | int size; | ||
234 | |||
235 | size = hp_private.gart_size / MB(1); | ||
236 | hp_zx1_sizes[0].size = size; | ||
237 | agp_bridge->current_size = (void *) &hp_zx1_sizes[0]; | ||
238 | return size; | ||
239 | } | ||
240 | |||
241 | static int | ||
242 | hp_zx1_configure (void) | ||
243 | { | ||
244 | struct _hp_private *hp = &hp_private; | ||
245 | |||
246 | agp_bridge->gart_bus_addr = hp->gart_base; | ||
247 | agp_bridge->capndx = hp->lba_cap_offset; | ||
248 | agp_bridge->mode = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS); | ||
249 | |||
250 | if (hp->io_pdir_owner) { | ||
251 | writel(virt_to_phys(hp->io_pdir), hp->ioc_regs+HP_ZX1_PDIR_BASE); | ||
252 | readl(hp->ioc_regs+HP_ZX1_PDIR_BASE); | ||
253 | writel(hp->io_tlb_ps, hp->ioc_regs+HP_ZX1_TCNFG); | ||
254 | readl(hp->ioc_regs+HP_ZX1_TCNFG); | ||
255 | writel(~(HP_ZX1_IOVA_SIZE-1), hp->ioc_regs+HP_ZX1_IMASK); | ||
256 | readl(hp->ioc_regs+HP_ZX1_IMASK); | ||
257 | writel(hp->iova_base|1, hp->ioc_regs+HP_ZX1_IBASE); | ||
258 | readl(hp->ioc_regs+HP_ZX1_IBASE); | ||
259 | writel(hp->iova_base|log2(HP_ZX1_IOVA_SIZE), hp->ioc_regs+HP_ZX1_PCOM); | ||
260 | readl(hp->ioc_regs+HP_ZX1_PCOM); | ||
261 | } | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static void | ||
267 | hp_zx1_cleanup (void) | ||
268 | { | ||
269 | struct _hp_private *hp = &hp_private; | ||
270 | |||
271 | if (hp->ioc_regs) { | ||
272 | if (hp->io_pdir_owner) { | ||
273 | writeq(0, hp->ioc_regs+HP_ZX1_IBASE); | ||
274 | readq(hp->ioc_regs+HP_ZX1_IBASE); | ||
275 | } | ||
276 | iounmap(hp->ioc_regs); | ||
277 | } | ||
278 | if (hp->lba_regs) | ||
279 | iounmap(hp->lba_regs); | ||
280 | } | ||
281 | |||
282 | static void | ||
283 | hp_zx1_tlbflush (struct agp_memory *mem) | ||
284 | { | ||
285 | struct _hp_private *hp = &hp_private; | ||
286 | |||
287 | writeq(hp->gart_base | log2(hp->gart_size), hp->ioc_regs+HP_ZX1_PCOM); | ||
288 | readq(hp->ioc_regs+HP_ZX1_PCOM); | ||
289 | } | ||
290 | |||
291 | static int | ||
292 | hp_zx1_create_gatt_table (struct agp_bridge_data *bridge) | ||
293 | { | ||
294 | struct _hp_private *hp = &hp_private; | ||
295 | int i; | ||
296 | |||
297 | if (hp->io_pdir_owner) { | ||
298 | hp->io_pdir = (u64 *) __get_free_pages(GFP_KERNEL, | ||
299 | get_order(hp->io_pdir_size)); | ||
300 | if (!hp->io_pdir) { | ||
301 | printk(KERN_ERR PFX "Couldn't allocate contiguous " | ||
302 | "memory for I/O PDIR\n"); | ||
303 | hp->gatt = NULL; | ||
304 | hp->gatt_entries = 0; | ||
305 | return -ENOMEM; | ||
306 | } | ||
307 | memset(hp->io_pdir, 0, hp->io_pdir_size); | ||
308 | |||
309 | hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; | ||
310 | } | ||
311 | |||
312 | for (i = 0; i < hp->gatt_entries; i++) { | ||
313 | hp->gatt[i] = (unsigned long) agp_bridge->scratch_page; | ||
314 | } | ||
315 | |||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static int | ||
320 | hp_zx1_free_gatt_table (struct agp_bridge_data *bridge) | ||
321 | { | ||
322 | struct _hp_private *hp = &hp_private; | ||
323 | |||
324 | if (hp->io_pdir_owner) | ||
325 | free_pages((unsigned long) hp->io_pdir, | ||
326 | get_order(hp->io_pdir_size)); | ||
327 | else | ||
328 | hp->gatt[0] = HP_ZX1_SBA_IOMMU_COOKIE; | ||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static int | ||
333 | hp_zx1_insert_memory (struct agp_memory *mem, off_t pg_start, int type) | ||
334 | { | ||
335 | struct _hp_private *hp = &hp_private; | ||
336 | int i, k; | ||
337 | off_t j, io_pg_start; | ||
338 | int io_pg_count; | ||
339 | |||
340 | if (type != 0 || mem->type != 0) { | ||
341 | return -EINVAL; | ||
342 | } | ||
343 | |||
344 | io_pg_start = hp->io_pages_per_kpage * pg_start; | ||
345 | io_pg_count = hp->io_pages_per_kpage * mem->page_count; | ||
346 | if ((io_pg_start + io_pg_count) > hp->gatt_entries) { | ||
347 | return -EINVAL; | ||
348 | } | ||
349 | |||
350 | j = io_pg_start; | ||
351 | while (j < (io_pg_start + io_pg_count)) { | ||
352 | if (hp->gatt[j]) { | ||
353 | return -EBUSY; | ||
354 | } | ||
355 | j++; | ||
356 | } | ||
357 | |||
358 | if (mem->is_flushed == FALSE) { | ||
359 | global_cache_flush(); | ||
360 | mem->is_flushed = TRUE; | ||
361 | } | ||
362 | |||
363 | for (i = 0, j = io_pg_start; i < mem->page_count; i++) { | ||
364 | unsigned long paddr; | ||
365 | |||
366 | paddr = mem->memory[i]; | ||
367 | for (k = 0; | ||
368 | k < hp->io_pages_per_kpage; | ||
369 | k++, j++, paddr += hp->io_page_size) { | ||
370 | hp->gatt[j] = | ||
371 | agp_bridge->driver->mask_memory(agp_bridge, | ||
372 | paddr, type); | ||
373 | } | ||
374 | } | ||
375 | |||
376 | agp_bridge->driver->tlb_flush(mem); | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static int | ||
381 | hp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type) | ||
382 | { | ||
383 | struct _hp_private *hp = &hp_private; | ||
384 | int i, io_pg_start, io_pg_count; | ||
385 | |||
386 | if (type != 0 || mem->type != 0) { | ||
387 | return -EINVAL; | ||
388 | } | ||
389 | |||
390 | io_pg_start = hp->io_pages_per_kpage * pg_start; | ||
391 | io_pg_count = hp->io_pages_per_kpage * mem->page_count; | ||
392 | for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) { | ||
393 | hp->gatt[i] = agp_bridge->scratch_page; | ||
394 | } | ||
395 | |||
396 | agp_bridge->driver->tlb_flush(mem); | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | static unsigned long | ||
401 | hp_zx1_mask_memory (struct agp_bridge_data *bridge, | ||
402 | unsigned long addr, int type) | ||
403 | { | ||
404 | return HP_ZX1_PDIR_VALID_BIT | addr; | ||
405 | } | ||
406 | |||
407 | static void | ||
408 | hp_zx1_enable (struct agp_bridge_data *bridge, u32 mode) | ||
409 | { | ||
410 | struct _hp_private *hp = &hp_private; | ||
411 | u32 command; | ||
412 | |||
413 | command = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS); | ||
414 | command = agp_collect_device_status(bridge, mode, command); | ||
415 | command |= 0x00000100; | ||
416 | |||
417 | writel(command, hp->lba_regs+hp->lba_cap_offset+PCI_AGP_COMMAND); | ||
418 | |||
419 | agp_device_command(command, (mode & AGP8X_MODE) != 0); | ||
420 | } | ||
421 | |||
422 | struct agp_bridge_driver hp_zx1_driver = { | ||
423 | .owner = THIS_MODULE, | ||
424 | .size_type = FIXED_APER_SIZE, | ||
425 | .configure = hp_zx1_configure, | ||
426 | .fetch_size = hp_zx1_fetch_size, | ||
427 | .cleanup = hp_zx1_cleanup, | ||
428 | .tlb_flush = hp_zx1_tlbflush, | ||
429 | .mask_memory = hp_zx1_mask_memory, | ||
430 | .masks = hp_zx1_masks, | ||
431 | .agp_enable = hp_zx1_enable, | ||
432 | .cache_flush = global_cache_flush, | ||
433 | .create_gatt_table = hp_zx1_create_gatt_table, | ||
434 | .free_gatt_table = hp_zx1_free_gatt_table, | ||
435 | .insert_memory = hp_zx1_insert_memory, | ||
436 | .remove_memory = hp_zx1_remove_memory, | ||
437 | .alloc_by_type = agp_generic_alloc_by_type, | ||
438 | .free_by_type = agp_generic_free_by_type, | ||
439 | .agp_alloc_page = agp_generic_alloc_page, | ||
440 | .agp_destroy_page = agp_generic_destroy_page, | ||
441 | .cant_use_aperture = 1, | ||
442 | }; | ||
443 | |||
444 | static int __init | ||
445 | hp_zx1_setup (u64 ioc_hpa, u64 lba_hpa) | ||
446 | { | ||
447 | struct agp_bridge_data *bridge; | ||
448 | int error = 0; | ||
449 | |||
450 | error = hp_zx1_ioc_init(ioc_hpa); | ||
451 | if (error) | ||
452 | goto fail; | ||
453 | |||
454 | error = hp_zx1_lba_init(lba_hpa); | ||
455 | if (error) | ||
456 | goto fail; | ||
457 | |||
458 | bridge = agp_alloc_bridge(); | ||
459 | if (!bridge) { | ||
460 | error = -ENOMEM; | ||
461 | goto fail; | ||
462 | } | ||
463 | bridge->driver = &hp_zx1_driver; | ||
464 | |||
465 | fake_bridge_dev.vendor = PCI_VENDOR_ID_HP; | ||
466 | fake_bridge_dev.device = PCI_DEVICE_ID_HP_PCIX_LBA; | ||
467 | bridge->dev = &fake_bridge_dev; | ||
468 | |||
469 | error = agp_add_bridge(bridge); | ||
470 | fail: | ||
471 | if (error) | ||
472 | hp_zx1_cleanup(); | ||
473 | return error; | ||
474 | } | ||
475 | |||
476 | static acpi_status __init | ||
477 | zx1_gart_probe (acpi_handle obj, u32 depth, void *context, void **ret) | ||
478 | { | ||
479 | acpi_handle handle, parent; | ||
480 | acpi_status status; | ||
481 | struct acpi_buffer buffer; | ||
482 | struct acpi_device_info *info; | ||
483 | u64 lba_hpa, sba_hpa, length; | ||
484 | int match; | ||
485 | |||
486 | status = hp_acpi_csr_space(obj, &lba_hpa, &length); | ||
487 | if (ACPI_FAILURE(status)) | ||
488 | return AE_OK; /* keep looking for another bridge */ | ||
489 | |||
490 | /* Look for an enclosing IOC scope and find its CSR space */ | ||
491 | handle = obj; | ||
492 | do { | ||
493 | buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; | ||
494 | status = acpi_get_object_info(handle, &buffer); | ||
495 | if (ACPI_SUCCESS(status)) { | ||
496 | /* TBD check _CID also */ | ||
497 | info = buffer.pointer; | ||
498 | info->hardware_id.value[sizeof(info->hardware_id)-1] = '\0'; | ||
499 | match = (strcmp(info->hardware_id.value, "HWP0001") == 0); | ||
500 | ACPI_MEM_FREE(info); | ||
501 | if (match) { | ||
502 | status = hp_acpi_csr_space(handle, &sba_hpa, &length); | ||
503 | if (ACPI_SUCCESS(status)) | ||
504 | break; | ||
505 | else { | ||
506 | printk(KERN_ERR PFX "Detected HP ZX1 " | ||
507 | "AGP LBA but no IOC.\n"); | ||
508 | return AE_OK; | ||
509 | } | ||
510 | } | ||
511 | } | ||
512 | |||
513 | status = acpi_get_parent(handle, &parent); | ||
514 | handle = parent; | ||
515 | } while (ACPI_SUCCESS(status)); | ||
516 | |||
517 | if (hp_zx1_setup(sba_hpa + HP_ZX1_IOC_OFFSET, lba_hpa)) | ||
518 | return AE_OK; | ||
519 | |||
520 | printk(KERN_INFO PFX "Detected HP ZX1 %s AGP chipset (ioc=%lx, lba=%lx)\n", | ||
521 | (char *) context, sba_hpa + HP_ZX1_IOC_OFFSET, lba_hpa); | ||
522 | |||
523 | hp_zx1_gart_found = 1; | ||
524 | return AE_CTRL_TERMINATE; /* we only support one bridge; quit looking */ | ||
525 | } | ||
526 | |||
527 | static int __init | ||
528 | agp_hp_init (void) | ||
529 | { | ||
530 | if (agp_off) | ||
531 | return -EINVAL; | ||
532 | |||
533 | acpi_get_devices("HWP0003", zx1_gart_probe, "HWP0003", NULL); | ||
534 | if (hp_zx1_gart_found) | ||
535 | return 0; | ||
536 | |||
537 | acpi_get_devices("HWP0007", zx1_gart_probe, "HWP0007", NULL); | ||
538 | if (hp_zx1_gart_found) | ||
539 | return 0; | ||
540 | |||
541 | return -ENODEV; | ||
542 | } | ||
543 | |||
544 | static void __exit | ||
545 | agp_hp_cleanup (void) | ||
546 | { | ||
547 | } | ||
548 | |||
549 | module_init(agp_hp_init); | ||
550 | module_exit(agp_hp_cleanup); | ||
551 | |||
552 | MODULE_LICENSE("GPL and additional rights"); | ||