aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/agp
diff options
context:
space:
mode:
authorKyle McMartin <kyle@parisc-linux.org>2006-08-24 21:33:40 -0400
committerMatthew Wilcox <willy@parisc-linux.org>2006-10-04 08:50:16 -0400
commit08a6436816f7a16113c73be767ee8d50440e494e (patch)
tree158c7aba479931fb8182905151fcd0258990b9bf /drivers/char/agp
parent983daeec99f07fca0a8a9180ba1ca65bbd40c820 (diff)
[PARISC] Add support for Quicksilver AGPGART
Signed-off-by: Kyle McMartin <kyle@parisc-linux.org>
Diffstat (limited to 'drivers/char/agp')
-rw-r--r--drivers/char/agp/Kconfig10
-rw-r--r--drivers/char/agp/Makefile1
-rw-r--r--drivers/char/agp/parisc-agp.c416
3 files changed, 426 insertions, 1 deletions
diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig
index 22f8cf218cc6..c603bf291580 100644
--- a/drivers/char/agp/Kconfig
+++ b/drivers/char/agp/Kconfig
@@ -1,6 +1,6 @@
1config AGP 1config AGP
2 tristate "/dev/agpgart (AGP Support)" 2 tristate "/dev/agpgart (AGP Support)"
3 depends on ALPHA || IA64 || PPC || X86 3 depends on ALPHA || IA64 || PARISC || PPC || X86
4 depends on PCI 4 depends on PCI
5 ---help--- 5 ---help---
6 AGP (Accelerated Graphics Port) is a bus system mainly used to 6 AGP (Accelerated Graphics Port) is a bus system mainly used to
@@ -122,6 +122,14 @@ config AGP_HP_ZX1
122 This option gives you AGP GART support for the HP ZX1 chipset 122 This option gives you AGP GART support for the HP ZX1 chipset
123 for IA64 processors. 123 for IA64 processors.
124 124
125config AGP_PARISC
126 tristate "HP Quicksilver AGP support"
127 depends on AGP && PARISC && 64BIT
128 help
129 This option gives you AGP GART support for the HP Quicksilver
130 AGP bus adapter on HP PA-RISC machines (Ok, just on the C8000
131 workstation...)
132
125config AGP_ALPHA_CORE 133config AGP_ALPHA_CORE
126 tristate "Alpha AGP support" 134 tristate "Alpha AGP support"
127 depends on AGP && (ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL) 135 depends on AGP && (ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL)
diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile
index d33a22f2fa0b..3e581603d0a8 100644
--- a/drivers/char/agp/Makefile
+++ b/drivers/char/agp/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_AGP_AMD64) += amd64-agp.o
8obj-$(CONFIG_AGP_ALPHA_CORE) += alpha-agp.o 8obj-$(CONFIG_AGP_ALPHA_CORE) += alpha-agp.o
9obj-$(CONFIG_AGP_EFFICEON) += efficeon-agp.o 9obj-$(CONFIG_AGP_EFFICEON) += efficeon-agp.o
10obj-$(CONFIG_AGP_HP_ZX1) += hp-agp.o 10obj-$(CONFIG_AGP_HP_ZX1) += hp-agp.o
11obj-$(CONFIG_AGP_PARISC) += parisc-agp.o
11obj-$(CONFIG_AGP_I460) += i460-agp.o 12obj-$(CONFIG_AGP_I460) += i460-agp.o
12obj-$(CONFIG_AGP_INTEL) += intel-agp.o 13obj-$(CONFIG_AGP_INTEL) += intel-agp.o
13obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o 14obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o
diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c
new file mode 100644
index 000000000000..17c50b0f83f0
--- /dev/null
+++ b/drivers/char/agp/parisc-agp.c
@@ -0,0 +1,416 @@
1/*
2 * HP Quicksilver AGP GART routines
3 *
4 * Copyright (c) 2006, Kyle McMartin <kyle@parisc-linux.org>
5 *
6 * Based on drivers/char/agpgart/hp-agp.c which is
7 * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P.
8 * Bjorn Helgaas <bjorn.helgaas@hp.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 */
15
16#include <linux/module.h>
17#include <linux/pci.h>
18#include <linux/init.h>
19#include <linux/klist.h>
20#include <linux/agp_backend.h>
21
22#include <asm-parisc/parisc-device.h>
23#include <asm-parisc/ropes.h>
24
25#include "agp.h"
26
27#define DRVNAME "quicksilver"
28#define DRVPFX DRVNAME ": "
29
30#ifndef log2
31#define log2(x) ffz(~(x))
32#endif
33
34#define AGP8X_MODE_BIT 3
35#define AGP8X_MODE (1 << AGP8X_MODE_BIT)
36
37static struct _parisc_agp_info {
38 void __iomem *ioc_regs;
39 void __iomem *lba_regs;
40
41 int lba_cap_offset;
42
43 u64 *gatt;
44 u64 gatt_entries;
45
46 u64 gart_base;
47 u64 gart_size;
48
49 int io_page_size;
50 int io_pages_per_kpage;
51} parisc_agp_info;
52
53static struct gatt_mask parisc_agp_masks[] =
54{
55 {
56 .mask = SBA_PDIR_VALID_BIT,
57 .type = 0
58 }
59};
60
61static struct aper_size_info_fixed parisc_agp_sizes[] =
62{
63 {0, 0, 0}, /* filled in by parisc_agp_fetch_size() */
64};
65
66static int
67parisc_agp_fetch_size(void)
68{
69 int size;
70
71 size = parisc_agp_info.gart_size / MB(1);
72 parisc_agp_sizes[0].size = size;
73 agp_bridge->current_size = (void *) &parisc_agp_sizes[0];
74
75 return size;
76}
77
78static int
79parisc_agp_configure(void)
80{
81 struct _parisc_agp_info *info = &parisc_agp_info;
82
83 agp_bridge->gart_bus_addr = info->gart_base;
84 agp_bridge->capndx = info->lba_cap_offset;
85 agp_bridge->mode = readl(info->lba_regs+info->lba_cap_offset+PCI_AGP_STATUS);
86
87 return 0;
88}
89
90static void
91parisc_agp_tlbflush(struct agp_memory *mem)
92{
93 struct _parisc_agp_info *info = &parisc_agp_info;
94
95 writeq(info->gart_base | log2(info->gart_size), info->ioc_regs+IOC_PCOM);
96 readq(info->ioc_regs+IOC_PCOM); /* flush */
97}
98
99static int
100parisc_agp_create_gatt_table(struct agp_bridge_data *bridge)
101{
102 struct _parisc_agp_info *info = &parisc_agp_info;
103 int i;
104
105 for (i = 0; i < info->gatt_entries; i++) {
106 info->gatt[i] = (unsigned long)agp_bridge->scratch_page;
107 }
108
109 return 0;
110}
111
112static int
113parisc_agp_free_gatt_table(struct agp_bridge_data *bridge)
114{
115 struct _parisc_agp_info *info = &parisc_agp_info;
116
117 info->gatt[0] = SBA_AGPGART_COOKIE;
118
119 return 0;
120}
121
122static int
123parisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
124{
125 struct _parisc_agp_info *info = &parisc_agp_info;
126 int i, k;
127 off_t j, io_pg_start;
128 int io_pg_count;
129
130 if (type != 0 || mem->type != 0) {
131 return -EINVAL;
132 }
133
134 io_pg_start = info->io_pages_per_kpage * pg_start;
135 io_pg_count = info->io_pages_per_kpage * mem->page_count;
136 if ((io_pg_start + io_pg_count) > info->gatt_entries) {
137 return -EINVAL;
138 }
139
140 j = io_pg_start;
141 while (j < (io_pg_start + io_pg_count)) {
142 if (info->gatt[j])
143 return -EBUSY;
144 j++;
145 }
146
147 if (mem->is_flushed == FALSE) {
148 global_cache_flush();
149 mem->is_flushed = TRUE;
150 }
151
152 for (i = 0, j = io_pg_start; i < mem->page_count; i++) {
153 unsigned long paddr;
154
155 paddr = mem->memory[i];
156 for (k = 0;
157 k < info->io_pages_per_kpage;
158 k++, j++, paddr += info->io_page_size) {
159 info->gatt[j] =
160 agp_bridge->driver->mask_memory(agp_bridge,
161 paddr, type);
162 }
163 }
164
165 agp_bridge->driver->tlb_flush(mem);
166
167 return 0;
168}
169
170static int
171parisc_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
172{
173 struct _parisc_agp_info *info = &parisc_agp_info;
174 int i, io_pg_start, io_pg_count;
175
176 if (type != 0 || mem->type != 0) {
177 return -EINVAL;
178 }
179
180 io_pg_start = info->io_pages_per_kpage * pg_start;
181 io_pg_count = info->io_pages_per_kpage * mem->page_count;
182 for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) {
183 info->gatt[i] = agp_bridge->scratch_page;
184 }
185
186 agp_bridge->driver->tlb_flush(mem);
187 return 0;
188}
189
190static unsigned long
191parisc_agp_mask_memory(struct agp_bridge_data *bridge,
192 unsigned long addr, int type)
193{
194 return SBA_PDIR_VALID_BIT | addr;
195}
196
197static void
198parisc_agp_enable(struct agp_bridge_data *bridge, u32 mode)
199{
200 struct _parisc_agp_info *info = &parisc_agp_info;
201 u32 command;
202
203 command = readl(info->lba_regs + info->lba_cap_offset + PCI_AGP_STATUS);
204
205 command = agp_collect_device_status(bridge, mode, command);
206 command |= 0x00000100;
207
208 writel(command, info->lba_regs + info->lba_cap_offset + PCI_AGP_COMMAND);
209
210 agp_device_command(command, (mode & AGP8X_MODE) != 0);
211}
212
213struct agp_bridge_driver parisc_agp_driver = {
214 .owner = THIS_MODULE,
215 .size_type = FIXED_APER_SIZE,
216 .configure = parisc_agp_configure,
217 .fetch_size = parisc_agp_fetch_size,
218 .tlb_flush = parisc_agp_tlbflush,
219 .mask_memory = parisc_agp_mask_memory,
220 .masks = parisc_agp_masks,
221 .agp_enable = parisc_agp_enable,
222 .cache_flush = global_cache_flush,
223 .create_gatt_table = parisc_agp_create_gatt_table,
224 .free_gatt_table = parisc_agp_free_gatt_table,
225 .insert_memory = parisc_agp_insert_memory,
226 .remove_memory = parisc_agp_remove_memory,
227 .alloc_by_type = agp_generic_alloc_by_type,
228 .free_by_type = agp_generic_free_by_type,
229 .agp_alloc_page = agp_generic_alloc_page,
230 .agp_destroy_page = agp_generic_destroy_page,
231 .cant_use_aperture = 1,
232};
233
234static int __init
235agp_ioc_init(void __iomem *ioc_regs)
236{
237 struct _parisc_agp_info *info = &parisc_agp_info;
238 u64 *iova_base, *io_pdir, io_tlb_ps;
239 int io_tlb_shift;
240
241 printk(KERN_INFO DRVPFX "IO PDIR shared with sba_iommu\n");
242
243 info->ioc_regs = ioc_regs;
244
245 io_tlb_ps = readq(info->ioc_regs+IOC_TCNFG);
246 switch (io_tlb_ps) {
247 case 0: io_tlb_shift = 12; break;
248 case 1: io_tlb_shift = 13; break;
249 case 2: io_tlb_shift = 14; break;
250 case 3: io_tlb_shift = 16; break;
251 default:
252 printk(KERN_ERR DRVPFX "Invalid IOTLB page size "
253 "configuration 0x%llx\n", io_tlb_ps);
254 info->gatt = NULL;
255 info->gatt_entries = 0;
256 return -ENODEV;
257 }
258 info->io_page_size = 1 << io_tlb_shift;
259 info->io_pages_per_kpage = PAGE_SIZE / info->io_page_size;
260
261 iova_base = readq(info->ioc_regs+IOC_IBASE) & ~0x1;
262 info->gart_base = iova_base + PLUTO_IOVA_SIZE - PLUTO_GART_SIZE;
263
264 info->gart_size = PLUTO_GART_SIZE;
265 info->gatt_entries = info->gart_size / info->io_page_size;
266
267 io_pdir = phys_to_virt(readq(info->ioc_regs+IOC_PDIR_BASE));
268 info->gatt = &io_pdir[(PLUTO_IOVA_SIZE/2) >> PAGE_SHIFT];
269
270 if (info->gatt[0] != SBA_AGPGART_COOKIE) {
271 info->gatt = NULL;
272 info->gatt_entries = 0;
273 printk(KERN_ERR DRVPFX "No reserved IO PDIR entry found; "
274 "GART disabled\n");
275 return -ENODEV;
276 }
277
278 return 0;
279}
280
281static int
282lba_find_capability(int cap)
283{
284 struct _parisc_agp_info *info = &parisc_agp_info;
285 u16 status;
286 u8 pos, id;
287 int ttl = 48;
288
289 status = readw(info->lba_regs + PCI_STATUS);
290 if (!(status & PCI_STATUS_CAP_LIST))
291 return 0;
292 pos = readb(info->lba_regs + PCI_CAPABILITY_LIST);
293 while (ttl-- && pos >= 0x40) {
294 pos &= ~3;
295 id = readb(info->lba_regs + pos + PCI_CAP_LIST_ID);
296 if (id == 0xff)
297 break;
298 if (id == cap)
299 return pos;
300 pos = readb(info->lba_regs + pos + PCI_CAP_LIST_NEXT);
301 }
302 return 0;
303}
304
305static int __init
306agp_lba_init(void __iomem *lba_hpa)
307{
308 struct _parisc_agp_info *info = &parisc_agp_info;
309 int cap;
310
311 info->lba_regs = lba_hpa;
312 info->lba_cap_offset = lba_find_capability(PCI_CAP_ID_AGP);
313
314 cap = readl(lba_hpa + info->lba_cap_offset) & 0xff;
315 if (cap != PCI_CAP_ID_AGP) {
316 printk(KERN_ERR DRVPFX "Invalid capability ID 0x%02x at 0x%x\n",
317 cap, info->lba_cap_offset);
318 return -ENODEV;
319 }
320
321 return 0;
322}
323
324static int __init
325parisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa)
326{
327 struct pci_dev *fake_bridge_dev = NULL;
328 struct agp_bridge_data *bridge;
329 int error = 0;
330
331 fake_bridge_dev = kmalloc(sizeof (struct pci_dev), GFP_KERNEL);
332 if (!fake_bridge_dev) {
333 error = -ENOMEM;
334 goto fail;
335 }
336
337 error = agp_ioc_init(ioc_hpa);
338 if (error)
339 goto fail;
340
341 error = agp_lba_init(lba_hpa);
342 if (error)
343 goto fail;
344
345 bridge = agp_alloc_bridge();
346 if (!bridge) {
347 error = -ENOMEM;
348 goto fail;
349 }
350 bridge->driver = &parisc_agp_driver;
351
352 fake_bridge_dev->vendor = PCI_VENDOR_ID_HP;
353 fake_bridge_dev->device = PCI_DEVICE_ID_HP_PCIX_LBA;
354 bridge->dev = fake_bridge_dev;
355
356 error = agp_add_bridge(bridge);
357
358fail:
359 return error;
360}
361
362static struct device *next_device(struct klist_iter *i) {
363 struct klist_node * n = klist_next(i);
364 return n ? container_of(n, struct device, knode_parent) : NULL;
365}
366
367static int
368parisc_agp_init(void)
369{
370 extern struct sba_device *sba_list;
371
372 int err = -1;
373 struct parisc_device *sba = NULL, *lba = NULL;
374 struct lba_device *lbadev = NULL;
375 struct device *dev = NULL;
376 struct klist_iter i;
377
378 if (!sba_list)
379 goto out;
380
381 /* Find our parent Pluto */
382 sba = sba_list->dev;
383 if (!IS_PLUTO(sba)) {
384 printk(KERN_INFO DRVPFX "No Pluto found, so no AGPGART for you.\n");
385 goto out;
386 }
387
388 /* Now search our Pluto for our precious AGP device... */
389 klist_iter_init(&sba->dev.klist_children, &i);
390 while ((dev = next_device(&i))) {
391 struct parisc_device *padev = to_parisc_device(dev);
392 if (IS_QUICKSILVER(padev))
393 lba = padev;
394 }
395 klist_iter_exit(&i);
396
397 if (!lba) {
398 printk(KERN_INFO DRVPFX "No AGP devices found.\n");
399 goto out;
400 }
401
402 lbadev = parisc_get_drvdata(lba);
403
404 /* w00t, let's go find our cookies... */
405 parisc_agp_setup(sba_list->ioc[0].ioc_hpa, lbadev->hba.base_addr);
406
407 return 0;
408
409out:
410 return err;
411}
412
413module_init(parisc_agp_init);
414
415MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>");
416MODULE_LICENSE("GPL");