2008-02-05 09:28:22 +03:00
/*
2009-01-20 07:09:06 +03:00
* linux / arch / arm / plat - pxa / gpio . c
2008-02-05 09:28:22 +03:00
*
* Generic PXA GPIO handling
*
* Author : Nicolas Pitre
* Created : Jun 15 , 2001
* Copyright : 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 version 2 as
* published by the Free Software Foundation .
*/
# include <linux/init.h>
2008-03-04 06:42:26 +03:00
# include <linux/irq.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2011-04-23 00:03:11 +04:00
# include <linux/syscore_ops.h>
2009-06-20 00:56:09 +04:00
# include <linux/slab.h>
2008-02-05 09:28:22 +03:00
2009-01-06 13:29:01 +03:00
# include <mach/gpio.h>
2008-02-05 09:28:22 +03:00
2009-01-07 06:30:49 +03:00
int pxa_last_gpio ;
2008-02-05 09:28:22 +03:00
struct pxa_gpio_chip {
struct gpio_chip chip ;
2009-01-07 13:01:51 +03:00
void __iomem * regbase ;
char label [ 10 ] ;
unsigned long irq_mask ;
unsigned long irq_edge_rise ;
unsigned long irq_edge_fall ;
# ifdef CONFIG_PM
unsigned long saved_gplr ;
unsigned long saved_gpdr ;
unsigned long saved_grer ;
unsigned long saved_gfer ;
# endif
2008-02-05 09:28:22 +03:00
} ;
2009-01-07 13:01:51 +03:00
static DEFINE_SPINLOCK ( gpio_lock ) ;
static struct pxa_gpio_chip * pxa_gpio_chips ;
# define for_each_gpio_chip(i, c) \
for ( i = 0 , c = & pxa_gpio_chips [ 0 ] ; i < = pxa_last_gpio ; i + = 32 , c + + )
static inline void __iomem * gpio_chip_base ( struct gpio_chip * c )
{
return container_of ( c , struct pxa_gpio_chip , chip ) - > regbase ;
}
static inline struct pxa_gpio_chip * gpio_to_chip ( unsigned gpio )
{
return & pxa_gpio_chips [ gpio_to_bank ( gpio ) ] ;
}
2008-02-05 09:28:22 +03:00
static int pxa_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
2009-01-07 13:01:51 +03:00
void __iomem * base = gpio_chip_base ( chip ) ;
uint32_t value , mask = 1 < < offset ;
unsigned long flags ;
spin_lock_irqsave ( & gpio_lock , flags ) ;
value = __raw_readl ( base + GPDR_OFFSET ) ;
2008-11-26 13:12:04 +03:00
if ( __gpio_is_inverted ( chip - > base + offset ) )
value | = mask ;
else
value & = ~ mask ;
2009-01-07 13:01:51 +03:00
__raw_writel ( value , base + GPDR_OFFSET ) ;
2008-02-05 09:28:22 +03:00
2009-01-07 13:01:51 +03:00
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
2008-02-05 09:28:22 +03:00
return 0 ;
}
static int pxa_gpio_direction_output ( struct gpio_chip * chip ,
2009-01-07 13:01:51 +03:00
unsigned offset , int value )
2008-02-05 09:28:22 +03:00
{
2009-01-07 13:01:51 +03:00
void __iomem * base = gpio_chip_base ( chip ) ;
uint32_t tmp , mask = 1 < < offset ;
unsigned long flags ;
__raw_writel ( mask , base + ( value ? GPSR_OFFSET : GPCR_OFFSET ) ) ;
spin_lock_irqsave ( & gpio_lock , flags ) ;
tmp = __raw_readl ( base + GPDR_OFFSET ) ;
2008-11-26 13:12:04 +03:00
if ( __gpio_is_inverted ( chip - > base + offset ) )
tmp & = ~ mask ;
else
tmp | = mask ;
2009-01-07 13:01:51 +03:00
__raw_writel ( tmp , base + GPDR_OFFSET ) ;
2008-02-05 09:28:22 +03:00
2009-01-07 13:01:51 +03:00
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
2008-02-05 09:28:22 +03:00
return 0 ;
}
static int pxa_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
2009-01-07 13:01:51 +03:00
return __raw_readl ( gpio_chip_base ( chip ) + GPLR_OFFSET ) & ( 1 < < offset ) ;
2008-02-05 09:28:22 +03:00
}
static void pxa_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
2009-01-07 13:01:51 +03:00
__raw_writel ( 1 < < offset , gpio_chip_base ( chip ) +
( value ? GPSR_OFFSET : GPCR_OFFSET ) ) ;
2008-02-05 09:28:22 +03:00
}
2009-01-07 13:01:51 +03:00
static int __init pxa_init_gpio_chip ( int gpio_end )
2009-01-06 12:37:37 +03:00
{
2009-01-07 13:01:51 +03:00
int i , gpio , nbanks = gpio_to_bank ( gpio_end ) + 1 ;
struct pxa_gpio_chip * chips ;
2009-01-06 12:37:37 +03:00
2009-06-20 00:56:09 +04:00
chips = kzalloc ( nbanks * sizeof ( struct pxa_gpio_chip ) , GFP_KERNEL ) ;
2009-01-07 13:01:51 +03:00
if ( chips = = NULL ) {
pr_err ( " %s: failed to allocate GPIO chips \n " , __func__ ) ;
return - ENOMEM ;
2009-01-06 12:37:37 +03:00
}
2009-01-07 13:01:51 +03:00
for ( i = 0 , gpio = 0 ; i < nbanks ; i + + , gpio + = 32 ) {
struct gpio_chip * c = & chips [ i ] . chip ;
2008-03-04 06:42:26 +03:00
2009-01-07 13:01:51 +03:00
sprintf ( chips [ i ] . label , " gpio-%d " , i ) ;
chips [ i ] . regbase = ( void __iomem * ) GPIO_BANK ( i ) ;
c - > base = gpio ;
c - > label = chips [ i ] . label ;
c - > direction_input = pxa_gpio_direction_input ;
c - > direction_output = pxa_gpio_direction_output ;
c - > get = pxa_gpio_get ;
c - > set = pxa_gpio_set ;
/* number of GPIOs on last bank may be less than 32 */
c - > ngpio = ( gpio + 31 > gpio_end ) ? ( gpio_end - gpio + 1 ) : 32 ;
gpiochip_add ( c ) ;
}
pxa_gpio_chips = chips ;
return 0 ;
}
2008-03-04 06:42:26 +03:00
2009-04-21 10:39:07 +04:00
/* Update only those GRERx and GFERx edge detection register bits if those
* bits are set in c - > irq_mask
*/
static inline void update_edge_detect ( struct pxa_gpio_chip * c )
{
uint32_t grer , gfer ;
grer = __raw_readl ( c - > regbase + GRER_OFFSET ) & ~ c - > irq_mask ;
gfer = __raw_readl ( c - > regbase + GFER_OFFSET ) & ~ c - > irq_mask ;
grer | = c - > irq_edge_rise & c - > irq_mask ;
gfer | = c - > irq_edge_fall & c - > irq_mask ;
__raw_writel ( grer , c - > regbase + GRER_OFFSET ) ;
__raw_writel ( gfer , c - > regbase + GFER_OFFSET ) ;
}
2010-11-29 13:18:26 +03:00
static int pxa_gpio_irq_type ( struct irq_data * d , unsigned int type )
2008-03-04 06:42:26 +03:00
{
2009-01-07 13:01:51 +03:00
struct pxa_gpio_chip * c ;
2010-11-29 13:18:26 +03:00
int gpio = irq_to_gpio ( d - > irq ) ;
2009-01-07 13:01:51 +03:00
unsigned long gpdr , mask = GPIO_bit ( gpio ) ;
2008-03-04 06:42:26 +03:00
2009-01-07 13:01:51 +03:00
c = gpio_to_chip ( gpio ) ;
2008-03-04 06:42:26 +03:00
if ( type = = IRQ_TYPE_PROBE ) {
/* Don't mess with enabled GPIOs using preconfigured edges or
* GPIOs set to alternate function or to output during probe
*/
2009-01-07 13:01:51 +03:00
if ( ( c - > irq_edge_rise | c - > irq_edge_fall ) & GPIO_bit ( gpio ) )
2008-03-04 06:42:26 +03:00
return 0 ;
2008-03-04 12:18:38 +03:00
if ( __gpio_is_occupied ( gpio ) )
2008-03-04 06:42:26 +03:00
return 0 ;
2008-03-04 12:18:38 +03:00
2008-03-04 06:42:26 +03:00
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING ;
}
2009-01-07 13:01:51 +03:00
gpdr = __raw_readl ( c - > regbase + GPDR_OFFSET ) ;
2008-11-26 13:12:04 +03:00
if ( __gpio_is_inverted ( gpio ) )
2009-01-07 13:01:51 +03:00
__raw_writel ( gpdr | mask , c - > regbase + GPDR_OFFSET ) ;
2008-11-26 13:12:04 +03:00
else
2009-01-07 13:01:51 +03:00
__raw_writel ( gpdr & ~ mask , c - > regbase + GPDR_OFFSET ) ;
2008-03-04 06:42:26 +03:00
if ( type & IRQ_TYPE_EDGE_RISING )
2009-01-07 13:01:51 +03:00
c - > irq_edge_rise | = mask ;
2008-03-04 06:42:26 +03:00
else
2009-01-07 13:01:51 +03:00
c - > irq_edge_rise & = ~ mask ;
2008-03-04 06:42:26 +03:00
if ( type & IRQ_TYPE_EDGE_FALLING )
2009-01-07 13:01:51 +03:00
c - > irq_edge_fall | = mask ;
2008-03-04 06:42:26 +03:00
else
2009-01-07 13:01:51 +03:00
c - > irq_edge_fall & = ~ mask ;
2008-03-04 06:42:26 +03:00
2009-04-21 10:39:07 +04:00
update_edge_detect ( c ) ;
2008-03-04 06:42:26 +03:00
2010-11-29 13:18:26 +03:00
pr_debug ( " %s: IRQ%d (GPIO%d) - edge%s%s \n " , __func__ , d - > irq , gpio ,
2008-03-04 06:42:26 +03:00
( ( type & IRQ_TYPE_EDGE_RISING ) ? " rising " : " " ) ,
( ( type & IRQ_TYPE_EDGE_FALLING ) ? " falling " : " " ) ) ;
return 0 ;
}
static void pxa_gpio_demux_handler ( unsigned int irq , struct irq_desc * desc )
{
2009-01-07 13:01:51 +03:00
struct pxa_gpio_chip * c ;
int loop , gpio , gpio_base , n ;
unsigned long gedr ;
2008-03-04 06:42:26 +03:00
do {
loop = 0 ;
2009-01-07 13:01:51 +03:00
for_each_gpio_chip ( gpio , c ) {
gpio_base = c - > chip . base ;
gedr = __raw_readl ( c - > regbase + GEDR_OFFSET ) ;
gedr = gedr & c - > irq_mask ;
__raw_writel ( gedr , c - > regbase + GEDR_OFFSET ) ;
2008-03-04 06:42:26 +03:00
2009-01-07 13:01:51 +03:00
n = find_first_bit ( & gedr , BITS_PER_LONG ) ;
while ( n < BITS_PER_LONG ) {
loop = 1 ;
2008-03-04 06:42:26 +03:00
2009-01-07 13:01:51 +03:00
generic_handle_irq ( gpio_to_irq ( gpio_base + n ) ) ;
n = find_next_bit ( & gedr , BITS_PER_LONG , n + 1 ) ;
}
2008-03-04 06:42:26 +03:00
}
} while ( loop ) ;
}
2010-11-29 13:18:26 +03:00
static void pxa_ack_muxed_gpio ( struct irq_data * d )
2008-03-04 06:42:26 +03:00
{
2010-11-29 13:18:26 +03:00
int gpio = irq_to_gpio ( d - > irq ) ;
2009-01-07 13:01:51 +03:00
struct pxa_gpio_chip * c = gpio_to_chip ( gpio ) ;
__raw_writel ( GPIO_bit ( gpio ) , c - > regbase + GEDR_OFFSET ) ;
2008-03-04 06:42:26 +03:00
}
2010-11-29 13:18:26 +03:00
static void pxa_mask_muxed_gpio ( struct irq_data * d )
2008-03-04 06:42:26 +03:00
{
2010-11-29 13:18:26 +03:00
int gpio = irq_to_gpio ( d - > irq ) ;
2009-01-07 13:01:51 +03:00
struct pxa_gpio_chip * c = gpio_to_chip ( gpio ) ;
uint32_t grer , gfer ;
c - > irq_mask & = ~ GPIO_bit ( gpio ) ;
grer = __raw_readl ( c - > regbase + GRER_OFFSET ) & ~ GPIO_bit ( gpio ) ;
gfer = __raw_readl ( c - > regbase + GFER_OFFSET ) & ~ GPIO_bit ( gpio ) ;
__raw_writel ( grer , c - > regbase + GRER_OFFSET ) ;
__raw_writel ( gfer , c - > regbase + GFER_OFFSET ) ;
2008-03-04 06:42:26 +03:00
}
2010-11-29 13:18:26 +03:00
static void pxa_unmask_muxed_gpio ( struct irq_data * d )
2008-03-04 06:42:26 +03:00
{
2010-11-29 13:18:26 +03:00
int gpio = irq_to_gpio ( d - > irq ) ;
2009-01-07 13:01:51 +03:00
struct pxa_gpio_chip * c = gpio_to_chip ( gpio ) ;
c - > irq_mask | = GPIO_bit ( gpio ) ;
2009-04-21 10:39:07 +04:00
update_edge_detect ( c ) ;
2008-03-04 06:42:26 +03:00
}
static struct irq_chip pxa_muxed_gpio_chip = {
. name = " GPIO " ,
2010-11-29 13:18:26 +03:00
. irq_ack = pxa_ack_muxed_gpio ,
. irq_mask = pxa_mask_muxed_gpio ,
. irq_unmask = pxa_unmask_muxed_gpio ,
. irq_set_type = pxa_gpio_irq_type ,
2008-03-04 06:42:26 +03:00
} ;
2009-01-06 12:37:37 +03:00
void __init pxa_init_gpio ( int mux_irq , int start , int end , set_wake_t fn )
2008-03-04 06:42:26 +03:00
{
2009-01-07 13:01:51 +03:00
struct pxa_gpio_chip * c ;
int gpio , irq ;
2008-03-04 06:42:26 +03:00
2009-01-06 12:37:37 +03:00
pxa_last_gpio = end ;
2008-03-04 06:42:26 +03:00
2009-01-07 13:01:51 +03:00
/* Initialize GPIO chips */
pxa_init_gpio_chip ( end ) ;
2008-03-04 06:42:26 +03:00
/* clear all GPIO edge detects */
2009-01-07 13:01:51 +03:00
for_each_gpio_chip ( gpio , c ) {
__raw_writel ( 0 , c - > regbase + GFER_OFFSET ) ;
__raw_writel ( 0 , c - > regbase + GRER_OFFSET ) ;
__raw_writel ( ~ 0 , c - > regbase + GEDR_OFFSET ) ;
2008-03-04 06:42:26 +03:00
}
2009-01-06 12:37:37 +03:00
for ( irq = gpio_to_irq ( start ) ; irq < = gpio_to_irq ( end ) ; irq + + ) {
2011-03-24 15:35:09 +03:00
irq_set_chip_and_handler ( irq , & pxa_muxed_gpio_chip ,
handle_edge_irq ) ;
2008-03-04 06:42:26 +03:00
set_irq_flags ( irq , IRQF_VALID | IRQF_PROBE ) ;
}
/* Install handler for GPIO>=2 edge detect interrupts */
2011-03-24 15:25:22 +03:00
irq_set_chained_handler ( mux_irq , pxa_gpio_demux_handler ) ;
2010-11-29 13:18:26 +03:00
pxa_muxed_gpio_chip . irq_set_wake = fn ;
2008-03-04 06:42:26 +03:00
}
2008-03-04 11:13:58 +03:00
# ifdef CONFIG_PM
2011-04-23 00:03:11 +04:00
static int pxa_gpio_suspend ( void )
2008-03-04 11:13:58 +03:00
{
2009-01-07 13:01:51 +03:00
struct pxa_gpio_chip * c ;
int gpio ;
2008-03-04 11:13:58 +03:00
2009-01-07 13:01:51 +03:00
for_each_gpio_chip ( gpio , c ) {
c - > saved_gplr = __raw_readl ( c - > regbase + GPLR_OFFSET ) ;
c - > saved_gpdr = __raw_readl ( c - > regbase + GPDR_OFFSET ) ;
c - > saved_grer = __raw_readl ( c - > regbase + GRER_OFFSET ) ;
c - > saved_gfer = __raw_readl ( c - > regbase + GFER_OFFSET ) ;
2008-03-04 11:13:58 +03:00
/* Clear GPIO transition detect bits */
2009-01-07 13:01:51 +03:00
__raw_writel ( 0xffffffff , c - > regbase + GEDR_OFFSET ) ;
2008-03-04 11:13:58 +03:00
}
return 0 ;
}
2011-04-23 00:03:11 +04:00
static void pxa_gpio_resume ( void )
2008-03-04 11:13:58 +03:00
{
2009-01-07 13:01:51 +03:00
struct pxa_gpio_chip * c ;
int gpio ;
2008-03-04 11:13:58 +03:00
2009-01-07 13:01:51 +03:00
for_each_gpio_chip ( gpio , c ) {
2008-03-04 11:13:58 +03:00
/* restore level with set/clear */
2009-01-07 13:01:51 +03:00
__raw_writel ( c - > saved_gplr , c - > regbase + GPSR_OFFSET ) ;
__raw_writel ( ~ c - > saved_gplr , c - > regbase + GPCR_OFFSET ) ;
2008-03-04 11:13:58 +03:00
2009-01-07 13:01:51 +03:00
__raw_writel ( c - > saved_grer , c - > regbase + GRER_OFFSET ) ;
__raw_writel ( c - > saved_gfer , c - > regbase + GFER_OFFSET ) ;
__raw_writel ( c - > saved_gpdr , c - > regbase + GPDR_OFFSET ) ;
2008-03-04 11:13:58 +03:00
}
}
# else
# define pxa_gpio_suspend NULL
# define pxa_gpio_resume NULL
# endif
2011-04-23 00:03:11 +04:00
struct syscore_ops pxa_gpio_syscore_ops = {
2008-03-04 11:13:58 +03:00
. suspend = pxa_gpio_suspend ,
. resume = pxa_gpio_resume ,
} ;