2007-05-08 01:28:38 +04:00
/*
* Board setup routines for the IBM 750 GX / CL platform w / TSI10x bridge
*
* Copyright 2007 IBM Corporation
*
* Stephen Winiecki < stevewin @ us . ibm . com >
* Josh Boyer < jwboyer @ linux . vnet . ibm . com >
*
* Based on code from mpc7448_hpc2 . c
*
* 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/stddef.h>
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/kdev_t.h>
# include <linux/console.h>
# include <linux/delay.h>
# include <linux/irq.h>
# include <linux/seq_file.h>
# include <linux/root_dev.h>
# include <linux/serial.h>
# include <linux/tty.h>
# include <linux/serial_core.h>
2007-11-13 20:13:03 +03:00
# include <linux/of_platform.h>
2011-05-27 21:23:32 +04:00
# include <linux/module.h>
2007-05-08 01:28:38 +04:00
# include <asm/time.h>
# include <asm/machdep.h>
# include <asm/prom.h>
# include <asm/udbg.h>
# include <asm/tsi108.h>
# include <asm/pci-bridge.h>
# include <asm/reg.h>
# include <mm/mmu_decl.h>
# include <asm/tsi108_irq.h>
# include <asm/tsi108_pci.h>
# include <asm/mpic.h>
# undef DEBUG
# define HOLLY_PCI_CFG_PHYS 0x7c000000
2007-06-22 09:23:57 +04:00
int holly_exclude_device ( struct pci_controller * hose , u_char bus , u_char devfn )
2007-05-08 01:28:38 +04:00
{
if ( bus = = 0 & & PCI_SLOT ( devfn ) = = 0 )
return PCIBIOS_DEVICE_NOT_FOUND ;
else
return PCIBIOS_SUCCESSFUL ;
}
static void holly_remap_bridge ( void )
{
u32 lut_val , lut_addr ;
int i ;
printk ( KERN_INFO " Remapping PCI bridge \n " ) ;
/* Re-init the PCI bridge and LUT registers to have mappings that don't
* rely on PIBS
*/
lut_addr = 0x900 ;
for ( i = 0 ; i < 31 ; i + + ) {
tsi108_write_reg ( TSI108_PB_OFFSET + lut_addr , 0x00000201 ) ;
lut_addr + = 4 ;
tsi108_write_reg ( TSI108_PB_OFFSET + lut_addr , 0x0 ) ;
lut_addr + = 4 ;
}
/* Reserve the last LUT entry for PCI I/O space */
tsi108_write_reg ( TSI108_PB_OFFSET + lut_addr , 0x00000241 ) ;
lut_addr + = 4 ;
tsi108_write_reg ( TSI108_PB_OFFSET + lut_addr , 0x0 ) ;
/* Map PCI I/O space */
tsi108_write_reg ( TSI108_PCI_PFAB_IO_UPPER , 0x0 ) ;
tsi108_write_reg ( TSI108_PCI_PFAB_IO , 0x1 ) ;
/* Map PCI CFG space */
tsi108_write_reg ( TSI108_PCI_PFAB_BAR0_UPPER , 0x0 ) ;
tsi108_write_reg ( TSI108_PCI_PFAB_BAR0 , 0x7c000000 | 0x01 ) ;
/* We don't need MEM32 and PRM remapping so disable them */
tsi108_write_reg ( TSI108_PCI_PFAB_MEM32 , 0x0 ) ;
tsi108_write_reg ( TSI108_PCI_PFAB_PFM3 , 0x0 ) ;
tsi108_write_reg ( TSI108_PCI_PFAB_PFM4 , 0x0 ) ;
/* Set P2O_BAR0 */
tsi108_write_reg ( TSI108_PCI_P2O_BAR0_UPPER , 0x0 ) ;
tsi108_write_reg ( TSI108_PCI_P2O_BAR0 , 0xc0000000 ) ;
/* Init the PCI LUTs to do no remapping */
lut_addr = 0x500 ;
lut_val = 0x00000002 ;
for ( i = 0 ; i < 32 ; i + + ) {
tsi108_write_reg ( TSI108_PCI_OFFSET + lut_addr , lut_val ) ;
lut_addr + = 4 ;
tsi108_write_reg ( TSI108_PCI_OFFSET + lut_addr , 0x40000000 ) ;
lut_addr + = 4 ;
lut_val + = 0x02000000 ;
}
tsi108_write_reg ( TSI108_PCI_P2O_PAGE_SIZES , 0x00007900 ) ;
/* Set 64-bit PCI bus address for system memory */
tsi108_write_reg ( TSI108_PCI_P2O_BAR2_UPPER , 0x0 ) ;
tsi108_write_reg ( TSI108_PCI_P2O_BAR2 , 0x0 ) ;
}
static void __init holly_setup_arch ( void )
{
struct device_node * np ;
if ( ppc_md . progress )
ppc_md . progress ( " holly_setup_arch():set_bridge " , 0 ) ;
tsi108_csr_vir_base = get_vir_csrbase ( ) ;
/* setup PCI host bridge */
holly_remap_bridge ( ) ;
np = of_find_node_by_type ( NULL , " pci " ) ;
if ( np )
tsi108_setup_pci ( np , HOLLY_PCI_CFG_PHYS , 1 ) ;
ppc_md . pci_exclude_device = holly_exclude_device ;
if ( ppc_md . progress )
ppc_md . progress ( " tsi108: resources set " , 0x100 ) ;
printk ( KERN_INFO " PPC750GX/CL Platform \n " ) ;
}
/*
2007-08-01 13:41:09 +04:00
* Interrupt setup and service . Interrupts on the holly come
2007-05-08 01:28:38 +04:00
* from the four external INT pins , PCI interrupts are routed via
* PCI interrupt control registers , it generates internal IRQ23
*
* Interrupt routing on the Holly Board :
* TSI108 : PB_INT [ 0 ] - > CPU0 : INT #
* TSI108 : PB_INT [ 1 ] - > CPU0 : MCP #
* TSI108 : PB_INT [ 2 ] - > N / C
* TSI108 : PB_INT [ 3 ] - > N / C
*/
static void __init holly_init_IRQ ( void )
{
struct mpic * mpic ;
# ifdef CONFIG_PCI
unsigned int cascade_pci_irq ;
struct device_node * tsi_pci ;
struct device_node * cascade_node = NULL ;
# endif
2011-12-22 14:19:14 +04:00
mpic = mpic_alloc ( NULL , 0 , MPIC_BIG_ENDIAN |
2007-05-08 01:28:38 +04:00
MPIC_SPV_EOI | MPIC_NO_PTHROU_DIS | MPIC_REGSET_TSI108 ,
2011-12-22 14:19:12 +04:00
24 , 0 ,
2007-05-08 01:28:38 +04:00
" Tsi108_PIC " ) ;
BUG_ON ( mpic = = NULL ) ;
2011-12-02 10:28:01 +04:00
mpic_assign_isu ( mpic , 0 , mpic - > paddr + 0x100 ) ;
2007-05-08 01:28:38 +04:00
mpic_init ( mpic ) ;
# ifdef CONFIG_PCI
tsi_pci = of_find_node_by_type ( NULL , " pci " ) ;
if ( tsi_pci = = NULL ) {
printk ( KERN_ERR " %s: No tsi108 pci node found ! \n " , __func__ ) ;
return ;
}
cascade_node = of_find_node_by_type ( NULL , " pic-router " ) ;
if ( cascade_node = = NULL ) {
printk ( KERN_ERR " %s: No tsi108 pci cascade node found ! \n " , __func__ ) ;
return ;
}
cascade_pci_irq = irq_of_parse_and_map ( tsi_pci , 0 ) ;
pr_debug ( " %s: tsi108 cascade_pci_irq = 0x%x \n " , __func__ , ( u32 ) cascade_pci_irq ) ;
tsi108_pci_int_init ( cascade_node ) ;
2011-03-25 18:45:20 +03:00
irq_set_handler_data ( cascade_pci_irq , mpic ) ;
irq_set_chained_handler ( cascade_pci_irq , tsi108_irq_cascade ) ;
2007-05-08 01:28:38 +04:00
# endif
/* Configure MPIC outputs to CPU0 */
tsi108_write_reg ( TSI108_MPIC_OFFSET + 0x30c , 0 ) ;
}
void holly_show_cpuinfo ( struct seq_file * m )
{
seq_printf ( m , " vendor \t \t : IBM \n " ) ;
seq_printf ( m , " machine \t \t : PPC750 GX/CL \n " ) ;
}
void holly_restart ( char * cmd )
{
__be32 __iomem * ocn_bar1 = NULL ;
unsigned long bar ;
struct device_node * bridge = NULL ;
const void * prop ;
int size ;
phys_addr_t addr = 0xc0000000 ;
local_irq_disable ( ) ;
bridge = of_find_node_by_type ( NULL , " tsi-bridge " ) ;
if ( bridge ) {
prop = of_get_property ( bridge , " reg " , & size ) ;
addr = of_translate_address ( bridge , prop ) ;
}
addr + = ( TSI108_PB_OFFSET + 0x414 ) ;
ocn_bar1 = ioremap ( addr , 0x4 ) ;
/* Turn on the BOOT bit so the addresses are correctly
* routed to the HLP interface */
bar = ioread32be ( ocn_bar1 ) ;
bar | = 2 ;
iowrite32be ( bar , ocn_bar1 ) ;
iosync ( ) ;
/* Set SRR0 to the reset vector and turn on MSR_IP */
mtspr ( SPRN_SRR0 , 0xfff00100 ) ;
mtspr ( SPRN_SRR1 , MSR_IP ) ;
/* Do an rfi to jump back to firmware. Somewhat evil,
* but it works
*/
__asm__ __volatile__ ( " rfi " : : : " memory " ) ;
/* Spin until reset happens. Shouldn't really get here */
for ( ; ; ) ;
}
void holly_power_off ( void )
{
local_irq_disable ( ) ;
/* No way to shut power off with software */
for ( ; ; ) ;
}
void holly_halt ( void )
{
holly_power_off ( ) ;
}
/*
* Called very early , device - tree isn ' t unflattened
*/
static int __init holly_probe ( void )
{
unsigned long root = of_get_flat_dt_root ( ) ;
if ( ! of_flat_dt_is_compatible ( root , " ibm,holly " ) )
return 0 ;
return 1 ;
}
static int ppc750_machine_check_exception ( struct pt_regs * regs )
{
const struct exception_table_entry * entry ;
/* Are we prepared to handle this fault */
if ( ( entry = search_exception_tables ( regs - > nip ) ) ! = NULL ) {
tsi108_clear_pci_cfg_error ( ) ;
regs - > msr | = MSR_RI ;
regs - > nip = entry - > fixup ;
return 1 ;
}
return 0 ;
}
define_machine ( holly ) {
. name = " PPC750 GX/CL TSI " ,
. probe = holly_probe ,
. setup_arch = holly_setup_arch ,
. init_IRQ = holly_init_IRQ ,
. show_cpuinfo = holly_show_cpuinfo ,
. get_irq = mpic_get_irq ,
. restart = holly_restart ,
. calibrate_decr = generic_calibrate_decr ,
. machine_check_exception = ppc750_machine_check_exception ,
. progress = udbg_progress ,
} ;