2009-02-04 13:39:17 -07:00
/*
* Support for ' media5200 - platform ' compatible boards .
*
* Copyright ( C ) 2008 Secret Lab Technologies Ltd .
*
* 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 .
*
* Description :
* This code implements support for the Freescape Media5200 platform
* ( built around the MPC5200 SoC ) .
*
* Notable characteristic of the Media5200 is the presence of an FPGA
* that has all external IRQ lines routed through it . This file implements
* a cascaded interrupt controller driver which attaches itself to the
* Virtual IRQ subsystem after the primary mpc5200 interrupt controller
* is initialized .
*
*/
# undef DEBUG
# include <linux/irq.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <asm/time.h>
# include <asm/prom.h>
# include <asm/machdep.h>
# include <asm/mpc52xx.h>
2014-09-10 21:56:38 +02:00
static const struct of_device_id mpc5200_gpio_ids [ ] __initconst = {
2009-02-04 13:39:17 -07:00
{ . compatible = " fsl,mpc5200-gpio " , } ,
{ . compatible = " mpc5200-gpio " , } ,
{ }
} ;
/* FPGA register set */
# define MEDIA5200_IRQ_ENABLE (0x40c)
# define MEDIA5200_IRQ_STATUS (0x410)
# define MEDIA5200_NUM_IRQS (6)
# define MEDIA5200_IRQ_SHIFT (32 - MEDIA5200_NUM_IRQS)
struct media5200_irq {
void __iomem * regs ;
spinlock_t lock ;
2012-02-14 14:06:50 -07:00
struct irq_domain * irqhost ;
2009-02-04 13:39:17 -07:00
} ;
struct media5200_irq media5200_irq ;
2011-03-08 22:26:47 +00:00
static void media5200_irq_unmask ( struct irq_data * d )
2009-02-04 13:39:17 -07:00
{
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & media5200_irq . lock , flags ) ;
val = in_be32 ( media5200_irq . regs + MEDIA5200_IRQ_ENABLE ) ;
2011-05-04 15:02:15 +10:00
val | = 1 < < ( MEDIA5200_IRQ_SHIFT + irqd_to_hwirq ( d ) ) ;
2009-02-04 13:39:17 -07:00
out_be32 ( media5200_irq . regs + MEDIA5200_IRQ_ENABLE , val ) ;
spin_unlock_irqrestore ( & media5200_irq . lock , flags ) ;
}
2011-03-08 22:26:47 +00:00
static void media5200_irq_mask ( struct irq_data * d )
2009-02-04 13:39:17 -07:00
{
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & media5200_irq . lock , flags ) ;
val = in_be32 ( media5200_irq . regs + MEDIA5200_IRQ_ENABLE ) ;
2011-05-04 15:02:15 +10:00
val & = ~ ( 1 < < ( MEDIA5200_IRQ_SHIFT + irqd_to_hwirq ( d ) ) ) ;
2009-02-04 13:39:17 -07:00
out_be32 ( media5200_irq . regs + MEDIA5200_IRQ_ENABLE , val ) ;
spin_unlock_irqrestore ( & media5200_irq . lock , flags ) ;
}
static struct irq_chip media5200_irq_chip = {
2009-11-18 23:44:21 +00:00
. name = " Media5200 FPGA " ,
2011-03-08 22:26:47 +00:00
. irq_unmask = media5200_irq_unmask ,
. irq_mask = media5200_irq_mask ,
. irq_mask_ack = media5200_irq_mask ,
2009-02-04 13:39:17 -07:00
} ;
void media5200_irq_cascade ( unsigned int virq , struct irq_desc * desc )
{
2011-03-25 16:45:20 +01:00
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
2009-02-04 13:39:17 -07:00
int sub_virq , val ;
u32 status , enable ;
/* Mask off the cascaded IRQ */
2009-11-17 16:46:45 +01:00
raw_spin_lock ( & desc - > lock ) ;
2011-03-08 22:26:47 +00:00
chip - > irq_mask ( & desc - > irq_data ) ;
2009-11-17 16:46:45 +01:00
raw_spin_unlock ( & desc - > lock ) ;
2009-02-04 13:39:17 -07:00
/* Ask the FPGA for IRQ status. If 'val' is 0, then no irqs
* are pending . ' ffs ( ) ' is 1 based */
status = in_be32 ( media5200_irq . regs + MEDIA5200_IRQ_ENABLE ) ;
enable = in_be32 ( media5200_irq . regs + MEDIA5200_IRQ_STATUS ) ;
val = ffs ( ( status & enable ) > > MEDIA5200_IRQ_SHIFT ) ;
if ( val ) {
sub_virq = irq_linear_revmap ( media5200_irq . irqhost , val - 1 ) ;
/* pr_debug("%s: virq=%i s=%.8x e=%.8x hwirq=%i subvirq=%i\n",
* __func__ , virq , status , enable , val - 1 , sub_virq ) ;
*/
generic_handle_irq ( sub_virq ) ;
}
/* Processing done; can reenable the cascade now */
2009-11-17 16:46:45 +01:00
raw_spin_lock ( & desc - > lock ) ;
2011-03-08 22:26:47 +00:00
chip - > irq_ack ( & desc - > irq_data ) ;
2011-03-25 15:43:57 +01:00
if ( ! irqd_irq_disabled ( & desc - > irq_data ) )
2011-03-08 22:26:47 +00:00
chip - > irq_unmask ( & desc - > irq_data ) ;
2009-11-17 16:46:45 +01:00
raw_spin_unlock ( & desc - > lock ) ;
2009-02-04 13:39:17 -07:00
}
2012-02-14 14:06:50 -07:00
static int media5200_irq_map ( struct irq_domain * h , unsigned int virq ,
2009-02-04 13:39:17 -07:00
irq_hw_number_t hw )
{
pr_debug ( " %s: h=%p, virq=%i, hwirq=%i \n " , __func__ , h , virq , ( int ) hw ) ;
2011-03-25 16:45:20 +01:00
irq_set_chip_data ( virq , & media5200_irq ) ;
irq_set_chip_and_handler ( virq , & media5200_irq_chip , handle_level_irq ) ;
2011-03-25 15:54:03 +01:00
irq_set_status_flags ( virq , IRQ_LEVEL ) ;
2009-02-04 13:39:17 -07:00
return 0 ;
}
2012-02-14 14:06:50 -07:00
static int media5200_irq_xlate ( struct irq_domain * h , struct device_node * ct ,
2009-12-08 02:39:50 +00:00
const u32 * intspec , unsigned int intsize ,
2009-02-04 13:39:17 -07:00
irq_hw_number_t * out_hwirq ,
unsigned int * out_flags )
{
if ( intsize ! = 2 )
return - 1 ;
pr_debug ( " %s: bank=%i, number=%i \n " , __func__ , intspec [ 0 ] , intspec [ 1 ] ) ;
* out_hwirq = intspec [ 1 ] ;
* out_flags = IRQ_TYPE_NONE ;
return 0 ;
}
2012-01-26 12:24:34 -07:00
static const struct irq_domain_ops media5200_irq_ops = {
2009-02-04 13:39:17 -07:00
. map = media5200_irq_map ,
. xlate = media5200_irq_xlate ,
} ;
/*
* Setup Media5200 IRQ mapping
*/
static void __init media5200_init_irq ( void )
{
struct device_node * fpga_np ;
int cascade_virq ;
/* First setup the regular MPC5200 interrupt controller */
mpc52xx_init_irq ( ) ;
/* Now find the FPGA IRQ */
fpga_np = of_find_compatible_node ( NULL , NULL , " fsl,media5200-fpga " ) ;
if ( ! fpga_np )
goto out ;
pr_debug ( " %s: found fpga node: %s \n " , __func__ , fpga_np - > full_name ) ;
media5200_irq . regs = of_iomap ( fpga_np , 0 ) ;
if ( ! media5200_irq . regs )
goto out ;
pr_debug ( " %s: mapped to %p \n " , __func__ , media5200_irq . regs ) ;
cascade_virq = irq_of_parse_and_map ( fpga_np , 0 ) ;
if ( ! cascade_virq )
goto out ;
pr_debug ( " %s: cascaded on virq=%i \n " , __func__ , cascade_virq ) ;
/* Disable all FPGA IRQs */
out_be32 ( media5200_irq . regs + MEDIA5200_IRQ_ENABLE , 0 ) ;
spin_lock_init ( & media5200_irq . lock ) ;
2012-02-14 14:06:54 -07:00
media5200_irq . irqhost = irq_domain_add_linear ( fpga_np ,
MEDIA5200_NUM_IRQS , & media5200_irq_ops , & media5200_irq ) ;
2009-02-04 13:39:17 -07:00
if ( ! media5200_irq . irqhost )
goto out ;
pr_debug ( " %s: allocated irqhost \n " , __func__ ) ;
2011-03-25 16:45:20 +01:00
irq_set_handler_data ( cascade_virq , & media5200_irq ) ;
irq_set_chained_handler ( cascade_virq , media5200_irq_cascade ) ;
2009-02-04 13:39:17 -07:00
return ;
out :
pr_err ( " Could not find Media5200 FPGA; PCI interrupts will not work \n " ) ;
}
/*
* Setup the architecture
*/
static void __init media5200_setup_arch ( void )
{
struct device_node * np ;
struct mpc52xx_gpio __iomem * gpio ;
u32 port_config ;
if ( ppc_md . progress )
ppc_md . progress ( " media5200_setup_arch() " , 0 ) ;
/* Map important registers from the internal memory map */
mpc52xx_map_common_devices ( ) ;
/* Some mpc5200 & mpc5200b related configuration */
mpc5200_setup_xlb_arbiter ( ) ;
mpc52xx_setup_pci ( ) ;
np = of_find_matching_node ( NULL , mpc5200_gpio_ids ) ;
gpio = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
if ( ! gpio ) {
printk ( KERN_ERR " %s() failed. expect abnormal behavior \n " ,
__func__ ) ;
return ;
}
/* Set port config */
port_config = in_be32 ( & gpio - > port_config ) ;
port_config & = ~ 0x03000000 ; /* ATA CS is on csb_4/5 */
port_config | = 0x01000000 ;
out_be32 ( & gpio - > port_config , port_config ) ;
/* Unmap zone */
iounmap ( gpio ) ;
}
/* list of the supported boards */
2012-10-04 17:11:37 -07:00
static const char * const board [ ] __initconst = {
2009-02-04 13:39:17 -07:00
" fsl,media5200 " ,
NULL
} ;
/*
* Called very early , MMU is off , device - tree isn ' t unflattened
*/
static int __init media5200_probe ( void )
{
2010-10-30 11:49:09 -04:00
return of_flat_dt_match ( of_get_flat_dt_root ( ) , board ) ;
2009-02-04 13:39:17 -07:00
}
define_machine ( media5200_platform ) {
. name = " media5200-platform " ,
. probe = media5200_probe ,
. setup_arch = media5200_setup_arch ,
. init = mpc52xx_declare_of_platform_devices ,
. init_IRQ = media5200_init_irq ,
. get_irq = mpc52xx_get_irq ,
. restart = mpc52xx_restart ,
. calibrate_decr = generic_calibrate_decr ,
} ;