2016-01-20 21:50:11 +03:00
/*
2016-05-02 01:44:39 +03:00
* GPIO driver for the ACCES 104 - DIO - 48 E series
2016-01-20 21:50:11 +03:00
* Copyright ( C ) 2016 William Breathitt Gray
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License , version 2 , as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
2016-05-02 01:44:39 +03:00
*
* This driver supports the following ACCES devices : 104 - DIO - 48 E and
* 104 - DIO - 24 E .
2016-01-20 21:50:11 +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:39 +03:00
# include <linux/isa.h>
2016-01-20 21:50:11 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/spinlock.h>
2016-05-02 01:44:39 +03:00
# define DIO48E_EXTENT 16
# define MAX_NUM_DIO48E max_num_isa_dev(DIO48E_EXTENT)
static unsigned int base [ MAX_NUM_DIO48E ] ;
static unsigned int num_dio48e ;
2017-04-04 18:54:22 +03:00
module_param_hw_array ( base , uint , ioport , & num_dio48e , 0 ) ;
2016-05-02 01:44:39 +03:00
MODULE_PARM_DESC ( base , " ACCES 104-DIO-48E base addresses " ) ;
static unsigned int irq [ MAX_NUM_DIO48E ] ;
2017-04-04 18:54:22 +03:00
module_param_hw_array ( irq , uint , irq , NULL , 0 ) ;
2016-05-02 01:44:39 +03:00
MODULE_PARM_DESC ( irq , " ACCES 104-DIO-48E interrupt line numbers " ) ;
2016-01-20 21:50:11 +03:00
/**
* struct dio48e_gpio - GPIO device private data structure
* @ chip : instance of the gpio_chip
* @ io_state : bit I / O state ( whether bit is set to input or output )
* @ out_state : output bits state
* @ control : Control registers state
* @ lock : synchronization lock to prevent I / O race conditions
* @ base : base port address of the GPIO device
* @ irq_mask : I / O bits affected by interrupts
*/
struct dio48e_gpio {
struct gpio_chip chip ;
unsigned char io_state [ 6 ] ;
unsigned char out_state [ 6 ] ;
unsigned char control [ 2 ] ;
2017-03-09 19:21:52 +03:00
raw_spinlock_t lock ;
2016-01-20 21:50:11 +03:00
unsigned base ;
unsigned char irq_mask ;
} ;
static int dio48e_gpio_get_direction ( struct gpio_chip * chip , unsigned offset )
{
struct dio48e_gpio * const dio48egpio = gpiochip_get_data ( chip ) ;
const unsigned port = offset / 8 ;
const unsigned mask = BIT ( offset % 8 ) ;
return ! ! ( dio48egpio - > io_state [ port ] & mask ) ;
}
static int dio48e_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
struct dio48e_gpio * const dio48egpio = gpiochip_get_data ( chip ) ;
const unsigned io_port = offset / 8 ;
2016-06-02 23:00:09 +03:00
const unsigned int control_port = io_port / 3 ;
2016-01-20 21:50:11 +03:00
const unsigned control_addr = dio48egpio - > base + 3 + control_port * 4 ;
unsigned long flags ;
unsigned control ;
2017-03-09 19:21:52 +03:00
raw_spin_lock_irqsave ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
/* Check if configuring Port C */
if ( io_port = = 2 | | io_port = = 5 ) {
/* Port C can be configured by nibble */
if ( offset % 8 > 3 ) {
dio48egpio - > io_state [ io_port ] | = 0xF0 ;
dio48egpio - > control [ control_port ] | = BIT ( 3 ) ;
} else {
dio48egpio - > io_state [ io_port ] | = 0x0F ;
dio48egpio - > control [ control_port ] | = BIT ( 0 ) ;
}
} else {
dio48egpio - > io_state [ io_port ] | = 0xFF ;
if ( io_port = = 0 | | io_port = = 3 )
dio48egpio - > control [ control_port ] | = BIT ( 4 ) ;
else
dio48egpio - > control [ control_port ] | = BIT ( 1 ) ;
}
control = BIT ( 7 ) | dio48egpio - > control [ control_port ] ;
outb ( control , control_addr ) ;
control & = ~ BIT ( 7 ) ;
outb ( control , control_addr ) ;
2017-03-09 19:21:52 +03:00
raw_spin_unlock_irqrestore ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
return 0 ;
}
static int dio48e_gpio_direction_output ( struct gpio_chip * chip , unsigned offset ,
int value )
{
struct dio48e_gpio * const dio48egpio = gpiochip_get_data ( chip ) ;
const unsigned io_port = offset / 8 ;
2016-06-02 23:00:09 +03:00
const unsigned int control_port = io_port / 3 ;
2016-01-20 21:50:11 +03:00
const unsigned mask = BIT ( offset % 8 ) ;
const unsigned control_addr = dio48egpio - > base + 3 + control_port * 4 ;
const unsigned out_port = ( io_port > 2 ) ? io_port + 1 : io_port ;
unsigned long flags ;
unsigned control ;
2017-03-09 19:21:52 +03:00
raw_spin_lock_irqsave ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
/* Check if configuring Port C */
if ( io_port = = 2 | | io_port = = 5 ) {
/* Port C can be configured by nibble */
if ( offset % 8 > 3 ) {
dio48egpio - > io_state [ io_port ] & = 0x0F ;
dio48egpio - > control [ control_port ] & = ~ BIT ( 3 ) ;
} else {
dio48egpio - > io_state [ io_port ] & = 0xF0 ;
dio48egpio - > control [ control_port ] & = ~ BIT ( 0 ) ;
}
} else {
dio48egpio - > io_state [ io_port ] & = 0x00 ;
if ( io_port = = 0 | | io_port = = 3 )
dio48egpio - > control [ control_port ] & = ~ BIT ( 4 ) ;
else
dio48egpio - > control [ control_port ] & = ~ BIT ( 1 ) ;
}
if ( value )
dio48egpio - > out_state [ io_port ] | = mask ;
else
dio48egpio - > out_state [ io_port ] & = ~ mask ;
control = BIT ( 7 ) | dio48egpio - > control [ control_port ] ;
outb ( control , control_addr ) ;
outb ( dio48egpio - > out_state [ io_port ] , dio48egpio - > base + out_port ) ;
control & = ~ BIT ( 7 ) ;
outb ( control , control_addr ) ;
2017-03-09 19:21:52 +03:00
raw_spin_unlock_irqrestore ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
return 0 ;
}
static int dio48e_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
struct dio48e_gpio * const dio48egpio = gpiochip_get_data ( chip ) ;
const unsigned port = offset / 8 ;
const unsigned mask = BIT ( offset % 8 ) ;
const unsigned in_port = ( port > 2 ) ? port + 1 : port ;
unsigned long flags ;
unsigned port_state ;
2017-03-09 19:21:52 +03:00
raw_spin_lock_irqsave ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
/* ensure that GPIO is set for input */
if ( ! ( dio48egpio - > io_state [ port ] & mask ) ) {
2017-03-09 19:21:52 +03:00
raw_spin_unlock_irqrestore ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
return - EINVAL ;
}
port_state = inb ( dio48egpio - > base + in_port ) ;
2017-03-09 19:21:52 +03:00
raw_spin_unlock_irqrestore ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
return ! ! ( port_state & mask ) ;
}
static void dio48e_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
struct dio48e_gpio * const dio48egpio = gpiochip_get_data ( chip ) ;
const unsigned port = offset / 8 ;
const unsigned mask = BIT ( offset % 8 ) ;
const unsigned out_port = ( port > 2 ) ? port + 1 : port ;
unsigned long flags ;
2017-03-09 19:21:52 +03:00
raw_spin_lock_irqsave ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
if ( value )
dio48egpio - > out_state [ port ] | = mask ;
else
dio48egpio - > out_state [ port ] & = ~ mask ;
outb ( dio48egpio - > out_state [ port ] , dio48egpio - > base + out_port ) ;
2017-03-09 19:21:52 +03:00
raw_spin_unlock_irqrestore ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
}
2017-01-19 18:05:27 +03:00
static void dio48e_gpio_set_multiple ( struct gpio_chip * chip ,
unsigned long * mask , unsigned long * bits )
{
struct dio48e_gpio * const dio48egpio = gpiochip_get_data ( chip ) ;
unsigned int i ;
const unsigned int gpio_reg_size = 8 ;
unsigned int port ;
unsigned int out_port ;
unsigned int bitmask ;
unsigned long flags ;
/* set bits are evaluated a gpio register size at a time */
for ( i = 0 ; i < chip - > ngpio ; i + = gpio_reg_size ) {
/* no more set bits in this mask word; skip to the next word */
if ( ! mask [ BIT_WORD ( i ) ] ) {
i = ( BIT_WORD ( i ) + 1 ) * BITS_PER_LONG - gpio_reg_size ;
continue ;
}
port = i / gpio_reg_size ;
out_port = ( port > 2 ) ? port + 1 : port ;
bitmask = mask [ BIT_WORD ( i ) ] & bits [ BIT_WORD ( i ) ] ;
2017-03-09 19:21:52 +03:00
raw_spin_lock_irqsave ( & dio48egpio - > lock , flags ) ;
2017-01-19 18:05:27 +03:00
/* update output state data and set device gpio register */
dio48egpio - > out_state [ port ] & = ~ mask [ BIT_WORD ( i ) ] ;
dio48egpio - > out_state [ port ] | = bitmask ;
outb ( dio48egpio - > out_state [ port ] , dio48egpio - > base + out_port ) ;
2017-03-09 19:21:52 +03:00
raw_spin_unlock_irqrestore ( & dio48egpio - > lock , flags ) ;
2017-01-19 18:05:27 +03:00
/* prepare for next gpio register set */
mask [ BIT_WORD ( i ) ] > > = gpio_reg_size ;
bits [ BIT_WORD ( i ) ] > > = gpio_reg_size ;
}
}
2016-01-20 21:50:11 +03:00
static void dio48e_irq_ack ( struct irq_data * data )
{
}
static void dio48e_irq_mask ( struct irq_data * data )
{
struct gpio_chip * chip = irq_data_get_irq_chip_data ( data ) ;
struct dio48e_gpio * const dio48egpio = gpiochip_get_data ( chip ) ;
const unsigned long offset = irqd_to_hwirq ( data ) ;
unsigned long flags ;
/* only bit 3 on each respective Port C supports interrupts */
if ( offset ! = 19 & & offset ! = 43 )
return ;
2017-03-09 19:21:52 +03:00
raw_spin_lock_irqsave ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
if ( offset = = 19 )
dio48egpio - > irq_mask & = ~ BIT ( 0 ) ;
else
dio48egpio - > irq_mask & = ~ BIT ( 1 ) ;
if ( ! dio48egpio - > irq_mask )
/* disable interrupts */
inb ( dio48egpio - > base + 0xB ) ;
2017-03-09 19:21:52 +03:00
raw_spin_unlock_irqrestore ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
}
static void dio48e_irq_unmask ( struct irq_data * data )
{
struct gpio_chip * chip = irq_data_get_irq_chip_data ( data ) ;
struct dio48e_gpio * const dio48egpio = gpiochip_get_data ( chip ) ;
const unsigned long offset = irqd_to_hwirq ( data ) ;
unsigned long flags ;
/* only bit 3 on each respective Port C supports interrupts */
if ( offset ! = 19 & & offset ! = 43 )
return ;
2017-03-09 19:21:52 +03:00
raw_spin_lock_irqsave ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
if ( ! dio48egpio - > irq_mask ) {
/* enable interrupts */
outb ( 0x00 , dio48egpio - > base + 0xF ) ;
outb ( 0x00 , dio48egpio - > base + 0xB ) ;
}
if ( offset = = 19 )
dio48egpio - > irq_mask | = BIT ( 0 ) ;
else
dio48egpio - > irq_mask | = BIT ( 1 ) ;
2017-03-09 19:21:52 +03:00
raw_spin_unlock_irqrestore ( & dio48egpio - > lock , flags ) ;
2016-01-20 21:50:11 +03:00
}
static int dio48e_irq_set_type ( struct irq_data * data , unsigned flow_type )
{
const unsigned long offset = irqd_to_hwirq ( data ) ;
/* only bit 3 on each respective Port C supports interrupts */
if ( offset ! = 19 & & offset ! = 43 )
return - EINVAL ;
if ( flow_type ! = IRQ_TYPE_NONE & & flow_type ! = IRQ_TYPE_EDGE_RISING )
return - EINVAL ;
return 0 ;
}
static struct irq_chip dio48e_irqchip = {
. name = " 104-dio-48e " ,
. irq_ack = dio48e_irq_ack ,
. irq_mask = dio48e_irq_mask ,
. irq_unmask = dio48e_irq_unmask ,
. irq_set_type = dio48e_irq_set_type
} ;
static irqreturn_t dio48e_irq_handler ( int irq , void * dev_id )
{
struct dio48e_gpio * const dio48egpio = dev_id ;
struct gpio_chip * const chip = & dio48egpio - > chip ;
const unsigned long irq_mask = dio48egpio - > irq_mask ;
unsigned long gpio ;
for_each_set_bit ( gpio , & irq_mask , 2 )
generic_handle_irq ( irq_find_mapping ( chip - > irqdomain ,
19 + gpio * 24 ) ) ;
2017-03-09 19:21:52 +03:00
raw_spin_lock ( & dio48egpio - > lock ) ;
2016-01-20 21:50:11 +03:00
outb ( 0x00 , dio48egpio - > base + 0xF ) ;
2017-03-09 19:21:52 +03:00
raw_spin_unlock ( & dio48egpio - > lock ) ;
2016-01-20 21:50:11 +03:00
return IRQ_HANDLED ;
}
2017-01-30 21:32:58 +03:00
# define DIO48E_NGPIO 48
static const char * dio48e_names [ DIO48E_NGPIO ] = {
" PPI Group 0 Port A 0 " , " PPI Group 0 Port A 1 " , " PPI Group 0 Port A 2 " ,
" PPI Group 0 Port A 3 " , " PPI Group 0 Port A 4 " , " PPI Group 0 Port A 5 " ,
" PPI Group 0 Port A 6 " , " PPI Group 0 Port A 7 " , " PPI Group 0 Port B 0 " ,
" PPI Group 0 Port B 1 " , " PPI Group 0 Port B 2 " , " PPI Group 0 Port B 3 " ,
" PPI Group 0 Port B 4 " , " PPI Group 0 Port B 5 " , " PPI Group 0 Port B 6 " ,
" PPI Group 0 Port B 7 " , " PPI Group 0 Port C 0 " , " PPI Group 0 Port C 1 " ,
" PPI Group 0 Port C 2 " , " PPI Group 0 Port C 3 " , " PPI Group 0 Port C 4 " ,
" PPI Group 0 Port C 5 " , " PPI Group 0 Port C 6 " , " PPI Group 0 Port C 7 " ,
" PPI Group 1 Port A 0 " , " PPI Group 1 Port A 1 " , " PPI Group 1 Port A 2 " ,
" PPI Group 1 Port A 3 " , " PPI Group 1 Port A 4 " , " PPI Group 1 Port A 5 " ,
" PPI Group 1 Port A 6 " , " PPI Group 1 Port A 7 " , " PPI Group 1 Port B 0 " ,
" PPI Group 1 Port B 1 " , " PPI Group 1 Port B 2 " , " PPI Group 1 Port B 3 " ,
" PPI Group 1 Port B 4 " , " PPI Group 1 Port B 5 " , " PPI Group 1 Port B 6 " ,
" PPI Group 1 Port B 7 " , " PPI Group 1 Port C 0 " , " PPI Group 1 Port C 1 " ,
" PPI Group 1 Port C 2 " , " PPI Group 1 Port C 3 " , " PPI Group 1 Port C 4 " ,
" PPI Group 1 Port C 5 " , " PPI Group 1 Port C 6 " , " PPI Group 1 Port C 7 "
} ;
2016-05-02 01:44:39 +03:00
static int dio48e_probe ( struct device * dev , unsigned int id )
2016-01-20 21:50:11 +03:00
{
struct dio48e_gpio * dio48egpio ;
const char * const name = dev_name ( dev ) ;
int err ;
dio48egpio = devm_kzalloc ( dev , sizeof ( * dio48egpio ) , GFP_KERNEL ) ;
if ( ! dio48egpio )
return - ENOMEM ;
2016-05-02 01:44:39 +03:00
if ( ! devm_request_region ( dev , base [ id ] , DIO48E_EXTENT , name ) ) {
2016-02-03 23:15:21 +03:00
dev_err ( dev , " Unable to lock port addresses (0x%X-0x%X) \n " ,
2016-05-02 01:44:39 +03:00
base [ id ] , base [ id ] + DIO48E_EXTENT ) ;
2016-02-03 23:15:21 +03:00
return - EBUSY ;
2016-01-20 21:50:11 +03:00
}
dio48egpio - > chip . label = name ;
dio48egpio - > chip . parent = dev ;
dio48egpio - > chip . owner = THIS_MODULE ;
dio48egpio - > chip . base = - 1 ;
2017-01-30 21:32:58 +03:00
dio48egpio - > chip . ngpio = DIO48E_NGPIO ;
dio48egpio - > chip . names = dio48e_names ;
2016-01-20 21:50:11 +03:00
dio48egpio - > chip . get_direction = dio48e_gpio_get_direction ;
dio48egpio - > chip . direction_input = dio48e_gpio_direction_input ;
dio48egpio - > chip . direction_output = dio48e_gpio_direction_output ;
dio48egpio - > chip . get = dio48e_gpio_get ;
dio48egpio - > chip . set = dio48e_gpio_set ;
2017-01-19 18:05:27 +03:00
dio48egpio - > chip . set_multiple = dio48e_gpio_set_multiple ;
2016-05-02 01:44:39 +03:00
dio48egpio - > base = base [ id ] ;
2016-01-20 21:50:11 +03:00
2017-03-09 19:21:52 +03:00
raw_spin_lock_init ( & dio48egpio - > lock ) ;
2016-01-20 21:50:11 +03:00
2017-01-24 23:00:31 +03:00
err = devm_gpiochip_add_data ( dev , & dio48egpio - > chip , dio48egpio ) ;
2016-01-20 21:50:11 +03:00
if ( err ) {
dev_err ( dev , " GPIO registering failed (%d) \n " , err ) ;
2016-02-03 23:15:21 +03:00
return err ;
2016-01-20 21:50:11 +03:00
}
/* initialize all GPIO as output */
2016-05-02 01:44:39 +03:00
outb ( 0x80 , base [ id ] + 3 ) ;
outb ( 0x00 , base [ id ] ) ;
outb ( 0x00 , base [ id ] + 1 ) ;
outb ( 0x00 , base [ id ] + 2 ) ;
outb ( 0x00 , base [ id ] + 3 ) ;
outb ( 0x80 , base [ id ] + 7 ) ;
outb ( 0x00 , base [ id ] + 4 ) ;
outb ( 0x00 , base [ id ] + 5 ) ;
outb ( 0x00 , base [ id ] + 6 ) ;
outb ( 0x00 , base [ id ] + 7 ) ;
2016-01-20 21:50:11 +03:00
/* disable IRQ by default */
2016-05-02 01:44:39 +03:00
inb ( base [ id ] + 0xB ) ;
2016-01-20 21:50:11 +03:00
err = gpiochip_irqchip_add ( & dio48egpio - > chip , & dio48e_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:31 +03:00
return err ;
2016-01-20 21:50:11 +03:00
}
2017-01-24 23:00:31 +03:00
err = devm_request_irq ( dev , irq [ id ] , dio48e_irq_handler , 0 , name ,
dio48egpio ) ;
2016-01-20 21:50:11 +03:00
if ( err ) {
dev_err ( dev , " IRQ handler registering failed (%d) \n " , err ) ;
2017-01-24 23:00:31 +03:00
return err ;
2016-01-20 21:50:11 +03:00
}
return 0 ;
}
2016-05-02 01:44:39 +03:00
static struct isa_driver dio48e_driver = {
. probe = dio48e_probe ,
2016-01-20 21:50:11 +03:00
. driver = {
. name = " 104-dio-48e "
} ,
} ;
2016-05-02 01:44:39 +03:00
module_isa_driver ( dio48e_driver , num_dio48e ) ;
2016-01-20 21:50:11 +03:00
MODULE_AUTHOR ( " William Breathitt Gray <vilhelm.gray@gmail.com> " ) ;
MODULE_DESCRIPTION ( " ACCES 104-DIO-48E GPIO driver " ) ;
2016-02-02 02:51:49 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;