2009-07-27 17:45:53 +04:00
/*
* wm831x - irq . c - - Interrupt controller support for Wolfson WM831x PMICs
*
* Copyright 2009 Wolfson Microelectronics PLC .
*
* Author : Mark Brown < broonie @ opensource . wolfsonmicro . com >
*
* 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/module.h>
# include <linux/i2c.h>
2009-11-11 19:10:22 +03:00
# include <linux/irq.h>
2009-07-27 17:45:53 +04:00
# include <linux/mfd/core.h>
# include <linux/interrupt.h>
2012-05-15 01:14:24 +04:00
# include <linux/irqdomain.h>
2009-07-27 17:45:53 +04:00
# include <linux/mfd/wm831x/core.h>
# include <linux/mfd/wm831x/pdata.h>
2010-05-07 21:39:25 +04:00
# include <linux/mfd/wm831x/gpio.h>
2009-07-27 17:45:53 +04:00
# include <linux/mfd/wm831x/irq.h>
# include <linux/delay.h>
struct wm831x_irq_data {
int primary ;
int reg ;
int mask ;
} ;
static struct wm831x_irq_data wm831x_irqs [ ] = {
[ WM831X_IRQ_TEMP_THW ] = {
. primary = WM831X_TEMP_INT ,
. reg = 1 ,
. mask = WM831X_TEMP_THW_EINT ,
} ,
[ WM831X_IRQ_GPIO_1 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP1_EINT ,
} ,
[ WM831X_IRQ_GPIO_2 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP2_EINT ,
} ,
[ WM831X_IRQ_GPIO_3 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP3_EINT ,
} ,
[ WM831X_IRQ_GPIO_4 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP4_EINT ,
} ,
[ WM831X_IRQ_GPIO_5 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP5_EINT ,
} ,
[ WM831X_IRQ_GPIO_6 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP6_EINT ,
} ,
[ WM831X_IRQ_GPIO_7 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP7_EINT ,
} ,
[ WM831X_IRQ_GPIO_8 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP8_EINT ,
} ,
[ WM831X_IRQ_GPIO_9 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP9_EINT ,
} ,
[ WM831X_IRQ_GPIO_10 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP10_EINT ,
} ,
[ WM831X_IRQ_GPIO_11 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP11_EINT ,
} ,
[ WM831X_IRQ_GPIO_12 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP12_EINT ,
} ,
[ WM831X_IRQ_GPIO_13 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP13_EINT ,
} ,
[ WM831X_IRQ_GPIO_14 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP14_EINT ,
} ,
[ WM831X_IRQ_GPIO_15 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP15_EINT ,
} ,
[ WM831X_IRQ_GPIO_16 ] = {
. primary = WM831X_GP_INT ,
. reg = 5 ,
. mask = WM831X_GP16_EINT ,
} ,
[ WM831X_IRQ_ON ] = {
. primary = WM831X_ON_PIN_INT ,
. reg = 1 ,
. mask = WM831X_ON_PIN_EINT ,
} ,
[ WM831X_IRQ_PPM_SYSLO ] = {
. primary = WM831X_PPM_INT ,
. reg = 1 ,
. mask = WM831X_PPM_SYSLO_EINT ,
} ,
[ WM831X_IRQ_PPM_PWR_SRC ] = {
. primary = WM831X_PPM_INT ,
. reg = 1 ,
. mask = WM831X_PPM_PWR_SRC_EINT ,
} ,
[ WM831X_IRQ_PPM_USB_CURR ] = {
. primary = WM831X_PPM_INT ,
. reg = 1 ,
. mask = WM831X_PPM_USB_CURR_EINT ,
} ,
[ WM831X_IRQ_WDOG_TO ] = {
. primary = WM831X_WDOG_INT ,
. reg = 1 ,
. mask = WM831X_WDOG_TO_EINT ,
} ,
[ WM831X_IRQ_RTC_PER ] = {
. primary = WM831X_RTC_INT ,
. reg = 1 ,
. mask = WM831X_RTC_PER_EINT ,
} ,
[ WM831X_IRQ_RTC_ALM ] = {
. primary = WM831X_RTC_INT ,
. reg = 1 ,
. mask = WM831X_RTC_ALM_EINT ,
} ,
[ WM831X_IRQ_CHG_BATT_HOT ] = {
. primary = WM831X_CHG_INT ,
. reg = 2 ,
. mask = WM831X_CHG_BATT_HOT_EINT ,
} ,
[ WM831X_IRQ_CHG_BATT_COLD ] = {
. primary = WM831X_CHG_INT ,
. reg = 2 ,
. mask = WM831X_CHG_BATT_COLD_EINT ,
} ,
[ WM831X_IRQ_CHG_BATT_FAIL ] = {
. primary = WM831X_CHG_INT ,
. reg = 2 ,
. mask = WM831X_CHG_BATT_FAIL_EINT ,
} ,
[ WM831X_IRQ_CHG_OV ] = {
. primary = WM831X_CHG_INT ,
. reg = 2 ,
. mask = WM831X_CHG_OV_EINT ,
} ,
[ WM831X_IRQ_CHG_END ] = {
. primary = WM831X_CHG_INT ,
. reg = 2 ,
. mask = WM831X_CHG_END_EINT ,
} ,
[ WM831X_IRQ_CHG_TO ] = {
. primary = WM831X_CHG_INT ,
. reg = 2 ,
. mask = WM831X_CHG_TO_EINT ,
} ,
[ WM831X_IRQ_CHG_MODE ] = {
. primary = WM831X_CHG_INT ,
. reg = 2 ,
. mask = WM831X_CHG_MODE_EINT ,
} ,
[ WM831X_IRQ_CHG_START ] = {
. primary = WM831X_CHG_INT ,
. reg = 2 ,
. mask = WM831X_CHG_START_EINT ,
} ,
[ WM831X_IRQ_TCHDATA ] = {
. primary = WM831X_TCHDATA_INT ,
. reg = 1 ,
. mask = WM831X_TCHDATA_EINT ,
} ,
[ WM831X_IRQ_TCHPD ] = {
. primary = WM831X_TCHPD_INT ,
. reg = 1 ,
. mask = WM831X_TCHPD_EINT ,
} ,
[ WM831X_IRQ_AUXADC_DATA ] = {
. primary = WM831X_AUXADC_INT ,
. reg = 1 ,
. mask = WM831X_AUXADC_DATA_EINT ,
} ,
[ WM831X_IRQ_AUXADC_DCOMP1 ] = {
. primary = WM831X_AUXADC_INT ,
. reg = 1 ,
. mask = WM831X_AUXADC_DCOMP1_EINT ,
} ,
[ WM831X_IRQ_AUXADC_DCOMP2 ] = {
. primary = WM831X_AUXADC_INT ,
. reg = 1 ,
. mask = WM831X_AUXADC_DCOMP2_EINT ,
} ,
[ WM831X_IRQ_AUXADC_DCOMP3 ] = {
. primary = WM831X_AUXADC_INT ,
. reg = 1 ,
. mask = WM831X_AUXADC_DCOMP3_EINT ,
} ,
[ WM831X_IRQ_AUXADC_DCOMP4 ] = {
. primary = WM831X_AUXADC_INT ,
. reg = 1 ,
. mask = WM831X_AUXADC_DCOMP4_EINT ,
} ,
[ WM831X_IRQ_CS1 ] = {
. primary = WM831X_CS_INT ,
. reg = 2 ,
. mask = WM831X_CS1_EINT ,
} ,
[ WM831X_IRQ_CS2 ] = {
. primary = WM831X_CS_INT ,
. reg = 2 ,
. mask = WM831X_CS2_EINT ,
} ,
[ WM831X_IRQ_HC_DC1 ] = {
. primary = WM831X_HC_INT ,
. reg = 4 ,
. mask = WM831X_HC_DC1_EINT ,
} ,
[ WM831X_IRQ_HC_DC2 ] = {
. primary = WM831X_HC_INT ,
. reg = 4 ,
. mask = WM831X_HC_DC2_EINT ,
} ,
[ WM831X_IRQ_UV_LDO1 ] = {
. primary = WM831X_UV_INT ,
. reg = 3 ,
. mask = WM831X_UV_LDO1_EINT ,
} ,
[ WM831X_IRQ_UV_LDO2 ] = {
. primary = WM831X_UV_INT ,
. reg = 3 ,
. mask = WM831X_UV_LDO2_EINT ,
} ,
[ WM831X_IRQ_UV_LDO3 ] = {
. primary = WM831X_UV_INT ,
. reg = 3 ,
. mask = WM831X_UV_LDO3_EINT ,
} ,
[ WM831X_IRQ_UV_LDO4 ] = {
. primary = WM831X_UV_INT ,
. reg = 3 ,
. mask = WM831X_UV_LDO4_EINT ,
} ,
[ WM831X_IRQ_UV_LDO5 ] = {
. primary = WM831X_UV_INT ,
. reg = 3 ,
. mask = WM831X_UV_LDO5_EINT ,
} ,
[ WM831X_IRQ_UV_LDO6 ] = {
. primary = WM831X_UV_INT ,
. reg = 3 ,
. mask = WM831X_UV_LDO6_EINT ,
} ,
[ WM831X_IRQ_UV_LDO7 ] = {
. primary = WM831X_UV_INT ,
. reg = 3 ,
. mask = WM831X_UV_LDO7_EINT ,
} ,
[ WM831X_IRQ_UV_LDO8 ] = {
. primary = WM831X_UV_INT ,
. reg = 3 ,
. mask = WM831X_UV_LDO8_EINT ,
} ,
[ WM831X_IRQ_UV_LDO9 ] = {
. primary = WM831X_UV_INT ,
. reg = 3 ,
. mask = WM831X_UV_LDO9_EINT ,
} ,
[ WM831X_IRQ_UV_LDO10 ] = {
. primary = WM831X_UV_INT ,
. reg = 3 ,
. mask = WM831X_UV_LDO10_EINT ,
} ,
[ WM831X_IRQ_UV_DC1 ] = {
. primary = WM831X_UV_INT ,
. reg = 4 ,
. mask = WM831X_UV_DC1_EINT ,
} ,
[ WM831X_IRQ_UV_DC2 ] = {
. primary = WM831X_UV_INT ,
. reg = 4 ,
. mask = WM831X_UV_DC2_EINT ,
} ,
[ WM831X_IRQ_UV_DC3 ] = {
. primary = WM831X_UV_INT ,
. reg = 4 ,
. mask = WM831X_UV_DC3_EINT ,
} ,
[ WM831X_IRQ_UV_DC4 ] = {
. primary = WM831X_UV_INT ,
. reg = 4 ,
. mask = WM831X_UV_DC4_EINT ,
} ,
} ;
static inline int irq_data_to_status_reg ( struct wm831x_irq_data * irq_data )
{
return WM831X_INTERRUPT_STATUS_1 - 1 + irq_data - > reg ;
}
2009-11-11 19:10:22 +03:00
static inline struct wm831x_irq_data * irq_to_wm831x_irq ( struct wm831x * wm831x ,
int irq )
2009-07-27 17:45:53 +04:00
{
2012-05-15 01:14:24 +04:00
return & wm831x_irqs [ irq ] ;
2009-07-27 17:45:53 +04:00
}
2010-11-24 21:01:42 +03:00
static void wm831x_irq_lock ( struct irq_data * data )
2009-07-27 17:45:53 +04:00
{
2010-12-11 16:21:21 +03:00
struct wm831x * wm831x = irq_data_get_irq_chip_data ( data ) ;
2009-07-27 17:45:53 +04:00
mutex_lock ( & wm831x - > irq_lock ) ;
}
2010-11-24 21:01:42 +03:00
static void wm831x_irq_sync_unlock ( struct irq_data * data )
2009-07-27 17:45:53 +04:00
{
2010-12-11 16:21:21 +03:00
struct wm831x * wm831x = irq_data_get_irq_chip_data ( data ) ;
2009-11-11 19:10:22 +03:00
int i ;
2011-06-02 22:18:47 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( wm831x - > gpio_update ) ; i + + ) {
if ( wm831x - > gpio_update [ i ] ) {
wm831x_set_bits ( wm831x , WM831X_GPIO1_CONTROL + i ,
WM831X_GPN_INT_MODE | WM831X_GPN_POL ,
wm831x - > gpio_update [ i ] ) ;
wm831x - > gpio_update [ i ] = 0 ;
}
}
2009-11-11 19:10:22 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( wm831x - > irq_masks_cur ) ; i + + ) {
/* If there's been a change in the mask write it back
* to the hardware . */
if ( wm831x - > irq_masks_cur [ i ] ! = wm831x - > irq_masks_cache [ i ] ) {
2011-03-01 23:12:44 +03:00
dev_dbg ( wm831x - > dev , " IRQ mask sync: %x = %x \n " ,
WM831X_INTERRUPT_STATUS_1_MASK + i ,
wm831x - > irq_masks_cur [ i ] ) ;
2009-11-11 19:10:22 +03:00
wm831x - > irq_masks_cache [ i ] = wm831x - > irq_masks_cur [ i ] ;
wm831x_reg_write ( wm831x ,
WM831X_INTERRUPT_STATUS_1_MASK + i ,
wm831x - > irq_masks_cur [ i ] ) ;
}
2009-07-27 17:45:53 +04:00
}
mutex_unlock ( & wm831x - > irq_lock ) ;
}
2011-03-01 23:12:44 +03:00
static void wm831x_irq_enable ( struct irq_data * data )
2009-07-27 17:45:53 +04:00
{
2010-12-11 16:21:21 +03:00
struct wm831x * wm831x = irq_data_get_irq_chip_data ( data ) ;
2010-11-24 21:01:42 +03:00
struct wm831x_irq_data * irq_data = irq_to_wm831x_irq ( wm831x ,
2012-05-15 01:14:24 +04:00
data - > hwirq ) ;
2009-07-27 17:45:53 +04:00
2009-11-11 19:10:22 +03:00
wm831x - > irq_masks_cur [ irq_data - > reg - 1 ] & = ~ irq_data - > mask ;
2009-07-27 17:45:53 +04:00
}
2011-03-01 23:12:44 +03:00
static void wm831x_irq_disable ( struct irq_data * data )
2009-07-27 17:45:53 +04:00
{
2010-12-11 16:21:21 +03:00
struct wm831x * wm831x = irq_data_get_irq_chip_data ( data ) ;
2010-11-24 21:01:42 +03:00
struct wm831x_irq_data * irq_data = irq_to_wm831x_irq ( wm831x ,
2012-05-15 01:14:24 +04:00
data - > hwirq ) ;
2009-11-11 19:10:22 +03:00
wm831x - > irq_masks_cur [ irq_data - > reg - 1 ] | = irq_data - > mask ;
2009-07-27 17:45:53 +04:00
}
2010-11-24 21:01:42 +03:00
static int wm831x_irq_set_type ( struct irq_data * data , unsigned int type )
2010-05-07 21:39:25 +04:00
{
2010-12-11 16:21:21 +03:00
struct wm831x * wm831x = irq_data_get_irq_chip_data ( data ) ;
2011-06-02 22:18:47 +04:00
int irq ;
2010-05-07 21:39:25 +04:00
2012-05-15 01:14:24 +04:00
irq = data - > hwirq ;
2010-05-07 21:39:25 +04:00
2010-08-16 23:26:51 +04:00
if ( irq < WM831X_IRQ_GPIO_1 | | irq > WM831X_IRQ_GPIO_11 ) {
/* Ignore internal-only IRQs */
if ( irq > = 0 & & irq < WM831X_NUM_IRQS )
return 0 ;
else
return - EINVAL ;
}
2010-05-07 21:39:25 +04:00
2011-06-22 03:23:08 +04:00
/* Rebase the IRQ into the GPIO range so we've got a sensible array
* index .
*/
irq - = WM831X_IRQ_GPIO_1 ;
2011-06-02 22:18:47 +04:00
/* We set the high bit to flag that we need an update; don't
* do the update here as we can be called with the bus lock
* held .
*/
2012-05-18 20:02:02 +04:00
wm831x - > gpio_level_low [ irq ] = false ;
wm831x - > gpio_level_high [ irq ] = false ;
2010-05-07 21:39:25 +04:00
switch ( type ) {
case IRQ_TYPE_EDGE_BOTH :
2011-06-02 22:18:47 +04:00
wm831x - > gpio_update [ irq ] = 0x10000 | WM831X_GPN_INT_MODE ;
2010-05-07 21:39:25 +04:00
break ;
case IRQ_TYPE_EDGE_RISING :
2011-06-02 22:18:47 +04:00
wm831x - > gpio_update [ irq ] = 0x10000 | WM831X_GPN_POL ;
2010-05-07 21:39:25 +04:00
break ;
case IRQ_TYPE_EDGE_FALLING :
2011-06-02 22:18:47 +04:00
wm831x - > gpio_update [ irq ] = 0x10000 ;
2011-09-16 16:21:47 +04:00
break ;
case IRQ_TYPE_LEVEL_HIGH :
wm831x - > gpio_update [ irq ] = 0x10000 | WM831X_GPN_POL ;
2012-05-18 20:02:02 +04:00
wm831x - > gpio_level_high [ irq ] = true ;
break ;
case IRQ_TYPE_LEVEL_LOW :
wm831x - > gpio_update [ irq ] = 0x10000 ;
wm831x - > gpio_level_low [ irq ] = true ;
2010-05-07 21:39:25 +04:00
break ;
default :
return - EINVAL ;
}
2011-06-02 22:18:47 +04:00
return 0 ;
2010-05-07 21:39:25 +04:00
}
2009-11-11 19:10:22 +03:00
static struct irq_chip wm831x_irq_chip = {
2010-11-24 21:01:42 +03:00
. name = " wm831x " ,
. irq_bus_lock = wm831x_irq_lock ,
. irq_bus_sync_unlock = wm831x_irq_sync_unlock ,
2011-03-01 23:12:44 +03:00
. irq_disable = wm831x_irq_disable ,
. irq_enable = wm831x_irq_enable ,
2010-11-24 21:01:42 +03:00
. irq_set_type = wm831x_irq_set_type ,
2009-11-11 19:10:22 +03:00
} ;
/* The processing of the primary interrupt occurs in a thread so that
* we can interact with the device over I2C or SPI . */
static irqreturn_t wm831x_irq_thread ( int irq , void * data )
2009-07-27 17:45:53 +04:00
{
2009-11-11 19:10:22 +03:00
struct wm831x * wm831x = data ;
2009-07-27 17:45:53 +04:00
unsigned int i ;
2011-09-16 16:21:47 +04:00
int primary , status_addr , ret ;
2009-11-11 19:10:22 +03:00
int status_regs [ WM831X_NUM_IRQ_REGS ] = { 0 } ;
int read [ WM831X_NUM_IRQ_REGS ] = { 0 } ;
2009-07-27 17:45:53 +04:00
int * status ;
primary = wm831x_reg_read ( wm831x , WM831X_SYSTEM_INTERRUPTS ) ;
if ( primary < 0 ) {
dev_err ( wm831x - > dev , " Failed to read system interrupt: %d \n " ,
primary ) ;
goto out ;
}
2011-02-01 14:46:13 +03:00
/* The touch interrupts are visible in the primary register as
* an optimisation ; open code this to avoid complicating the
* main handling loop and so we can also skip iterating the
* descriptors .
*/
if ( primary & WM831X_TCHPD_INT )
2012-05-15 01:14:24 +04:00
handle_nested_irq ( irq_find_mapping ( wm831x - > irq_domain ,
WM831X_IRQ_TCHPD ) ) ;
2011-02-01 14:46:13 +03:00
if ( primary & WM831X_TCHDATA_INT )
2012-05-15 01:14:24 +04:00
handle_nested_irq ( irq_find_mapping ( wm831x - > irq_domain ,
WM831X_IRQ_TCHDATA ) ) ;
2011-12-27 21:20:10 +04:00
primary & = ~ ( WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT ) ;
2011-02-01 14:46:13 +03:00
2009-07-27 17:45:53 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( wm831x_irqs ) ; i + + ) {
int offset = wm831x_irqs [ i ] . reg - 1 ;
if ( ! ( primary & wm831x_irqs [ i ] . primary ) )
continue ;
status = & status_regs [ offset ] ;
/* Hopefully there should only be one register to read
* each time otherwise we ought to do a block read . */
if ( ! read [ offset ] ) {
2011-07-20 20:05:13 +04:00
status_addr = irq_data_to_status_reg ( & wm831x_irqs [ i ] ) ;
* status = wm831x_reg_read ( wm831x , status_addr ) ;
2009-07-27 17:45:53 +04:00
if ( * status < 0 ) {
dev_err ( wm831x - > dev ,
" Failed to read IRQ status: %d \n " ,
* status ) ;
2009-11-11 19:10:22 +03:00
goto out ;
2009-07-27 17:45:53 +04:00
}
read [ offset ] = 1 ;
2011-07-20 20:05:13 +04:00
/* Ignore any bits that we don't think are masked */
* status & = ~ wm831x - > irq_masks_cur [ offset ] ;
/* Acknowledge now so we don't miss
* notifications while we handle .
*/
wm831x_reg_write ( wm831x , status_addr , * status ) ;
2009-07-27 17:45:53 +04:00
}
2011-07-20 20:05:13 +04:00
if ( * status & wm831x_irqs [ i ] . mask )
2012-05-15 01:14:24 +04:00
handle_nested_irq ( irq_find_mapping ( wm831x - > irq_domain ,
i ) ) ;
2011-09-16 16:21:47 +04:00
/* Simulate an edge triggered IRQ by polling the input
* status . This is sucky but improves interoperability .
*/
if ( primary = = WM831X_GP_INT & &
2012-05-18 20:02:02 +04:00
wm831x - > gpio_level_high [ i - WM831X_IRQ_GPIO_1 ] ) {
2011-09-16 16:21:47 +04:00
ret = wm831x_reg_read ( wm831x , WM831X_GPIO_LEVEL ) ;
while ( ret & 1 < < ( i - WM831X_IRQ_GPIO_1 ) ) {
2012-05-18 20:02:02 +04:00
handle_nested_irq ( irq_find_mapping ( wm831x - > irq_domain ,
i ) ) ;
ret = wm831x_reg_read ( wm831x ,
WM831X_GPIO_LEVEL ) ;
}
}
if ( primary = = WM831X_GP_INT & &
wm831x - > gpio_level_low [ i - WM831X_IRQ_GPIO_1 ] ) {
ret = wm831x_reg_read ( wm831x , WM831X_GPIO_LEVEL ) ;
while ( ! ( ret & 1 < < ( i - WM831X_IRQ_GPIO_1 ) ) ) {
2012-05-15 01:14:24 +04:00
handle_nested_irq ( irq_find_mapping ( wm831x - > irq_domain ,
i ) ) ;
2011-09-16 16:21:47 +04:00
ret = wm831x_reg_read ( wm831x ,
WM831X_GPIO_LEVEL ) ;
}
}
2009-07-27 17:45:53 +04:00
}
out :
return IRQ_HANDLED ;
}
2012-05-15 01:14:24 +04:00
static int wm831x_irq_map ( struct irq_domain * h , unsigned int virq ,
irq_hw_number_t hw )
{
irq_set_chip_data ( virq , h - > host_data ) ;
irq_set_chip_and_handler ( virq , & wm831x_irq_chip , handle_edge_irq ) ;
irq_set_nested_thread ( virq , 1 ) ;
irq_set_noprobe ( virq ) ;
return 0 ;
}
2015-04-27 15:54:13 +03:00
static const struct irq_domain_ops wm831x_irq_domain_ops = {
2012-05-15 01:14:24 +04:00
. map = wm831x_irq_map ,
. xlate = irq_domain_xlate_twocell ,
} ;
2009-07-27 17:45:53 +04:00
int wm831x_irq_init ( struct wm831x * wm831x , int irq )
{
2017-03-17 13:05:18 +03:00
struct wm831x_pdata * pdata = & wm831x - > pdata ;
2012-05-15 01:14:24 +04:00
struct irq_domain * domain ;
int i , ret , irq_base ;
2009-07-27 17:45:53 +04:00
2009-10-19 14:07:05 +04:00
mutex_init ( & wm831x - > irq_lock ) ;
2010-04-05 19:14:17 +04:00
/* Mask the individual interrupt sources */
for ( i = 0 ; i < ARRAY_SIZE ( wm831x - > irq_masks_cur ) ; i + + ) {
wm831x - > irq_masks_cur [ i ] = 0xffff ;
wm831x - > irq_masks_cache [ i ] = 0xffff ;
wm831x_reg_write ( wm831x , WM831X_INTERRUPT_STATUS_1_MASK + i ,
0xffff ) ;
}
2011-06-02 22:18:51 +04:00
/* Try to dynamically allocate IRQs if no base is specified */
2017-03-17 13:05:18 +03:00
if ( pdata - > irq_base ) {
2012-05-15 01:14:24 +04:00
irq_base = irq_alloc_descs ( pdata - > irq_base , 0 ,
WM831X_NUM_IRQS , 0 ) ;
if ( irq_base < 0 ) {
dev_warn ( wm831x - > dev , " Failed to allocate IRQs: %d \n " ,
irq_base ) ;
irq_base = 0 ;
}
} else {
irq_base = 0 ;
}
if ( irq_base )
domain = irq_domain_add_legacy ( wm831x - > dev - > of_node ,
ARRAY_SIZE ( wm831x_irqs ) ,
irq_base , 0 ,
& wm831x_irq_domain_ops ,
wm831x ) ;
2011-06-02 22:18:51 +04:00
else
2012-05-15 01:14:24 +04:00
domain = irq_domain_add_linear ( wm831x - > dev - > of_node ,
ARRAY_SIZE ( wm831x_irqs ) ,
& wm831x_irq_domain_ops ,
wm831x ) ;
2011-06-02 22:18:51 +04:00
2012-05-15 01:14:24 +04:00
if ( ! domain ) {
dev_warn ( wm831x - > dev , " Failed to allocate IRQ domain \n " ) ;
return - EINVAL ;
2009-07-27 17:45:53 +04:00
}
2017-03-17 13:05:18 +03:00
if ( pdata - > irq_cmos )
2011-01-21 16:26:46 +03:00
i = 0 ;
else
i = WM831X_IRQ_OD ;
wm831x_set_bits ( wm831x , WM831X_IRQ_CONFIG ,
WM831X_IRQ_OD , i ) ;
2009-07-27 17:45:53 +04:00
wm831x - > irq = irq ;
2012-05-15 01:14:24 +04:00
wm831x - > irq_domain = domain ;
2009-07-27 17:45:53 +04:00
2011-04-04 06:59:57 +04:00
if ( irq ) {
2012-05-14 00:53:45 +04:00
/* Try to flag /IRQ as a wake source; there are a number of
* unconditional wake sources in the PMIC so this isn ' t
* conditional but we don ' t actually care * too * much if it
* fails .
*/
ret = enable_irq_wake ( irq ) ;
if ( ret ! = 0 ) {
dev_warn ( wm831x - > dev ,
" Can't enable IRQ as wake source: %d \n " ,
ret ) ;
}
2011-04-04 06:59:57 +04:00
ret = request_threaded_irq ( irq , NULL , wm831x_irq_thread ,
IRQF_TRIGGER_LOW | IRQF_ONESHOT ,
" wm831x " , wm831x ) ;
if ( ret ! = 0 ) {
dev_err ( wm831x - > dev , " Failed to request IRQ %d: %d \n " ,
irq , ret ) ;
return ret ;
}
} else {
dev_warn ( wm831x - > dev ,
" No interrupt specified - functionality limited \n " ) ;
2009-07-27 17:45:53 +04:00
}
2009-11-11 19:10:22 +03:00
/* Enable top level interrupts, we mask at secondary level */
wm831x_reg_write ( wm831x , WM831X_SYSTEM_INTERRUPTS_MASK , 0 ) ;
2009-07-27 17:45:53 +04:00
return 0 ;
}
void wm831x_irq_exit ( struct wm831x * wm831x )
{
if ( wm831x - > irq )
free_irq ( wm831x - > irq , wm831x ) ;
}