162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HP zx1 AGPGART routines. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P. 662306a36Sopenharmony_ci * Bjorn Helgaas <bjorn.helgaas@hp.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/acpi.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/agp_backend.h> 1462306a36Sopenharmony_ci#include <linux/log2.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/acpi-ext.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "agp.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define HP_ZX1_IOC_OFFSET 0x1000 /* ACPI reports SBA, we want IOC */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* HP ZX1 IOC registers */ 2462306a36Sopenharmony_ci#define HP_ZX1_IBASE 0x300 2562306a36Sopenharmony_ci#define HP_ZX1_IMASK 0x308 2662306a36Sopenharmony_ci#define HP_ZX1_PCOM 0x310 2762306a36Sopenharmony_ci#define HP_ZX1_TCNFG 0x318 2862306a36Sopenharmony_ci#define HP_ZX1_PDIR_BASE 0x320 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define HP_ZX1_IOVA_BASE GB(1UL) 3162306a36Sopenharmony_ci#define HP_ZX1_IOVA_SIZE GB(1UL) 3262306a36Sopenharmony_ci#define HP_ZX1_GART_SIZE (HP_ZX1_IOVA_SIZE / 2) 3362306a36Sopenharmony_ci#define HP_ZX1_SBA_IOMMU_COOKIE 0x0000badbadc0ffeeUL 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define HP_ZX1_PDIR_VALID_BIT 0x8000000000000000UL 3662306a36Sopenharmony_ci#define HP_ZX1_IOVA_TO_PDIR(va) ((va - hp_private.iova_base) >> hp_private.io_tlb_shift) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define AGP8X_MODE_BIT 3 3962306a36Sopenharmony_ci#define AGP8X_MODE (1 << AGP8X_MODE_BIT) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* AGP bridge need not be PCI device, but DRM thinks it is. */ 4262306a36Sopenharmony_cistatic struct pci_dev fake_bridge_dev; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int hp_zx1_gart_found; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct aper_size_info_fixed hp_zx1_sizes[] = 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci {0, 0, 0}, /* filled in by hp_zx1_fetch_size() */ 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic struct gatt_mask hp_zx1_masks[] = 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci {.mask = HP_ZX1_PDIR_VALID_BIT, .type = 0} 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic struct _hp_private { 5762306a36Sopenharmony_ci volatile u8 __iomem *ioc_regs; 5862306a36Sopenharmony_ci volatile u8 __iomem *lba_regs; 5962306a36Sopenharmony_ci int lba_cap_offset; 6062306a36Sopenharmony_ci u64 *io_pdir; // PDIR for entire IOVA 6162306a36Sopenharmony_ci u64 *gatt; // PDIR just for GART (subset of above) 6262306a36Sopenharmony_ci u64 gatt_entries; 6362306a36Sopenharmony_ci u64 iova_base; 6462306a36Sopenharmony_ci u64 gart_base; 6562306a36Sopenharmony_ci u64 gart_size; 6662306a36Sopenharmony_ci u64 io_pdir_size; 6762306a36Sopenharmony_ci int io_pdir_owner; // do we own it, or share it with sba_iommu? 6862306a36Sopenharmony_ci int io_page_size; 6962306a36Sopenharmony_ci int io_tlb_shift; 7062306a36Sopenharmony_ci int io_tlb_ps; // IOC ps config 7162306a36Sopenharmony_ci int io_pages_per_kpage; 7262306a36Sopenharmony_ci} hp_private; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int __init hp_zx1_ioc_shared(void) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR shared with sba_iommu\n"); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * IOC already configured by sba_iommu module; just use 8262306a36Sopenharmony_ci * its setup. We assume: 8362306a36Sopenharmony_ci * - IOVA space is 1Gb in size 8462306a36Sopenharmony_ci * - first 512Mb is IOMMU, second 512Mb is GART 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci hp->io_tlb_ps = readq(hp->ioc_regs+HP_ZX1_TCNFG); 8762306a36Sopenharmony_ci switch (hp->io_tlb_ps) { 8862306a36Sopenharmony_ci case 0: hp->io_tlb_shift = 12; break; 8962306a36Sopenharmony_ci case 1: hp->io_tlb_shift = 13; break; 9062306a36Sopenharmony_ci case 2: hp->io_tlb_shift = 14; break; 9162306a36Sopenharmony_ci case 3: hp->io_tlb_shift = 16; break; 9262306a36Sopenharmony_ci default: 9362306a36Sopenharmony_ci printk(KERN_ERR PFX "Invalid IOTLB page size " 9462306a36Sopenharmony_ci "configuration 0x%x\n", hp->io_tlb_ps); 9562306a36Sopenharmony_ci hp->gatt = NULL; 9662306a36Sopenharmony_ci hp->gatt_entries = 0; 9762306a36Sopenharmony_ci return -ENODEV; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci hp->io_page_size = 1 << hp->io_tlb_shift; 10062306a36Sopenharmony_ci hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci hp->iova_base = readq(hp->ioc_regs+HP_ZX1_IBASE) & ~0x1; 10362306a36Sopenharmony_ci hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - HP_ZX1_GART_SIZE; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci hp->gart_size = HP_ZX1_GART_SIZE; 10662306a36Sopenharmony_ci hp->gatt_entries = hp->gart_size / hp->io_page_size; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci hp->io_pdir = phys_to_virt(readq(hp->ioc_regs+HP_ZX1_PDIR_BASE)); 10962306a36Sopenharmony_ci hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) { 11262306a36Sopenharmony_ci /* Normal case when no AGP device in system */ 11362306a36Sopenharmony_ci hp->gatt = NULL; 11462306a36Sopenharmony_ci hp->gatt_entries = 0; 11562306a36Sopenharmony_ci printk(KERN_ERR PFX "No reserved IO PDIR entry found; " 11662306a36Sopenharmony_ci "GART disabled\n"); 11762306a36Sopenharmony_ci return -ENODEV; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int __init 12462306a36Sopenharmony_cihp_zx1_ioc_owner (void) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR dedicated to GART\n"); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * Select an IOV page size no larger than system page size. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci if (PAGE_SIZE >= KB(64)) { 13462306a36Sopenharmony_ci hp->io_tlb_shift = 16; 13562306a36Sopenharmony_ci hp->io_tlb_ps = 3; 13662306a36Sopenharmony_ci } else if (PAGE_SIZE >= KB(16)) { 13762306a36Sopenharmony_ci hp->io_tlb_shift = 14; 13862306a36Sopenharmony_ci hp->io_tlb_ps = 2; 13962306a36Sopenharmony_ci } else if (PAGE_SIZE >= KB(8)) { 14062306a36Sopenharmony_ci hp->io_tlb_shift = 13; 14162306a36Sopenharmony_ci hp->io_tlb_ps = 1; 14262306a36Sopenharmony_ci } else { 14362306a36Sopenharmony_ci hp->io_tlb_shift = 12; 14462306a36Sopenharmony_ci hp->io_tlb_ps = 0; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci hp->io_page_size = 1 << hp->io_tlb_shift; 14762306a36Sopenharmony_ci hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci hp->iova_base = HP_ZX1_IOVA_BASE; 15062306a36Sopenharmony_ci hp->gart_size = HP_ZX1_GART_SIZE; 15162306a36Sopenharmony_ci hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - hp->gart_size; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci hp->gatt_entries = hp->gart_size / hp->io_page_size; 15462306a36Sopenharmony_ci hp->io_pdir_size = (HP_ZX1_IOVA_SIZE / hp->io_page_size) * sizeof(u64); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int __init 16062306a36Sopenharmony_cihp_zx1_ioc_init (u64 hpa) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci hp->ioc_regs = ioremap(hpa, 1024); 16562306a36Sopenharmony_ci if (!hp->ioc_regs) 16662306a36Sopenharmony_ci return -ENOMEM; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * If the IOTLB is currently disabled, we can take it over. 17062306a36Sopenharmony_ci * Otherwise, we have to share with sba_iommu. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci hp->io_pdir_owner = (readq(hp->ioc_regs+HP_ZX1_IBASE) & 0x1) == 0; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (hp->io_pdir_owner) 17562306a36Sopenharmony_ci return hp_zx1_ioc_owner(); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return hp_zx1_ioc_shared(); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int 18162306a36Sopenharmony_cihp_zx1_lba_find_capability (volatile u8 __iomem *hpa, int cap) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci u16 status; 18462306a36Sopenharmony_ci u8 pos, id; 18562306a36Sopenharmony_ci int ttl = 48; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci status = readw(hpa+PCI_STATUS); 18862306a36Sopenharmony_ci if (!(status & PCI_STATUS_CAP_LIST)) 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci pos = readb(hpa+PCI_CAPABILITY_LIST); 19162306a36Sopenharmony_ci while (ttl-- && pos >= 0x40) { 19262306a36Sopenharmony_ci pos &= ~3; 19362306a36Sopenharmony_ci id = readb(hpa+pos+PCI_CAP_LIST_ID); 19462306a36Sopenharmony_ci if (id == 0xff) 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci if (id == cap) 19762306a36Sopenharmony_ci return pos; 19862306a36Sopenharmony_ci pos = readb(hpa+pos+PCI_CAP_LIST_NEXT); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int __init 20462306a36Sopenharmony_cihp_zx1_lba_init (u64 hpa) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 20762306a36Sopenharmony_ci int cap; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci hp->lba_regs = ioremap(hpa, 256); 21062306a36Sopenharmony_ci if (!hp->lba_regs) 21162306a36Sopenharmony_ci return -ENOMEM; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci hp->lba_cap_offset = hp_zx1_lba_find_capability(hp->lba_regs, PCI_CAP_ID_AGP); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci cap = readl(hp->lba_regs+hp->lba_cap_offset) & 0xff; 21662306a36Sopenharmony_ci if (cap != PCI_CAP_ID_AGP) { 21762306a36Sopenharmony_ci printk(KERN_ERR PFX "Invalid capability ID 0x%02x at 0x%x\n", 21862306a36Sopenharmony_ci cap, hp->lba_cap_offset); 21962306a36Sopenharmony_ci iounmap(hp->lba_regs); 22062306a36Sopenharmony_ci return -ENODEV; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int 22762306a36Sopenharmony_cihp_zx1_fetch_size(void) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci int size; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci size = hp_private.gart_size / MB(1); 23262306a36Sopenharmony_ci hp_zx1_sizes[0].size = size; 23362306a36Sopenharmony_ci agp_bridge->current_size = (void *) &hp_zx1_sizes[0]; 23462306a36Sopenharmony_ci return size; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int 23862306a36Sopenharmony_cihp_zx1_configure (void) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci agp_bridge->gart_bus_addr = hp->gart_base; 24362306a36Sopenharmony_ci agp_bridge->capndx = hp->lba_cap_offset; 24462306a36Sopenharmony_ci agp_bridge->mode = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (hp->io_pdir_owner) { 24762306a36Sopenharmony_ci writel(virt_to_phys(hp->io_pdir), hp->ioc_regs+HP_ZX1_PDIR_BASE); 24862306a36Sopenharmony_ci readl(hp->ioc_regs+HP_ZX1_PDIR_BASE); 24962306a36Sopenharmony_ci writel(hp->io_tlb_ps, hp->ioc_regs+HP_ZX1_TCNFG); 25062306a36Sopenharmony_ci readl(hp->ioc_regs+HP_ZX1_TCNFG); 25162306a36Sopenharmony_ci writel((unsigned int)(~(HP_ZX1_IOVA_SIZE-1)), hp->ioc_regs+HP_ZX1_IMASK); 25262306a36Sopenharmony_ci readl(hp->ioc_regs+HP_ZX1_IMASK); 25362306a36Sopenharmony_ci writel(hp->iova_base|1, hp->ioc_regs+HP_ZX1_IBASE); 25462306a36Sopenharmony_ci readl(hp->ioc_regs+HP_ZX1_IBASE); 25562306a36Sopenharmony_ci writel(hp->iova_base|ilog2(HP_ZX1_IOVA_SIZE), hp->ioc_regs+HP_ZX1_PCOM); 25662306a36Sopenharmony_ci readl(hp->ioc_regs+HP_ZX1_PCOM); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void 26362306a36Sopenharmony_cihp_zx1_cleanup (void) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (hp->ioc_regs) { 26862306a36Sopenharmony_ci if (hp->io_pdir_owner) { 26962306a36Sopenharmony_ci writeq(0, hp->ioc_regs+HP_ZX1_IBASE); 27062306a36Sopenharmony_ci readq(hp->ioc_regs+HP_ZX1_IBASE); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci iounmap(hp->ioc_regs); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci if (hp->lba_regs) 27562306a36Sopenharmony_ci iounmap(hp->lba_regs); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void 27962306a36Sopenharmony_cihp_zx1_tlbflush (struct agp_memory *mem) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci writeq(hp->gart_base | ilog2(hp->gart_size), hp->ioc_regs+HP_ZX1_PCOM); 28462306a36Sopenharmony_ci readq(hp->ioc_regs+HP_ZX1_PCOM); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int 28862306a36Sopenharmony_cihp_zx1_create_gatt_table (struct agp_bridge_data *bridge) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 29162306a36Sopenharmony_ci int i; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (hp->io_pdir_owner) { 29462306a36Sopenharmony_ci hp->io_pdir = (u64 *) __get_free_pages(GFP_KERNEL, 29562306a36Sopenharmony_ci get_order(hp->io_pdir_size)); 29662306a36Sopenharmony_ci if (!hp->io_pdir) { 29762306a36Sopenharmony_ci printk(KERN_ERR PFX "Couldn't allocate contiguous " 29862306a36Sopenharmony_ci "memory for I/O PDIR\n"); 29962306a36Sopenharmony_ci hp->gatt = NULL; 30062306a36Sopenharmony_ci hp->gatt_entries = 0; 30162306a36Sopenharmony_ci return -ENOMEM; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci memset(hp->io_pdir, 0, hp->io_pdir_size); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci for (i = 0; i < hp->gatt_entries; i++) { 30962306a36Sopenharmony_ci hp->gatt[i] = (unsigned long) agp_bridge->scratch_page; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int 31662306a36Sopenharmony_cihp_zx1_free_gatt_table (struct agp_bridge_data *bridge) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (hp->io_pdir_owner) 32162306a36Sopenharmony_ci free_pages((unsigned long) hp->io_pdir, 32262306a36Sopenharmony_ci get_order(hp->io_pdir_size)); 32362306a36Sopenharmony_ci else 32462306a36Sopenharmony_ci hp->gatt[0] = HP_ZX1_SBA_IOMMU_COOKIE; 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int 32962306a36Sopenharmony_cihp_zx1_insert_memory (struct agp_memory *mem, off_t pg_start, int type) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 33262306a36Sopenharmony_ci int i, k; 33362306a36Sopenharmony_ci off_t j, io_pg_start; 33462306a36Sopenharmony_ci int io_pg_count; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (type != mem->type || 33762306a36Sopenharmony_ci agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) { 33862306a36Sopenharmony_ci return -EINVAL; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci io_pg_start = hp->io_pages_per_kpage * pg_start; 34262306a36Sopenharmony_ci io_pg_count = hp->io_pages_per_kpage * mem->page_count; 34362306a36Sopenharmony_ci if ((io_pg_start + io_pg_count) > hp->gatt_entries) { 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci j = io_pg_start; 34862306a36Sopenharmony_ci while (j < (io_pg_start + io_pg_count)) { 34962306a36Sopenharmony_ci if (hp->gatt[j]) { 35062306a36Sopenharmony_ci return -EBUSY; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci j++; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!mem->is_flushed) { 35662306a36Sopenharmony_ci global_cache_flush(); 35762306a36Sopenharmony_ci mem->is_flushed = true; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci for (i = 0, j = io_pg_start; i < mem->page_count; i++) { 36162306a36Sopenharmony_ci unsigned long paddr; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci paddr = page_to_phys(mem->pages[i]); 36462306a36Sopenharmony_ci for (k = 0; 36562306a36Sopenharmony_ci k < hp->io_pages_per_kpage; 36662306a36Sopenharmony_ci k++, j++, paddr += hp->io_page_size) { 36762306a36Sopenharmony_ci hp->gatt[j] = HP_ZX1_PDIR_VALID_BIT | paddr; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci agp_bridge->driver->tlb_flush(mem); 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int 37662306a36Sopenharmony_cihp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 37962306a36Sopenharmony_ci int i, io_pg_start, io_pg_count; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (type != mem->type || 38262306a36Sopenharmony_ci agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) { 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci io_pg_start = hp->io_pages_per_kpage * pg_start; 38762306a36Sopenharmony_ci io_pg_count = hp->io_pages_per_kpage * mem->page_count; 38862306a36Sopenharmony_ci for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) { 38962306a36Sopenharmony_ci hp->gatt[i] = agp_bridge->scratch_page; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci agp_bridge->driver->tlb_flush(mem); 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic unsigned long 39762306a36Sopenharmony_cihp_zx1_mask_memory (struct agp_bridge_data *bridge, dma_addr_t addr, int type) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci return HP_ZX1_PDIR_VALID_BIT | addr; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void 40362306a36Sopenharmony_cihp_zx1_enable (struct agp_bridge_data *bridge, u32 mode) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct _hp_private *hp = &hp_private; 40662306a36Sopenharmony_ci u32 command; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci command = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS); 40962306a36Sopenharmony_ci command = agp_collect_device_status(bridge, mode, command); 41062306a36Sopenharmony_ci command |= 0x00000100; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci writel(command, hp->lba_regs+hp->lba_cap_offset+PCI_AGP_COMMAND); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci agp_device_command(command, (mode & AGP8X_MODE) != 0); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ciconst struct agp_bridge_driver hp_zx1_driver = { 41862306a36Sopenharmony_ci .owner = THIS_MODULE, 41962306a36Sopenharmony_ci .size_type = FIXED_APER_SIZE, 42062306a36Sopenharmony_ci .configure = hp_zx1_configure, 42162306a36Sopenharmony_ci .fetch_size = hp_zx1_fetch_size, 42262306a36Sopenharmony_ci .cleanup = hp_zx1_cleanup, 42362306a36Sopenharmony_ci .tlb_flush = hp_zx1_tlbflush, 42462306a36Sopenharmony_ci .mask_memory = hp_zx1_mask_memory, 42562306a36Sopenharmony_ci .masks = hp_zx1_masks, 42662306a36Sopenharmony_ci .agp_enable = hp_zx1_enable, 42762306a36Sopenharmony_ci .cache_flush = global_cache_flush, 42862306a36Sopenharmony_ci .create_gatt_table = hp_zx1_create_gatt_table, 42962306a36Sopenharmony_ci .free_gatt_table = hp_zx1_free_gatt_table, 43062306a36Sopenharmony_ci .insert_memory = hp_zx1_insert_memory, 43162306a36Sopenharmony_ci .remove_memory = hp_zx1_remove_memory, 43262306a36Sopenharmony_ci .alloc_by_type = agp_generic_alloc_by_type, 43362306a36Sopenharmony_ci .free_by_type = agp_generic_free_by_type, 43462306a36Sopenharmony_ci .agp_alloc_page = agp_generic_alloc_page, 43562306a36Sopenharmony_ci .agp_alloc_pages = agp_generic_alloc_pages, 43662306a36Sopenharmony_ci .agp_destroy_page = agp_generic_destroy_page, 43762306a36Sopenharmony_ci .agp_destroy_pages = agp_generic_destroy_pages, 43862306a36Sopenharmony_ci .agp_type_to_mask_type = agp_generic_type_to_mask_type, 43962306a36Sopenharmony_ci .cant_use_aperture = true, 44062306a36Sopenharmony_ci}; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int __init 44362306a36Sopenharmony_cihp_zx1_setup (u64 ioc_hpa, u64 lba_hpa) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct agp_bridge_data *bridge; 44662306a36Sopenharmony_ci int error = 0; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci error = hp_zx1_ioc_init(ioc_hpa); 44962306a36Sopenharmony_ci if (error) 45062306a36Sopenharmony_ci goto fail; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci error = hp_zx1_lba_init(lba_hpa); 45362306a36Sopenharmony_ci if (error) 45462306a36Sopenharmony_ci goto fail; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci bridge = agp_alloc_bridge(); 45762306a36Sopenharmony_ci if (!bridge) { 45862306a36Sopenharmony_ci error = -ENOMEM; 45962306a36Sopenharmony_ci goto fail; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci bridge->driver = &hp_zx1_driver; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci fake_bridge_dev.vendor = PCI_VENDOR_ID_HP; 46462306a36Sopenharmony_ci fake_bridge_dev.device = PCI_DEVICE_ID_HP_PCIX_LBA; 46562306a36Sopenharmony_ci bridge->dev = &fake_bridge_dev; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci error = agp_add_bridge(bridge); 46862306a36Sopenharmony_ci fail: 46962306a36Sopenharmony_ci if (error) 47062306a36Sopenharmony_ci hp_zx1_cleanup(); 47162306a36Sopenharmony_ci return error; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic acpi_status __init 47562306a36Sopenharmony_cizx1_gart_probe (acpi_handle obj, u32 depth, void *context, void **ret) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci acpi_handle handle, parent; 47862306a36Sopenharmony_ci acpi_status status; 47962306a36Sopenharmony_ci struct acpi_device_info *info; 48062306a36Sopenharmony_ci u64 lba_hpa, sba_hpa, length; 48162306a36Sopenharmony_ci int match; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci status = hp_acpi_csr_space(obj, &lba_hpa, &length); 48462306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 48562306a36Sopenharmony_ci return AE_OK; /* keep looking for another bridge */ 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Look for an enclosing IOC scope and find its CSR space */ 48862306a36Sopenharmony_ci handle = obj; 48962306a36Sopenharmony_ci do { 49062306a36Sopenharmony_ci status = acpi_get_object_info(handle, &info); 49162306a36Sopenharmony_ci if (ACPI_SUCCESS(status) && (info->valid & ACPI_VALID_HID)) { 49262306a36Sopenharmony_ci /* TBD check _CID also */ 49362306a36Sopenharmony_ci match = (strcmp(info->hardware_id.string, "HWP0001") == 0); 49462306a36Sopenharmony_ci kfree(info); 49562306a36Sopenharmony_ci if (match) { 49662306a36Sopenharmony_ci status = hp_acpi_csr_space(handle, &sba_hpa, &length); 49762306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci else { 50062306a36Sopenharmony_ci printk(KERN_ERR PFX "Detected HP ZX1 " 50162306a36Sopenharmony_ci "AGP LBA but no IOC.\n"); 50262306a36Sopenharmony_ci return AE_OK; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci status = acpi_get_parent(handle, &parent); 50862306a36Sopenharmony_ci handle = parent; 50962306a36Sopenharmony_ci } while (ACPI_SUCCESS(status)); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 51262306a36Sopenharmony_ci return AE_OK; /* found no enclosing IOC */ 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (hp_zx1_setup(sba_hpa + HP_ZX1_IOC_OFFSET, lba_hpa)) 51562306a36Sopenharmony_ci return AE_OK; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci printk(KERN_INFO PFX "Detected HP ZX1 %s AGP chipset " 51862306a36Sopenharmony_ci "(ioc=%llx, lba=%llx)\n", (char *)context, 51962306a36Sopenharmony_ci sba_hpa + HP_ZX1_IOC_OFFSET, lba_hpa); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci hp_zx1_gart_found = 1; 52262306a36Sopenharmony_ci return AE_CTRL_TERMINATE; /* we only support one bridge; quit looking */ 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic int __init 52662306a36Sopenharmony_ciagp_hp_init (void) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci if (agp_off) 52962306a36Sopenharmony_ci return -EINVAL; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci acpi_get_devices("HWP0003", zx1_gart_probe, "HWP0003", NULL); 53262306a36Sopenharmony_ci if (hp_zx1_gart_found) 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci acpi_get_devices("HWP0007", zx1_gart_probe, "HWP0007", NULL); 53662306a36Sopenharmony_ci if (hp_zx1_gart_found) 53762306a36Sopenharmony_ci return 0; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return -ENODEV; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic void __exit 54362306a36Sopenharmony_ciagp_hp_cleanup (void) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cimodule_init(agp_hp_init); 54862306a36Sopenharmony_cimodule_exit(agp_hp_cleanup); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ciMODULE_LICENSE("GPL and additional rights"); 551