2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2010-04-28 01:03:59 +00:00
/*
* Amstrad E3 FIQ handling
*
* Copyright ( C ) 2009 Janusz Krzysztofik
* Copyright ( c ) 2006 Matt Callow
* Copyright ( c ) 2004 Amstrad Plc
* Copyright ( C ) 2001 RidgeRun , Inc .
*
* Parts of this code are taken from linux / arch / arm / mach - omap / irq . c
* in the MontaVista 2.4 kernel ( and the Amstrad changes therein )
*/
2018-06-22 00:41:24 +02:00
# include <linux/gpio/consumer.h>
2019-04-26 14:40:18 +02:00
# include <linux/gpio/machine.h>
2018-06-22 00:41:24 +02:00
# include <linux/gpio/driver.h>
2010-04-28 01:03:59 +00:00
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/io.h>
2018-06-22 00:41:26 +02:00
# include <linux/platform_data/ams-delta-fiq.h>
2018-06-22 00:41:27 +02:00
# include <linux/platform_device.h>
2010-04-28 01:03:59 +00:00
# include <asm/fiq.h>
2012-02-24 10:34:34 -08:00
2018-06-22 00:41:26 +02:00
# include "ams-delta-fiq.h"
2018-11-06 00:11:26 +01:00
# include "board-ams-delta.h"
2010-04-28 01:03:59 +00:00
static struct fiq_handler fh = {
. name = " ams-delta-fiq "
} ;
/*
* This buffer is shared between FIQ and IRQ contexts .
* The FIQ and IRQ isrs can both read and write it .
* It is structured as a header section several 32 bit slots ,
* followed by the circular buffer where the FIQ isr stores
2018-06-22 00:41:26 +02:00
* keystrokes received from the qwerty keyboard . See
* < linux / platform_data / ams - delta - fiq . h > for details of offsets .
2010-04-28 01:03:59 +00:00
*/
2018-06-22 00:41:28 +02:00
static unsigned int fiq_buffer [ 1024 ] ;
2010-04-28 01:03:59 +00:00
2018-06-22 00:41:24 +02:00
static struct irq_chip * irq_chip ;
static struct irq_data * irq_data [ 16 ] ;
2010-04-28 01:03:59 +00:00
static unsigned int irq_counter [ 16 ] ;
2018-06-22 00:41:25 +02:00
static const char * pin_name [ 16 ] __initconst = {
[ AMS_DELTA_GPIO_PIN_KEYBRD_DATA ] = " keybrd_data " ,
[ AMS_DELTA_GPIO_PIN_KEYBRD_CLK ] = " keybrd_clk " ,
} ;
2010-04-28 01:03:59 +00:00
static irqreturn_t deferred_fiq ( int irq , void * dev_id )
{
2018-06-22 00:41:24 +02:00
struct irq_data * d ;
2010-04-28 01:03:59 +00:00
int gpio , irq_num , fiq_count ;
/*
* For each handled GPIO interrupt , keep calling its interrupt handler
* until the IRQ counter catches the FIQ incremented interrupt counter .
*/
for ( gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK ;
gpio < = AMS_DELTA_GPIO_PIN_HOOK_SWITCH ; gpio + + ) {
2018-06-22 00:41:24 +02:00
d = irq_data [ gpio ] ;
irq_num = d - > irq ;
2010-04-28 01:03:59 +00:00
fiq_count = fiq_buffer [ FIQ_CNT_INT_00 + gpio ] ;
2018-05-02 20:32:03 +02:00
if ( irq_counter [ gpio ] < fiq_count & &
gpio ! = AMS_DELTA_GPIO_PIN_KEYBRD_CLK ) {
/*
* handle_simple_irq ( ) that OMAP GPIO edge
* interrupts default to since commit 80 ac93c27441
* requires interrupt already acked and unmasked .
*/
2019-08-11 10:48:02 +02:00
if ( ! WARN_ON_ONCE ( ! irq_chip - > irq_unmask ) )
2018-06-22 00:41:24 +02:00
irq_chip - > irq_unmask ( d ) ;
2010-04-28 01:03:59 +00:00
}
2018-05-02 20:32:03 +02:00
for ( ; irq_counter [ gpio ] < fiq_count ; irq_counter [ gpio ] + + )
generic_handle_irq ( irq_num ) ;
2010-04-28 01:03:59 +00:00
}
return IRQ_HANDLED ;
}
2018-06-22 00:41:27 +02:00
void __init ams_delta_init_fiq ( struct gpio_chip * chip ,
struct platform_device * serio )
2010-04-28 01:03:59 +00:00
{
2018-06-22 00:41:25 +02:00
struct gpio_desc * gpiod , * data = NULL , * clk = NULL ;
2010-04-28 01:03:59 +00:00
void * fiqhandler_start ;
unsigned int fiqhandler_length ;
struct pt_regs FIQ_regs ;
unsigned long val , offset ;
int i , retval ;
2018-06-22 00:41:24 +02:00
/* Store irq_chip location for IRQ handler use */
irq_chip = chip - > irq . chip ;
if ( ! irq_chip ) {
pr_err ( " %s: GPIO chip %s is missing IRQ function \n " , __func__ ,
chip - > label ) ;
return ;
}
for ( i = 0 ; i < ARRAY_SIZE ( irq_data ) ; i + + ) {
2019-04-26 14:40:18 +02:00
gpiod = gpiochip_request_own_desc ( chip , i , pin_name [ i ] ,
GPIO_ACTIVE_HIGH , GPIOD_IN ) ;
2018-06-22 00:41:24 +02:00
if ( IS_ERR ( gpiod ) ) {
pr_err ( " %s: failed to get GPIO pin %d (%ld) \n " ,
__func__ , i , PTR_ERR ( gpiod ) ) ;
return ;
}
/* Store irq_data location for IRQ handler use */
irq_data [ i ] = irq_get_irq_data ( gpiod_to_irq ( gpiod ) ) ;
2018-06-22 00:41:25 +02:00
/*
* FIQ handler takes full control over serio data and clk GPIO
* pins . Initiaize them and keep requested so nobody can
* interfere . Fail if any of those two couldn ' t be requested .
*/
switch ( i ) {
case AMS_DELTA_GPIO_PIN_KEYBRD_DATA :
data = gpiod ;
gpiod_direction_input ( data ) ;
break ;
case AMS_DELTA_GPIO_PIN_KEYBRD_CLK :
clk = gpiod ;
gpiod_direction_input ( clk ) ;
break ;
default :
gpiochip_free_own_desc ( gpiod ) ;
break ;
}
2018-06-22 00:41:24 +02:00
}
2018-06-22 00:41:25 +02:00
if ( ! data | | ! clk )
goto out_gpio ;
2018-06-22 00:41:24 +02:00
2010-04-28 01:03:59 +00:00
fiqhandler_start = & qwerty_fiqin_start ;
fiqhandler_length = & qwerty_fiqin_end - & qwerty_fiqin_start ;
pr_info ( " Installing fiq handler from %p, length 0x%x \n " ,
fiqhandler_start , fiqhandler_length ) ;
retval = claim_fiq ( & fh ) ;
if ( retval ) {
pr_err ( " ams_delta_init_fiq(): couldn't claim FIQ, ret=%d \n " ,
retval ) ;
2018-06-22 00:41:25 +02:00
goto out_gpio ;
2010-04-28 01:03:59 +00:00
}
retval = request_irq ( INT_DEFERRED_FIQ , deferred_fiq ,
2012-04-13 06:34:28 -06:00
IRQ_TYPE_EDGE_RISING , " deferred_fiq " , NULL ) ;
2010-04-28 01:03:59 +00:00
if ( retval < 0 ) {
pr_err ( " Failed to get deferred_fiq IRQ, ret=%d \n " , retval ) ;
release_fiq ( & fh ) ;
2018-06-22 00:41:25 +02:00
goto out_gpio ;
2010-04-28 01:03:59 +00:00
}
/*
* Since no set_type ( ) method is provided by OMAP irq chip ,
* switch to edge triggered interrupt type manually .
*/
2016-06-16 21:56:30 +02:00
offset = IRQ_ILR0_REG_OFFSET +
( ( INT_DEFERRED_FIQ - NR_IRQS_LEGACY ) & 0x1f ) * 0x4 ;
2010-04-28 01:03:59 +00:00
val = omap_readl ( DEFERRED_FIQ_IH_BASE + offset ) & ~ ( 1 < < 1 ) ;
omap_writel ( val , DEFERRED_FIQ_IH_BASE + offset ) ;
set_fiq_handler ( fiqhandler_start , fiqhandler_length ) ;
/*
* Initialise the buffer which is shared
* between FIQ mode and IRQ mode
*/
fiq_buffer [ FIQ_GPIO_INT_MASK ] = 0 ;
fiq_buffer [ FIQ_MASK ] = 0 ;
fiq_buffer [ FIQ_STATE ] = 0 ;
fiq_buffer [ FIQ_KEY ] = 0 ;
fiq_buffer [ FIQ_KEYS_CNT ] = 0 ;
fiq_buffer [ FIQ_KEYS_HICNT ] = 0 ;
fiq_buffer [ FIQ_TAIL_OFFSET ] = 0 ;
fiq_buffer [ FIQ_HEAD_OFFSET ] = 0 ;
fiq_buffer [ FIQ_BUF_LEN ] = 256 ;
fiq_buffer [ FIQ_MISSED_KEYS ] = 0 ;
fiq_buffer [ FIQ_BUFFER_START ] =
( unsigned int ) & fiq_buffer [ FIQ_CIRC_BUFF ] ;
for ( i = FIQ_CNT_INT_00 ; i < = FIQ_CNT_INT_15 ; i + + )
fiq_buffer [ i ] = 0 ;
/*
2016-05-21 13:49:48 +02:00
* FIQ mode r9 always points to the fiq_buffer , because the FIQ isr
2010-04-28 01:03:59 +00:00
* will run in an unpredictable context . The fiq_buffer is the FIQ isr ' s
* only means of communication with the IRQ level and other kernel
* context code .
*/
FIQ_regs . ARM_r9 = ( unsigned int ) fiq_buffer ;
set_fiq_regs ( & FIQ_regs ) ;
pr_info ( " request_fiq(): fiq_buffer = %p \n " , fiq_buffer ) ;
/*
* Redirect GPIO interrupts to FIQ
*/
2016-06-16 21:56:30 +02:00
offset = IRQ_ILR0_REG_OFFSET + ( INT_GPIO_BANK1 - NR_IRQS_LEGACY ) * 0x4 ;
2010-04-28 01:03:59 +00:00
val = omap_readl ( OMAP_IH1_BASE + offset ) | 1 ;
omap_writel ( val , OMAP_IH1_BASE + offset ) ;
2018-06-22 00:41:25 +02:00
2018-06-22 00:41:28 +02:00
/* Initialize serio device IRQ resource and platform_data */
2018-06-22 00:41:27 +02:00
serio - > resource [ 0 ] . start = gpiod_to_irq ( clk ) ;
serio - > resource [ 0 ] . end = serio - > resource [ 0 ] . start ;
2018-06-22 00:41:28 +02:00
serio - > dev . platform_data = fiq_buffer ;
2018-06-22 00:41:27 +02:00
/*
* Since FIQ handler performs handling of GPIO registers for
* " keybrd_clk " IRQ pin , ams_delta_serio driver used to set
* handle_simple_irq ( ) as active IRQ handler for that pin to avoid
* bad interaction with gpio - omap driver . This is no longer needed
* as handle_simple_irq ( ) is now the default handler for OMAP GPIO
* edge interrupts .
* This comment replaces the obsolete code which has been removed
* from the ams_delta_serio driver and stands here only as a reminder
* of that dependency on gpio - omap driver behavior .
*/
2018-06-22 00:41:25 +02:00
return ;
out_gpio :
if ( data )
gpiochip_free_own_desc ( data ) ;
if ( clk )
gpiochip_free_own_desc ( clk ) ;
2010-04-28 01:03:59 +00:00
}