2006-08-25 05:33:40 +04:00
/*
* 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>
2007-10-17 10:26:25 +04:00
# include <linux/log2.h>
2006-08-25 05:33:40 +04:00
# include <asm-parisc/parisc-device.h>
# include <asm-parisc/ropes.h>
# include "agp.h"
# define DRVNAME "quicksilver"
# define DRVPFX DRVNAME ": "
# 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 ;
2007-10-17 10:26:25 +04:00
writeq ( info - > gart_base | ilog2 ( info - > gart_size ) , info - > ioc_regs + IOC_PCOM ) ;
2006-08-25 05:33:40 +04:00
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 + + ;
}
2008-03-27 00:10:02 +03:00
if ( ! mem - > is_flushed ) {
2006-08-25 05:33:40 +04:00
global_cache_flush ( ) ;
2008-03-27 00:10:02 +03:00
mem - > is_flushed = true ;
2006-08-25 05:33:40 +04:00
}
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 ) ;
}
2007-02-27 05:29:26 +03:00
static const struct agp_bridge_driver parisc_agp_driver = {
2006-08-25 05:33:40 +04:00
. 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 ,
2008-08-21 21:15:46 +04:00
. agp_alloc_pages = agp_generic_alloc_pages ,
2006-08-25 05:33:40 +04:00
. agp_destroy_page = agp_generic_destroy_page ,
2008-08-21 21:15:46 +04:00
. agp_destroy_pages = agp_generic_destroy_pages ,
2007-02-05 16:44:23 +03:00
. agp_type_to_mask_type = agp_generic_type_to_mask_type ,
2008-03-27 00:10:02 +03:00
. cant_use_aperture = true ,
2006-08-25 05:33:40 +04:00
} ;
static int __init
agp_ioc_init ( void __iomem * ioc_regs )
{
struct _parisc_agp_info * info = & parisc_agp_info ;
2006-10-24 23:49:54 +04:00
u64 iova_base , * io_pdir , io_tlb_ps ;
2006-08-25 05:33:40 +04:00
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 ;
2007-04-05 11:19:09 +04:00
fake_bridge_dev = alloc_pci_dev ( ) ;
2006-08-25 05:33:40 +04:00
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 " ) ;