2008-10-20 03:51:03 +04:00
/*
* arch / arm / plat - orion / gpio . c
*
* Marvell Orion SoC GPIO handling .
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/kernel.h>
# include <linux/init.h>
2008-10-20 03:51:03 +04:00
# include <linux/irq.h>
2008-10-20 03:51:03 +04:00
# include <linux/module.h>
# include <linux/spinlock.h>
# include <linux/bitops.h>
# include <linux/io.h>
2009-05-29 04:08:55 +04:00
# include <linux/gpio.h>
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
/*
* GPIO unit register offsets .
*/
# define GPIO_OUT_OFF 0x0000
# define GPIO_IO_CONF_OFF 0x0004
# define GPIO_BLINK_EN_OFF 0x0008
# define GPIO_IN_POL_OFF 0x000c
# define GPIO_DATA_IN_OFF 0x0010
# define GPIO_EDGE_CAUSE_OFF 0x0014
# define GPIO_EDGE_MASK_OFF 0x0018
# define GPIO_LEVEL_MASK_OFF 0x001c
struct orion_gpio_chip {
struct gpio_chip chip ;
spinlock_t lock ;
void __iomem * base ;
unsigned long valid_input ;
unsigned long valid_output ;
int mask_offset ;
int secondary_irq_base ;
} ;
static void __iomem * GPIO_OUT ( struct orion_gpio_chip * ochip )
{
return ochip - > base + GPIO_OUT_OFF ;
}
static void __iomem * GPIO_IO_CONF ( struct orion_gpio_chip * ochip )
{
return ochip - > base + GPIO_IO_CONF_OFF ;
}
static void __iomem * GPIO_BLINK_EN ( struct orion_gpio_chip * ochip )
{
return ochip - > base + GPIO_BLINK_EN_OFF ;
}
static void __iomem * GPIO_IN_POL ( struct orion_gpio_chip * ochip )
{
return ochip - > base + GPIO_IN_POL_OFF ;
}
static void __iomem * GPIO_DATA_IN ( struct orion_gpio_chip * ochip )
{
return ochip - > base + GPIO_DATA_IN_OFF ;
}
static void __iomem * GPIO_EDGE_CAUSE ( struct orion_gpio_chip * ochip )
{
return ochip - > base + GPIO_EDGE_CAUSE_OFF ;
}
static void __iomem * GPIO_EDGE_MASK ( struct orion_gpio_chip * ochip )
{
return ochip - > base + ochip - > mask_offset + GPIO_EDGE_MASK_OFF ;
}
static void __iomem * GPIO_LEVEL_MASK ( struct orion_gpio_chip * ochip )
{
return ochip - > base + ochip - > mask_offset + GPIO_LEVEL_MASK_OFF ;
}
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
static struct orion_gpio_chip orion_gpio_chips [ 2 ] ;
static int orion_gpio_chip_count ;
static inline void
__set_direction ( struct orion_gpio_chip * ochip , unsigned pin , int input )
2008-10-20 03:51:03 +04:00
{
u32 u ;
2010-12-14 14:54:03 +03:00
u = readl ( GPIO_IO_CONF ( ochip ) ) ;
2008-10-20 03:51:03 +04:00
if ( input )
2010-12-14 14:54:03 +03:00
u | = 1 < < pin ;
2008-10-20 03:51:03 +04:00
else
2010-12-14 14:54:03 +03:00
u & = ~ ( 1 < < pin ) ;
writel ( u , GPIO_IO_CONF ( ochip ) ) ;
2008-10-20 03:51:03 +04:00
}
2010-12-14 14:54:03 +03:00
static void __set_level ( struct orion_gpio_chip * ochip , unsigned pin , int high )
2008-10-20 03:51:03 +04:00
{
u32 u ;
2010-12-14 14:54:03 +03:00
u = readl ( GPIO_OUT ( ochip ) ) ;
2008-10-20 03:51:03 +04:00
if ( high )
2010-12-14 14:54:03 +03:00
u | = 1 < < pin ;
2008-10-20 03:51:03 +04:00
else
2010-12-14 14:54:03 +03:00
u & = ~ ( 1 < < pin ) ;
writel ( u , GPIO_OUT ( ochip ) ) ;
2008-10-20 03:51:03 +04:00
}
2010-12-14 14:54:03 +03:00
static inline void
__set_blinking ( struct orion_gpio_chip * ochip , unsigned pin , int blink )
2008-10-20 03:51:03 +04:00
{
2009-05-29 04:08:55 +04:00
u32 u ;
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
u = readl ( GPIO_BLINK_EN ( ochip ) ) ;
2009-05-29 04:08:55 +04:00
if ( blink )
2010-12-14 14:54:03 +03:00
u | = 1 < < pin ;
2009-05-29 04:08:55 +04:00
else
2010-12-14 14:54:03 +03:00
u & = ~ ( 1 < < pin ) ;
writel ( u , GPIO_BLINK_EN ( ochip ) ) ;
2009-05-29 04:08:55 +04:00
}
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
static inline int
orion_gpio_is_valid ( struct orion_gpio_chip * ochip , unsigned pin , int mode )
2009-05-29 04:08:55 +04:00
{
2010-12-14 14:54:03 +03:00
if ( pin > = ochip - > chip . ngpio )
goto err_out ;
if ( ( mode & GPIO_INPUT_OK ) & & ! test_bit ( pin , & ochip - > valid_input ) )
goto err_out ;
if ( ( mode & GPIO_OUTPUT_OK ) & & ! test_bit ( pin , & ochip - > valid_output ) )
goto err_out ;
return 1 ;
2008-10-20 03:51:03 +04:00
2009-05-29 04:08:55 +04:00
err_out :
pr_debug ( " %s: invalid GPIO %d \n " , __func__ , pin ) ;
return false ;
2008-10-20 03:51:03 +04:00
}
2009-05-29 04:08:55 +04:00
/*
* GENERIC_GPIO primitives .
*/
2010-12-14 14:54:03 +03:00
static int orion_gpio_request ( struct gpio_chip * chip , unsigned pin )
{
struct orion_gpio_chip * ochip =
container_of ( chip , struct orion_gpio_chip , chip ) ;
if ( orion_gpio_is_valid ( ochip , pin , GPIO_INPUT_OK ) | |
orion_gpio_is_valid ( ochip , pin , GPIO_OUTPUT_OK ) )
return 0 ;
return - EINVAL ;
}
2009-05-29 04:08:55 +04:00
static int orion_gpio_direction_input ( struct gpio_chip * chip , unsigned pin )
2008-10-20 03:51:03 +04:00
{
2010-12-14 14:54:03 +03:00
struct orion_gpio_chip * ochip =
container_of ( chip , struct orion_gpio_chip , chip ) ;
2008-10-20 03:51:03 +04:00
unsigned long flags ;
2010-12-14 14:54:03 +03:00
if ( ! orion_gpio_is_valid ( ochip , pin , GPIO_INPUT_OK ) )
2008-10-20 03:51:03 +04:00
return - EINVAL ;
2010-12-14 14:54:03 +03:00
spin_lock_irqsave ( & ochip - > lock , flags ) ;
__set_direction ( ochip , pin , 1 ) ;
spin_unlock_irqrestore ( & ochip - > lock , flags ) ;
2008-10-20 03:51:03 +04:00
return 0 ;
}
2010-12-14 14:54:03 +03:00
static int orion_gpio_get ( struct gpio_chip * chip , unsigned pin )
2008-10-20 03:51:03 +04:00
{
2010-12-14 14:54:03 +03:00
struct orion_gpio_chip * ochip =
container_of ( chip , struct orion_gpio_chip , chip ) ;
2008-10-20 03:51:03 +04:00
int val ;
2010-12-14 14:54:03 +03:00
if ( readl ( GPIO_IO_CONF ( ochip ) ) & ( 1 < < pin ) ) {
val = readl ( GPIO_DATA_IN ( ochip ) ) ^ readl ( GPIO_IN_POL ( ochip ) ) ;
} else {
val = readl ( GPIO_OUT ( ochip ) ) ;
}
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
return ( val > > pin ) & 1 ;
2008-10-20 03:51:03 +04:00
}
2010-12-14 14:54:03 +03:00
static int
orion_gpio_direction_output ( struct gpio_chip * chip , unsigned pin , int value )
2008-10-20 03:51:03 +04:00
{
2010-12-14 14:54:03 +03:00
struct orion_gpio_chip * ochip =
container_of ( chip , struct orion_gpio_chip , chip ) ;
2008-10-20 03:51:03 +04:00
unsigned long flags ;
2009-05-29 04:08:55 +04:00
2010-12-14 14:54:03 +03:00
if ( ! orion_gpio_is_valid ( ochip , pin , GPIO_OUTPUT_OK ) )
2009-05-29 04:08:55 +04:00
return - EINVAL ;
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
spin_lock_irqsave ( & ochip - > lock , flags ) ;
__set_blinking ( ochip , pin , 0 ) ;
__set_level ( ochip , pin , value ) ;
__set_direction ( ochip , pin , 0 ) ;
spin_unlock_irqrestore ( & ochip - > lock , flags ) ;
2009-05-29 04:08:55 +04:00
return 0 ;
2008-10-20 03:51:03 +04:00
}
2010-12-14 14:54:03 +03:00
static void orion_gpio_set ( struct gpio_chip * chip , unsigned pin , int value )
2008-10-20 03:51:03 +04:00
{
2010-12-14 14:54:03 +03:00
struct orion_gpio_chip * ochip =
container_of ( chip , struct orion_gpio_chip , chip ) ;
2008-10-20 03:51:03 +04:00
unsigned long flags ;
2010-12-14 14:54:03 +03:00
spin_lock_irqsave ( & ochip - > lock , flags ) ;
__set_level ( ochip , pin , value ) ;
spin_unlock_irqrestore ( & ochip - > lock , flags ) ;
2008-10-20 03:51:03 +04:00
}
2010-12-14 14:54:03 +03:00
static int orion_gpio_to_irq ( struct gpio_chip * chip , unsigned pin )
2008-10-20 03:51:03 +04:00
{
2010-12-14 14:54:03 +03:00
struct orion_gpio_chip * ochip =
container_of ( chip , struct orion_gpio_chip , chip ) ;
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
return ochip - > secondary_irq_base + pin ;
2009-05-29 04:08:55 +04:00
}
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
2008-10-20 03:51:03 +04:00
/*
* Orion - specific GPIO API extensions .
*/
2010-12-14 14:54:03 +03:00
static struct orion_gpio_chip * orion_gpio_chip_find ( int pin )
{
int i ;
for ( i = 0 ; i < orion_gpio_chip_count ; i + + ) {
struct orion_gpio_chip * ochip = orion_gpio_chips + i ;
struct gpio_chip * chip = & ochip - > chip ;
if ( pin > = chip - > base & & pin < chip - > base + chip - > ngpio )
return ochip ;
}
return NULL ;
}
2008-10-20 03:51:03 +04:00
void __init orion_gpio_set_unused ( unsigned pin )
{
2010-12-14 14:54:03 +03:00
struct orion_gpio_chip * ochip = orion_gpio_chip_find ( pin ) ;
if ( ochip = = NULL )
return ;
pin - = ochip - > chip . base ;
2009-05-29 04:08:55 +04:00
/* Configure as output, drive low. */
2010-12-14 14:54:03 +03:00
__set_level ( ochip , pin , 0 ) ;
__set_direction ( ochip , pin , 0 ) ;
2008-10-20 03:51:03 +04:00
}
2009-02-02 23:27:55 +03:00
void __init orion_gpio_set_valid ( unsigned pin , int mode )
2008-10-20 03:51:03 +04:00
{
2010-12-14 14:54:03 +03:00
struct orion_gpio_chip * ochip = orion_gpio_chip_find ( pin ) ;
if ( ochip = = NULL )
return ;
pin - = ochip - > chip . base ;
2009-02-02 23:27:55 +03:00
if ( mode = = 1 )
mode = GPIO_INPUT_OK | GPIO_OUTPUT_OK ;
2010-12-14 14:54:03 +03:00
2009-02-02 23:27:55 +03:00
if ( mode & GPIO_INPUT_OK )
2010-12-14 14:54:03 +03:00
__set_bit ( pin , & ochip - > valid_input ) ;
2008-10-20 03:51:03 +04:00
else
2010-12-14 14:54:03 +03:00
__clear_bit ( pin , & ochip - > valid_input ) ;
2009-02-02 23:27:55 +03:00
if ( mode & GPIO_OUTPUT_OK )
2010-12-14 14:54:03 +03:00
__set_bit ( pin , & ochip - > valid_output ) ;
2009-02-02 23:27:55 +03:00
else
2010-12-14 14:54:03 +03:00
__clear_bit ( pin , & ochip - > valid_output ) ;
2008-10-20 03:51:03 +04:00
}
void orion_gpio_set_blink ( unsigned pin , int blink )
{
2010-12-14 14:54:03 +03:00
struct orion_gpio_chip * ochip = orion_gpio_chip_find ( pin ) ;
2008-10-20 03:51:03 +04:00
unsigned long flags ;
2010-12-14 14:54:03 +03:00
if ( ochip = = NULL )
return ;
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
spin_lock_irqsave ( & ochip - > lock , flags ) ;
__set_level ( ochip , pin , 0 ) ;
__set_blinking ( ochip , pin , blink ) ;
spin_unlock_irqrestore ( & ochip - > lock , flags ) ;
2008-10-20 03:51:03 +04:00
}
EXPORT_SYMBOL ( orion_gpio_set_blink ) ;
2008-10-20 03:51:03 +04:00
/*****************************************************************************
* Orion GPIO IRQ
*
* GPIO_IN_POL register controls whether GPIO_DATA_IN will hold the same
* value of the line or the opposite value .
*
* Level IRQ handlers : DATA_IN is used directly as cause register .
* Interrupt are masked by LEVEL_MASK registers .
* Edge IRQ handlers : Change in DATA_IN are latched in EDGE_CAUSE .
* Interrupt are masked by EDGE_MASK registers .
* Both - edge handlers : Similar to regular Edge handlers , but also swaps
* the polarity to catch the next line transaction .
* This is a race condition that might not perfectly
* work on some use cases .
*
* Every eight GPIO lines are grouped ( OR ' ed ) before going up to main
* cause register .
*
* EDGE cause mask
* data - in / - - - - - - - - | | - - - - - | | - - - - \
* - - - - - | | - - - - - - - - - to main cause reg
* X \ - - - - - - - - - - - - - - - - | | - - - - /
* polarity LEVEL mask
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-11-29 13:17:38 +03:00
static int gpio_irq_set_type ( struct irq_data * d , u32 type )
2008-10-20 03:51:03 +04:00
{
2011-04-14 21:17:57 +04:00
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
struct orion_gpio_chip * ochip = gc - > private ;
2010-12-14 14:54:03 +03:00
int pin ;
2008-10-20 03:51:03 +04:00
u32 u ;
2011-04-14 21:17:57 +04:00
pin = d - > irq - gc - > irq_base ;
2010-12-14 14:54:03 +03:00
u = readl ( GPIO_IO_CONF ( ochip ) ) & ( 1 < < pin ) ;
2008-10-20 03:51:03 +04:00
if ( ! u ) {
printk ( KERN_ERR " orion gpio_irq_set_type failed "
2010-11-29 13:17:38 +03:00
" (irq %d, pin %d). \n " , d - > irq , pin ) ;
2008-10-20 03:51:03 +04:00
return - EINVAL ;
}
2011-04-14 21:17:57 +04:00
type & = IRQ_TYPE_SENSE_MASK ;
if ( type = = IRQ_TYPE_NONE )
2008-10-20 03:51:03 +04:00
return - EINVAL ;
2011-04-14 21:17:57 +04:00
/* Check if we need to change chip and handler */
if ( ! ( ct - > type & type ) )
if ( irq_setup_alt_chip ( d , type ) )
return - EINVAL ;
2008-10-20 03:51:03 +04:00
/*
* Configure interrupt polarity .
*/
if ( type = = IRQ_TYPE_EDGE_RISING | | type = = IRQ_TYPE_LEVEL_HIGH ) {
2010-12-14 14:54:03 +03:00
u = readl ( GPIO_IN_POL ( ochip ) ) ;
u & = ~ ( 1 < < pin ) ;
writel ( u , GPIO_IN_POL ( ochip ) ) ;
2008-10-20 03:51:03 +04:00
} else if ( type = = IRQ_TYPE_EDGE_FALLING | | type = = IRQ_TYPE_LEVEL_LOW ) {
2010-12-14 14:54:03 +03:00
u = readl ( GPIO_IN_POL ( ochip ) ) ;
u | = 1 < < pin ;
writel ( u , GPIO_IN_POL ( ochip ) ) ;
2008-10-20 03:51:03 +04:00
} else if ( type = = IRQ_TYPE_EDGE_BOTH ) {
u32 v ;
2010-12-14 14:54:03 +03:00
v = readl ( GPIO_IN_POL ( ochip ) ) ^ readl ( GPIO_DATA_IN ( ochip ) ) ;
2008-10-20 03:51:03 +04:00
/*
* set initial polarity based on current input level
*/
2010-12-14 14:54:03 +03:00
u = readl ( GPIO_IN_POL ( ochip ) ) ;
if ( v & ( 1 < < pin ) )
u | = 1 < < pin ; /* falling */
2008-10-20 03:51:03 +04:00
else
2010-12-14 14:54:03 +03:00
u & = ~ ( 1 < < pin ) ; /* rising */
writel ( u , GPIO_IN_POL ( ochip ) ) ;
2008-10-20 03:51:03 +04:00
}
return 0 ;
}
2010-12-14 14:54:03 +03:00
void __init orion_gpio_init ( int gpio_base , int ngpio ,
u32 base , int mask_offset , int secondary_irq_base )
{
struct orion_gpio_chip * ochip ;
2011-04-14 21:17:57 +04:00
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
2010-12-14 14:54:03 +03:00
if ( orion_gpio_chip_count = = ARRAY_SIZE ( orion_gpio_chips ) )
return ;
ochip = orion_gpio_chips + orion_gpio_chip_count ;
ochip - > chip . label = " orion_gpio " ;
ochip - > chip . request = orion_gpio_request ;
ochip - > chip . direction_input = orion_gpio_direction_input ;
ochip - > chip . get = orion_gpio_get ;
ochip - > chip . direction_output = orion_gpio_direction_output ;
ochip - > chip . set = orion_gpio_set ;
ochip - > chip . to_irq = orion_gpio_to_irq ;
ochip - > chip . base = gpio_base ;
ochip - > chip . ngpio = ngpio ;
ochip - > chip . can_sleep = 0 ;
spin_lock_init ( & ochip - > lock ) ;
ochip - > base = ( void __iomem * ) base ;
ochip - > valid_input = 0 ;
ochip - > valid_output = 0 ;
ochip - > mask_offset = mask_offset ;
ochip - > secondary_irq_base = secondary_irq_base ;
gpiochip_add ( & ochip - > chip ) ;
orion_gpio_chip_count + + ;
/*
* Mask and clear GPIO interrupts .
*/
writel ( 0 , GPIO_EDGE_CAUSE ( ochip ) ) ;
writel ( 0 , GPIO_EDGE_MASK ( ochip ) ) ;
writel ( 0 , GPIO_LEVEL_MASK ( ochip ) ) ;
2011-04-14 21:17:57 +04:00
gc = irq_alloc_generic_chip ( " orion_gpio_irq " , 2 , secondary_irq_base ,
ochip - > base , handle_level_irq ) ;
gc - > private = ochip ;
ct = gc - > chip_types ;
ct - > regs . mask = ochip - > mask_offset + GPIO_LEVEL_MASK_OFF ;
ct - > type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW ;
ct - > chip . irq_mask = irq_gc_mask_clr_bit ;
ct - > chip . irq_unmask = irq_gc_mask_set_bit ;
ct - > chip . irq_set_type = gpio_irq_set_type ;
ct + + ;
ct - > regs . mask = ochip - > mask_offset + GPIO_EDGE_MASK_OFF ;
ct - > regs . ack = GPIO_EDGE_CAUSE_OFF ;
ct - > type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING ;
2011-07-06 20:41:31 +04:00
ct - > chip . irq_ack = irq_gc_ack_clr_bit ;
2011-04-14 21:17:57 +04:00
ct - > chip . irq_mask = irq_gc_mask_clr_bit ;
ct - > chip . irq_unmask = irq_gc_mask_set_bit ;
ct - > chip . irq_set_type = gpio_irq_set_type ;
ct - > handler = handle_edge_irq ;
irq_setup_generic_chip ( gc , IRQ_MSK ( ngpio ) , IRQ_GC_INIT_MASK_CACHE ,
IRQ_NOREQUEST , IRQ_LEVEL | IRQ_NOPROBE ) ;
2010-12-14 14:54:03 +03:00
}
2008-10-20 03:51:03 +04:00
void orion_gpio_irq_handler ( int pinoff )
{
2010-12-14 14:54:03 +03:00
struct orion_gpio_chip * ochip ;
2011-03-24 14:35:19 +03:00
u32 cause , type ;
2010-12-14 14:54:03 +03:00
int i ;
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
ochip = orion_gpio_chip_find ( pinoff ) ;
if ( ochip = = NULL )
return ;
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
cause = readl ( GPIO_DATA_IN ( ochip ) ) & readl ( GPIO_LEVEL_MASK ( ochip ) ) ;
cause | = readl ( GPIO_EDGE_CAUSE ( ochip ) ) & readl ( GPIO_EDGE_MASK ( ochip ) ) ;
2008-10-20 03:51:03 +04:00
2010-12-14 14:54:03 +03:00
for ( i = 0 ; i < ochip - > chip . ngpio ; i + + ) {
int irq ;
irq = ochip - > secondary_irq_base + i ;
if ( ! ( cause & ( 1 < < i ) ) )
2008-10-20 03:51:03 +04:00
continue ;
2011-03-24 14:35:19 +03:00
type = irqd_get_trigger_type ( irq_get_irq_data ( irq ) ) ;
if ( ( type & IRQ_TYPE_SENSE_MASK ) = = IRQ_TYPE_EDGE_BOTH ) {
2008-10-20 03:51:03 +04:00
/* Swap polarity (race with GPIO line) */
u32 polarity ;
2010-12-14 14:54:03 +03:00
polarity = readl ( GPIO_IN_POL ( ochip ) ) ;
polarity ^ = 1 < < i ;
writel ( polarity , GPIO_IN_POL ( ochip ) ) ;
2008-10-20 03:51:03 +04:00
}
2011-03-24 14:35:19 +03:00
generic_handle_irq ( irq ) ;
2008-10-20 03:51:03 +04:00
}
}