2009-03-26 11:06:27 +03:00
/*
* Gemini gpiochip and interrupt routines
*
* Copyright ( C ) 2008 - 2009 Paulius Zaleckas < paulius . zaleckas @ teltonika . lt >
*
* Based on plat - mxc / gpio . c :
* MXC GPIO support . ( c ) 2008 Daniel Mack < daniel @ caiaq . de >
* Copyright 2008 Juergen Beisert , kernel @ pengutronix . de
*
* 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/kernel.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/gpio.h>
# include <mach/hardware.h>
# include <mach/irqs.h>
# define GPIO_BASE(x) IO_ADDRESS(GEMINI_GPIO_BASE(x))
/* GPIO registers definition */
# define GPIO_DATA_OUT 0x0
# define GPIO_DATA_IN 0x4
# define GPIO_DIR 0x8
# define GPIO_DATA_SET 0x10
# define GPIO_DATA_CLR 0x14
# define GPIO_PULL_EN 0x18
# define GPIO_PULL_TYPE 0x1C
# define GPIO_INT_EN 0x20
# define GPIO_INT_STAT 0x24
# define GPIO_INT_MASK 0x2C
# define GPIO_INT_CLR 0x30
# define GPIO_INT_TYPE 0x34
# define GPIO_INT_BOTH_EDGE 0x38
# define GPIO_INT_LEVEL 0x3C
# define GPIO_DEBOUNCE_EN 0x40
# define GPIO_DEBOUNCE_PRESCALE 0x44
# define GPIO_PORT_NUM 3
static void _set_gpio_irqenable ( unsigned int base , unsigned int index ,
int enable )
{
unsigned int reg ;
reg = __raw_readl ( base + GPIO_INT_EN ) ;
reg = ( reg & ( ~ ( 1 < < index ) ) ) | ( ! ! enable < < index ) ;
__raw_writel ( reg , base + GPIO_INT_EN ) ;
}
static void gpio_ack_irq ( unsigned int irq )
{
unsigned int gpio = irq_to_gpio ( irq ) ;
unsigned int base = GPIO_BASE ( gpio / 32 ) ;
__raw_writel ( 1 < < ( gpio % 32 ) , base + GPIO_INT_CLR ) ;
}
static void gpio_mask_irq ( unsigned int irq )
{
unsigned int gpio = irq_to_gpio ( irq ) ;
unsigned int base = GPIO_BASE ( gpio / 32 ) ;
_set_gpio_irqenable ( base , gpio % 32 , 0 ) ;
}
static void gpio_unmask_irq ( unsigned int irq )
{
unsigned int gpio = irq_to_gpio ( irq ) ;
unsigned int base = GPIO_BASE ( gpio / 32 ) ;
_set_gpio_irqenable ( base , gpio % 32 , 1 ) ;
}
static int gpio_set_irq_type ( unsigned int irq , unsigned int type )
{
unsigned int gpio = irq_to_gpio ( irq ) ;
unsigned int gpio_mask = 1 < < ( gpio % 32 ) ;
unsigned int base = GPIO_BASE ( gpio / 32 ) ;
unsigned int reg_both , reg_level , reg_type ;
reg_type = __raw_readl ( base + GPIO_INT_TYPE ) ;
2010-02-18 22:54:11 +03:00
reg_level = __raw_readl ( base + GPIO_INT_LEVEL ) ;
2009-03-26 11:06:27 +03:00
reg_both = __raw_readl ( base + GPIO_INT_BOTH_EDGE ) ;
switch ( type ) {
case IRQ_TYPE_EDGE_BOTH :
reg_type & = ~ gpio_mask ;
reg_both | = gpio_mask ;
break ;
case IRQ_TYPE_EDGE_RISING :
reg_type & = ~ gpio_mask ;
reg_both & = ~ gpio_mask ;
reg_level & = ~ gpio_mask ;
break ;
case IRQ_TYPE_EDGE_FALLING :
reg_type & = ~ gpio_mask ;
reg_both & = ~ gpio_mask ;
reg_level | = gpio_mask ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
reg_type | = gpio_mask ;
reg_level & = ~ gpio_mask ;
break ;
case IRQ_TYPE_LEVEL_LOW :
reg_type | = gpio_mask ;
reg_level | = gpio_mask ;
break ;
default :
return - EINVAL ;
}
__raw_writel ( reg_type , base + GPIO_INT_TYPE ) ;
2010-02-18 22:54:11 +03:00
__raw_writel ( reg_level , base + GPIO_INT_LEVEL ) ;
2009-03-26 11:06:27 +03:00
__raw_writel ( reg_both , base + GPIO_INT_BOTH_EDGE ) ;
gpio_ack_irq ( irq ) ;
return 0 ;
}
static void gpio_irq_handler ( unsigned int irq , struct irq_desc * desc )
{
unsigned int gpio_irq_no , irq_stat ;
unsigned int port = ( unsigned int ) get_irq_data ( irq ) ;
irq_stat = __raw_readl ( GPIO_BASE ( port ) + GPIO_INT_STAT ) ;
gpio_irq_no = GPIO_IRQ_BASE + port * 32 ;
for ( ; irq_stat ! = 0 ; irq_stat > > = 1 , gpio_irq_no + + ) {
if ( ( irq_stat & 1 ) = = 0 )
continue ;
BUG_ON ( ! ( irq_desc [ gpio_irq_no ] . handle_irq ) ) ;
irq_desc [ gpio_irq_no ] . handle_irq ( gpio_irq_no ,
& irq_desc [ gpio_irq_no ] ) ;
}
}
static struct irq_chip gpio_irq_chip = {
. name = " GPIO " ,
. 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 )
{
unsigned int base = GPIO_BASE ( offset / 32 ) ;
unsigned int reg ;
reg = __raw_readl ( base + GPIO_DIR ) ;
if ( dir )
reg | = 1 < < ( offset % 32 ) ;
else
reg & = ~ ( 1 < < ( offset % 32 ) ) ;
__raw_writel ( reg , base + GPIO_DIR ) ;
}
static void gemini_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
unsigned int base = GPIO_BASE ( offset / 32 ) ;
if ( value )
__raw_writel ( 1 < < ( offset % 32 ) , base + GPIO_DATA_SET ) ;
else
__raw_writel ( 1 < < ( offset % 32 ) , base + GPIO_DATA_CLR ) ;
}
static int gemini_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
unsigned int base = GPIO_BASE ( offset / 32 ) ;
return ( __raw_readl ( base + GPIO_DATA_IN ) > > ( offset % 32 ) ) & 1 ;
}
static int gemini_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
_set_gpio_direction ( chip , offset , 0 ) ;
return 0 ;
}
static int gemini_gpio_direction_output ( struct gpio_chip * chip , unsigned offset ,
int value )
{
_set_gpio_direction ( chip , offset , 1 ) ;
gemini_gpio_set ( chip , offset , value ) ;
return 0 ;
}
static struct gpio_chip gemini_gpio_chip = {
. label = " Gemini " ,
. direction_input = gemini_gpio_direction_input ,
. get = gemini_gpio_get ,
. direction_output = gemini_gpio_direction_output ,
. set = gemini_gpio_set ,
. base = 0 ,
. ngpio = GPIO_PORT_NUM * 32 ,
} ;
void __init gemini_gpio_init ( void )
{
int i , j ;
for ( i = 0 ; i < GPIO_PORT_NUM ; i + + ) {
/* disable, unmask and clear all interrupts */
__raw_writel ( 0x0 , GPIO_BASE ( i ) + GPIO_INT_EN ) ;
__raw_writel ( 0x0 , GPIO_BASE ( i ) + GPIO_INT_MASK ) ;
__raw_writel ( ~ 0x0 , GPIO_BASE ( i ) + GPIO_INT_CLR ) ;
for ( j = GPIO_IRQ_BASE + i * 32 ;
j < GPIO_IRQ_BASE + ( i + 1 ) * 32 ; j + + ) {
set_irq_chip ( j , & gpio_irq_chip ) ;
set_irq_handler ( j , handle_edge_irq ) ;
set_irq_flags ( j , IRQF_VALID ) ;
}
set_irq_chained_handler ( IRQ_GPIO ( i ) , gpio_irq_handler ) ;
set_irq_data ( IRQ_GPIO ( i ) , ( void * ) i ) ;
}
BUG_ON ( gpiochip_add ( & gemini_gpio_chip ) ) ;
}