2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-11-23 20:54:50 +03:00
/*
* GPIO driver for the ACCES 104 - IDI - 48 family
* Copyright ( C ) 2015 William Breathitt Gray
*
2016-05-02 01:44:55 +03:00
* This driver supports the following ACCES devices : 104 - IDI - 48 A ,
* 104 - IDI - 48 AC , 104 - IDI - 48 B , and 104 - IDI - 48 BC .
2015-11-23 20:54:50 +03:00
*/
2018-03-22 16:00:31 +03:00
# include <linux/bitmap.h>
2015-11-23 20:54:50 +03:00
# include <linux/bitops.h>
# include <linux/device.h>
# include <linux/errno.h>
# include <linux/gpio/driver.h>
# include <linux/io.h>
# include <linux/ioport.h>
# include <linux/interrupt.h>
# include <linux/irqdesc.h>
2016-05-02 01:44:55 +03:00
# include <linux/isa.h>
2015-11-23 20:54:50 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/spinlock.h>
2016-05-02 01:44:55 +03:00
# define IDI_48_EXTENT 8
# define MAX_NUM_IDI_48 max_num_isa_dev(IDI_48_EXTENT)
static unsigned int base [ MAX_NUM_IDI_48 ] ;
static unsigned int num_idi_48 ;
2017-04-04 18:54:22 +03:00
module_param_hw_array ( base , uint , ioport , & num_idi_48 , 0 ) ;
2016-05-02 01:44:55 +03:00
MODULE_PARM_DESC ( base , " ACCES 104-IDI-48 base addresses " ) ;
static unsigned int irq [ MAX_NUM_IDI_48 ] ;
2017-04-04 18:54:22 +03:00
module_param_hw_array ( irq , uint , irq , NULL , 0 ) ;
2016-05-02 01:44:55 +03:00
MODULE_PARM_DESC ( irq , " ACCES 104-IDI-48 interrupt line numbers " ) ;
2015-11-23 20:54:50 +03:00
/**
* struct idi_48_gpio - GPIO device private data structure
* @ chip : instance of the gpio_chip
* @ lock : synchronization lock to prevent I / O race conditions
2015-12-16 02:52:44 +03:00
* @ ack_lock : synchronization lock to prevent IRQ handler race conditions
2015-11-23 20:54:50 +03:00
* @ irq_mask : input bits affected by interrupts
* @ base : base port address of the GPIO device
* @ cos_enb : Change - Of - State IRQ enable boundaries mask
*/
struct idi_48_gpio {
struct gpio_chip chip ;
2017-03-22 01:43:07 +03:00
raw_spinlock_t lock ;
2015-12-16 02:52:44 +03:00
spinlock_t ack_lock ;
2015-11-23 20:54:50 +03:00
unsigned char irq_mask [ 6 ] ;
unsigned base ;
unsigned char cos_enb ;
} ;
static int idi_48_gpio_get_direction ( struct gpio_chip * chip , unsigned offset )
{
2019-11-06 11:54:12 +03:00
return GPIO_LINE_DIRECTION_IN ;
2015-11-23 20:54:50 +03:00
}
static int idi_48_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
return 0 ;
}
static int idi_48_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
2015-12-03 17:31:48 +03:00
struct idi_48_gpio * const idi48gpio = gpiochip_get_data ( chip ) ;
2015-11-23 20:54:50 +03:00
unsigned i ;
2019-10-06 17:42:56 +03:00
static const unsigned int register_offset [ 6 ] = { 0 , 1 , 2 , 4 , 5 , 6 } ;
2015-11-23 20:54:50 +03:00
unsigned base_offset ;
unsigned mask ;
for ( i = 0 ; i < 48 ; i + = 8 )
if ( offset < i + 8 ) {
base_offset = register_offset [ i / 8 ] ;
mask = BIT ( offset - i ) ;
return ! ! ( inb ( idi48gpio - > base + base_offset ) & mask ) ;
}
/* The following line should never execute since offset < 48 */
return 0 ;
}
2018-03-22 16:00:31 +03:00
static int idi_48_gpio_get_multiple ( struct gpio_chip * chip , unsigned long * mask ,
unsigned long * bits )
{
struct idi_48_gpio * const idi48gpio = gpiochip_get_data ( chip ) ;
2019-12-05 03:51:08 +03:00
unsigned long offset ;
unsigned long gpio_mask ;
2018-04-18 20:26:34 +03:00
static const size_t ports [ ] = { 0 , 1 , 2 , 4 , 5 , 6 } ;
2019-12-05 03:51:08 +03:00
unsigned int port_addr ;
2018-03-22 16:00:31 +03:00
unsigned long port_state ;
/* clear bits array to a clean slate */
bitmap_zero ( bits , chip - > ngpio ) ;
2019-12-05 03:51:08 +03:00
for_each_set_clump8 ( offset , gpio_mask , mask , ARRAY_SIZE ( ports ) * 8 ) {
port_addr = idi48gpio - > base + ports [ offset / 8 ] ;
port_state = inb ( port_addr ) & gpio_mask ;
2018-03-22 16:00:31 +03:00
2019-12-05 03:51:08 +03:00
bitmap_set_value8 ( bits , port_state , offset ) ;
2018-03-22 16:00:31 +03:00
}
return 0 ;
}
2015-11-23 20:54:50 +03:00
static void idi_48_irq_ack ( struct irq_data * data )
{
}
static void idi_48_irq_mask ( struct irq_data * data )
{
struct gpio_chip * chip = irq_data_get_irq_chip_data ( data ) ;
2015-12-03 17:31:48 +03:00
struct idi_48_gpio * const idi48gpio = gpiochip_get_data ( chip ) ;
2015-11-23 20:54:50 +03:00
const unsigned offset = irqd_to_hwirq ( data ) ;
unsigned i ;
unsigned mask ;
unsigned boundary ;
unsigned long flags ;
for ( i = 0 ; i < 48 ; i + = 8 )
if ( offset < i + 8 ) {
mask = BIT ( offset - i ) ;
boundary = i / 8 ;
idi48gpio - > irq_mask [ boundary ] & = ~ mask ;
if ( ! idi48gpio - > irq_mask [ boundary ] ) {
idi48gpio - > cos_enb & = ~ BIT ( boundary ) ;
2017-03-22 01:43:07 +03:00
raw_spin_lock_irqsave ( & idi48gpio - > lock , flags ) ;
2015-11-23 20:54:50 +03:00
outb ( idi48gpio - > cos_enb , idi48gpio - > base + 7 ) ;
2017-03-22 01:43:07 +03:00
raw_spin_unlock_irqrestore ( & idi48gpio - > lock ,
flags ) ;
2015-11-23 20:54:50 +03:00
}
return ;
}
}
static void idi_48_irq_unmask ( struct irq_data * data )
{
struct gpio_chip * chip = irq_data_get_irq_chip_data ( data ) ;
2015-12-03 17:31:48 +03:00
struct idi_48_gpio * const idi48gpio = gpiochip_get_data ( chip ) ;
2015-11-23 20:54:50 +03:00
const unsigned offset = irqd_to_hwirq ( data ) ;
unsigned i ;
unsigned mask ;
unsigned boundary ;
unsigned prev_irq_mask ;
unsigned long flags ;
for ( i = 0 ; i < 48 ; i + = 8 )
if ( offset < i + 8 ) {
mask = BIT ( offset - i ) ;
boundary = i / 8 ;
prev_irq_mask = idi48gpio - > irq_mask [ boundary ] ;
idi48gpio - > irq_mask [ boundary ] | = mask ;
if ( ! prev_irq_mask ) {
idi48gpio - > cos_enb | = BIT ( boundary ) ;
2017-03-22 01:43:07 +03:00
raw_spin_lock_irqsave ( & idi48gpio - > lock , flags ) ;
2015-11-23 20:54:50 +03:00
outb ( idi48gpio - > cos_enb , idi48gpio - > base + 7 ) ;
2017-03-22 01:43:07 +03:00
raw_spin_unlock_irqrestore ( & idi48gpio - > lock ,
flags ) ;
2015-11-23 20:54:50 +03:00
}
return ;
}
}
static int idi_48_irq_set_type ( struct irq_data * data , unsigned flow_type )
{
/* The only valid irq types are none and both-edges */
if ( flow_type ! = IRQ_TYPE_NONE & &
( flow_type & IRQ_TYPE_EDGE_BOTH ) ! = IRQ_TYPE_EDGE_BOTH )
return - EINVAL ;
return 0 ;
}
static struct irq_chip idi_48_irqchip = {
. name = " 104-idi-48 " ,
. irq_ack = idi_48_irq_ack ,
. irq_mask = idi_48_irq_mask ,
. irq_unmask = idi_48_irq_unmask ,
. irq_set_type = idi_48_irq_set_type
} ;
static irqreturn_t idi_48_irq_handler ( int irq , void * dev_id )
{
struct idi_48_gpio * const idi48gpio = dev_id ;
unsigned long cos_status ;
unsigned long boundary ;
unsigned long irq_mask ;
unsigned long bit_num ;
unsigned long gpio ;
struct gpio_chip * const chip = & idi48gpio - > chip ;
2015-12-16 02:52:44 +03:00
spin_lock ( & idi48gpio - > ack_lock ) ;
2017-03-22 01:43:07 +03:00
raw_spin_lock ( & idi48gpio - > lock ) ;
2015-11-23 20:54:50 +03:00
cos_status = inb ( idi48gpio - > base + 7 ) ;
2017-03-22 01:43:07 +03:00
raw_spin_unlock ( & idi48gpio - > lock ) ;
2015-11-23 20:54:50 +03:00
/* IRQ Status (bit 6) is active low (0 = IRQ generated by device) */
2015-12-16 02:52:44 +03:00
if ( cos_status & BIT ( 6 ) ) {
spin_unlock ( & idi48gpio - > ack_lock ) ;
2015-11-23 20:54:50 +03:00
return IRQ_NONE ;
2015-12-16 02:52:44 +03:00
}
2015-11-23 20:54:50 +03:00
/* Bit 0-5 indicate which Change-Of-State boundary triggered the IRQ */
cos_status & = 0x3F ;
for_each_set_bit ( boundary , & cos_status , 6 ) {
irq_mask = idi48gpio - > irq_mask [ boundary ] ;
for_each_set_bit ( bit_num , & irq_mask , 8 ) {
gpio = bit_num + boundary * 8 ;
2017-11-07 21:15:47 +03:00
generic_handle_irq ( irq_find_mapping ( chip - > irq . domain ,
2015-11-23 20:54:50 +03:00
gpio ) ) ;
}
}
2015-12-16 02:52:44 +03:00
spin_unlock ( & idi48gpio - > ack_lock ) ;
2015-11-23 20:54:50 +03:00
return IRQ_HANDLED ;
}
2017-01-30 21:33:11 +03:00
# define IDI48_NGPIO 48
static const char * idi48_names [ IDI48_NGPIO ] = {
" Bit 0 A " , " Bit 1 A " , " Bit 2 A " , " Bit 3 A " , " Bit 4 A " , " Bit 5 A " ,
" Bit 6 A " , " Bit 7 A " , " Bit 8 A " , " Bit 9 A " , " Bit 10 A " , " Bit 11 A " ,
" Bit 12 A " , " Bit 13 A " , " Bit 14 A " , " Bit 15 A " , " Bit 16 A " , " Bit 17 A " ,
" Bit 18 A " , " Bit 19 A " , " Bit 20 A " , " Bit 21 A " , " Bit 22 A " , " Bit 23 A " ,
" Bit 0 B " , " Bit 1 B " , " Bit 2 B " , " Bit 3 B " , " Bit 4 B " , " Bit 5 B " ,
" Bit 6 B " , " Bit 7 B " , " Bit 8 B " , " Bit 9 B " , " Bit 10 B " , " Bit 11 B " ,
" Bit 12 B " , " Bit 13 B " , " Bit 14 B " , " Bit 15 B " , " Bit 16 B " , " Bit 17 B " ,
" Bit 18 B " , " Bit 19 B " , " Bit 20 B " , " Bit 21 B " , " Bit 22 B " , " Bit 23 B "
} ;
2016-05-02 01:44:55 +03:00
static int idi_48_probe ( struct device * dev , unsigned int id )
2015-11-23 20:54:50 +03:00
{
struct idi_48_gpio * idi48gpio ;
const char * const name = dev_name ( dev ) ;
int err ;
idi48gpio = devm_kzalloc ( dev , sizeof ( * idi48gpio ) , GFP_KERNEL ) ;
if ( ! idi48gpio )
return - ENOMEM ;
2016-05-02 01:44:55 +03:00
if ( ! devm_request_region ( dev , base [ id ] , IDI_48_EXTENT , name ) ) {
2016-02-03 23:15:22 +03:00
dev_err ( dev , " Unable to lock port addresses (0x%X-0x%X) \n " ,
2016-05-02 01:44:55 +03:00
base [ id ] , base [ id ] + IDI_48_EXTENT ) ;
2016-02-03 23:15:22 +03:00
return - EBUSY ;
2015-11-23 20:54:50 +03:00
}
idi48gpio - > chip . label = name ;
idi48gpio - > chip . parent = dev ;
idi48gpio - > chip . owner = THIS_MODULE ;
idi48gpio - > chip . base = - 1 ;
2017-01-30 21:33:11 +03:00
idi48gpio - > chip . ngpio = IDI48_NGPIO ;
idi48gpio - > chip . names = idi48_names ;
2015-11-23 20:54:50 +03:00
idi48gpio - > chip . get_direction = idi_48_gpio_get_direction ;
idi48gpio - > chip . direction_input = idi_48_gpio_direction_input ;
idi48gpio - > chip . get = idi_48_gpio_get ;
2018-03-22 16:00:31 +03:00
idi48gpio - > chip . get_multiple = idi_48_gpio_get_multiple ;
2016-05-02 01:44:55 +03:00
idi48gpio - > base = base [ id ] ;
2015-11-23 20:54:50 +03:00
2017-03-22 01:43:07 +03:00
raw_spin_lock_init ( & idi48gpio - > lock ) ;
2016-06-13 08:48:53 +03:00
spin_lock_init ( & idi48gpio - > ack_lock ) ;
2015-11-23 20:54:50 +03:00
2017-01-24 23:00:43 +03:00
err = devm_gpiochip_add_data ( dev , & idi48gpio - > chip , idi48gpio ) ;
2015-11-23 20:54:50 +03:00
if ( err ) {
dev_err ( dev , " GPIO registering failed (%d) \n " , err ) ;
2016-02-03 23:15:22 +03:00
return err ;
2015-11-23 20:54:50 +03:00
}
/* Disable IRQ by default */
2016-05-02 01:44:55 +03:00
outb ( 0 , base [ id ] + 7 ) ;
inb ( base [ id ] + 7 ) ;
2015-11-23 20:54:50 +03:00
err = gpiochip_irqchip_add ( & idi48gpio - > chip , & idi_48_irqchip , 0 ,
handle_edge_irq , IRQ_TYPE_NONE ) ;
if ( err ) {
dev_err ( dev , " Could not add irqchip (%d) \n " , err ) ;
2017-01-24 23:00:43 +03:00
return err ;
2015-11-23 20:54:50 +03:00
}
2017-01-24 23:00:43 +03:00
err = devm_request_irq ( dev , irq [ id ] , idi_48_irq_handler , IRQF_SHARED ,
name , idi48gpio ) ;
2015-11-23 20:54:50 +03:00
if ( err ) {
dev_err ( dev , " IRQ handler registering failed (%d) \n " , err ) ;
2017-01-24 23:00:43 +03:00
return err ;
2015-11-23 20:54:50 +03:00
}
return 0 ;
}
2016-05-02 01:44:55 +03:00
static struct isa_driver idi_48_driver = {
. probe = idi_48_probe ,
2015-11-23 20:54:50 +03:00
. driver = {
. name = " 104-idi-48 "
} ,
} ;
2016-05-02 01:44:55 +03:00
module_isa_driver ( idi_48_driver , num_idi_48 ) ;
2015-11-23 20:54:50 +03:00
MODULE_AUTHOR ( " William Breathitt Gray <vilhelm.gray@gmail.com> " ) ;
MODULE_DESCRIPTION ( " ACCES 104-IDI-48 GPIO driver " ) ;
2016-02-02 02:51:49 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;