2005-04-16 15:20:36 -07:00
/* pci-vdk.c: MB93090-MB00 (VDK) PCI support
*
* Copyright ( C ) 2003 , 2004 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* 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/types.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <asm/segment.h>
# include <asm/io.h>
# include <asm/mb-regs.h>
# include <asm/mb86943a.h>
# include "pci-frv.h"
unsigned int __nongpreldata pci_probe = 1 ;
struct pci_ops * __nongpreldata pci_root_ops ;
2009-04-21 12:24:51 -07:00
/*
* The accessible PCI window does not cover the entire CPU address space , but
* there are devices we want to access outside of that window , so we need to
* insert specific PCI bus resources instead of using the platform - level bus
* resources directly for the PCI root bus .
*
* These are configured and inserted by pcibios_init ( ) and are attached to the
* root bus by pcibios_fixup_bus ( ) .
*/
static struct resource pci_ioport_resource = {
. name = " PCI IO " ,
. start = 0 ,
. end = IO_SPACE_LIMIT ,
. flags = IORESOURCE_IO ,
} ;
static struct resource pci_iomem_resource = {
. name = " PCI mem " ,
. start = 0 ,
. end = - 1 ,
. flags = IORESOURCE_MEM ,
} ;
2005-04-16 15:20:36 -07:00
/*
* Functions for accessing PCI configuration space
*/
# define CONFIG_CMD(bus, dev, where) \
( 0x80000000 | ( bus - > number < < 16 ) | ( devfn < < 8 ) | ( where & ~ 3 ) )
# define __set_PciCfgAddr(A) writel((A), (volatile void __iomem *) __region_CS1 + 0x80)
# define __get_PciCfgDataB(A) readb((volatile void __iomem *) __region_CS1 + 0x88 + ((A) & 3))
# define __get_PciCfgDataW(A) readw((volatile void __iomem *) __region_CS1 + 0x88 + ((A) & 2))
# define __get_PciCfgDataL(A) readl((volatile void __iomem *) __region_CS1 + 0x88)
# define __set_PciCfgDataB(A,V) \
writeb ( ( V ) , ( volatile void __iomem * ) __region_CS1 + 0x88 + ( 3 - ( ( A ) & 3 ) ) )
# define __set_PciCfgDataW(A,V) \
writew ( ( V ) , ( volatile void __iomem * ) __region_CS1 + 0x88 + ( 2 - ( ( A ) & 2 ) ) )
# define __set_PciCfgDataL(A,V) \
writel ( ( V ) , ( volatile void __iomem * ) __region_CS1 + 0x88 )
# define __get_PciBridgeDataB(A) readb((volatile void __iomem *) __region_CS1 + 0x800 + (A))
# define __get_PciBridgeDataW(A) readw((volatile void __iomem *) __region_CS1 + 0x800 + (A))
# define __get_PciBridgeDataL(A) readl((volatile void __iomem *) __region_CS1 + 0x800 + (A))
# define __set_PciBridgeDataB(A,V) writeb((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A))
# define __set_PciBridgeDataW(A,V) writew((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A))
# define __set_PciBridgeDataL(A,V) writel((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A))
static inline int __query ( const struct pci_dev * dev )
{
// return dev->bus->number==0 && (dev->devfn==PCI_DEVFN(0,0));
// return dev->bus->number==1;
// return dev->bus->number==0 &&
// (dev->devfn==PCI_DEVFN(2,0) || dev->devfn==PCI_DEVFN(3,0));
return 0 ;
}
/*****************************************************************************/
/*
*
*/
static int pci_frv_read_config ( struct pci_bus * bus , unsigned int devfn , int where , int size ,
u32 * val )
{
u32 _value ;
if ( bus - > number = = 0 & & devfn = = PCI_DEVFN ( 0 , 0 ) ) {
_value = __get_PciBridgeDataL ( where & ~ 3 ) ;
}
else {
__set_PciCfgAddr ( CONFIG_CMD ( bus , devfn , where ) ) ;
_value = __get_PciCfgDataL ( where & ~ 3 ) ;
}
switch ( size ) {
case 1 :
_value = _value > > ( ( where & 3 ) * 8 ) ;
break ;
case 2 :
_value = _value > > ( ( where & 2 ) * 8 ) ;
break ;
case 4 :
break ;
default :
BUG ( ) ;
}
* val = _value ;
return PCIBIOS_SUCCESSFUL ;
}
static int pci_frv_write_config ( struct pci_bus * bus , unsigned int devfn , int where , int size ,
u32 value )
{
switch ( size ) {
case 1 :
if ( bus - > number = = 0 & & devfn = = PCI_DEVFN ( 0 , 0 ) ) {
__set_PciBridgeDataB ( where , value ) ;
}
else {
__set_PciCfgAddr ( CONFIG_CMD ( bus , devfn , where ) ) ;
__set_PciCfgDataB ( where , value ) ;
}
break ;
case 2 :
if ( bus - > number = = 0 & & devfn = = PCI_DEVFN ( 0 , 0 ) ) {
__set_PciBridgeDataW ( where , value ) ;
}
else {
__set_PciCfgAddr ( CONFIG_CMD ( bus , devfn , where ) ) ;
__set_PciCfgDataW ( where , value ) ;
}
break ;
case 4 :
if ( bus - > number = = 0 & & devfn = = PCI_DEVFN ( 0 , 0 ) ) {
__set_PciBridgeDataL ( where , value ) ;
}
else {
__set_PciCfgAddr ( CONFIG_CMD ( bus , devfn , where ) ) ;
__set_PciCfgDataL ( where , value ) ;
}
break ;
default :
BUG ( ) ;
}
return PCIBIOS_SUCCESSFUL ;
}
static struct pci_ops pci_direct_frv = {
pci_frv_read_config ,
pci_frv_write_config ,
} ;
/*
* Before we decide to use direct hardware access mechanisms , we try to do some
* trivial checks to ensure it at least _seems_ to be working - - we just test
* whether bus 00 contains a host bridge ( this is similar to checking
* techniques used in XFree86 , but ours should be more reliable since we
* attempt to make use of direct access hints provided by the PCI BIOS ) .
*
* This should be close to trivial , but it isn ' t , because there are buggy
* chipsets ( yes , you guessed it , by Intel and Compaq ) that have no class ID .
*/
static int __init pci_sanity_check ( struct pci_ops * o )
{
struct pci_bus bus ; /* Fake bus and device */
u32 id ;
bus . number = 0 ;
if ( o - > read ( & bus , 0 , PCI_VENDOR_ID , 4 , & id ) = = PCIBIOS_SUCCESSFUL ) {
printk ( " PCI: VDK Bridge device:vendor: %08x \n " , id ) ;
if ( id = = 0x200e10cf )
return 1 ;
}
printk ( " PCI: VDK Bridge: Sanity check failed \n " ) ;
return 0 ;
}
static struct pci_ops * __init pci_check_direct ( void )
{
unsigned long flags ;
local_irq_save ( flags ) ;
/* check if access works */
if ( pci_sanity_check ( & pci_direct_frv ) ) {
local_irq_restore ( flags ) ;
printk ( " PCI: Using configuration frv \n " ) ;
// request_mem_region(0xBE040000, 256, "FRV bridge");
// request_mem_region(0xBFFFFFF4, 12, "PCI frv");
return & pci_direct_frv ;
}
local_irq_restore ( flags ) ;
return NULL ;
}
/*
* Exceptions for specific devices . Usually work - arounds for fatal design flaws .
*/
static void __init pci_fixup_umc_ide ( struct pci_dev * d )
{
/*
* UM8886BF IDE controller sets region type bits incorrectly ,
* therefore they look like memory despite of them being I / O .
*/
int i ;
printk ( " PCI: Fixing base address flags for device %s \n " , pci_name ( d ) ) ;
for ( i = 0 ; i < 4 ; i + + )
d - > resource [ i ] . flags | = PCI_BASE_ADDRESS_SPACE_IO ;
}
2012-12-21 14:06:37 -08:00
static void pci_fixup_ide_bases ( struct pci_dev * d )
2005-04-16 15:20:36 -07:00
{
int i ;
/*
* PCI IDE controllers use non - standard I / O port decoding , respect it .
*/
if ( ( d - > class > > 8 ) ! = PCI_CLASS_STORAGE_IDE )
return ;
printk ( " PCI: IDE base address fixup for %s \n " , pci_name ( d ) ) ;
for ( i = 0 ; i < 4 ; i + + ) {
struct resource * r = & d - > resource [ i ] ;
if ( ( r - > start & ~ 0x80 ) = = 0x374 ) {
r - > start | = 2 ;
r - > end = r - > start ;
}
}
}
2012-12-21 14:06:37 -08:00
static void pci_fixup_ide_trash ( struct pci_dev * d )
2005-04-16 15:20:36 -07:00
{
int i ;
/*
* There exist PCI IDE controllers which have utter garbage
* in first four base registers . Ignore that .
*/
printk ( " PCI: IDE base address trash cleared for %s \n " , pci_name ( d ) ) ;
for ( i = 0 ; i < 4 ; i + + )
d - > resource [ i ] . start = d - > resource [ i ] . end = d - > resource [ i ] . flags = 0 ;
}
2012-12-21 14:06:37 -08:00
static void pci_fixup_latency ( struct pci_dev * d )
2005-04-16 15:20:36 -07:00
{
/*
* SiS 5597 and 5598 chipsets require latency timer set to
* at most 32 to avoid lockups .
*/
DBG ( " PCI: Setting max latency to 32 \n " ) ;
pcibios_max_latency = 32 ;
}
DECLARE_PCI_FIXUP_HEADER ( PCI_VENDOR_ID_UMC , PCI_DEVICE_ID_UMC_UM8886BF , pci_fixup_umc_ide ) ;
DECLARE_PCI_FIXUP_HEADER ( PCI_VENDOR_ID_SI , PCI_DEVICE_ID_SI_5513 , pci_fixup_ide_trash ) ;
DECLARE_PCI_FIXUP_HEADER ( PCI_VENDOR_ID_SI , PCI_DEVICE_ID_SI_5597 , pci_fixup_latency ) ;
DECLARE_PCI_FIXUP_HEADER ( PCI_VENDOR_ID_SI , PCI_DEVICE_ID_SI_5598 , pci_fixup_latency ) ;
DECLARE_PCI_FIXUP_HEADER ( PCI_ANY_ID , PCI_ANY_ID , pci_fixup_ide_bases ) ;
/*
* Called after each bus is probed , but before its children
* are examined .
*/
2013-07-05 15:17:27 -06:00
void pcibios_fixup_bus ( struct pci_bus * bus )
2005-04-16 15:20:36 -07:00
{
#if 0
printk ( " ### PCIBIOS_FIXUP_BUS(%d) \n " , bus - > number ) ;
# endif
2009-04-21 12:24:51 -07:00
2005-04-16 15:20:36 -07:00
pci_read_bridge_bases ( bus ) ;
if ( bus - > number = = 0 ) {
struct pci_dev * dev ;
2012-08-16 16:44:18 -06:00
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
2005-04-16 15:20:36 -07:00
if ( dev - > devfn = = 0 ) {
dev - > resource [ 0 ] . start = 0 ;
dev - > resource [ 0 ] . end = 0 ;
}
}
}
}
/*
* Initialization . Try all known PCI access methods . Note that we support
* using both PCI BIOS and direct access : in such cases , we use I / O ports
* to access config space , but we still keep BIOS order of cards to be
* compatible with 2.0 . X . This should go away some day .
*/
int __init pcibios_init ( void )
{
struct pci_ops * dir = NULL ;
2011-10-28 16:26:21 -06:00
LIST_HEAD ( resources ) ;
2005-04-16 15:20:36 -07:00
if ( ! mb93090_mb00_detected )
return - ENXIO ;
__reg_MB86943_sl_ctl | = MB86943_SL_CTL_DRCT_MASTER_SWAP | MB86943_SL_CTL_DRCT_SLAVE_SWAP ;
__reg_MB86943_ecs_base ( 1 ) = ( ( __region_CS2 + 0x01000000 ) > > 9 ) | 0x08000000 ;
__reg_MB86943_ecs_base ( 2 ) = ( ( __region_CS2 + 0x00000000 ) > > 9 ) | 0x08000000 ;
* ( volatile uint32_t * ) ( __region_CS1 + 0x848 ) = 0xe0000000 ;
* ( volatile uint32_t * ) ( __region_CS1 + 0x8b8 ) = 0x00000000 ;
__reg_MB86943_sl_pci_io_base = ( __region_CS2 + 0x04000000 ) > > 9 ;
__reg_MB86943_sl_pci_mem_base = ( __region_CS2 + 0x08000000 ) > > 9 ;
__reg_MB86943_pci_sl_io_base = __region_CS2 + 0x04000000 ;
__reg_MB86943_pci_sl_mem_base = __region_CS2 + 0x08000000 ;
mb ( ) ;
2007-08-01 19:04:51 +01:00
/* enable PCI arbitration */
__reg_MB86943_pci_arbiter = MB86943_PCIARB_EN ;
2005-04-16 15:20:36 -07:00
2009-04-21 12:24:51 -07:00
pci_ioport_resource . start = ( __reg_MB86943_sl_pci_io_base < < 9 ) & 0xfffffc00 ;
pci_ioport_resource . end = ( __reg_MB86943_sl_pci_io_range < < 9 ) | 0x3ff ;
pci_ioport_resource . end + = pci_ioport_resource . start ;
2005-04-16 15:20:36 -07:00
2006-07-10 04:44:51 -07:00
printk ( " PCI IO window: %08llx-%08llx \n " ,
2009-04-21 12:24:51 -07:00
( unsigned long long ) pci_ioport_resource . start ,
( unsigned long long ) pci_ioport_resource . end ) ;
2005-04-16 15:20:36 -07:00
2009-04-21 12:24:51 -07:00
pci_iomem_resource . start = ( __reg_MB86943_sl_pci_mem_base < < 9 ) & 0xfffffc00 ;
pci_iomem_resource . end = ( __reg_MB86943_sl_pci_mem_range < < 9 ) | 0x3ff ;
pci_iomem_resource . end + = pci_iomem_resource . start ;
2005-04-16 15:20:36 -07:00
2009-04-21 12:24:51 -07:00
/* Reserve somewhere to write to flush posted writes. This is used by
* __flush_PCI_writes ( ) from asm / io . h to force the write FIFO in the
* CPU - PCI bridge to flush as this doesn ' t happen automatically when a
* read is performed on the MB93090 development kit motherboard .
*/
pci_iomem_resource . start + = 0x400 ;
2005-04-16 15:20:36 -07:00
2006-07-10 04:44:51 -07:00
printk ( " PCI MEM window: %08llx-%08llx \n " ,
2009-04-21 12:24:51 -07:00
( unsigned long long ) pci_iomem_resource . start ,
( unsigned long long ) pci_iomem_resource . end ) ;
2006-07-10 04:44:51 -07:00
printk ( " PCI DMA memory: %08lx-%08lx \n " ,
dma_coherent_mem_start , dma_coherent_mem_end ) ;
2005-04-16 15:20:36 -07:00
2009-04-21 12:24:51 -07:00
if ( insert_resource ( & iomem_resource , & pci_iomem_resource ) < 0 )
panic ( " Unable to insert PCI IOMEM resource \n " ) ;
if ( insert_resource ( & ioport_resource , & pci_ioport_resource ) < 0 )
panic ( " Unable to insert PCI IOPORT resource \n " ) ;
2005-04-16 15:20:36 -07:00
if ( ! pci_probe )
return - ENXIO ;
dir = pci_check_direct ( ) ;
if ( dir )
pci_root_ops = dir ;
else {
printk ( " PCI: No PCI bus detected \n " ) ;
return - ENXIO ;
}
printk ( " PCI: Probing PCI hardware \n " ) ;
2011-10-28 16:26:21 -06:00
pci_add_resource ( & resources , & pci_ioport_resource ) ;
pci_add_resource ( & resources , & pci_iomem_resource ) ;
2013-01-03 15:30:09 -07:00
pci_scan_root_bus ( NULL , 0 , pci_root_ops , NULL , & resources ) ;
2005-04-16 15:20:36 -07:00
pcibios_irq_init ( ) ;
pcibios_fixup_irqs ( ) ;
pcibios_resource_survey ( ) ;
return 0 ;
}
arch_initcall ( pcibios_init ) ;
char * __init pcibios_setup ( char * str )
{
if ( ! strcmp ( str , " off " ) ) {
pci_probe = 0 ;
return NULL ;
}
return str ;
}
int pcibios_enable_device ( struct pci_dev * dev , int mask )
{
int err ;
2008-10-15 16:50:48 +01:00
if ( ( err = pci_enable_resources ( dev , mask ) ) < 0 )
2005-04-16 15:20:36 -07:00
return err ;
2007-03-28 15:36:09 +02:00
if ( ! dev - > msi_enabled )
pcibios_enable_irq ( dev ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}