2006-09-27 16:43:28 +09:00
/*
* Generic SH - 4 / SH - 4 A PCIC operations ( SH7751 , SH7780 ) .
*
2009-04-17 14:07:57 +09:00
* Copyright ( C ) 2002 - 2009 Paul Mundt
2006-09-27 16:43:28 +09:00
*
* This file is subject to the terms and conditions of the GNU General Public
* License v2 . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/pci.h>
2009-04-17 14:07:57 +09:00
# include <linux/io.h>
2006-09-27 16:43:28 +09:00
# include <asm/addrspace.h>
# include "pci-sh4.h"
/*
* Direct access to PCI hardware . . .
*/
# define CONFIG_CMD(bus, devfn, where) \
2009-04-17 14:07:57 +09:00
( P1SEG | ( bus - > number < < 16 ) | ( devfn < < 8 ) | ( where & ~ 3 ) )
2006-09-27 16:43:28 +09:00
static DEFINE_SPINLOCK ( sh4_pci_lock ) ;
/*
* Functions for accessing PCI configuration space with type 1 accesses
*/
static int sh4_pci_read ( struct pci_bus * bus , unsigned int devfn ,
int where , int size , u32 * val )
{
2008-02-19 21:34:55 +09:00
struct pci_channel * chan = bus - > sysdata ;
2006-09-27 16:43:28 +09:00
unsigned long flags ;
u32 data ;
/*
* PCIPDR may only be accessed as 32 bit words ,
* so we must do byte alignment by hand
*/
spin_lock_irqsave ( & sh4_pci_lock , flags ) ;
2008-02-19 21:34:55 +09:00
pci_write_reg ( chan , CONFIG_CMD ( bus , devfn , where ) , SH4_PCIPAR ) ;
data = pci_read_reg ( chan , SH4_PCIPDR ) ;
2006-09-27 16:43:28 +09:00
spin_unlock_irqrestore ( & sh4_pci_lock , flags ) ;
switch ( size ) {
case 1 :
* val = ( data > > ( ( where & 3 ) < < 3 ) ) & 0xff ;
break ;
case 2 :
* val = ( data > > ( ( where & 2 ) < < 3 ) ) & 0xffff ;
break ;
case 4 :
* val = data ;
break ;
default :
return PCIBIOS_FUNC_NOT_SUPPORTED ;
}
return PCIBIOS_SUCCESSFUL ;
}
/*
* Since SH4 only does 32 bit access we ' ll have to do a read ,
* mask , write operation .
* We ' ll allow an odd byte offset , though it should be illegal .
*/
static int sh4_pci_write ( struct pci_bus * bus , unsigned int devfn ,
int where , int size , u32 val )
{
2008-02-19 21:34:55 +09:00
struct pci_channel * chan = bus - > sysdata ;
2006-09-27 16:43:28 +09:00
unsigned long flags ;
int shift ;
u32 data ;
spin_lock_irqsave ( & sh4_pci_lock , flags ) ;
2008-02-19 21:34:55 +09:00
pci_write_reg ( chan , CONFIG_CMD ( bus , devfn , where ) , SH4_PCIPAR ) ;
data = pci_read_reg ( chan , SH4_PCIPDR ) ;
2006-09-27 16:43:28 +09:00
spin_unlock_irqrestore ( & sh4_pci_lock , flags ) ;
switch ( size ) {
case 1 :
shift = ( where & 3 ) < < 3 ;
data & = ~ ( 0xff < < shift ) ;
data | = ( ( val & 0xff ) < < shift ) ;
break ;
case 2 :
shift = ( where & 2 ) < < 3 ;
data & = ~ ( 0xffff < < shift ) ;
data | = ( ( val & 0xffff ) < < shift ) ;
break ;
case 4 :
data = val ;
break ;
default :
return PCIBIOS_FUNC_NOT_SUPPORTED ;
}
2008-02-19 21:34:55 +09:00
pci_write_reg ( chan , data , SH4_PCIPDR ) ;
2006-09-27 16:43:28 +09:00
return PCIBIOS_SUCCESSFUL ;
}
struct pci_ops sh4_pci_ops = {
. read = sh4_pci_read ,
. write = sh4_pci_write ,
} ;
/*
* Not really related to pci_ops , but it ' s common and not worth shoving
* somewhere else for now . .
*/
2009-03-11 15:41:51 +09:00
int __init sh4_pci_check_direct ( struct pci_channel * chan )
2006-09-27 16:43:28 +09:00
{
/*
* Check if configuration works .
*/
2009-04-20 19:48:48 +09:00
unsigned int tmp = pci_read_reg ( chan , SH4_PCIPAR ) ;
2006-09-27 16:43:28 +09:00
2009-04-20 19:48:48 +09:00
pci_write_reg ( chan , P1SEG , SH4_PCIPAR ) ;
2006-09-27 16:43:28 +09:00
2009-04-20 19:48:48 +09:00
if ( pci_read_reg ( chan , SH4_PCIPAR ) = = P1SEG ) {
2009-03-11 15:41:51 +09:00
pci_write_reg ( chan , tmp , SH4_PCIPAR ) ;
2009-04-20 19:48:48 +09:00
printk ( KERN_INFO " PCI: Using configuration type 1 \n " ) ;
request_region ( chan - > reg_base + SH4_PCIPAR , 8 ,
" PCI conf1 " ) ;
return 0 ;
2006-09-27 16:43:28 +09:00
}
2009-04-20 19:48:48 +09:00
pci_write_reg ( chan , tmp , SH4_PCIPAR ) ;
printk ( KERN_ERR " PCI: %s failed \n " , __func__ ) ;
2006-09-27 16:43:28 +09:00
return - EINVAL ;
}
2009-03-11 15:41:51 +09:00
int __attribute__ ( ( weak ) ) pci_fixup_pcic ( struct pci_channel * chan )
2007-03-29 00:04:39 +09:00
{
/* Nothing to do. */
return 0 ;
}