2006-01-09 20:05:41 +03:00
/*
* linux / arch / arm / mach - at91rm9200 / gpio . c
*
* Copyright ( C ) 2005 HP Labs
*
* 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/errno.h>
2006-07-02 02:01:50 +04:00
# include <linux/interrupt.h>
# include <linux/irq.h>
2006-01-09 20:05:41 +03:00
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/module.h>
# include <asm/io.h>
2006-06-20 22:53:16 +04:00
# include <asm/hardware.h>
2006-01-09 20:05:41 +03:00
# include <asm/arch/gpio.h>
static const u32 pio_controller_offset [ 4 ] = {
AT91_PIOA ,
AT91_PIOB ,
AT91_PIOC ,
AT91_PIOD ,
} ;
static inline void __iomem * pin_to_controller ( unsigned pin )
{
void __iomem * sys_base = ( void __iomem * ) AT91_VA_BASE_SYS ;
pin - = PIN_BASE ;
pin / = 32 ;
if ( likely ( pin < BGA_GPIO_BANKS ) )
return sys_base + pio_controller_offset [ pin ] ;
return NULL ;
}
static inline unsigned pin_to_mask ( unsigned pin )
{
pin - = PIN_BASE ;
return 1 < < ( pin % 32 ) ;
}
/*--------------------------------------------------------------------------*/
/* Not all hardware capabilities are exposed through these calls; they
* only encapsulate the most common features and modes . ( So if you
* want to change signals in groups , do it directly . )
*
* Bootloaders will usually handle some of the pin multiplexing setup .
* The intent is certainly that by the time Linux is fully booted , all
* pins should have been fully initialized . These setup calls should
* only be used by board setup routines , or possibly in driver probe ( ) .
*
* For bootloaders doing all that setup , these calls could be inlined
* as NOPs so Linux won ' t duplicate any setup code
*/
/*
* mux the pin to the " A " internal peripheral role .
*/
int __init_or_module at91_set_A_periph ( unsigned pin , int use_pullup )
{
void __iomem * pio = pin_to_controller ( pin ) ;
unsigned mask = pin_to_mask ( pin ) ;
if ( ! pio )
return - EINVAL ;
__raw_writel ( mask , pio + PIO_IDR ) ;
__raw_writel ( mask , pio + ( use_pullup ? PIO_PUER : PIO_PUDR ) ) ;
__raw_writel ( mask , pio + PIO_ASR ) ;
__raw_writel ( mask , pio + PIO_PDR ) ;
return 0 ;
}
EXPORT_SYMBOL ( at91_set_A_periph ) ;
/*
* mux the pin to the " B " internal peripheral role .
*/
int __init_or_module at91_set_B_periph ( unsigned pin , int use_pullup )
{
void __iomem * pio = pin_to_controller ( pin ) ;
unsigned mask = pin_to_mask ( pin ) ;
if ( ! pio )
return - EINVAL ;
__raw_writel ( mask , pio + PIO_IDR ) ;
__raw_writel ( mask , pio + ( use_pullup ? PIO_PUER : PIO_PUDR ) ) ;
__raw_writel ( mask , pio + PIO_BSR ) ;
__raw_writel ( mask , pio + PIO_PDR ) ;
return 0 ;
}
EXPORT_SYMBOL ( at91_set_B_periph ) ;
/*
* mux the pin to the gpio controller ( instead of " A " or " B " peripheral ) , and
* configure it for an input .
*/
int __init_or_module at91_set_gpio_input ( unsigned pin , int use_pullup )
{
void __iomem * pio = pin_to_controller ( pin ) ;
unsigned mask = pin_to_mask ( pin ) ;
if ( ! pio )
return - EINVAL ;
__raw_writel ( mask , pio + PIO_IDR ) ;
__raw_writel ( mask , pio + ( use_pullup ? PIO_PUER : PIO_PUDR ) ) ;
__raw_writel ( mask , pio + PIO_ODR ) ;
__raw_writel ( mask , pio + PIO_PER ) ;
return 0 ;
}
EXPORT_SYMBOL ( at91_set_gpio_input ) ;
/*
* mux the pin to the gpio controller ( instead of " A " or " B " peripheral ) ,
* and configure it for an output .
*/
int __init_or_module at91_set_gpio_output ( unsigned pin , int value )
{
void __iomem * pio = pin_to_controller ( pin ) ;
unsigned mask = pin_to_mask ( pin ) ;
if ( ! pio )
return - EINVAL ;
__raw_writel ( mask , pio + PIO_IDR ) ;
__raw_writel ( mask , pio + PIO_PUDR ) ;
__raw_writel ( mask , pio + ( value ? PIO_SODR : PIO_CODR ) ) ;
__raw_writel ( mask , pio + PIO_OER ) ;
__raw_writel ( mask , pio + PIO_PER ) ;
return 0 ;
}
EXPORT_SYMBOL ( at91_set_gpio_output ) ;
/*
* enable / disable the glitch filter ; mostly used with IRQ handling .
*/
int __init_or_module at91_set_deglitch ( unsigned pin , int is_on )
{
void __iomem * pio = pin_to_controller ( pin ) ;
unsigned mask = pin_to_mask ( pin ) ;
if ( ! pio )
return - EINVAL ;
__raw_writel ( mask , pio + ( is_on ? PIO_IFER : PIO_IFDR ) ) ;
return 0 ;
}
EXPORT_SYMBOL ( at91_set_deglitch ) ;
2006-02-23 00:23:35 +03:00
/*
* enable / disable the multi - driver ; This is only valid for output and
* allows the output pin to run as an open collector output .
*/
int __init_or_module at91_set_multi_drive ( unsigned pin , int is_on )
{
void __iomem * pio = pin_to_controller ( pin ) ;
unsigned mask = pin_to_mask ( pin ) ;
if ( ! pio )
return - EINVAL ;
__raw_writel ( mask , pio + ( is_on ? PIO_MDER : PIO_MDDR ) ) ;
return 0 ;
}
EXPORT_SYMBOL ( at91_set_multi_drive ) ;
2006-01-09 20:05:41 +03:00
/*--------------------------------------------------------------------------*/
/*
* assuming the pin is muxed as a gpio output , set its value .
*/
int at91_set_gpio_value ( unsigned pin , int value )
{
void __iomem * pio = pin_to_controller ( pin ) ;
unsigned mask = pin_to_mask ( pin ) ;
if ( ! pio )
return - EINVAL ;
__raw_writel ( mask , pio + ( value ? PIO_SODR : PIO_CODR ) ) ;
return 0 ;
}
EXPORT_SYMBOL ( at91_set_gpio_value ) ;
/*
* read the pin ' s value ( works even if it ' s not muxed as a gpio ) .
*/
int at91_get_gpio_value ( unsigned pin )
{
void __iomem * pio = pin_to_controller ( pin ) ;
unsigned mask = pin_to_mask ( pin ) ;
u32 pdsr ;
if ( ! pio )
return - EINVAL ;
pdsr = __raw_readl ( pio + PIO_PDSR ) ;
return ( pdsr & mask ) ! = 0 ;
}
EXPORT_SYMBOL ( at91_get_gpio_value ) ;
/*--------------------------------------------------------------------------*/
2006-06-19 18:26:54 +04:00
# ifdef CONFIG_PM
static u32 wakeups [ BGA_GPIO_BANKS ] ;
static u32 backups [ BGA_GPIO_BANKS ] ;
static int gpio_irq_set_wake ( unsigned pin , unsigned state )
{
unsigned mask = pin_to_mask ( pin ) ;
pin - = PIN_BASE ;
pin / = 32 ;
if ( unlikely ( pin > = BGA_GPIO_BANKS ) )
return - EINVAL ;
if ( state )
wakeups [ pin ] | = mask ;
else
wakeups [ pin ] & = ~ mask ;
return 0 ;
}
void at91_gpio_suspend ( void )
{
int i ;
for ( i = 0 ; i < BGA_GPIO_BANKS ; i + + ) {
u32 pio = pio_controller_offset [ i ] ;
/*
* Note : drivers should have disabled GPIO interrupts that
* aren ' t supposed to be wakeup sources .
* But that is not much good on ARM . . . . . disable_irq ( ) does
* not update the hardware immediately , so the hardware mask
* ( IMR ) has the wrong value ( not current , too much is
* permitted ) .
*
* Our workaround is to disable all non - wakeup IRQs . . .
* which is exactly what correct drivers asked for in the
* first place !
*/
backups [ i ] = at91_sys_read ( pio + PIO_IMR ) ;
at91_sys_write ( pio_controller_offset [ i ] + PIO_IDR , backups [ i ] ) ;
at91_sys_write ( pio_controller_offset [ i ] + PIO_IER , wakeups [ i ] ) ;
if ( ! wakeups [ i ] ) {
disable_irq_wake ( AT91_ID_PIOA + i ) ;
at91_sys_write ( AT91_PMC_PCDR , 1 < < ( AT91_ID_PIOA + i ) ) ;
} else {
enable_irq_wake ( AT91_ID_PIOA + i ) ;
# ifdef CONFIG_PM_DEBUG
printk ( KERN_DEBUG " GPIO-%c may wake for %08x \n " , " ABCD " [ i ] , wakeups [ i ] ) ;
# endif
}
}
}
void at91_gpio_resume ( void )
{
int i ;
for ( i = 0 ; i < BGA_GPIO_BANKS ; i + + ) {
at91_sys_write ( pio_controller_offset [ i ] + PIO_IDR , wakeups [ i ] ) ;
at91_sys_write ( pio_controller_offset [ i ] + PIO_IER , backups [ i ] ) ;
}
at91_sys_write ( AT91_PMC_PCER ,
( 1 < < AT91_ID_PIOA )
| ( 1 < < AT91_ID_PIOB )
| ( 1 < < AT91_ID_PIOC )
| ( 1 < < AT91_ID_PIOD ) ) ;
}
# else
# define gpio_irq_set_wake NULL
# endif
2006-01-09 20:05:41 +03:00
/* Several AIC controller irqs are dispatched through this GPIO handler.
* To use any AT91_PIN_ * as an externally triggered IRQ , first call
* at91_set_gpio_input ( ) then maybe enable its glitch filter .
* Then just request_irq ( ) with the pin ID ; it works like any ARM IRQ
* handler , though it always triggers on rising and falling edges .
*
* Alternatively , certain pins may be used directly as IRQ0 . . IRQ6 after
* configuring them with at91_set_a_periph ( ) or at91_set_b_periph ( ) .
* IRQ0 . . IRQ6 should be configurable , e . g . level vs edge triggering .
*/
static void gpio_irq_mask ( unsigned pin )
{
void __iomem * pio = pin_to_controller ( pin ) ;
unsigned mask = pin_to_mask ( pin ) ;
if ( pio )
__raw_writel ( mask , pio + PIO_IDR ) ;
}
static void gpio_irq_unmask ( unsigned pin )
{
void __iomem * pio = pin_to_controller ( pin ) ;
unsigned mask = pin_to_mask ( pin ) ;
if ( pio )
__raw_writel ( mask , pio + PIO_IER ) ;
}
static int gpio_irq_type ( unsigned pin , unsigned type )
{
return ( type = = IRQT_BOTHEDGE ) ? 0 : - EINVAL ;
}
static struct irqchip gpio_irqchip = {
. mask = gpio_irq_mask ,
. unmask = gpio_irq_unmask ,
. set_type = gpio_irq_type ,
2006-06-19 18:26:54 +04:00
. set_wake = gpio_irq_set_wake ,
2006-01-09 20:05:41 +03:00
} ;
static void gpio_irq_handler ( unsigned irq , struct irqdesc * desc , struct pt_regs * regs )
{
unsigned pin ;
struct irqdesc * gpio ;
void __iomem * pio ;
u32 isr ;
2006-07-02 02:01:50 +04:00
pio = get_irq_chip_data ( irq ) ;
2006-01-09 20:05:41 +03:00
/* temporarily mask (level sensitive) parent IRQ */
desc - > chip - > ack ( irq ) ;
for ( ; ; ) {
2006-06-19 18:26:54 +04:00
/* reading ISR acks the pending (edge triggered) GPIO interrupt */
2006-01-09 20:05:41 +03:00
isr = __raw_readl ( pio + PIO_ISR ) & __raw_readl ( pio + PIO_IMR ) ;
if ( ! isr )
break ;
2006-07-02 02:01:50 +04:00
pin = ( unsigned ) get_irq_data ( irq ) ;
2006-01-09 20:05:41 +03:00
gpio = & irq_desc [ pin ] ;
while ( isr ) {
2006-02-25 01:27:50 +03:00
if ( isr & 1 ) {
2006-07-02 02:01:50 +04:00
if ( unlikely ( gpio - > depth ) ) {
2006-02-25 01:27:50 +03:00
/*
* The core ARM interrupt handler lazily disables IRQs so
* another IRQ must be generated before it actually gets
* here to be disabled on the GPIO controller .
*/
gpio_irq_mask ( pin ) ;
}
else
2006-07-02 02:01:50 +04:00
desc_handle_irq ( pin , gpio , regs ) ;
2006-02-25 01:27:50 +03:00
}
2006-01-09 20:05:41 +03:00
pin + + ;
gpio + + ;
isr > > = 1 ;
}
}
desc - > chip - > unmask ( irq ) ;
/* now it may re-trigger */
}
/* call this from board-specific init_irq */
void __init at91_gpio_irq_setup ( unsigned banks )
{
unsigned pioc , pin , id ;
if ( banks > 4 )
banks = 4 ;
for ( pioc = 0 , pin = PIN_BASE , id = AT91_ID_PIOA ;
pioc < banks ;
pioc + + , id + + ) {
void __iomem * controller ;
unsigned i ;
controller = ( void __iomem * ) AT91_VA_BASE_SYS + pio_controller_offset [ pioc ] ;
__raw_writel ( ~ 0 , controller + PIO_IDR ) ;
set_irq_data ( id , ( void * ) pin ) ;
2006-03-15 18:43:04 +03:00
set_irq_chipdata ( id , controller ) ;
2006-01-09 20:05:41 +03:00
for ( i = 0 ; i < 32 ; i + + , pin + + ) {
2006-06-19 18:26:54 +04:00
/*
* Can use the " simple " and not " edge " handler since it ' s
* shorter , and the AIC handles interupts sanely .
*/
2006-01-09 20:05:41 +03:00
set_irq_chip ( pin , & gpio_irqchip ) ;
set_irq_handler ( pin , do_simple_IRQ ) ;
set_irq_flags ( pin , IRQF_VALID ) ;
}
set_irq_chained_handler ( id , gpio_irq_handler ) ;
}
pr_info ( " AT91: %d gpio irqs in %d banks \n " , pin - PIN_BASE , banks ) ;
}