2009-12-12 09:31:41 +03:00
/*
* arch / powerpc / platforms / embedded6xx / flipper - pic . c
*
* Nintendo GameCube / Wii " Flipper " interrupt controller support .
* Copyright ( C ) 2004 - 2009 The GameCube Linux Team
* Copyright ( C ) 2007 , 2008 , 2009 Albert Herranz
*
* 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 .
*
*/
# define DRV_MODULE_NAME "flipper-pic"
# define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/irq.h>
# include <linux/of.h>
2013-09-26 16:40:04 +04:00
# include <linux/of_address.h>
2009-12-12 09:31:41 +03:00
# include <asm/io.h>
# include "flipper-pic.h"
# define FLIPPER_NR_IRQS 32
/*
* Each interrupt has a corresponding bit in both
* the Interrupt Cause ( ICR ) and Interrupt Mask ( IMR ) registers .
*
* Enabling / disabling an interrupt line involves setting / clearing
* the corresponding bit in IMR .
* Except for the RSW interrupt , all interrupts get deasserted automatically
* when the source deasserts the interrupt .
*/
# define FLIPPER_ICR 0x00
# define FLIPPER_ICR_RSS (1<<16) /* reset switch state */
# define FLIPPER_IMR 0x04
# define FLIPPER_RESET 0x24
/*
* IRQ chip hooks .
*
*/
2011-03-09 01:26:53 +03:00
static void flipper_pic_mask_and_ack ( struct irq_data * d )
2009-12-12 09:31:41 +03:00
{
2011-05-04 09:02:15 +04:00
int irq = irqd_to_hwirq ( d ) ;
2011-03-09 01:26:53 +03:00
void __iomem * io_base = irq_data_get_irq_chip_data ( d ) ;
2009-12-12 09:31:41 +03:00
u32 mask = 1 < < irq ;
clrbits32 ( io_base + FLIPPER_IMR , mask ) ;
/* this is at least needed for RSW */
out_be32 ( io_base + FLIPPER_ICR , mask ) ;
}
2011-03-09 01:26:53 +03:00
static void flipper_pic_ack ( struct irq_data * d )
2009-12-12 09:31:41 +03:00
{
2011-05-04 09:02:15 +04:00
int irq = irqd_to_hwirq ( d ) ;
2011-03-09 01:26:53 +03:00
void __iomem * io_base = irq_data_get_irq_chip_data ( d ) ;
2009-12-12 09:31:41 +03:00
/* this is at least needed for RSW */
out_be32 ( io_base + FLIPPER_ICR , 1 < < irq ) ;
}
2011-03-09 01:26:53 +03:00
static void flipper_pic_mask ( struct irq_data * d )
2009-12-12 09:31:41 +03:00
{
2011-05-04 09:02:15 +04:00
int irq = irqd_to_hwirq ( d ) ;
2011-03-09 01:26:53 +03:00
void __iomem * io_base = irq_data_get_irq_chip_data ( d ) ;
2009-12-12 09:31:41 +03:00
clrbits32 ( io_base + FLIPPER_IMR , 1 < < irq ) ;
}
2011-03-09 01:26:53 +03:00
static void flipper_pic_unmask ( struct irq_data * d )
2009-12-12 09:31:41 +03:00
{
2011-05-04 09:02:15 +04:00
int irq = irqd_to_hwirq ( d ) ;
2011-03-09 01:26:53 +03:00
void __iomem * io_base = irq_data_get_irq_chip_data ( d ) ;
2009-12-12 09:31:41 +03:00
setbits32 ( io_base + FLIPPER_IMR , 1 < < irq ) ;
}
static struct irq_chip flipper_pic = {
. name = " flipper-pic " ,
2011-03-09 01:26:53 +03:00
. irq_ack = flipper_pic_ack ,
. irq_mask_ack = flipper_pic_mask_and_ack ,
. irq_mask = flipper_pic_mask ,
. irq_unmask = flipper_pic_unmask ,
2009-12-12 09:31:41 +03:00
} ;
/*
* IRQ host hooks .
*
*/
2012-02-15 01:06:50 +04:00
static struct irq_domain * flipper_irq_host ;
2009-12-12 09:31:41 +03:00
2012-02-15 01:06:50 +04:00
static int flipper_pic_map ( struct irq_domain * h , unsigned int virq ,
2009-12-12 09:31:41 +03:00
irq_hw_number_t hwirq )
{
2011-03-25 18:45:20 +03:00
irq_set_chip_data ( virq , h - > host_data ) ;
2011-03-25 17:43:57 +03:00
irq_set_status_flags ( virq , IRQ_LEVEL ) ;
2011-03-25 18:45:20 +03:00
irq_set_chip_and_handler ( virq , & flipper_pic , handle_level_irq ) ;
2009-12-12 09:31:41 +03:00
return 0 ;
}
2012-02-15 01:06:50 +04:00
static int flipper_pic_match ( struct irq_domain * h , struct device_node * np )
2009-12-12 09:31:41 +03:00
{
return 1 ;
}
2012-01-26 23:24:34 +04:00
static const struct irq_domain_ops flipper_irq_domain_ops = {
2009-12-12 09:31:41 +03:00
. map = flipper_pic_map ,
. match = flipper_pic_match ,
} ;
/*
* Platform hooks .
*
*/
static void __flipper_quiesce ( void __iomem * io_base )
{
/* mask and ack all IRQs */
out_be32 ( io_base + FLIPPER_IMR , 0x00000000 ) ;
out_be32 ( io_base + FLIPPER_ICR , 0xffffffff ) ;
}
2012-02-15 01:06:50 +04:00
struct irq_domain * __init flipper_pic_init ( struct device_node * np )
2009-12-12 09:31:41 +03:00
{
struct device_node * pi ;
2012-02-15 01:06:50 +04:00
struct irq_domain * irq_domain = NULL ;
2009-12-12 09:31:41 +03:00
struct resource res ;
void __iomem * io_base ;
int retval ;
pi = of_get_parent ( np ) ;
if ( ! pi ) {
pr_err ( " no parent found \n " ) ;
goto out ;
}
if ( ! of_device_is_compatible ( pi , " nintendo,flipper-pi " ) ) {
pr_err ( " unexpected parent compatible \n " ) ;
goto out ;
}
retval = of_address_to_resource ( pi , 0 , & res ) ;
if ( retval ) {
pr_err ( " no io memory range found \n " ) ;
goto out ;
}
io_base = ioremap ( res . start , resource_size ( & res ) ) ;
pr_info ( " controller at 0x%08x mapped to 0x%p \n " , res . start , io_base ) ;
__flipper_quiesce ( io_base ) ;
2012-02-15 01:06:54 +04:00
irq_domain = irq_domain_add_linear ( np , FLIPPER_NR_IRQS ,
& flipper_irq_domain_ops , io_base ) ;
2012-02-15 01:06:50 +04:00
if ( ! irq_domain ) {
pr_err ( " failed to allocate irq_domain \n " ) ;
2009-12-12 09:31:41 +03:00
return NULL ;
}
out :
2012-02-15 01:06:50 +04:00
return irq_domain ;
2009-12-12 09:31:41 +03:00
}
unsigned int flipper_pic_get_irq ( void )
{
2012-02-23 03:35:03 +04:00
void __iomem * io_base = flipper_irq_host - > host_data ;
2009-12-12 09:31:41 +03:00
int irq ;
u32 irq_status ;
irq_status = in_be32 ( io_base + FLIPPER_ICR ) &
in_be32 ( io_base + FLIPPER_IMR ) ;
if ( irq_status = = 0 )
return NO_IRQ ; /* no more IRQs pending */
irq = __ffs ( irq_status ) ;
2012-02-23 03:35:03 +04:00
return irq_linear_revmap ( flipper_irq_host , irq ) ;
2009-12-12 09:31:41 +03:00
}
/*
* Probe function .
*
*/
void __init flipper_pic_probe ( void )
{
struct device_node * np ;
np = of_find_compatible_node ( NULL , NULL , " nintendo,flipper-pic " ) ;
BUG_ON ( ! np ) ;
2012-02-23 03:35:03 +04:00
flipper_irq_host = flipper_pic_init ( np ) ;
2009-12-12 09:31:41 +03:00
BUG_ON ( ! flipper_irq_host ) ;
irq_set_default_host ( flipper_irq_host ) ;
of_node_put ( np ) ;
}
/*
* Misc functions related to the flipper chipset .
*
*/
/**
* flipper_quiesce ( ) - quiesce flipper irq controller
*
* Mask and ack all interrupt sources .
*
*/
void flipper_quiesce ( void )
{
void __iomem * io_base = flipper_irq_host - > host_data ;
__flipper_quiesce ( io_base ) ;
}
/*
* Resets the platform .
*/
void flipper_platform_reset ( void )
{
void __iomem * io_base ;
if ( flipper_irq_host & & flipper_irq_host - > host_data ) {
io_base = flipper_irq_host - > host_data ;
out_8 ( io_base + FLIPPER_RESET , 0x00 ) ;
}
}
/*
* Returns non - zero if the reset button is pressed .
*/
int flipper_is_reset_button_pressed ( void )
{
void __iomem * io_base ;
u32 icr ;
if ( flipper_irq_host & & flipper_irq_host - > host_data ) {
io_base = flipper_irq_host - > host_data ;
icr = in_be32 ( io_base + FLIPPER_ICR ) ;
return ! ( icr & FLIPPER_ICR_RSS ) ;
}
return 0 ;
}