Commit 08a64368 authored by Kyle McMartin's avatar Kyle McMartin Committed by Matthew Wilcox

[PARISC] Add support for Quicksilver AGPGART

Signed-off-by: default avatarKyle McMartin <kyle@parisc-linux.org>
parent 983daeec
config AGP config AGP
tristate "/dev/agpgart (AGP Support)" tristate "/dev/agpgart (AGP Support)"
depends on ALPHA || IA64 || PPC || X86 depends on ALPHA || IA64 || PARISC || PPC || X86
depends on PCI depends on PCI
---help--- ---help---
AGP (Accelerated Graphics Port) is a bus system mainly used to AGP (Accelerated Graphics Port) is a bus system mainly used to
...@@ -122,6 +122,14 @@ config AGP_HP_ZX1 ...@@ -122,6 +122,14 @@ config AGP_HP_ZX1
This option gives you AGP GART support for the HP ZX1 chipset This option gives you AGP GART support for the HP ZX1 chipset
for IA64 processors. for IA64 processors.
config AGP_PARISC
tristate "HP Quicksilver AGP support"
depends on AGP && PARISC && 64BIT
help
This option gives you AGP GART support for the HP Quicksilver
AGP bus adapter on HP PA-RISC machines (Ok, just on the C8000
workstation...)
config AGP_ALPHA_CORE config AGP_ALPHA_CORE
tristate "Alpha AGP support" tristate "Alpha AGP support"
depends on AGP && (ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL) depends on AGP && (ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL)
......
...@@ -8,6 +8,7 @@ obj-$(CONFIG_AGP_AMD64) += amd64-agp.o ...@@ -8,6 +8,7 @@ obj-$(CONFIG_AGP_AMD64) += amd64-agp.o
obj-$(CONFIG_AGP_ALPHA_CORE) += alpha-agp.o obj-$(CONFIG_AGP_ALPHA_CORE) += alpha-agp.o
obj-$(CONFIG_AGP_EFFICEON) += efficeon-agp.o obj-$(CONFIG_AGP_EFFICEON) += efficeon-agp.o
obj-$(CONFIG_AGP_HP_ZX1) += hp-agp.o obj-$(CONFIG_AGP_HP_ZX1) += hp-agp.o
obj-$(CONFIG_AGP_PARISC) += parisc-agp.o
obj-$(CONFIG_AGP_I460) += i460-agp.o obj-$(CONFIG_AGP_I460) += i460-agp.o
obj-$(CONFIG_AGP_INTEL) += intel-agp.o obj-$(CONFIG_AGP_INTEL) += intel-agp.o
obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o
......
/*
* HP Quicksilver AGP GART routines
*
* Copyright (c) 2006, Kyle McMartin <kyle@parisc-linux.org>
*
* Based on drivers/char/agpgart/hp-agp.c which is
* (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P.
* Bjorn Helgaas <bjorn.helgaas@hp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/klist.h>
#include <linux/agp_backend.h>
#include <asm-parisc/parisc-device.h>
#include <asm-parisc/ropes.h>
#include "agp.h"
#define DRVNAME "quicksilver"
#define DRVPFX DRVNAME ": "
#ifndef log2
#define log2(x) ffz(~(x))
#endif
#define AGP8X_MODE_BIT 3
#define AGP8X_MODE (1 << AGP8X_MODE_BIT)
static struct _parisc_agp_info {
void __iomem *ioc_regs;
void __iomem *lba_regs;
int lba_cap_offset;
u64 *gatt;
u64 gatt_entries;
u64 gart_base;
u64 gart_size;
int io_page_size;
int io_pages_per_kpage;
} parisc_agp_info;
static struct gatt_mask parisc_agp_masks[] =
{
{
.mask = SBA_PDIR_VALID_BIT,
.type = 0
}
};
static struct aper_size_info_fixed parisc_agp_sizes[] =
{
{0, 0, 0}, /* filled in by parisc_agp_fetch_size() */
};
static int
parisc_agp_fetch_size(void)
{
int size;
size = parisc_agp_info.gart_size / MB(1);
parisc_agp_sizes[0].size = size;
agp_bridge->current_size = (void *) &parisc_agp_sizes[0];
return size;
}
static int
parisc_agp_configure(void)
{
struct _parisc_agp_info *info = &parisc_agp_info;
agp_bridge->gart_bus_addr = info->gart_base;
agp_bridge->capndx = info->lba_cap_offset;
agp_bridge->mode = readl(info->lba_regs+info->lba_cap_offset+PCI_AGP_STATUS);
return 0;
}
static void
parisc_agp_tlbflush(struct agp_memory *mem)
{
struct _parisc_agp_info *info = &parisc_agp_info;
writeq(info->gart_base | log2(info->gart_size), info->ioc_regs+IOC_PCOM);
readq(info->ioc_regs+IOC_PCOM); /* flush */
}
static int
parisc_agp_create_gatt_table(struct agp_bridge_data *bridge)
{
struct _parisc_agp_info *info = &parisc_agp_info;
int i;
for (i = 0; i < info->gatt_entries; i++) {
info->gatt[i] = (unsigned long)agp_bridge->scratch_page;
}
return 0;
}
static int
parisc_agp_free_gatt_table(struct agp_bridge_data *bridge)
{
struct _parisc_agp_info *info = &parisc_agp_info;
info->gatt[0] = SBA_AGPGART_COOKIE;
return 0;
}
static int
parisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
{
struct _parisc_agp_info *info = &parisc_agp_info;
int i, k;
off_t j, io_pg_start;
int io_pg_count;
if (type != 0 || mem->type != 0) {
return -EINVAL;
}
io_pg_start = info->io_pages_per_kpage * pg_start;
io_pg_count = info->io_pages_per_kpage * mem->page_count;
if ((io_pg_start + io_pg_count) > info->gatt_entries) {
return -EINVAL;
}
j = io_pg_start;
while (j < (io_pg_start + io_pg_count)) {
if (info->gatt[j])
return -EBUSY;
j++;
}
if (mem->is_flushed == FALSE) {
global_cache_flush();
mem->is_flushed = TRUE;
}
for (i = 0, j = io_pg_start; i < mem->page_count; i++) {
unsigned long paddr;
paddr = mem->memory[i];
for (k = 0;
k < info->io_pages_per_kpage;
k++, j++, paddr += info->io_page_size) {
info->gatt[j] =
agp_bridge->driver->mask_memory(agp_bridge,
paddr, type);
}
}
agp_bridge->driver->tlb_flush(mem);
return 0;
}
static int
parisc_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
{
struct _parisc_agp_info *info = &parisc_agp_info;
int i, io_pg_start, io_pg_count;
if (type != 0 || mem->type != 0) {
return -EINVAL;
}
io_pg_start = info->io_pages_per_kpage * pg_start;
io_pg_count = info->io_pages_per_kpage * mem->page_count;
for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) {
info->gatt[i] = agp_bridge->scratch_page;
}
agp_bridge->driver->tlb_flush(mem);
return 0;
}
static unsigned long
parisc_agp_mask_memory(struct agp_bridge_data *bridge,
unsigned long addr, int type)
{
return SBA_PDIR_VALID_BIT | addr;
}
static void
parisc_agp_enable(struct agp_bridge_data *bridge, u32 mode)
{
struct _parisc_agp_info *info = &parisc_agp_info;
u32 command;
command = readl(info->lba_regs + info->lba_cap_offset + PCI_AGP_STATUS);
command = agp_collect_device_status(bridge, mode, command);
command |= 0x00000100;
writel(command, info->lba_regs + info->lba_cap_offset + PCI_AGP_COMMAND);
agp_device_command(command, (mode & AGP8X_MODE) != 0);
}
struct agp_bridge_driver parisc_agp_driver = {
.owner = THIS_MODULE,
.size_type = FIXED_APER_SIZE,
.configure = parisc_agp_configure,
.fetch_size = parisc_agp_fetch_size,
.tlb_flush = parisc_agp_tlbflush,
.mask_memory = parisc_agp_mask_memory,
.masks = parisc_agp_masks,
.agp_enable = parisc_agp_enable,
.cache_flush = global_cache_flush,
.create_gatt_table = parisc_agp_create_gatt_table,
.free_gatt_table = parisc_agp_free_gatt_table,
.insert_memory = parisc_agp_insert_memory,
.remove_memory = parisc_agp_remove_memory,
.alloc_by_type = agp_generic_alloc_by_type,
.free_by_type = agp_generic_free_by_type,
.agp_alloc_page = agp_generic_alloc_page,
.agp_destroy_page = agp_generic_destroy_page,
.cant_use_aperture = 1,
};
static int __init
agp_ioc_init(void __iomem *ioc_regs)
{
struct _parisc_agp_info *info = &parisc_agp_info;
u64 *iova_base, *io_pdir, io_tlb_ps;
int io_tlb_shift;
printk(KERN_INFO DRVPFX "IO PDIR shared with sba_iommu\n");
info->ioc_regs = ioc_regs;
io_tlb_ps = readq(info->ioc_regs+IOC_TCNFG);
switch (io_tlb_ps) {
case 0: io_tlb_shift = 12; break;
case 1: io_tlb_shift = 13; break;
case 2: io_tlb_shift = 14; break;
case 3: io_tlb_shift = 16; break;
default:
printk(KERN_ERR DRVPFX "Invalid IOTLB page size "
"configuration 0x%llx\n", io_tlb_ps);
info->gatt = NULL;
info->gatt_entries = 0;
return -ENODEV;
}
info->io_page_size = 1 << io_tlb_shift;
info->io_pages_per_kpage = PAGE_SIZE / info->io_page_size;
iova_base = readq(info->ioc_regs+IOC_IBASE) & ~0x1;
info->gart_base = iova_base + PLUTO_IOVA_SIZE - PLUTO_GART_SIZE;
info->gart_size = PLUTO_GART_SIZE;
info->gatt_entries = info->gart_size / info->io_page_size;
io_pdir = phys_to_virt(readq(info->ioc_regs+IOC_PDIR_BASE));
info->gatt = &io_pdir[(PLUTO_IOVA_SIZE/2) >> PAGE_SHIFT];
if (info->gatt[0] != SBA_AGPGART_COOKIE) {
info->gatt = NULL;
info->gatt_entries = 0;
printk(KERN_ERR DRVPFX "No reserved IO PDIR entry found; "
"GART disabled\n");
return -ENODEV;
}
return 0;
}
static int
lba_find_capability(int cap)
{
struct _parisc_agp_info *info = &parisc_agp_info;
u16 status;
u8 pos, id;
int ttl = 48;
status = readw(info->lba_regs + PCI_STATUS);
if (!(status & PCI_STATUS_CAP_LIST))
return 0;
pos = readb(info->lba_regs + PCI_CAPABILITY_LIST);
while (ttl-- && pos >= 0x40) {
pos &= ~3;
id = readb(info->lba_regs + pos + PCI_CAP_LIST_ID);
if (id == 0xff)
break;
if (id == cap)
return pos;
pos = readb(info->lba_regs + pos + PCI_CAP_LIST_NEXT);
}
return 0;
}
static int __init
agp_lba_init(void __iomem *lba_hpa)
{
struct _parisc_agp_info *info = &parisc_agp_info;
int cap;
info->lba_regs = lba_hpa;
info->lba_cap_offset = lba_find_capability(PCI_CAP_ID_AGP);
cap = readl(lba_hpa + info->lba_cap_offset) & 0xff;
if (cap != PCI_CAP_ID_AGP) {
printk(KERN_ERR DRVPFX "Invalid capability ID 0x%02x at 0x%x\n",
cap, info->lba_cap_offset);
return -ENODEV;
}
return 0;
}
static int __init
parisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa)
{
struct pci_dev *fake_bridge_dev = NULL;
struct agp_bridge_data *bridge;
int error = 0;
fake_bridge_dev = kmalloc(sizeof (struct pci_dev), GFP_KERNEL);
if (!fake_bridge_dev) {
error = -ENOMEM;
goto fail;
}
error = agp_ioc_init(ioc_hpa);
if (error)
goto fail;
error = agp_lba_init(lba_hpa);
if (error)
goto fail;
bridge = agp_alloc_bridge();
if (!bridge) {
error = -ENOMEM;
goto fail;
}
bridge->driver = &parisc_agp_driver;
fake_bridge_dev->vendor = PCI_VENDOR_ID_HP;
fake_bridge_dev->device = PCI_DEVICE_ID_HP_PCIX_LBA;
bridge->dev = fake_bridge_dev;
error = agp_add_bridge(bridge);
fail:
return error;
}
static struct device *next_device(struct klist_iter *i) {
struct klist_node * n = klist_next(i);
return n ? container_of(n, struct device, knode_parent) : NULL;
}
static int
parisc_agp_init(void)
{
extern struct sba_device *sba_list;
int err = -1;
struct parisc_device *sba = NULL, *lba = NULL;
struct lba_device *lbadev = NULL;
struct device *dev = NULL;
struct klist_iter i;
if (!sba_list)
goto out;
/* Find our parent Pluto */
sba = sba_list->dev;
if (!IS_PLUTO(sba)) {
printk(KERN_INFO DRVPFX "No Pluto found, so no AGPGART for you.\n");
goto out;
}
/* Now search our Pluto for our precious AGP device... */
klist_iter_init(&sba->dev.klist_children, &i);
while ((dev = next_device(&i))) {
struct parisc_device *padev = to_parisc_device(dev);
if (IS_QUICKSILVER(padev))
lba = padev;
}
klist_iter_exit(&i);
if (!lba) {
printk(KERN_INFO DRVPFX "No AGP devices found.\n");
goto out;
}
lbadev = parisc_get_drvdata(lba);
/* w00t, let's go find our cookies... */
parisc_agp_setup(sba_list->ioc[0].ioc_hpa, lbadev->hba.base_addr);
return 0;
out:
return err;
}
module_init(parisc_agp_init);
MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>");
MODULE_LICENSE("GPL");
...@@ -89,7 +89,8 @@ ...@@ -89,7 +89,8 @@
#define DEFAULT_DMA_HINT_REG 0 #define DEFAULT_DMA_HINT_REG 0
static struct sba_device *sba_list; struct sba_device *sba_list;
EXPORT_SYMBOL_GPL(sba_list);
static unsigned long ioc_needs_fdc = 0; static unsigned long ioc_needs_fdc = 0;
...@@ -102,8 +103,14 @@ static unsigned long piranha_bad_128k = 0; ...@@ -102,8 +103,14 @@ static unsigned long piranha_bad_128k = 0;
/* Looks nice and keeps the compiler happy */ /* Looks nice and keeps the compiler happy */
#define SBA_DEV(d) ((struct sba_device *) (d)) #define SBA_DEV(d) ((struct sba_device *) (d))
#ifdef CONFIG_AGP_PARISC
#define SBA_AGP_SUPPORT
#endif /*CONFIG_AGP_PARISC*/
#ifdef SBA_AGP_SUPPORT #ifdef SBA_AGP_SUPPORT
static int reserve_sba_gart = 1; static int sba_reserve_agpgart = 1;
module_param(sba_reserve_agpgart, int, 1);
MODULE_PARM_DESC(sba_reserve_agpgart, "Reserve half of IO pdir as AGPGART");
#endif #endif
#define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1)) #define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1))
...@@ -1300,6 +1307,10 @@ sba_ioc_init_pluto(struct parisc_device *sba, struct ioc *ioc, int ioc_num) ...@@ -1300,6 +1307,10 @@ sba_ioc_init_pluto(struct parisc_device *sba, struct ioc *ioc, int ioc_num)
WRITE_REG(ioc->ibase | 31, ioc->ioc_hpa + IOC_PCOM); WRITE_REG(ioc->ibase | 31, ioc->ioc_hpa + IOC_PCOM);
#ifdef SBA_AGP_SUPPORT #ifdef SBA_AGP_SUPPORT
{
struct klist_iter i;
struct device *dev = NULL;
/* /*
** If an AGP device is present, only use half of the IOV space ** If an AGP device is present, only use half of the IOV space
** for PCI DMA. Unfortunately we can't know ahead of time ** for PCI DMA. Unfortunately we can't know ahead of time
...@@ -1308,20 +1319,22 @@ sba_ioc_init_pluto(struct parisc_device *sba, struct ioc *ioc, int ioc_num) ...@@ -1308,20 +1319,22 @@ sba_ioc_init_pluto(struct parisc_device *sba, struct ioc *ioc, int ioc_num)
** We program the next pdir index after we stop w/ a key for ** We program the next pdir index after we stop w/ a key for
** the GART code to handshake on. ** the GART code to handshake on.
*/ */
device=NULL; klist_iter_init(&sba->dev.klist_children, &i);
for (lba = sba->child; lba; lba = lba->sibling) { while (dev = next_device(&i)) {
struct parisc_device *lba = to_parisc_device(dev);
if (IS_QUICKSILVER(lba)) if (IS_QUICKSILVER(lba))
break; agp_found = 1;
} }
klist_iter_exit(&sba->dev.klist_children, &i);
if (lba) { if (agp_found && sba_reserve_agpgart) {
DBG_INIT("%s: Reserving half of IOVA space for AGP GART support\n", __FUNCTION__); printk(KERN_INFO "%s: reserving %dMb of IOVA space for agpgart\n",
__FUNCTION__, (iova_space_size/2) >> 20);
ioc->pdir_size /= 2; ioc->pdir_size /= 2;
((u64 *)ioc->pdir_base)[PDIR_INDEX(iova_space_size/2)] = SBA_IOMMU_COOKIE; ioc->pdir_base[PDIR_INDEX(iova_space_size/2)] = SBA_AGPGART_COOKIE;
} else {
DBG_INIT("%s: No GART needed - no AGP controller found\n", __FUNCTION__);
} }
#endif /* 0 */ }
#endif /*SBA_AGP_SUPPORT*/
} }
......
#ifndef _ASM_PARISC_AGP_H
#define _ASM_PARISC_AGP_H
/*
* PARISC specific AGP definitions.
* Copyright (c) 2006 Kyle McMartin <kyle@parisc-linux.org>
*
*/
#define map_page_into_agp(page) /* nothing */
#define unmap_page_from_agp(page) /* nothing */
#define flush_agp_mappings() /* nothing */
#define flush_agp_cache() mb()
/* Convert a physical address to an address suitable for the GART. */
#define phys_to_gart(x) (x)
#define gart_to_phys(x) (x)
/* GATT allocation. Returns/accepts GATT kernel virtual address. */
#define alloc_gatt_pages(order) \
((char *)__get_free_pages(GFP_KERNEL, (order)))
#define free_gatt_pages(table, order) \
free_pages((unsigned long)(table), (order))
#endif /* _ASM_PARISC_AGP_H */
#ifndef _ASM_PARISC_ROPES_H_ #ifndef _ASM_PARISC_ROPES_H_
#define _ASM_PARISC_ROPES_H_ #define _ASM_PARISC_ROPES_H_
#include <asm-parisc/parisc-device.h>
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
/* "low end" PA8800 machines use ZX1 chipset: PAT PDC and only run 64-bit */ /* "low end" PA8800 machines use ZX1 chipset: PAT PDC and only run 64-bit */
#define ZX1_SUPPORT #define ZX1_SUPPORT
...@@ -231,6 +233,16 @@ static inline int IS_QUICKSILVER(struct parisc_device *d) { ...@@ -231,6 +233,16 @@ static inline int IS_QUICKSILVER(struct parisc_device *d) {
return (d->id.hversion == QUICKSILVER_HVERS); return (d->id.hversion == QUICKSILVER_HVERS);
} }
static inline int agp_mode_mercury(void __iomem *hpa) {
u64 bus_mode;
bus_mode = readl(hpa + 0x0620);
if (bus_mode & 1)
return 1;
return 0;
}
/* /*
** I/O SAPIC init function ** I/O SAPIC init function
** Caller knows where an I/O SAPIC is. LBA has an integrated I/O SAPIC. ** Caller knows where an I/O SAPIC is. LBA has an integrated I/O SAPIC.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment