2005-04-16 15:20:36 -07:00
/*
* arch / arm / mach - ixp2000 / ixdp2x00 . c
*
* Code common to IXDP2400 and IXDP2800 platforms .
*
* Original Author : Naeem Afzal < naeem . m . afzal @ intel . com >
* Maintainer : Deepak Saxena < dsaxena @ plexity . net >
*
* Copyright ( C ) 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/kernel.h>
# include <linux/init.h>
# include <linux/mm.h>
# include <linux/sched.h>
# include <linux/interrupt.h>
2005-10-29 19:07:23 +01:00
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
# include <linux/bitops.h>
# include <linux/pci.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/pgtable.h>
# include <asm/page.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-types.h>
# include <asm/mach/pci.h>
# include <asm/mach/map.h>
# include <asm/mach/irq.h>
# include <asm/mach/time.h>
# include <asm/mach/flash.h>
# include <asm/mach/arch.h>
2008-08-05 16:14:15 +01:00
# include <mach/gpio.h>
2005-06-25 16:58:22 +01:00
2005-04-16 15:20:36 -07:00
/*************************************************************************
* IXDP2x00 IRQ Initialization
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static volatile unsigned long * board_irq_mask ;
static volatile unsigned long * board_irq_stat ;
static unsigned long board_irq_count ;
# ifdef CONFIG_ARCH_IXDP2400
/*
* Slowport configuration for accessing CPLD registers on IXDP2x00
*/
static struct slowport_cfg slowport_cpld_cfg = {
. CCR = SLOWPORT_CCR_DIV_2 ,
. WTC = 0x00000070 ,
. RTC = 0x00000070 ,
. PCR = SLOWPORT_MODE_FLASH ,
. ADC = SLOWPORT_ADDR_WIDTH_24 | SLOWPORT_DATA_WIDTH_8
} ;
# endif
static void ixdp2x00_irq_mask ( unsigned int irq )
{
unsigned long dummy ;
static struct slowport_cfg old_cfg ;
/*
* This is ugly in common code but really don ' t know
* of a better way to handle it . : (
*/
# ifdef CONFIG_ARCH_IXDP2400
if ( machine_is_ixdp2400 ( ) )
ixp2000_acquire_slowport ( & slowport_cpld_cfg , & old_cfg ) ;
# endif
dummy = * board_irq_mask ;
dummy | = IXP2000_BOARD_IRQ_MASK ( irq ) ;
2005-11-01 19:44:26 +00:00
ixp2000_reg_wrb ( board_irq_mask , dummy ) ;
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_ARCH_IXDP2400
if ( machine_is_ixdp2400 ( ) )
ixp2000_release_slowport ( & old_cfg ) ;
# endif
}
static void ixdp2x00_irq_unmask ( unsigned int irq )
{
unsigned long dummy ;
static struct slowport_cfg old_cfg ;
# ifdef CONFIG_ARCH_IXDP2400
if ( machine_is_ixdp2400 ( ) )
ixp2000_acquire_slowport ( & slowport_cpld_cfg , & old_cfg ) ;
# endif
dummy = * board_irq_mask ;
dummy & = ~ IXP2000_BOARD_IRQ_MASK ( irq ) ;
2005-11-01 19:44:26 +00:00
ixp2000_reg_wrb ( board_irq_mask , dummy ) ;
2005-04-16 15:20:36 -07:00
if ( machine_is_ixdp2400 ( ) )
ixp2000_release_slowport ( & old_cfg ) ;
}
2006-11-23 11:41:32 +00:00
static void ixdp2x00_irq_handler ( unsigned int irq , struct irq_desc * desc )
2005-04-16 15:20:36 -07:00
{
volatile u32 ex_interrupt = 0 ;
static struct slowport_cfg old_cfg ;
int i ;
desc - > chip - > mask ( irq ) ;
# ifdef CONFIG_ARCH_IXDP2400
if ( machine_is_ixdp2400 ( ) )
ixp2000_acquire_slowport ( & slowport_cpld_cfg , & old_cfg ) ;
# endif
ex_interrupt = * board_irq_stat & 0xff ;
if ( machine_is_ixdp2400 ( ) )
ixp2000_release_slowport ( & old_cfg ) ;
if ( ! ex_interrupt ) {
printk ( KERN_ERR " Spurious IXDP2x00 CPLD interrupt! \n " ) ;
return ;
}
for ( i = 0 ; i < board_irq_count ; i + + ) {
if ( ex_interrupt & ( 1 < < i ) ) {
int cpld_irq = IXP2000_BOARD_IRQ ( 0 ) + i ;
2008-10-09 13:36:24 +01:00
generic_handle_irq ( cpld_irq ) ;
2005-04-16 15:20:36 -07:00
}
}
desc - > chip - > unmask ( irq ) ;
}
2006-11-23 11:41:32 +00:00
static struct irq_chip ixdp2x00_cpld_irq_chip = {
2005-04-16 15:20:36 -07:00
. ack = ixdp2x00_irq_mask ,
. mask = ixdp2x00_irq_mask ,
. unmask = ixdp2x00_irq_unmask
} ;
2008-10-16 15:33:18 +02:00
void __init ixdp2x00_init_irq ( volatile unsigned long * stat_reg , volatile unsigned long * mask_reg , unsigned long nr_of_irqs )
2005-04-16 15:20:36 -07:00
{
unsigned int irq ;
ixp2000_init_irq ( ) ;
if ( ! ixdp2x00_master_npu ( ) )
return ;
board_irq_stat = stat_reg ;
board_irq_mask = mask_reg ;
2008-10-16 15:33:18 +02:00
board_irq_count = nr_of_irqs ;
2005-04-16 15:20:36 -07:00
* board_irq_mask = 0xffffffff ;
for ( irq = IXP2000_BOARD_IRQ ( 0 ) ; irq < IXP2000_BOARD_IRQ ( board_irq_count ) ; irq + + ) {
set_irq_chip ( irq , & ixdp2x00_cpld_irq_chip ) ;
2006-11-23 11:41:32 +00:00
set_irq_handler ( irq , handle_level_irq ) ;
2005-04-16 15:20:36 -07:00
set_irq_flags ( irq , IRQF_VALID ) ;
}
/* Hook into PCI interrupt */
2006-07-01 22:32:18 +01:00
set_irq_chained_handler ( IRQ_IXP2000_PCIB , ixdp2x00_irq_handler ) ;
2005-04-16 15:20:36 -07:00
}
/*************************************************************************
* IXDP2x00 memory map
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static struct map_desc ixdp2x00_io_desc __initdata = {
. virtual = IXDP2X00_VIRT_CPLD_BASE ,
2005-10-28 15:18:58 +01:00
. pfn = __phys_to_pfn ( IXDP2X00_PHYS_CPLD_BASE ) ,
2005-04-16 15:20:36 -07:00
. length = IXDP2X00_CPLD_SIZE ,
. type = MT_DEVICE
} ;
void __init ixdp2x00_map_io ( void )
{
ixp2000_map_io ( ) ;
iotable_init ( & ixdp2x00_io_desc , 1 ) ;
}
/*************************************************************************
* IXDP2x00 - common PCI init
*
* The IXDP2 [ 48 ] 00 has a horrid PCI bus layout . Basically the board
* contains two NPUs ( ingress and egress ) connected over PCI , both running
* instances of the kernel . So far so good . Peers on the PCI bus running
* Linux is a common design in telecom systems . The problem is that instead
* of all the devices being controlled by a single host , different
2007-05-11 20:40:30 +01:00
* devices are controlled by different NPUs on the same bus , leading to
2005-04-16 15:20:36 -07:00
* multiple hosts on the bus . The exact bus layout looks like :
*
* Bus 0
* Master NPU < - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - > Slave NPU
* |
* |
* P2P
* |
*
* Bus 1 |
* < - - + - - - - - - + - - - - - - - - - + - - - - - - - - - + - - - - - - + - - >
* | | | | |
* | | | | |
* . . . Dev PMC Media Eth0 Eth1 . . .
*
2007-05-11 20:40:30 +01:00
* The master controls all but Eth1 , which is controlled by the
2005-04-16 15:20:36 -07:00
* slave . What this means is that the both the master and the slave
* have to scan the bus , but only one of them can enumerate the bus .
* In addition , after the bus is scanned , each kernel must remove
* the device ( s ) it does not control from the PCI dev list otherwise
* a driver on each NPU will try to manage it and we will have horrible
* conflicts . Oh . . and the slave NPU needs to see the master NPU
* for Intel ' s drivers to work properly . Closed source drivers . . .
*
* The way we deal with this is fairly simple but ugly :
*
* 1 ) Let master scan and enumerate the bus completely .
* 2 ) Master deletes Eth1 from device list .
* 3 ) Slave scans bus and then deletes all but Eth1 ( Eth0 on slave )
* from device list .
* 4 ) Find HW designers and LART them .
*
* The boards also do not do normal PCI IRQ routing , or any sort of
* sensical swizzling , so we just need to check where on the bus a
* device sits and figure out to which CPLD pin the interrupt is routed .
* See ixdp2 [ 48 ] 00. c files .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void ixdp2x00_slave_pci_postinit ( void )
{
struct pci_dev * dev ;
/*
* Remove PMC device is there is one
*/
2006-10-16 16:49:50 +01:00
if ( ( dev = pci_get_bus_and_slot ( 1 , IXDP2X00_PMC_DEVFN ) ) ) {
2005-04-16 15:20:36 -07:00
pci_remove_bus_device ( dev ) ;
2006-10-16 16:49:50 +01:00
pci_dev_put ( dev ) ;
}
2005-04-16 15:20:36 -07:00
2006-10-16 16:49:50 +01:00
dev = pci_get_bus_and_slot ( 0 , IXDP2X00_21555_DEVFN ) ;
2005-04-16 15:20:36 -07:00
pci_remove_bus_device ( dev ) ;
2006-10-16 16:49:50 +01:00
pci_dev_put ( dev ) ;
2005-04-16 15:20:36 -07:00
}
/**************************************************************************
* IXDP2x00 Machine Setup
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static struct flash_platform_data ixdp2x00_platform_data = {
. map_name = " cfi_probe " ,
. width = 1 ,
} ;
static struct ixp2000_flash_data ixdp2x00_flash_data = {
. platform_data = & ixdp2x00_platform_data ,
. nr_banks = 1
} ;
static struct resource ixdp2x00_flash_resource = {
. start = 0xc4000000 ,
. end = 0xc4000000 + 0x00ffffff ,
. flags = IORESOURCE_MEM ,
} ;
static struct platform_device ixdp2x00_flash = {
. name = " IXP2000-Flash " ,
. id = 0 ,
. dev = {
. platform_data = & ixdp2x00_flash_data ,
} ,
. num_resources = 1 ,
. resource = & ixdp2x00_flash_resource ,
} ;
static struct ixp2000_i2c_pins ixdp2x00_i2c_gpio_pins = {
. sda_pin = IXDP2X00_GPIO_SDA ,
. scl_pin = IXDP2X00_GPIO_SCL ,
} ;
static struct platform_device ixdp2x00_i2c_controller = {
. name = " IXP2000-I2C " ,
. id = 0 ,
. dev = {
. platform_data = & ixdp2x00_i2c_gpio_pins ,
} ,
. num_resources = 0
} ;
static struct platform_device * ixdp2x00_devices [ ] __initdata = {
& ixdp2x00_flash ,
& ixdp2x00_i2c_controller
} ;
void __init ixdp2x00_init_machine ( void )
{
gpio_line_set ( IXDP2X00_GPIO_I2C_ENABLE , 1 ) ;
gpio_line_config ( IXDP2X00_GPIO_I2C_ENABLE , GPIO_OUT ) ;
platform_add_devices ( ixdp2x00_devices , ARRAY_SIZE ( ixdp2x00_devices ) ) ;
2005-07-10 19:44:53 +01:00
ixp2000_uart_init ( ) ;
2005-04-16 15:20:36 -07:00
}