2011-06-09 23:50:13 +04:00
/*
* tps65912 - irq . c - - TI TPS6591x
*
* Copyright 2011 Texas Instruments Inc .
*
* Author : Margarita Olaya < magi @ slimlogic . co . uk >
*
* 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 driver is based on wm8350 implementation .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/bug.h>
# include <linux/device.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/gpio.h>
# include <linux/mfd/tps65912.h>
static inline int irq_to_tps65912_irq ( struct tps65912 * tps65912 ,
int irq )
{
return irq - tps65912 - > irq_base ;
}
/*
* This is a threaded IRQ handler so can access I2C / SPI . Since the
* IRQ handler explicitly clears the IRQ it handles the IRQ line
* will be reasserted and the physical IRQ will be handled again if
* another interrupt is asserted while we run - in the normal course
* of events this is a rare occurrence so we save I2C / SPI reads . We ' re
* also assuming that it ' s rare to get lots of interrupts firing
* simultaneously so try to minimise I / O .
*/
static irqreturn_t tps65912_irq ( int irq , void * irq_data )
{
struct tps65912 * tps65912 = irq_data ;
u32 irq_sts ;
u32 irq_mask ;
u8 reg ;
int i ;
tps65912 - > read ( tps65912 , TPS65912_INT_STS , 1 , & reg ) ;
irq_sts = reg ;
tps65912 - > read ( tps65912 , TPS65912_INT_STS2 , 1 , & reg ) ;
irq_sts | = reg < < 8 ;
tps65912 - > read ( tps65912 , TPS65912_INT_STS3 , 1 , & reg ) ;
irq_sts | = reg < < 16 ;
tps65912 - > read ( tps65912 , TPS65912_INT_STS4 , 1 , & reg ) ;
irq_sts | = reg < < 24 ;
tps65912 - > read ( tps65912 , TPS65912_INT_MSK , 1 , & reg ) ;
irq_mask = reg ;
tps65912 - > read ( tps65912 , TPS65912_INT_MSK2 , 1 , & reg ) ;
irq_mask | = reg < < 8 ;
tps65912 - > read ( tps65912 , TPS65912_INT_MSK3 , 1 , & reg ) ;
irq_mask | = reg < < 16 ;
tps65912 - > read ( tps65912 , TPS65912_INT_MSK4 , 1 , & reg ) ;
irq_mask | = reg < < 24 ;
irq_sts & = ~ irq_mask ;
if ( ! irq_sts )
return IRQ_NONE ;
for ( i = 0 ; i < tps65912 - > irq_num ; i + + ) {
if ( ! ( irq_sts & ( 1 < < i ) ) )
continue ;
handle_nested_irq ( tps65912 - > irq_base + i ) ;
}
/* Write the STS register back to clear IRQs we handled */
reg = irq_sts & 0xFF ;
irq_sts > > = 8 ;
if ( reg )
tps65912 - > write ( tps65912 , TPS65912_INT_STS , 1 , & reg ) ;
reg = irq_sts & 0xFF ;
irq_sts > > = 8 ;
if ( reg )
tps65912 - > write ( tps65912 , TPS65912_INT_STS2 , 1 , & reg ) ;
reg = irq_sts & 0xFF ;
irq_sts > > = 8 ;
if ( reg )
tps65912 - > write ( tps65912 , TPS65912_INT_STS3 , 1 , & reg ) ;
reg = irq_sts & 0xFF ;
if ( reg )
tps65912 - > write ( tps65912 , TPS65912_INT_STS4 , 1 , & reg ) ;
return IRQ_HANDLED ;
}
static void tps65912_irq_lock ( struct irq_data * data )
{
struct tps65912 * tps65912 = irq_data_get_irq_chip_data ( data ) ;
mutex_lock ( & tps65912 - > irq_lock ) ;
}
static void tps65912_irq_sync_unlock ( struct irq_data * data )
{
struct tps65912 * tps65912 = irq_data_get_irq_chip_data ( data ) ;
u32 reg_mask ;
u8 reg ;
tps65912 - > read ( tps65912 , TPS65912_INT_MSK , 1 , & reg ) ;
reg_mask = reg ;
tps65912 - > read ( tps65912 , TPS65912_INT_MSK2 , 1 , & reg ) ;
reg_mask | = reg < < 8 ;
tps65912 - > read ( tps65912 , TPS65912_INT_MSK3 , 1 , & reg ) ;
reg_mask | = reg < < 16 ;
tps65912 - > read ( tps65912 , TPS65912_INT_MSK4 , 1 , & reg ) ;
reg_mask | = reg < < 24 ;
if ( tps65912 - > irq_mask ! = reg_mask ) {
reg = tps65912 - > irq_mask & 0xFF ;
tps65912 - > write ( tps65912 , TPS65912_INT_MSK , 1 , & reg ) ;
reg = tps65912 - > irq_mask > > 8 & 0xFF ;
tps65912 - > write ( tps65912 , TPS65912_INT_MSK2 , 1 , & reg ) ;
reg = tps65912 - > irq_mask > > 16 & 0xFF ;
tps65912 - > write ( tps65912 , TPS65912_INT_MSK3 , 1 , & reg ) ;
reg = tps65912 - > irq_mask > > 24 & 0xFF ;
tps65912 - > write ( tps65912 , TPS65912_INT_MSK4 , 1 , & reg ) ;
}
mutex_unlock ( & tps65912 - > irq_lock ) ;
}
static void tps65912_irq_enable ( struct irq_data * data )
{
struct tps65912 * tps65912 = irq_data_get_irq_chip_data ( data ) ;
tps65912 - > irq_mask & = ~ ( 1 < < irq_to_tps65912_irq ( tps65912 , data - > irq ) ) ;
}
static void tps65912_irq_disable ( struct irq_data * data )
{
struct tps65912 * tps65912 = irq_data_get_irq_chip_data ( data ) ;
tps65912 - > irq_mask | = ( 1 < < irq_to_tps65912_irq ( tps65912 , data - > irq ) ) ;
}
static struct irq_chip tps65912_irq_chip = {
. name = " tps65912 " ,
. irq_bus_lock = tps65912_irq_lock ,
. irq_bus_sync_unlock = tps65912_irq_sync_unlock ,
. irq_disable = tps65912_irq_disable ,
. irq_enable = tps65912_irq_enable ,
} ;
int tps65912_irq_init ( struct tps65912 * tps65912 , int irq ,
struct tps65912_platform_data * pdata )
{
int ret , cur_irq ;
int flags = IRQF_ONESHOT ;
u8 reg ;
if ( ! irq ) {
dev_warn ( tps65912 - > dev , " No interrupt support, no core IRQ \n " ) ;
return 0 ;
}
if ( ! pdata | | ! pdata - > irq_base ) {
dev_warn ( tps65912 - > dev , " No interrupt support, no IRQ base \n " ) ;
return 0 ;
}
/* Clear unattended interrupts */
tps65912 - > read ( tps65912 , TPS65912_INT_STS , 1 , & reg ) ;
tps65912 - > write ( tps65912 , TPS65912_INT_STS , 1 , & reg ) ;
tps65912 - > read ( tps65912 , TPS65912_INT_STS2 , 1 , & reg ) ;
tps65912 - > write ( tps65912 , TPS65912_INT_STS2 , 1 , & reg ) ;
tps65912 - > read ( tps65912 , TPS65912_INT_STS3 , 1 , & reg ) ;
tps65912 - > write ( tps65912 , TPS65912_INT_STS3 , 1 , & reg ) ;
tps65912 - > read ( tps65912 , TPS65912_INT_STS4 , 1 , & reg ) ;
tps65912 - > write ( tps65912 , TPS65912_INT_STS4 , 1 , & reg ) ;
/* Mask top level interrupts */
tps65912 - > irq_mask = 0xFFFFFFFF ;
mutex_init ( & tps65912 - > irq_lock ) ;
tps65912 - > chip_irq = irq ;
tps65912 - > irq_base = pdata - > irq_base ;
tps65912 - > irq_num = TPS65912_NUM_IRQ ;
/* Register with genirq */
for ( cur_irq = tps65912 - > irq_base ;
cur_irq < tps65912 - > irq_num + tps65912 - > irq_base ;
cur_irq + + ) {
irq_set_chip_data ( cur_irq , tps65912 ) ;
irq_set_chip_and_handler ( cur_irq , & tps65912_irq_chip ,
handle_edge_irq ) ;
irq_set_nested_thread ( cur_irq , 1 ) ;
2015-07-27 23:55:20 +03:00
irq_clear_status_flags ( cur_irq , IRQ_NOREQUEST | IRQ_NOPROBE ) ;
2011-06-09 23:50:13 +04:00
}
ret = request_threaded_irq ( irq , NULL , tps65912_irq , flags ,
" tps65912 " , tps65912 ) ;
irq_set_irq_type ( irq , IRQ_TYPE_LEVEL_LOW ) ;
if ( ret ! = 0 )
dev_err ( tps65912 - > dev , " Failed to request IRQ: %d \n " , ret ) ;
return ret ;
}
int tps65912_irq_exit ( struct tps65912 * tps65912 )
{
free_irq ( tps65912 - > chip_irq , tps65912 ) ;
return 0 ;
}