2008-07-05 10:02:49 +02:00
/*
* MXC GPIO support . ( c ) 2008 Daniel Mack < daniel @ caiaq . de >
* Copyright 2008 Juergen Beisert , kernel @ pengutronix . de
*
* Based on code from Freescale ,
* Copyright 2004 - 2006 Freescale Semiconductor , Inc . All Rights Reserved .
*
* 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 .
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
# include <linux/init.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/gpio.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2008-07-05 10:02:49 +02:00
# include <asm-generic/bug.h>
static struct mxc_gpio_port * mxc_gpio_ports ;
static int gpio_table_size ;
2009-05-27 18:26:51 +02:00
# define cpu_is_mx1_mx2() (cpu_is_mx1() || cpu_is_mx2())
# define GPIO_DR (cpu_is_mx1_mx2() ? 0x1c : 0x00)
# define GPIO_GDIR (cpu_is_mx1_mx2() ? 0x00 : 0x04)
# define GPIO_PSR (cpu_is_mx1_mx2() ? 0x24 : 0x08)
# define GPIO_ICR1 (cpu_is_mx1_mx2() ? 0x28 : 0x0C)
# define GPIO_ICR2 (cpu_is_mx1_mx2() ? 0x2C : 0x10)
# define GPIO_IMR (cpu_is_mx1_mx2() ? 0x30 : 0x14)
# define GPIO_ISR (cpu_is_mx1_mx2() ? 0x34 : 0x18)
# define GPIO_ISR (cpu_is_mx1_mx2() ? 0x34 : 0x18)
# define GPIO_INT_LOW_LEV (cpu_is_mx1_mx2() ? 0x3 : 0x0)
# define GPIO_INT_HIGH_LEV (cpu_is_mx1_mx2() ? 0x2 : 0x1)
# define GPIO_INT_RISE_EDGE (cpu_is_mx1_mx2() ? 0x0 : 0x2)
# define GPIO_INT_FALL_EDGE (cpu_is_mx1_mx2() ? 0x1 : 0x3)
# define GPIO_INT_NONE 0x4
2008-07-05 10:02:49 +02:00
/* Note: This driver assumes 32 GPIOs are handled in one register */
static void _clear_gpio_irqstatus ( struct mxc_gpio_port * port , u32 index )
{
__raw_writel ( 1 < < index , port - > base + GPIO_ISR ) ;
}
static void _set_gpio_irqenable ( struct mxc_gpio_port * port , u32 index ,
int enable )
{
u32 l ;
l = __raw_readl ( port - > base + GPIO_IMR ) ;
l = ( l & ( ~ ( 1 < < index ) ) ) | ( ! ! enable < < index ) ;
__raw_writel ( l , port - > base + GPIO_IMR ) ;
}
static void gpio_ack_irq ( u32 irq )
{
u32 gpio = irq_to_gpio ( irq ) ;
_clear_gpio_irqstatus ( & mxc_gpio_ports [ gpio / 32 ] , gpio & 0x1f ) ;
}
static void gpio_mask_irq ( u32 irq )
{
u32 gpio = irq_to_gpio ( irq ) ;
_set_gpio_irqenable ( & mxc_gpio_ports [ gpio / 32 ] , gpio & 0x1f , 0 ) ;
}
static void gpio_unmask_irq ( u32 irq )
{
u32 gpio = irq_to_gpio ( irq ) ;
_set_gpio_irqenable ( & mxc_gpio_ports [ gpio / 32 ] , gpio & 0x1f , 1 ) ;
}
2009-03-12 12:46:41 +01:00
static int mxc_gpio_get ( struct gpio_chip * chip , unsigned offset ) ;
2008-07-05 10:02:49 +02:00
static int gpio_set_irq_type ( u32 irq , u32 type )
{
u32 gpio = irq_to_gpio ( irq ) ;
struct mxc_gpio_port * port = & mxc_gpio_ports [ gpio / 32 ] ;
u32 bit , val ;
int edge ;
void __iomem * reg = port - > base ;
2009-03-12 12:46:41 +01:00
port - > both_edges & = ~ ( 1 < < ( gpio & 31 ) ) ;
2008-07-05 10:02:49 +02:00
switch ( type ) {
2008-07-27 04:23:31 +01:00
case IRQ_TYPE_EDGE_RISING :
2008-07-05 10:02:49 +02:00
edge = GPIO_INT_RISE_EDGE ;
break ;
2008-07-27 04:23:31 +01:00
case IRQ_TYPE_EDGE_FALLING :
2008-07-05 10:02:49 +02:00
edge = GPIO_INT_FALL_EDGE ;
break ;
2009-03-12 12:46:41 +01:00
case IRQ_TYPE_EDGE_BOTH :
val = mxc_gpio_get ( & port - > chip , gpio & 31 ) ;
if ( val ) {
edge = GPIO_INT_LOW_LEV ;
pr_debug ( " mxc: set GPIO %d to low trigger \n " , gpio ) ;
} else {
edge = GPIO_INT_HIGH_LEV ;
pr_debug ( " mxc: set GPIO %d to high trigger \n " , gpio ) ;
}
port - > both_edges | = 1 < < ( gpio & 31 ) ;
break ;
2008-07-27 04:23:31 +01:00
case IRQ_TYPE_LEVEL_LOW :
2008-07-05 10:02:49 +02:00
edge = GPIO_INT_LOW_LEV ;
break ;
2008-07-27 04:23:31 +01:00
case IRQ_TYPE_LEVEL_HIGH :
2008-07-05 10:02:49 +02:00
edge = GPIO_INT_HIGH_LEV ;
break ;
2009-03-12 12:46:41 +01:00
default :
2008-07-05 10:02:49 +02:00
return - EINVAL ;
}
reg + = GPIO_ICR1 + ( ( gpio & 0x10 ) > > 2 ) ; /* lower or upper register */
bit = gpio & 0xf ;
val = __raw_readl ( reg ) & ~ ( 0x3 < < ( bit < < 1 ) ) ;
__raw_writel ( val | ( edge < < ( bit < < 1 ) ) , reg ) ;
_clear_gpio_irqstatus ( port , gpio & 0x1f ) ;
return 0 ;
}
2009-03-12 12:46:41 +01:00
static void mxc_flip_edge ( struct mxc_gpio_port * port , u32 gpio )
{
void __iomem * reg = port - > base ;
u32 bit , val ;
int edge ;
reg + = GPIO_ICR1 + ( ( gpio & 0x10 ) > > 2 ) ; /* lower or upper register */
bit = gpio & 0xf ;
val = __raw_readl ( reg ) ;
edge = ( val > > ( bit < < 1 ) ) & 3 ;
val & = ~ ( 0x3 < < ( bit < < 1 ) ) ;
switch ( edge ) {
case GPIO_INT_HIGH_LEV :
edge = GPIO_INT_LOW_LEV ;
pr_debug ( " mxc: switch GPIO %d to low trigger \n " , gpio ) ;
break ;
case GPIO_INT_LOW_LEV :
edge = GPIO_INT_HIGH_LEV ;
pr_debug ( " mxc: switch GPIO %d to high trigger \n " , gpio ) ;
break ;
default :
pr_err ( " mxc: invalid configuration for GPIO %d: %x \n " ,
gpio , edge ) ;
return ;
}
__raw_writel ( val | ( edge < < ( bit < < 1 ) ) , reg ) ;
}
2008-07-05 10:02:49 +02:00
/* handle n interrupts in one status register */
static void mxc_gpio_irq_handler ( struct mxc_gpio_port * port , u32 irq_stat )
{
u32 gpio_irq_no ;
gpio_irq_no = port - > virtual_irq_start ;
for ( ; irq_stat ! = 0 ; irq_stat > > = 1 , gpio_irq_no + + ) {
2009-03-12 12:46:41 +01:00
u32 gpio = irq_to_gpio ( gpio_irq_no ) ;
2008-07-05 10:02:49 +02:00
if ( ( irq_stat & 1 ) = = 0 )
continue ;
BUG_ON ( ! ( irq_desc [ gpio_irq_no ] . handle_irq ) ) ;
2009-03-12 12:46:41 +01:00
if ( port - > both_edges & ( 1 < < ( gpio & 31 ) ) )
mxc_flip_edge ( port , gpio ) ;
2008-07-05 10:02:49 +02:00
irq_desc [ gpio_irq_no ] . handle_irq ( gpio_irq_no ,
& irq_desc [ gpio_irq_no ] ) ;
}
}
2008-11-14 11:01:38 +01:00
/* MX1 and MX3 has one interrupt *per* gpio port */
2008-07-05 10:02:49 +02:00
static void mx3_gpio_irq_handler ( u32 irq , struct irq_desc * desc )
{
u32 irq_stat ;
struct mxc_gpio_port * port = ( struct mxc_gpio_port * ) get_irq_data ( irq ) ;
irq_stat = __raw_readl ( port - > base + GPIO_ISR ) &
__raw_readl ( port - > base + GPIO_IMR ) ;
2009-04-21 12:39:59 +02:00
2008-07-05 10:02:49 +02:00
mxc_gpio_irq_handler ( port , irq_stat ) ;
}
/* MX2 has one interrupt *for all* gpio ports */
static void mx2_gpio_irq_handler ( u32 irq , struct irq_desc * desc )
{
int i ;
u32 irq_msk , irq_stat ;
struct mxc_gpio_port * port = ( struct mxc_gpio_port * ) get_irq_data ( irq ) ;
/* walk through all interrupt status registers */
for ( i = 0 ; i < gpio_table_size ; i + + ) {
irq_msk = __raw_readl ( port [ i ] . base + GPIO_IMR ) ;
if ( ! irq_msk )
continue ;
irq_stat = __raw_readl ( port [ i ] . base + GPIO_ISR ) & irq_msk ;
if ( irq_stat )
mxc_gpio_irq_handler ( & port [ i ] , irq_stat ) ;
}
}
static struct irq_chip gpio_irq_chip = {
. ack = gpio_ack_irq ,
. mask = gpio_mask_irq ,
. unmask = gpio_unmask_irq ,
. set_type = gpio_set_irq_type ,
} ;
static void _set_gpio_direction ( struct gpio_chip * chip , unsigned offset ,
int dir )
{
struct mxc_gpio_port * port =
container_of ( chip , struct mxc_gpio_port , chip ) ;
u32 l ;
l = __raw_readl ( port - > base + GPIO_GDIR ) ;
if ( dir )
l | = 1 < < offset ;
else
l & = ~ ( 1 < < offset ) ;
__raw_writel ( l , port - > base + GPIO_GDIR ) ;
}
static void mxc_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
struct mxc_gpio_port * port =
container_of ( chip , struct mxc_gpio_port , chip ) ;
void __iomem * reg = port - > base + GPIO_DR ;
u32 l ;
l = ( __raw_readl ( reg ) & ( ~ ( 1 < < offset ) ) ) | ( value < < offset ) ;
__raw_writel ( l , reg ) ;
}
static int mxc_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
struct mxc_gpio_port * port =
container_of ( chip , struct mxc_gpio_port , chip ) ;
2008-10-15 10:38:30 +02:00
return ( __raw_readl ( port - > base + GPIO_PSR ) > > offset ) & 1 ;
2008-07-05 10:02:49 +02:00
}
static int mxc_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
_set_gpio_direction ( chip , offset , 0 ) ;
return 0 ;
}
static int mxc_gpio_direction_output ( struct gpio_chip * chip ,
unsigned offset , int value )
{
mxc_gpio_set ( chip , offset , value ) ;
2009-02-12 14:27:22 +01:00
_set_gpio_direction ( chip , offset , 1 ) ;
2008-07-05 10:02:49 +02:00
return 0 ;
}
int __init mxc_gpio_init ( struct mxc_gpio_port * port , int cnt )
{
int i , j ;
/* save for local usage */
mxc_gpio_ports = port ;
gpio_table_size = cnt ;
printk ( KERN_INFO " MXC GPIO hardware \n " ) ;
for ( i = 0 ; i < cnt ; i + + ) {
/* disable the interrupt and clear the status */
__raw_writel ( 0 , port [ i ] . base + GPIO_IMR ) ;
__raw_writel ( ~ 0 , port [ i ] . base + GPIO_ISR ) ;
for ( j = port [ i ] . virtual_irq_start ;
j < port [ i ] . virtual_irq_start + 32 ; j + + ) {
set_irq_chip ( j , & gpio_irq_chip ) ;
set_irq_handler ( j , handle_edge_irq ) ;
set_irq_flags ( j , IRQF_VALID ) ;
}
/* register gpio chip */
port [ i ] . chip . direction_input = mxc_gpio_direction_input ;
port [ i ] . chip . direction_output = mxc_gpio_direction_output ;
port [ i ] . chip . get = mxc_gpio_get ;
port [ i ] . chip . set = mxc_gpio_set ;
port [ i ] . chip . base = i * 32 ;
port [ i ] . chip . ngpio = 32 ;
/* its a serious configuration bug when it fails */
BUG_ON ( gpiochip_add ( & port [ i ] . chip ) < 0 ) ;
2009-06-04 11:32:12 +02:00
if ( cpu_is_mx1 ( ) | | cpu_is_mx3 ( ) | | cpu_is_mx25 ( ) ) {
2009-06-15 12:36:25 +02:00
/* setup one handler for each entry */
set_irq_chained_handler ( port [ i ] . irq , mx3_gpio_irq_handler ) ;
set_irq_data ( port [ i ] . irq , & port [ i ] ) ;
}
}
if ( cpu_is_mx2 ( ) ) {
/* setup one handler for all GPIO interrupts */
set_irq_chained_handler ( port [ 0 ] . irq , mx2_gpio_irq_handler ) ;
set_irq_data ( port [ 0 ] . irq , port ) ;
2008-07-05 10:02:49 +02:00
}
return 0 ;
}