18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HP Quicksilver AGP GART routines 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2006, Kyle McMartin <kyle@parisc-linux.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on drivers/char/agpgart/hp-agp.c which is 88c2ecf20Sopenharmony_ci * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P. 98c2ecf20Sopenharmony_ci * Bjorn Helgaas <bjorn.helgaas@hp.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/klist.h> 168c2ecf20Sopenharmony_ci#include <linux/agp_backend.h> 178c2ecf20Sopenharmony_ci#include <linux/log2.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/parisc-device.h> 218c2ecf20Sopenharmony_ci#include <asm/ropes.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "agp.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRVNAME "quicksilver" 268c2ecf20Sopenharmony_ci#define DRVPFX DRVNAME ": " 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define AGP8X_MODE_BIT 3 298c2ecf20Sopenharmony_ci#define AGP8X_MODE (1 << AGP8X_MODE_BIT) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic unsigned long 328c2ecf20Sopenharmony_ciparisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, 338c2ecf20Sopenharmony_ci int type); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct _parisc_agp_info { 368c2ecf20Sopenharmony_ci void __iomem *ioc_regs; 378c2ecf20Sopenharmony_ci void __iomem *lba_regs; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci int lba_cap_offset; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci u64 *gatt; 428c2ecf20Sopenharmony_ci u64 gatt_entries; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci u64 gart_base; 458c2ecf20Sopenharmony_ci u64 gart_size; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci int io_page_size; 488c2ecf20Sopenharmony_ci int io_pages_per_kpage; 498c2ecf20Sopenharmony_ci} parisc_agp_info; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic struct gatt_mask parisc_agp_masks[] = 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci { 548c2ecf20Sopenharmony_ci .mask = SBA_PDIR_VALID_BIT, 558c2ecf20Sopenharmony_ci .type = 0 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct aper_size_info_fixed parisc_agp_sizes[] = 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci {0, 0, 0}, /* filled in by parisc_agp_fetch_size() */ 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int 658c2ecf20Sopenharmony_ciparisc_agp_fetch_size(void) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int size; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci size = parisc_agp_info.gart_size / MB(1); 708c2ecf20Sopenharmony_ci parisc_agp_sizes[0].size = size; 718c2ecf20Sopenharmony_ci agp_bridge->current_size = (void *) &parisc_agp_sizes[0]; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return size; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int 778c2ecf20Sopenharmony_ciparisc_agp_configure(void) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct _parisc_agp_info *info = &parisc_agp_info; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci agp_bridge->gart_bus_addr = info->gart_base; 828c2ecf20Sopenharmony_ci agp_bridge->capndx = info->lba_cap_offset; 838c2ecf20Sopenharmony_ci agp_bridge->mode = readl(info->lba_regs+info->lba_cap_offset+PCI_AGP_STATUS); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void 898c2ecf20Sopenharmony_ciparisc_agp_tlbflush(struct agp_memory *mem) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct _parisc_agp_info *info = &parisc_agp_info; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* force fdc ops to be visible to IOMMU */ 948c2ecf20Sopenharmony_ci asm_io_sync(); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci writeq(info->gart_base | ilog2(info->gart_size), info->ioc_regs+IOC_PCOM); 978c2ecf20Sopenharmony_ci readq(info->ioc_regs+IOC_PCOM); /* flush */ 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int 1018c2ecf20Sopenharmony_ciparisc_agp_create_gatt_table(struct agp_bridge_data *bridge) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct _parisc_agp_info *info = &parisc_agp_info; 1048c2ecf20Sopenharmony_ci int i; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (i = 0; i < info->gatt_entries; i++) { 1078c2ecf20Sopenharmony_ci info->gatt[i] = (unsigned long)agp_bridge->scratch_page; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int 1148c2ecf20Sopenharmony_ciparisc_agp_free_gatt_table(struct agp_bridge_data *bridge) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct _parisc_agp_info *info = &parisc_agp_info; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci info->gatt[0] = SBA_AGPGART_COOKIE; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int 1248c2ecf20Sopenharmony_ciparisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct _parisc_agp_info *info = &parisc_agp_info; 1278c2ecf20Sopenharmony_ci int i, k; 1288c2ecf20Sopenharmony_ci off_t j, io_pg_start; 1298c2ecf20Sopenharmony_ci int io_pg_count; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (type != mem->type || 1328c2ecf20Sopenharmony_ci agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) { 1338c2ecf20Sopenharmony_ci return -EINVAL; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci io_pg_start = info->io_pages_per_kpage * pg_start; 1378c2ecf20Sopenharmony_ci io_pg_count = info->io_pages_per_kpage * mem->page_count; 1388c2ecf20Sopenharmony_ci if ((io_pg_start + io_pg_count) > info->gatt_entries) { 1398c2ecf20Sopenharmony_ci return -EINVAL; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci j = io_pg_start; 1438c2ecf20Sopenharmony_ci while (j < (io_pg_start + io_pg_count)) { 1448c2ecf20Sopenharmony_ci if (info->gatt[j]) 1458c2ecf20Sopenharmony_ci return -EBUSY; 1468c2ecf20Sopenharmony_ci j++; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!mem->is_flushed) { 1508c2ecf20Sopenharmony_ci global_cache_flush(); 1518c2ecf20Sopenharmony_ci mem->is_flushed = true; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci for (i = 0, j = io_pg_start; i < mem->page_count; i++) { 1558c2ecf20Sopenharmony_ci unsigned long paddr; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci paddr = page_to_phys(mem->pages[i]); 1588c2ecf20Sopenharmony_ci for (k = 0; 1598c2ecf20Sopenharmony_ci k < info->io_pages_per_kpage; 1608c2ecf20Sopenharmony_ci k++, j++, paddr += info->io_page_size) { 1618c2ecf20Sopenharmony_ci info->gatt[j] = 1628c2ecf20Sopenharmony_ci parisc_agp_mask_memory(agp_bridge, 1638c2ecf20Sopenharmony_ci paddr, type); 1648c2ecf20Sopenharmony_ci asm_io_fdc(&info->gatt[j]); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci agp_bridge->driver->tlb_flush(mem); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int 1748c2ecf20Sopenharmony_ciparisc_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct _parisc_agp_info *info = &parisc_agp_info; 1778c2ecf20Sopenharmony_ci int i, io_pg_start, io_pg_count; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (type != mem->type || 1808c2ecf20Sopenharmony_ci agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) { 1818c2ecf20Sopenharmony_ci return -EINVAL; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci io_pg_start = info->io_pages_per_kpage * pg_start; 1858c2ecf20Sopenharmony_ci io_pg_count = info->io_pages_per_kpage * mem->page_count; 1868c2ecf20Sopenharmony_ci for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) { 1878c2ecf20Sopenharmony_ci info->gatt[i] = agp_bridge->scratch_page; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci agp_bridge->driver->tlb_flush(mem); 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic unsigned long 1958c2ecf20Sopenharmony_ciparisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, 1968c2ecf20Sopenharmony_ci int type) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci unsigned ci; /* coherent index */ 1998c2ecf20Sopenharmony_ci dma_addr_t pa; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci pa = addr & IOVP_MASK; 2028c2ecf20Sopenharmony_ci asm("lci 0(%1), %0" : "=r" (ci) : "r" (phys_to_virt(pa))); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci pa |= (ci >> PAGE_SHIFT) & 0xff;/* move CI (8 bits) into lowest byte */ 2058c2ecf20Sopenharmony_ci pa |= SBA_PDIR_VALID_BIT; /* set "valid" bit */ 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return cpu_to_le64(pa); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void 2118c2ecf20Sopenharmony_ciparisc_agp_enable(struct agp_bridge_data *bridge, u32 mode) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct _parisc_agp_info *info = &parisc_agp_info; 2148c2ecf20Sopenharmony_ci u32 command; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci command = readl(info->lba_regs + info->lba_cap_offset + PCI_AGP_STATUS); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci command = agp_collect_device_status(bridge, mode, command); 2198c2ecf20Sopenharmony_ci command |= 0x00000100; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci writel(command, info->lba_regs + info->lba_cap_offset + PCI_AGP_COMMAND); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci agp_device_command(command, (mode & AGP8X_MODE) != 0); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic const struct agp_bridge_driver parisc_agp_driver = { 2278c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2288c2ecf20Sopenharmony_ci .size_type = FIXED_APER_SIZE, 2298c2ecf20Sopenharmony_ci .configure = parisc_agp_configure, 2308c2ecf20Sopenharmony_ci .fetch_size = parisc_agp_fetch_size, 2318c2ecf20Sopenharmony_ci .tlb_flush = parisc_agp_tlbflush, 2328c2ecf20Sopenharmony_ci .mask_memory = parisc_agp_mask_memory, 2338c2ecf20Sopenharmony_ci .masks = parisc_agp_masks, 2348c2ecf20Sopenharmony_ci .agp_enable = parisc_agp_enable, 2358c2ecf20Sopenharmony_ci .cache_flush = global_cache_flush, 2368c2ecf20Sopenharmony_ci .create_gatt_table = parisc_agp_create_gatt_table, 2378c2ecf20Sopenharmony_ci .free_gatt_table = parisc_agp_free_gatt_table, 2388c2ecf20Sopenharmony_ci .insert_memory = parisc_agp_insert_memory, 2398c2ecf20Sopenharmony_ci .remove_memory = parisc_agp_remove_memory, 2408c2ecf20Sopenharmony_ci .alloc_by_type = agp_generic_alloc_by_type, 2418c2ecf20Sopenharmony_ci .free_by_type = agp_generic_free_by_type, 2428c2ecf20Sopenharmony_ci .agp_alloc_page = agp_generic_alloc_page, 2438c2ecf20Sopenharmony_ci .agp_alloc_pages = agp_generic_alloc_pages, 2448c2ecf20Sopenharmony_ci .agp_destroy_page = agp_generic_destroy_page, 2458c2ecf20Sopenharmony_ci .agp_destroy_pages = agp_generic_destroy_pages, 2468c2ecf20Sopenharmony_ci .agp_type_to_mask_type = agp_generic_type_to_mask_type, 2478c2ecf20Sopenharmony_ci .cant_use_aperture = true, 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int __init 2518c2ecf20Sopenharmony_ciagp_ioc_init(void __iomem *ioc_regs) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct _parisc_agp_info *info = &parisc_agp_info; 2548c2ecf20Sopenharmony_ci u64 iova_base, *io_pdir, io_tlb_ps; 2558c2ecf20Sopenharmony_ci int io_tlb_shift; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci printk(KERN_INFO DRVPFX "IO PDIR shared with sba_iommu\n"); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci info->ioc_regs = ioc_regs; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci io_tlb_ps = readq(info->ioc_regs+IOC_TCNFG); 2628c2ecf20Sopenharmony_ci switch (io_tlb_ps) { 2638c2ecf20Sopenharmony_ci case 0: io_tlb_shift = 12; break; 2648c2ecf20Sopenharmony_ci case 1: io_tlb_shift = 13; break; 2658c2ecf20Sopenharmony_ci case 2: io_tlb_shift = 14; break; 2668c2ecf20Sopenharmony_ci case 3: io_tlb_shift = 16; break; 2678c2ecf20Sopenharmony_ci default: 2688c2ecf20Sopenharmony_ci printk(KERN_ERR DRVPFX "Invalid IOTLB page size " 2698c2ecf20Sopenharmony_ci "configuration 0x%llx\n", io_tlb_ps); 2708c2ecf20Sopenharmony_ci info->gatt = NULL; 2718c2ecf20Sopenharmony_ci info->gatt_entries = 0; 2728c2ecf20Sopenharmony_ci return -ENODEV; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci info->io_page_size = 1 << io_tlb_shift; 2758c2ecf20Sopenharmony_ci info->io_pages_per_kpage = PAGE_SIZE / info->io_page_size; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci iova_base = readq(info->ioc_regs+IOC_IBASE) & ~0x1; 2788c2ecf20Sopenharmony_ci info->gart_base = iova_base + PLUTO_IOVA_SIZE - PLUTO_GART_SIZE; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci info->gart_size = PLUTO_GART_SIZE; 2818c2ecf20Sopenharmony_ci info->gatt_entries = info->gart_size / info->io_page_size; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci io_pdir = phys_to_virt(readq(info->ioc_regs+IOC_PDIR_BASE)); 2848c2ecf20Sopenharmony_ci info->gatt = &io_pdir[(PLUTO_IOVA_SIZE/2) >> PAGE_SHIFT]; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (info->gatt[0] != SBA_AGPGART_COOKIE) { 2878c2ecf20Sopenharmony_ci info->gatt = NULL; 2888c2ecf20Sopenharmony_ci info->gatt_entries = 0; 2898c2ecf20Sopenharmony_ci printk(KERN_ERR DRVPFX "No reserved IO PDIR entry found; " 2908c2ecf20Sopenharmony_ci "GART disabled\n"); 2918c2ecf20Sopenharmony_ci return -ENODEV; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int __init 2988c2ecf20Sopenharmony_cilba_find_capability(int cap) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct _parisc_agp_info *info = &parisc_agp_info; 3018c2ecf20Sopenharmony_ci u16 status; 3028c2ecf20Sopenharmony_ci u8 pos, id; 3038c2ecf20Sopenharmony_ci int ttl = 48; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci status = readw(info->lba_regs + PCI_STATUS); 3068c2ecf20Sopenharmony_ci if (!(status & PCI_STATUS_CAP_LIST)) 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci pos = readb(info->lba_regs + PCI_CAPABILITY_LIST); 3098c2ecf20Sopenharmony_ci while (ttl-- && pos >= 0x40) { 3108c2ecf20Sopenharmony_ci pos &= ~3; 3118c2ecf20Sopenharmony_ci id = readb(info->lba_regs + pos + PCI_CAP_LIST_ID); 3128c2ecf20Sopenharmony_ci if (id == 0xff) 3138c2ecf20Sopenharmony_ci break; 3148c2ecf20Sopenharmony_ci if (id == cap) 3158c2ecf20Sopenharmony_ci return pos; 3168c2ecf20Sopenharmony_ci pos = readb(info->lba_regs + pos + PCI_CAP_LIST_NEXT); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int __init 3228c2ecf20Sopenharmony_ciagp_lba_init(void __iomem *lba_hpa) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct _parisc_agp_info *info = &parisc_agp_info; 3258c2ecf20Sopenharmony_ci int cap; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci info->lba_regs = lba_hpa; 3288c2ecf20Sopenharmony_ci info->lba_cap_offset = lba_find_capability(PCI_CAP_ID_AGP); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci cap = readl(lba_hpa + info->lba_cap_offset) & 0xff; 3318c2ecf20Sopenharmony_ci if (cap != PCI_CAP_ID_AGP) { 3328c2ecf20Sopenharmony_ci printk(KERN_ERR DRVPFX "Invalid capability ID 0x%02x at 0x%x\n", 3338c2ecf20Sopenharmony_ci cap, info->lba_cap_offset); 3348c2ecf20Sopenharmony_ci return -ENODEV; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int __init 3418c2ecf20Sopenharmony_ciparisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct pci_dev *fake_bridge_dev = NULL; 3448c2ecf20Sopenharmony_ci struct agp_bridge_data *bridge; 3458c2ecf20Sopenharmony_ci int error = 0; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci fake_bridge_dev = pci_alloc_dev(NULL); 3488c2ecf20Sopenharmony_ci if (!fake_bridge_dev) { 3498c2ecf20Sopenharmony_ci error = -ENOMEM; 3508c2ecf20Sopenharmony_ci goto fail; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci error = agp_ioc_init(ioc_hpa); 3548c2ecf20Sopenharmony_ci if (error) 3558c2ecf20Sopenharmony_ci goto fail; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci error = agp_lba_init(lba_hpa); 3588c2ecf20Sopenharmony_ci if (error) 3598c2ecf20Sopenharmony_ci goto fail; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci bridge = agp_alloc_bridge(); 3628c2ecf20Sopenharmony_ci if (!bridge) { 3638c2ecf20Sopenharmony_ci error = -ENOMEM; 3648c2ecf20Sopenharmony_ci goto fail; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci bridge->driver = &parisc_agp_driver; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci fake_bridge_dev->vendor = PCI_VENDOR_ID_HP; 3698c2ecf20Sopenharmony_ci fake_bridge_dev->device = PCI_DEVICE_ID_HP_PCIX_LBA; 3708c2ecf20Sopenharmony_ci bridge->dev = fake_bridge_dev; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci error = agp_add_bridge(bridge); 3738c2ecf20Sopenharmony_ci if (error) 3748c2ecf20Sopenharmony_ci goto fail; 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cifail: 3788c2ecf20Sopenharmony_ci kfree(fake_bridge_dev); 3798c2ecf20Sopenharmony_ci return error; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int __init 3838c2ecf20Sopenharmony_cifind_quicksilver(struct device *dev, void *data) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct parisc_device **lba = data; 3868c2ecf20Sopenharmony_ci struct parisc_device *padev = to_parisc_device(dev); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (IS_QUICKSILVER(padev)) 3898c2ecf20Sopenharmony_ci *lba = padev; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int __init 3958c2ecf20Sopenharmony_ciparisc_agp_init(void) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci int err = -1; 3988c2ecf20Sopenharmony_ci struct parisc_device *sba = NULL, *lba = NULL; 3998c2ecf20Sopenharmony_ci struct lba_device *lbadev = NULL; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!sba_list) 4028c2ecf20Sopenharmony_ci goto out; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* Find our parent Pluto */ 4058c2ecf20Sopenharmony_ci sba = sba_list->dev; 4068c2ecf20Sopenharmony_ci if (!IS_PLUTO(sba)) { 4078c2ecf20Sopenharmony_ci printk(KERN_INFO DRVPFX "No Pluto found, so no AGPGART for you.\n"); 4088c2ecf20Sopenharmony_ci goto out; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* Now search our Pluto for our precious AGP device... */ 4128c2ecf20Sopenharmony_ci device_for_each_child(&sba->dev, &lba, find_quicksilver); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (!lba) { 4158c2ecf20Sopenharmony_ci printk(KERN_INFO DRVPFX "No AGP devices found.\n"); 4168c2ecf20Sopenharmony_ci goto out; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci lbadev = parisc_get_drvdata(lba); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* w00t, let's go find our cookies... */ 4228c2ecf20Sopenharmony_ci parisc_agp_setup(sba_list->ioc[0].ioc_hpa, lbadev->hba.base_addr); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ciout: 4278c2ecf20Sopenharmony_ci return err; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cimodule_init(parisc_agp_init); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>"); 4338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 434