2005-04-16 15:20:36 -07:00
/*
* arch / arm / mach - ixp2000 / pci . c
*
* PCI routines for IXDP2400 / IXDP2800 boards
*
* Original Author : Naeem Afzal < naeem . m . afzal @ intel . com >
* Maintained by : Deepak Saxena < dsaxena @ plexity . net >
*
* Copyright 2002 Intel Corp .
* Copyright ( C ) 2003 - 2004 MontaVista Software , Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
# include <linux/mm.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/delay.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2005-04-16 15:20:36 -07:00
# include <asm/irq.h>
# include <asm/system.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2005-04-16 15:20:36 -07:00
# include <asm/mach/pci.h>
2006-12-01 16:02:40 +01:00
static volatile int pci_master_aborts = 0 ;
2005-04-16 15:20:36 -07:00
static int clear_master_aborts ( void ) ;
2005-04-29 21:58:15 +01:00
u32 *
2005-04-16 15:20:36 -07:00
ixp2000_pci_config_addr ( unsigned int bus_nr , unsigned int devfn , int where )
{
u32 * paddress ;
if ( PCI_SLOT ( devfn ) > 7 )
return 0 ;
/* Must be dword aligned */
where & = ~ 3 ;
/*
* For top bus , generate type 0 , else type 1
*/
if ( ! bus_nr ) {
/* only bits[23:16] are used for IDSEL */
paddress = ( u32 * ) ( IXP2000_PCI_CFG0_VIRT_BASE
| ( 1 < < ( PCI_SLOT ( devfn ) + 16 ) )
| ( PCI_FUNC ( devfn ) < < 8 ) | where ) ;
} else {
paddress = ( u32 * ) ( IXP2000_PCI_CFG1_VIRT_BASE
| ( bus_nr < < 16 )
| ( PCI_SLOT ( devfn ) < < 11 )
| ( PCI_FUNC ( devfn ) < < 8 ) | where ) ;
}
return paddress ;
}
/*
* Mask table , bits to mask for quantity of size 1 , 2 or 4 bytes .
* 0 and 3 are not valid indexes . . .
*/
static u32 bytemask [ ] = {
/*0*/ 0 ,
/*1*/ 0xff ,
/*2*/ 0xffff ,
/*3*/ 0 ,
/*4*/ 0xffffffff ,
} ;
int ixp2000_pci_read_config ( struct pci_bus * bus , unsigned int devfn , int where ,
int size , u32 * value )
{
u32 n ;
u32 * addr ;
n = where % 4 ;
addr = ixp2000_pci_config_addr ( bus - > number , devfn , where ) ;
if ( ! addr )
return PCIBIOS_DEVICE_NOT_FOUND ;
pci_master_aborts = 0 ;
* value = ( * addr > > ( 8 * n ) ) & bytemask [ size ] ;
if ( pci_master_aborts ) {
pci_master_aborts = 0 ;
* value = 0xffffffff ;
return PCIBIOS_DEVICE_NOT_FOUND ;
}
return PCIBIOS_SUCCESSFUL ;
}
/*
2007-05-11 20:40:30 +01:00
* We don ' t do error checks by calling clear_master_aborts ( ) b / c the
2005-04-16 15:20:36 -07:00
* assumption is that the caller did a read first to make sure a device
* exists .
*/
int ixp2000_pci_write_config ( struct pci_bus * bus , unsigned int devfn , int where ,
int size , u32 value )
{
u32 mask ;
u32 * addr ;
u32 temp ;
mask = ~ ( bytemask [ size ] < < ( ( where % 0x4 ) * 8 ) ) ;
addr = ixp2000_pci_config_addr ( bus - > number , devfn , where ) ;
if ( ! addr )
return PCIBIOS_DEVICE_NOT_FOUND ;
temp = ( u32 ) ( value ) < < ( ( where % 0x4 ) * 8 ) ;
* addr = ( * addr & mask ) | temp ;
clear_master_aborts ( ) ;
return PCIBIOS_SUCCESSFUL ;
}
static struct pci_ops ixp2000_pci_ops = {
. read = ixp2000_pci_read_config ,
. write = ixp2000_pci_write_config
} ;
struct pci_bus * ixp2000_pci_scan_bus ( int nr , struct pci_sys_data * sysdata )
{
return pci_scan_bus ( sysdata - > busnr , & ixp2000_pci_ops , sysdata ) ;
}
int ixp2000_pci_abort_handler ( unsigned long addr , unsigned int fsr , struct pt_regs * regs )
{
volatile u32 temp ;
unsigned long flags ;
pci_master_aborts = 1 ;
local_irq_save ( flags ) ;
temp = * ( IXP2000_PCI_CONTROL ) ;
if ( temp & ( ( 1 < < 8 ) | ( 1 < < 5 ) ) ) {
2005-11-01 19:44:26 +00:00
ixp2000_reg_wrb ( IXP2000_PCI_CONTROL , temp ) ;
2005-04-16 15:20:36 -07:00
}
temp = * ( IXP2000_PCI_CMDSTAT ) ;
if ( temp & ( 1 < < 29 ) ) {
while ( temp & ( 1 < < 29 ) ) {
ixp2000_reg_write ( IXP2000_PCI_CMDSTAT , temp ) ;
temp = * ( IXP2000_PCI_CMDSTAT ) ;
}
}
local_irq_restore ( flags ) ;
/*
* If it was an imprecise abort , then we need to correct the
* return address to be _after_ the instruction .
*/
if ( fsr & ( 1 < < 10 ) )
regs - > ARM_pc + = 4 ;
return 0 ;
}
int
clear_master_aborts ( void )
{
volatile u32 temp ;
unsigned long flags ;
local_irq_save ( flags ) ;
temp = * ( IXP2000_PCI_CONTROL ) ;
2005-11-01 19:44:26 +00:00
if ( temp & ( ( 1 < < 8 ) | ( 1 < < 5 ) ) ) {
ixp2000_reg_wrb ( IXP2000_PCI_CONTROL , temp ) ;
2005-04-16 15:20:36 -07:00
}
temp = * ( IXP2000_PCI_CMDSTAT ) ;
if ( temp & ( 1 < < 29 ) ) {
while ( temp & ( 1 < < 29 ) ) {
ixp2000_reg_write ( IXP2000_PCI_CMDSTAT , temp ) ;
temp = * ( IXP2000_PCI_CMDSTAT ) ;
}
}
local_irq_restore ( flags ) ;
return 0 ;
}
void __init
ixp2000_pci_preinit ( void )
{
2005-06-25 19:30:04 +01:00
# ifndef CONFIG_IXP2000_SUPPORT_BROKEN_PCI_IO
/*
* Configure the PCI unit to properly byteswap I / O transactions ,
* and verify that it worked .
*/
ixp2000_reg_write ( IXP2000_PCI_CONTROL ,
( * IXP2000_PCI_CONTROL | PCI_CONTROL_IEE ) ) ;
if ( ( * IXP2000_PCI_CONTROL & PCI_CONTROL_IEE ) = = 0 )
panic ( " IXP2000: PCI I/O is broken on this ixp model, and "
" the needed workaround has not been configured in " ) ;
# endif
2010-07-22 13:18:19 +01:00
hook_fault_code ( 16 + 6 , ixp2000_pci_abort_handler , SIGBUS , 0 ,
2005-04-16 15:20:36 -07:00
" PCI config cycle to non-existent device " ) ;
}
/*
* IXP2000 systems often have large resource requirements , so we just
* use our own resource space .
*/
static struct resource ixp2000_pci_mem_space = {
2005-04-29 21:58:15 +01:00
. start = 0xe0000000 ,
2005-04-16 15:20:36 -07:00
. end = 0xffffffff ,
. flags = IORESOURCE_MEM ,
. name = " PCI Mem Space "
} ;
static struct resource ixp2000_pci_io_space = {
2005-04-29 21:58:16 +01:00
. start = 0x00010000 ,
. end = 0x0001ffff ,
2005-04-16 15:20:36 -07:00
. flags = IORESOURCE_IO ,
. name = " PCI I/O Space "
} ;
int ixp2000_pci_setup ( int nr , struct pci_sys_data * sys )
{
if ( nr > = 1 )
return 0 ;
sys - > resource [ 0 ] = & ixp2000_pci_io_space ;
sys - > resource [ 1 ] = & ixp2000_pci_mem_space ;
sys - > resource [ 2 ] = NULL ;
return 1 ;
}