2005-04-17 02:20:36 +04:00
/*
* hades - pci . c - Hardware specific PCI BIOS functions the Hades Atari clone .
*
* Written by Wout Klaren .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <asm/io.h>
#if 0
# define DBG_DEVS(args) printk args
# else
# define DBG_DEVS(args)
# endif
# if defined(CONFIG_PCI) && defined(CONFIG_HADES)
# include <linux/slab.h>
# include <linux/mm.h>
# include <linux/pci.h>
# include <asm/atarihw.h>
# include <asm/atariints.h>
# include <asm/byteorder.h>
# include <asm/pci.h>
# define HADES_MEM_BASE 0x80000000
# define HADES_MEM_SIZE 0x20000000
# define HADES_CONFIG_BASE 0xA0000000
# define HADES_CONFIG_SIZE 0x10000000
# define HADES_IO_BASE 0xB0000000
# define HADES_IO_SIZE 0x10000000
# define HADES_VIRT_IO_SIZE 0x00010000 /* Only 64k is remapped and actually used. */
# define N_SLOTS 4 /* Number of PCI slots. */
static const char pci_mem_name [ ] = " PCI memory space " ;
static const char pci_io_name [ ] = " PCI I/O space " ;
static const char pci_config_name [ ] = " PCI config space " ;
static struct resource config_space = {
. name = pci_config_name ,
. start = HADES_CONFIG_BASE ,
. end = HADES_CONFIG_BASE + HADES_CONFIG_SIZE - 1
} ;
static struct resource io_space = {
. name = pci_io_name ,
. start = HADES_IO_BASE ,
. end = HADES_IO_BASE + HADES_IO_SIZE - 1
} ;
static const unsigned long pci_conf_base_phys [ ] = {
0xA0080000 , 0xA0040000 , 0xA0020000 , 0xA0010000
} ;
static unsigned long pci_conf_base_virt [ N_SLOTS ] ;
static unsigned long pci_io_base_virt ;
/*
* static void * mk_conf_addr ( unsigned char bus , unsigned char device_fn ,
* unsigned char where )
*
* Calculate the address of the PCI configuration area of the given
* device .
*
* BUG : boards with multiple functions are probably not correctly
* supported .
*/
static void * mk_conf_addr ( struct pci_dev * dev , int where )
{
int device = dev - > devfn > > 3 , function = dev - > devfn & 7 ;
void * result ;
DBG_DEVS ( ( " mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p) \n " ,
dev - > bus - > number , dev - > devfn , where , pci_addr ) ) ;
if ( device > 3 )
{
DBG_DEVS ( ( " mk_conf_addr: device (%d) > 3, returning NULL \n " , device ) ) ;
return NULL ;
}
if ( dev - > bus - > number ! = 0 )
{
DBG_DEVS ( ( " mk_conf_addr: bus (%d) > 0, returning NULL \n " , device ) ) ;
return NULL ;
}
result = ( void * ) ( pci_conf_base_virt [ device ] | ( function < < 8 ) | ( where ) ) ;
DBG_DEVS ( ( " mk_conf_addr: returning pci_addr 0x%lx \n " , ( unsigned long ) result ) ) ;
return result ;
}
static int hades_read_config_byte ( struct pci_dev * dev , int where , u8 * value )
{
volatile unsigned char * pci_addr ;
* value = 0xff ;
if ( ( pci_addr = ( unsigned char * ) mk_conf_addr ( dev , where ) ) = = NULL )
return PCIBIOS_DEVICE_NOT_FOUND ;
* value = * pci_addr ;
return PCIBIOS_SUCCESSFUL ;
}
static int hades_read_config_word ( struct pci_dev * dev , int where , u16 * value )
{
volatile unsigned short * pci_addr ;
* value = 0xffff ;
if ( where & 0x1 )
return PCIBIOS_BAD_REGISTER_NUMBER ;
if ( ( pci_addr = ( unsigned short * ) mk_conf_addr ( dev , where ) ) = = NULL )
return PCIBIOS_DEVICE_NOT_FOUND ;
* value = le16_to_cpu ( * pci_addr ) ;
return PCIBIOS_SUCCESSFUL ;
}
static int hades_read_config_dword ( struct pci_dev * dev , int where , u32 * value )
{
volatile unsigned int * pci_addr ;
unsigned char header_type ;
int result ;
* value = 0xffffffff ;
if ( where & 0x3 )
return PCIBIOS_BAD_REGISTER_NUMBER ;
if ( ( pci_addr = ( unsigned int * ) mk_conf_addr ( dev , where ) ) = = NULL )
return PCIBIOS_DEVICE_NOT_FOUND ;
* value = le32_to_cpu ( * pci_addr ) ;
/*
* Check if the value is an address on the bus . If true , add the
* base address of the PCI memory or PCI I / O area on the Hades .
*/
if ( ( result = hades_read_config_byte ( dev , PCI_HEADER_TYPE ,
& header_type ) ) ! = PCIBIOS_SUCCESSFUL )
return result ;
if ( ( ( where > = PCI_BASE_ADDRESS_0 ) & & ( where < = PCI_BASE_ADDRESS_1 ) ) | |
( ( header_type ! = PCI_HEADER_TYPE_BRIDGE ) & & ( ( where > = PCI_BASE_ADDRESS_2 ) & &
( where < = PCI_BASE_ADDRESS_5 ) ) ) )
{
if ( ( * value & PCI_BASE_ADDRESS_SPACE ) = = PCI_BASE_ADDRESS_SPACE_IO )
{
/*
* Base address register that contains an I / O address . If the
* address is valid on the Hades ( 0 < = * value < HADES_VIRT_IO_SIZE ) ,
* add ' pci_io_base_virt ' to the value .
*/
if ( * value < HADES_VIRT_IO_SIZE )
* value + = pci_io_base_virt ;
}
else
{
/*
* Base address register that contains an memory address . If the
* address is valid on the Hades ( 0 < = * value < HADES_MEM_SIZE ) ,
* add HADES_MEM_BASE to the value .
*/
if ( * value = = 0 )
{
/*
* Base address is 0. Test if this base
* address register is used .
*/
* pci_addr = 0xffffffff ;
if ( * pci_addr ! = 0 )
{
* pci_addr = * value ;
if ( * value < HADES_MEM_SIZE )
* value + = HADES_MEM_BASE ;
}
}
else
{
if ( * value < HADES_MEM_SIZE )
* value + = HADES_MEM_BASE ;
}
}
}
return PCIBIOS_SUCCESSFUL ;
}
static int hades_write_config_byte ( struct pci_dev * dev , int where , u8 value )
{
volatile unsigned char * pci_addr ;
if ( ( pci_addr = ( unsigned char * ) mk_conf_addr ( dev , where ) ) = = NULL )
return PCIBIOS_DEVICE_NOT_FOUND ;
* pci_addr = value ;
return PCIBIOS_SUCCESSFUL ;
}
static int hades_write_config_word ( struct pci_dev * dev , int where , u16 value )
{
volatile unsigned short * pci_addr ;
if ( ( pci_addr = ( unsigned short * ) mk_conf_addr ( dev , where ) ) = = NULL )
return PCIBIOS_DEVICE_NOT_FOUND ;
* pci_addr = cpu_to_le16 ( value ) ;
return PCIBIOS_SUCCESSFUL ;
}
static int hades_write_config_dword ( struct pci_dev * dev , int where , u32 value )
{
volatile unsigned int * pci_addr ;
unsigned char header_type ;
int result ;
if ( ( pci_addr = ( unsigned int * ) mk_conf_addr ( dev , where ) ) = = NULL )
return PCIBIOS_DEVICE_NOT_FOUND ;
/*
* Check if the value is an address on the bus . If true , subtract the
* base address of the PCI memory or PCI I / O area on the Hades .
*/
if ( ( result = hades_read_config_byte ( dev , PCI_HEADER_TYPE ,
& header_type ) ) ! = PCIBIOS_SUCCESSFUL )
return result ;
if ( ( ( where > = PCI_BASE_ADDRESS_0 ) & & ( where < = PCI_BASE_ADDRESS_1 ) ) | |
( ( header_type ! = PCI_HEADER_TYPE_BRIDGE ) & & ( ( where > = PCI_BASE_ADDRESS_2 ) & &
( where < = PCI_BASE_ADDRESS_5 ) ) ) )
{
if ( ( value & PCI_BASE_ADDRESS_SPACE ) = =
PCI_BASE_ADDRESS_SPACE_IO )
{
/*
* I / O address . Check if the address is valid address on
* the Hades ( pci_io_base_virt < = value < pci_io_base_virt +
* HADES_VIRT_IO_SIZE ) or if the value is 0xffffffff . If not
* true do not write the base address register . If it is a
* valid base address subtract ' pci_io_base_virt ' from the value .
*/
if ( ( value > = pci_io_base_virt ) & & ( value < ( pci_io_base_virt +
HADES_VIRT_IO_SIZE ) ) )
value - = pci_io_base_virt ;
else
{
if ( value ! = 0xffffffff )
return PCIBIOS_SET_FAILED ;
}
}
else
{
/*
* Memory address . Check if the address is valid address on
* the Hades ( HADES_MEM_BASE < = value < HADES_MEM_BASE + HADES_MEM_SIZE ) or
* if the value is 0xffffffff . If not true do not write
* the base address register . If it is a valid base address
* subtract HADES_MEM_BASE from the value .
*/
if ( ( value > = HADES_MEM_BASE ) & & ( value < ( HADES_MEM_BASE + HADES_MEM_SIZE ) ) )
value - = HADES_MEM_BASE ;
else
{
if ( value ! = 0xffffffff )
return PCIBIOS_SET_FAILED ;
}
}
}
* pci_addr = cpu_to_le32 ( value ) ;
return PCIBIOS_SUCCESSFUL ;
}
/*
* static inline void hades_fixup ( void )
*
* Assign IRQ numbers as used by Linux to the interrupt pins
* of the PCI cards .
*/
static void __init hades_fixup ( int pci_modify )
{
char irq_tab [ 4 ] = {
[ 0 ] = IRQ_TT_MFP_IO0 , /* Slot 0. */
[ 1 ] = IRQ_TT_MFP_IO1 , /* Slot 1. */
[ 2 ] = IRQ_TT_MFP_SCC , /* Slot 2. */
[ 3 ] = IRQ_TT_MFP_SCSIDMA /* Slot 3. */
} ;
struct pci_dev * dev = NULL ;
unsigned char slot ;
/*
* Go through all devices , fixing up irqs as we see fit :
*/
while ( ( dev = pci_get_device ( PCI_ANY_ID , PCI_ANY_ID , dev ) ) ! = NULL )
{
if ( dev - > class > > 16 ! = PCI_BASE_CLASS_BRIDGE )
{
slot = PCI_SLOT ( dev - > devfn ) ; /* Determine slot number. */
dev - > irq = irq_tab [ slot ] ;
if ( pci_modify )
pci_write_config_byte ( dev , PCI_INTERRUPT_LINE , dev - > irq ) ;
}
}
}
/*
* static void hades_conf_device ( struct pci_dev * dev )
*
* Machine dependent Configure the given device .
*
* Parameters :
*
* dev - the pci device .
*/
static void __init hades_conf_device ( struct pci_dev * dev )
{
pci_write_config_byte ( dev , PCI_CACHE_LINE_SIZE , 0 ) ;
}
static struct pci_ops hades_pci_ops = {
. read_byte = hades_read_config_byte ,
. read_word = hades_read_config_word ,
. read_dword = hades_read_config_dword ,
. write_byte = hades_write_config_byte ,
. write_word = hades_write_config_word ,
. write_dword = hades_write_config_dword
} ;
/*
* struct pci_bus_info * init_hades_pci ( void )
*
* Machine specific initialisation :
*
* - Allocate and initialise a ' pci_bus_info ' structure
* - Initialise hardware
*
* Result : pointer to ' pci_bus_info ' structure .
*/
struct pci_bus_info * __init init_hades_pci ( void )
{
struct pci_bus_info * bus ;
int i ;
/*
* Remap I / O and configuration space .
*/
pci_io_base_virt = ( unsigned long ) ioremap ( HADES_IO_BASE , HADES_VIRT_IO_SIZE ) ;
for ( i = 0 ; i < N_SLOTS ; i + + )
pci_conf_base_virt [ i ] = ( unsigned long ) ioremap ( pci_conf_base_phys [ i ] , 0x10000 ) ;
/*
* Allocate memory for bus info structure .
*/
2006-12-07 07:34:51 +03:00
bus = kzalloc ( sizeof ( struct pci_bus_info ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! bus )
return NULL ;
/*
* Claim resources . The m68k has no separate I / O space , both
* PCI memory space and PCI I / O space are in memory space . Therefore
* the I / O resources are requested in memory space as well .
*/
if ( request_resource ( & iomem_resource , & config_space ) ! = 0 )
{
kfree ( bus ) ;
return NULL ;
}
if ( request_resource ( & iomem_resource , & io_space ) ! = 0 )
{
release_resource ( & config_space ) ;
kfree ( bus ) ;
return NULL ;
}
bus - > mem_space . start = HADES_MEM_BASE ;
bus - > mem_space . end = HADES_MEM_BASE + HADES_MEM_SIZE - 1 ;
bus - > mem_space . name = pci_mem_name ;
# if 1
if ( request_resource ( & iomem_resource , & bus - > mem_space ) ! = 0 )
{
release_resource ( & io_space ) ;
release_resource ( & config_space ) ;
kfree ( bus ) ;
return NULL ;
}
# endif
bus - > io_space . start = pci_io_base_virt ;
bus - > io_space . end = pci_io_base_virt + HADES_VIRT_IO_SIZE - 1 ;
bus - > io_space . name = pci_io_name ;
# if 1
if ( request_resource ( & ioport_resource , & bus - > io_space ) ! = 0 )
{
release_resource ( & bus - > mem_space ) ;
release_resource ( & io_space ) ;
release_resource ( & config_space ) ;
kfree ( bus ) ;
return NULL ;
}
# endif
/*
* Set hardware dependent functions .
*/
bus - > m68k_pci_ops = & hades_pci_ops ;
bus - > fixup = hades_fixup ;
bus - > conf_device = hades_conf_device ;
/*
* Select high to low edge for PCI interrupts .
*/
tt_mfp . active_edge & = ~ 0x27 ;
return bus ;
}
# endif