162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * HP Quicksilver AGP GART routines
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2006, Kyle McMartin <kyle@parisc-linux.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on drivers/char/agpgart/hp-agp.c which is
862306a36Sopenharmony_ci * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P.
962306a36Sopenharmony_ci *	Bjorn Helgaas <bjorn.helgaas@hp.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/klist.h>
1662306a36Sopenharmony_ci#include <linux/agp_backend.h>
1762306a36Sopenharmony_ci#include <linux/log2.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <asm/parisc-device.h>
2162306a36Sopenharmony_ci#include <asm/ropes.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "agp.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define DRVNAME	"quicksilver"
2662306a36Sopenharmony_ci#define DRVPFX	DRVNAME ": "
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define AGP8X_MODE_BIT		3
2962306a36Sopenharmony_ci#define AGP8X_MODE		(1 << AGP8X_MODE_BIT)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic unsigned long
3262306a36Sopenharmony_ciparisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr,
3362306a36Sopenharmony_ci		       int type);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic struct _parisc_agp_info {
3662306a36Sopenharmony_ci	void __iomem *ioc_regs;
3762306a36Sopenharmony_ci	void __iomem *lba_regs;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	int lba_cap_offset;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	__le64 *gatt;
4262306a36Sopenharmony_ci	u64 gatt_entries;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	u64 gart_base;
4562306a36Sopenharmony_ci	u64 gart_size;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	int io_page_size;
4862306a36Sopenharmony_ci	int io_pages_per_kpage;
4962306a36Sopenharmony_ci} parisc_agp_info;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic struct gatt_mask parisc_agp_masks[] =
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci        {
5462306a36Sopenharmony_ci		.mask = SBA_PDIR_VALID_BIT,
5562306a36Sopenharmony_ci		.type = 0
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic struct aper_size_info_fixed parisc_agp_sizes[] =
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci        {0, 0, 0},              /* filled in by parisc_agp_fetch_size() */
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int
6562306a36Sopenharmony_ciparisc_agp_fetch_size(void)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	int size;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	size = parisc_agp_info.gart_size / MB(1);
7062306a36Sopenharmony_ci	parisc_agp_sizes[0].size = size;
7162306a36Sopenharmony_ci	agp_bridge->current_size = (void *) &parisc_agp_sizes[0];
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return size;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int
7762306a36Sopenharmony_ciparisc_agp_configure(void)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct _parisc_agp_info *info = &parisc_agp_info;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	agp_bridge->gart_bus_addr = info->gart_base;
8262306a36Sopenharmony_ci	agp_bridge->capndx = info->lba_cap_offset;
8362306a36Sopenharmony_ci	agp_bridge->mode = readl(info->lba_regs+info->lba_cap_offset+PCI_AGP_STATUS);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return 0;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void
8962306a36Sopenharmony_ciparisc_agp_tlbflush(struct agp_memory *mem)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct _parisc_agp_info *info = &parisc_agp_info;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* force fdc ops to be visible to IOMMU */
9462306a36Sopenharmony_ci	asm_io_sync();
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	writeq(info->gart_base | ilog2(info->gart_size), info->ioc_regs+IOC_PCOM);
9762306a36Sopenharmony_ci	readq(info->ioc_regs+IOC_PCOM);	/* flush */
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int
10162306a36Sopenharmony_ciparisc_agp_create_gatt_table(struct agp_bridge_data *bridge)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct _parisc_agp_info *info = &parisc_agp_info;
10462306a36Sopenharmony_ci	int i;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	for (i = 0; i < info->gatt_entries; i++) {
10762306a36Sopenharmony_ci		info->gatt[i] = cpu_to_le64(agp_bridge->scratch_page);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int
11462306a36Sopenharmony_ciparisc_agp_free_gatt_table(struct agp_bridge_data *bridge)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct _parisc_agp_info *info = &parisc_agp_info;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	info->gatt[0] = SBA_AGPGART_COOKIE;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return 0;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int
12462306a36Sopenharmony_ciparisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct _parisc_agp_info *info = &parisc_agp_info;
12762306a36Sopenharmony_ci	int i, k;
12862306a36Sopenharmony_ci	off_t j, io_pg_start;
12962306a36Sopenharmony_ci	int io_pg_count;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (type != mem->type ||
13262306a36Sopenharmony_ci		agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) {
13362306a36Sopenharmony_ci		return -EINVAL;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	io_pg_start = info->io_pages_per_kpage * pg_start;
13762306a36Sopenharmony_ci	io_pg_count = info->io_pages_per_kpage * mem->page_count;
13862306a36Sopenharmony_ci	if ((io_pg_start + io_pg_count) > info->gatt_entries) {
13962306a36Sopenharmony_ci		return -EINVAL;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	j = io_pg_start;
14362306a36Sopenharmony_ci	while (j < (io_pg_start + io_pg_count)) {
14462306a36Sopenharmony_ci		if (info->gatt[j])
14562306a36Sopenharmony_ci			return -EBUSY;
14662306a36Sopenharmony_ci		j++;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (!mem->is_flushed) {
15062306a36Sopenharmony_ci		global_cache_flush();
15162306a36Sopenharmony_ci		mem->is_flushed = true;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	for (i = 0, j = io_pg_start; i < mem->page_count; i++) {
15562306a36Sopenharmony_ci		unsigned long paddr;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		paddr = page_to_phys(mem->pages[i]);
15862306a36Sopenharmony_ci		for (k = 0;
15962306a36Sopenharmony_ci		     k < info->io_pages_per_kpage;
16062306a36Sopenharmony_ci		     k++, j++, paddr += info->io_page_size) {
16162306a36Sopenharmony_ci			info->gatt[j] = cpu_to_le64(
16262306a36Sopenharmony_ci				parisc_agp_mask_memory(agp_bridge,
16362306a36Sopenharmony_ci					paddr, type));
16462306a36Sopenharmony_ci			asm_io_fdc(&info->gatt[j]);
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	agp_bridge->driver->tlb_flush(mem);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int
17462306a36Sopenharmony_ciparisc_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct _parisc_agp_info *info = &parisc_agp_info;
17762306a36Sopenharmony_ci	int i, io_pg_start, io_pg_count;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (type != mem->type ||
18062306a36Sopenharmony_ci		agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) {
18162306a36Sopenharmony_ci		return -EINVAL;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	io_pg_start = info->io_pages_per_kpage * pg_start;
18562306a36Sopenharmony_ci	io_pg_count = info->io_pages_per_kpage * mem->page_count;
18662306a36Sopenharmony_ci	for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) {
18762306a36Sopenharmony_ci		info->gatt[i] = cpu_to_le64(agp_bridge->scratch_page);
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	agp_bridge->driver->tlb_flush(mem);
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic unsigned long
19562306a36Sopenharmony_ciparisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr,
19662306a36Sopenharmony_ci		       int type)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	unsigned ci;			/* coherent index */
19962306a36Sopenharmony_ci	dma_addr_t pa;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	pa = addr & IOVP_MASK;
20262306a36Sopenharmony_ci	asm("lci 0(%1), %0" : "=r" (ci) : "r" (phys_to_virt(pa)));
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	pa |= (ci >> PAGE_SHIFT) & 0xff;/* move CI (8 bits) into lowest byte */
20562306a36Sopenharmony_ci	pa |= SBA_PDIR_VALID_BIT;	/* set "valid" bit */
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* return native (big-endian) PDIR entry */
20862306a36Sopenharmony_ci	return pa;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic void
21262306a36Sopenharmony_ciparisc_agp_enable(struct agp_bridge_data *bridge, u32 mode)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct _parisc_agp_info *info = &parisc_agp_info;
21562306a36Sopenharmony_ci	u32 command;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	command = readl(info->lba_regs + info->lba_cap_offset + PCI_AGP_STATUS);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	command = agp_collect_device_status(bridge, mode, command);
22062306a36Sopenharmony_ci	command |= 0x00000100;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	writel(command, info->lba_regs + info->lba_cap_offset + PCI_AGP_COMMAND);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	agp_device_command(command, (mode & AGP8X_MODE) != 0);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic const struct agp_bridge_driver parisc_agp_driver = {
22862306a36Sopenharmony_ci	.owner			= THIS_MODULE,
22962306a36Sopenharmony_ci	.size_type		= FIXED_APER_SIZE,
23062306a36Sopenharmony_ci	.configure		= parisc_agp_configure,
23162306a36Sopenharmony_ci	.fetch_size		= parisc_agp_fetch_size,
23262306a36Sopenharmony_ci	.tlb_flush		= parisc_agp_tlbflush,
23362306a36Sopenharmony_ci	.mask_memory		= parisc_agp_mask_memory,
23462306a36Sopenharmony_ci	.masks			= parisc_agp_masks,
23562306a36Sopenharmony_ci	.agp_enable		= parisc_agp_enable,
23662306a36Sopenharmony_ci	.cache_flush		= global_cache_flush,
23762306a36Sopenharmony_ci	.create_gatt_table	= parisc_agp_create_gatt_table,
23862306a36Sopenharmony_ci	.free_gatt_table	= parisc_agp_free_gatt_table,
23962306a36Sopenharmony_ci	.insert_memory		= parisc_agp_insert_memory,
24062306a36Sopenharmony_ci	.remove_memory		= parisc_agp_remove_memory,
24162306a36Sopenharmony_ci	.alloc_by_type		= agp_generic_alloc_by_type,
24262306a36Sopenharmony_ci	.free_by_type		= agp_generic_free_by_type,
24362306a36Sopenharmony_ci	.agp_alloc_page		= agp_generic_alloc_page,
24462306a36Sopenharmony_ci	.agp_alloc_pages	= agp_generic_alloc_pages,
24562306a36Sopenharmony_ci	.agp_destroy_page	= agp_generic_destroy_page,
24662306a36Sopenharmony_ci	.agp_destroy_pages	= agp_generic_destroy_pages,
24762306a36Sopenharmony_ci	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
24862306a36Sopenharmony_ci	.cant_use_aperture	= true,
24962306a36Sopenharmony_ci};
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic int __init
25262306a36Sopenharmony_ciagp_ioc_init(void __iomem *ioc_regs)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct _parisc_agp_info *info = &parisc_agp_info;
25562306a36Sopenharmony_ci        u64 iova_base, io_tlb_ps;
25662306a36Sopenharmony_ci	__le64 *io_pdir;
25762306a36Sopenharmony_ci        int io_tlb_shift;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci        printk(KERN_INFO DRVPFX "IO PDIR shared with sba_iommu\n");
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci        info->ioc_regs = ioc_regs;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci        io_tlb_ps = readq(info->ioc_regs+IOC_TCNFG);
26462306a36Sopenharmony_ci        switch (io_tlb_ps) {
26562306a36Sopenharmony_ci        case 0: io_tlb_shift = 12; break;
26662306a36Sopenharmony_ci        case 1: io_tlb_shift = 13; break;
26762306a36Sopenharmony_ci        case 2: io_tlb_shift = 14; break;
26862306a36Sopenharmony_ci        case 3: io_tlb_shift = 16; break;
26962306a36Sopenharmony_ci        default:
27062306a36Sopenharmony_ci                printk(KERN_ERR DRVPFX "Invalid IOTLB page size "
27162306a36Sopenharmony_ci                       "configuration 0x%llx\n", io_tlb_ps);
27262306a36Sopenharmony_ci                info->gatt = NULL;
27362306a36Sopenharmony_ci                info->gatt_entries = 0;
27462306a36Sopenharmony_ci                return -ENODEV;
27562306a36Sopenharmony_ci        }
27662306a36Sopenharmony_ci        info->io_page_size = 1 << io_tlb_shift;
27762306a36Sopenharmony_ci        info->io_pages_per_kpage = PAGE_SIZE / info->io_page_size;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci        iova_base = readq(info->ioc_regs+IOC_IBASE) & ~0x1;
28062306a36Sopenharmony_ci        info->gart_base = iova_base + PLUTO_IOVA_SIZE - PLUTO_GART_SIZE;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci        info->gart_size = PLUTO_GART_SIZE;
28362306a36Sopenharmony_ci        info->gatt_entries = info->gart_size / info->io_page_size;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci        io_pdir = phys_to_virt(readq(info->ioc_regs+IOC_PDIR_BASE));
28662306a36Sopenharmony_ci        info->gatt = &io_pdir[(PLUTO_IOVA_SIZE/2) >> PAGE_SHIFT];
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci        if (info->gatt[0] != SBA_AGPGART_COOKIE) {
28962306a36Sopenharmony_ci                info->gatt = NULL;
29062306a36Sopenharmony_ci                info->gatt_entries = 0;
29162306a36Sopenharmony_ci                printk(KERN_ERR DRVPFX "No reserved IO PDIR entry found; "
29262306a36Sopenharmony_ci                       "GART disabled\n");
29362306a36Sopenharmony_ci                return -ENODEV;
29462306a36Sopenharmony_ci        }
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci        return 0;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int __init
30062306a36Sopenharmony_cilba_find_capability(int cap)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct _parisc_agp_info *info = &parisc_agp_info;
30362306a36Sopenharmony_ci        u16 status;
30462306a36Sopenharmony_ci        u8 pos, id;
30562306a36Sopenharmony_ci        int ttl = 48;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci        status = readw(info->lba_regs + PCI_STATUS);
30862306a36Sopenharmony_ci        if (!(status & PCI_STATUS_CAP_LIST))
30962306a36Sopenharmony_ci                return 0;
31062306a36Sopenharmony_ci        pos = readb(info->lba_regs + PCI_CAPABILITY_LIST);
31162306a36Sopenharmony_ci        while (ttl-- && pos >= 0x40) {
31262306a36Sopenharmony_ci                pos &= ~3;
31362306a36Sopenharmony_ci                id = readb(info->lba_regs + pos + PCI_CAP_LIST_ID);
31462306a36Sopenharmony_ci                if (id == 0xff)
31562306a36Sopenharmony_ci                        break;
31662306a36Sopenharmony_ci                if (id == cap)
31762306a36Sopenharmony_ci                        return pos;
31862306a36Sopenharmony_ci                pos = readb(info->lba_regs + pos + PCI_CAP_LIST_NEXT);
31962306a36Sopenharmony_ci        }
32062306a36Sopenharmony_ci        return 0;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int __init
32462306a36Sopenharmony_ciagp_lba_init(void __iomem *lba_hpa)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct _parisc_agp_info *info = &parisc_agp_info;
32762306a36Sopenharmony_ci        int cap;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	info->lba_regs = lba_hpa;
33062306a36Sopenharmony_ci        info->lba_cap_offset = lba_find_capability(PCI_CAP_ID_AGP);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci        cap = readl(lba_hpa + info->lba_cap_offset) & 0xff;
33362306a36Sopenharmony_ci        if (cap != PCI_CAP_ID_AGP) {
33462306a36Sopenharmony_ci                printk(KERN_ERR DRVPFX "Invalid capability ID 0x%02x at 0x%x\n",
33562306a36Sopenharmony_ci                       cap, info->lba_cap_offset);
33662306a36Sopenharmony_ci                return -ENODEV;
33762306a36Sopenharmony_ci        }
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci        return 0;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int __init
34362306a36Sopenharmony_ciparisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct pci_dev *fake_bridge_dev = NULL;
34662306a36Sopenharmony_ci	struct agp_bridge_data *bridge;
34762306a36Sopenharmony_ci	int error = 0;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	fake_bridge_dev = pci_alloc_dev(NULL);
35062306a36Sopenharmony_ci	if (!fake_bridge_dev) {
35162306a36Sopenharmony_ci		error = -ENOMEM;
35262306a36Sopenharmony_ci		goto fail;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	error = agp_ioc_init(ioc_hpa);
35662306a36Sopenharmony_ci	if (error)
35762306a36Sopenharmony_ci		goto fail;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	error = agp_lba_init(lba_hpa);
36062306a36Sopenharmony_ci	if (error)
36162306a36Sopenharmony_ci		goto fail;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	bridge = agp_alloc_bridge();
36462306a36Sopenharmony_ci	if (!bridge) {
36562306a36Sopenharmony_ci		error = -ENOMEM;
36662306a36Sopenharmony_ci		goto fail;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	bridge->driver = &parisc_agp_driver;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	fake_bridge_dev->vendor = PCI_VENDOR_ID_HP;
37162306a36Sopenharmony_ci	fake_bridge_dev->device = PCI_DEVICE_ID_HP_PCIX_LBA;
37262306a36Sopenharmony_ci	bridge->dev = fake_bridge_dev;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	error = agp_add_bridge(bridge);
37562306a36Sopenharmony_ci	if (error)
37662306a36Sopenharmony_ci		goto fail;
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cifail:
38062306a36Sopenharmony_ci	kfree(fake_bridge_dev);
38162306a36Sopenharmony_ci	return error;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int __init
38562306a36Sopenharmony_cifind_quicksilver(struct device *dev, void *data)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct parisc_device **lba = data;
38862306a36Sopenharmony_ci	struct parisc_device *padev = to_parisc_device(dev);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (IS_QUICKSILVER(padev))
39162306a36Sopenharmony_ci		*lba = padev;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return 0;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int __init
39762306a36Sopenharmony_ciparisc_agp_init(void)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	int err = -1;
40062306a36Sopenharmony_ci	struct parisc_device *sba = NULL, *lba = NULL;
40162306a36Sopenharmony_ci	struct lba_device *lbadev = NULL;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (!sba_list)
40462306a36Sopenharmony_ci		goto out;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* Find our parent Pluto */
40762306a36Sopenharmony_ci	sba = sba_list->dev;
40862306a36Sopenharmony_ci	if (!IS_PLUTO(sba)) {
40962306a36Sopenharmony_ci		printk(KERN_INFO DRVPFX "No Pluto found, so no AGPGART for you.\n");
41062306a36Sopenharmony_ci		goto out;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* Now search our Pluto for our precious AGP device... */
41462306a36Sopenharmony_ci	device_for_each_child(&sba->dev, &lba, find_quicksilver);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if (!lba) {
41762306a36Sopenharmony_ci		printk(KERN_INFO DRVPFX "No AGP devices found.\n");
41862306a36Sopenharmony_ci		goto out;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	lbadev = parisc_get_drvdata(lba);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* w00t, let's go find our cookies... */
42462306a36Sopenharmony_ci	parisc_agp_setup(sba_list->ioc[0].ioc_hpa, lbadev->hba.base_addr);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	return 0;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ciout:
42962306a36Sopenharmony_ci	return err;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cimodule_init(parisc_agp_init);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ciMODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>");
43562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
436