2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
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
*
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
*/
2022-07-20 16:46:00 +03:00
# include <linux/bits.h>
2016-01-20 21:50:11 +03:00
# include <linux/device.h>
2022-12-27 17:09:40 +03:00
# include <linux/err.h>
2016-01-20 21:50:11 +03:00
# include <linux/ioport.h>
2022-12-27 17:09:40 +03:00
# include <linux/irq.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>
2022-12-27 17:09:40 +03:00
# include <linux/regmap.h>
2022-07-20 16:46:00 +03:00
# include <linux/types.h>
# include "gpio-i8255.h"
MODULE_IMPORT_NS ( I8255 ) ;
2016-01-20 21:50:11 +03:00
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 ] ;
2022-08-18 19:28:12 +03:00
static unsigned int num_irq ;
module_param_hw_array ( irq , uint , irq , & num_irq , 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
2022-12-27 17:09:40 +03:00
# define DIO48E_ENABLE_INTERRUPT 0xB
# define DIO48E_DISABLE_INTERRUPT DIO48E_ENABLE_INTERRUPT
# define DIO48E_CLEAR_INTERRUPT 0xF
2022-07-20 16:46:00 +03:00
# define DIO48E_NUM_PPI 2
2022-12-27 17:09:40 +03:00
static const struct regmap_range dio48e_wr_ranges [ ] = {
regmap_reg_range ( 0x0 , 0x9 ) , regmap_reg_range ( 0xB , 0xB ) ,
regmap_reg_range ( 0xD , 0xD ) , regmap_reg_range ( 0xF , 0xF ) ,
} ;
static const struct regmap_range dio48e_rd_ranges [ ] = {
regmap_reg_range ( 0x0 , 0x2 ) , regmap_reg_range ( 0x4 , 0x6 ) ,
regmap_reg_range ( 0xB , 0xB ) , regmap_reg_range ( 0xD , 0xD ) ,
regmap_reg_range ( 0xF , 0xF ) ,
} ;
static const struct regmap_range dio48e_volatile_ranges [ ] = {
i8255_volatile_regmap_range ( 0x0 ) , i8255_volatile_regmap_range ( 0x4 ) ,
regmap_reg_range ( 0xB , 0xB ) , regmap_reg_range ( 0xD , 0xD ) ,
regmap_reg_range ( 0xF , 0xF ) ,
} ;
static const struct regmap_range dio48e_precious_ranges [ ] = {
regmap_reg_range ( 0xB , 0xB ) , regmap_reg_range ( 0xD , 0xD ) ,
regmap_reg_range ( 0xF , 0xF ) ,
} ;
static const struct regmap_access_table dio48e_wr_table = {
. yes_ranges = dio48e_wr_ranges ,
. n_yes_ranges = ARRAY_SIZE ( dio48e_wr_ranges ) ,
} ;
static const struct regmap_access_table dio48e_rd_table = {
. yes_ranges = dio48e_rd_ranges ,
. n_yes_ranges = ARRAY_SIZE ( dio48e_rd_ranges ) ,
} ;
static const struct regmap_access_table dio48e_volatile_table = {
. yes_ranges = dio48e_volatile_ranges ,
. n_yes_ranges = ARRAY_SIZE ( dio48e_volatile_ranges ) ,
} ;
static const struct regmap_access_table dio48e_precious_table = {
. yes_ranges = dio48e_precious_ranges ,
. n_yes_ranges = ARRAY_SIZE ( dio48e_precious_ranges ) ,
} ;
static const struct regmap_config dio48e_regmap_config = {
. reg_bits = 8 ,
. reg_stride = 1 ,
. val_bits = 8 ,
. io_port = true ,
. max_register = 0xF ,
. wr_table = & dio48e_wr_table ,
. rd_table = & dio48e_rd_table ,
. volatile_table = & dio48e_volatile_table ,
. precious_table = & dio48e_precious_table ,
. cache_type = REGCACHE_FLAT ,
} ;
2016-01-20 21:50:11 +03:00
2022-12-27 17:09:40 +03:00
/* only bit 3 on each respective Port C supports interrupts */
# define DIO48E_REGMAP_IRQ(_ppi) \
[ 19 + ( _ppi ) * 24 ] = { \
. mask = BIT ( _ppi ) , \
. type = { . types_supported = IRQ_TYPE_EDGE_RISING } , \
2016-01-20 21:50:11 +03:00
}
2022-12-27 17:09:40 +03:00
static const struct regmap_irq dio48e_regmap_irqs [ ] = {
DIO48E_REGMAP_IRQ ( 0 ) , DIO48E_REGMAP_IRQ ( 1 ) ,
2016-01-20 21:50:11 +03:00
} ;
2022-12-27 17:09:40 +03:00
static int dio48e_handle_mask_sync ( struct regmap * const map , const int index ,
const unsigned int mask_buf_def ,
const unsigned int mask_buf ,
void * const irq_drv_data )
2016-01-20 21:50:11 +03:00
{
2022-12-27 17:09:40 +03:00
unsigned int * const irq_mask = irq_drv_data ;
const unsigned int prev_mask = * irq_mask ;
const unsigned int all_masked = GENMASK ( 1 , 0 ) ;
int err ;
unsigned int val ;
2016-01-20 21:50:11 +03:00
2022-12-27 17:09:40 +03:00
/* exit early if no change since the previous mask */
if ( mask_buf = = prev_mask )
return 0 ;
2016-01-20 21:50:11 +03:00
2022-12-27 17:09:40 +03:00
/* remember the current mask for the next mask sync */
* irq_mask = mask_buf ;
2016-01-20 21:50:11 +03:00
2022-12-27 17:09:40 +03:00
/* if all previously masked, enable interrupts when unmasking */
if ( prev_mask = = all_masked ) {
err = regmap_write ( map , DIO48E_CLEAR_INTERRUPT , 0x00 ) ;
if ( err )
return err ;
return regmap_write ( map , DIO48E_ENABLE_INTERRUPT , 0x00 ) ;
}
2016-01-20 21:50:11 +03:00
2022-12-27 17:09:40 +03:00
/* if all are currently masked, disable interrupts */
if ( mask_buf = = all_masked )
return regmap_read ( map , DIO48E_DISABLE_INTERRUPT , & val ) ;
2016-01-20 21:50:11 +03:00
2022-12-27 17:09:40 +03:00
return 0 ;
2016-01-20 21:50:11 +03:00
}
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 "
} ;
2022-12-27 17:09:40 +03:00
static int dio48e_irq_init_hw ( struct regmap * const map )
2020-07-22 13:39:15 +03:00
{
2022-12-27 17:09:40 +03:00
unsigned int val ;
2020-07-22 13:39:15 +03:00
/* Disable IRQ by default */
2022-12-27 17:09:40 +03:00
return regmap_read ( map , DIO48E_DISABLE_INTERRUPT , & val ) ;
2020-07-22 13:39:15 +03:00
}
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
{
const char * const name = dev_name ( dev ) ;
2022-12-27 17:09:44 +03:00
struct i8255_regmap_config config = { } ;
2022-12-27 17:09:40 +03:00
void __iomem * regs ;
struct regmap * map ;
2016-01-20 21:50:11 +03:00
int err ;
2022-12-27 17:09:40 +03:00
struct regmap_irq_chip * chip ;
unsigned int irq_mask ;
struct regmap_irq_chip_data * chip_data ;
2016-01-20 21:50:11 +03:00
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
}
2022-12-27 17:09:40 +03:00
regs = devm_ioport_map ( dev , base [ id ] , DIO48E_EXTENT ) ;
if ( ! regs )
return - ENOMEM ;
map = devm_regmap_init_mmio ( dev , regs , & dio48e_regmap_config ) ;
if ( IS_ERR ( map ) )
return dev_err_probe ( dev , PTR_ERR ( map ) ,
" Unable to initialize register map \n " ) ;
chip = devm_kzalloc ( dev , sizeof ( * chip ) , GFP_KERNEL ) ;
if ( ! chip )
return - ENOMEM ;
chip - > irq_drv_data = devm_kzalloc ( dev , sizeof ( irq_mask ) , GFP_KERNEL ) ;
if ( ! chip - > irq_drv_data )
2022-05-10 20:30:54 +03:00
return - ENOMEM ;
2022-12-27 17:09:40 +03:00
chip - > name = name ;
/* No IRQ status register so use CLEAR_INTERRUPT register instead */
chip - > status_base = DIO48E_CLEAR_INTERRUPT ;
chip - > mask_base = DIO48E_ENABLE_INTERRUPT ;
chip - > ack_base = DIO48E_CLEAR_INTERRUPT ;
/* CLEAR_INTERRUPT doubles as status register so we need it cleared */
chip - > clear_ack = true ;
chip - > status_invert = true ;
chip - > num_regs = 1 ;
chip - > irqs = dio48e_regmap_irqs ;
chip - > num_irqs = ARRAY_SIZE ( dio48e_regmap_irqs ) ;
chip - > handle_mask_sync = dio48e_handle_mask_sync ;
/* Initialize to prevent spurious interrupts before we're ready */
err = dio48e_irq_init_hw ( map ) ;
if ( err )
return err ;
err = devm_regmap_add_irq_chip ( dev , map , irq [ id ] , 0 , 0 , chip , & chip_data ) ;
if ( err )
return dev_err_probe ( dev , err , " IRQ registration failed \n " ) ;
2022-12-27 17:09:44 +03:00
config . parent = dev ;
config . map = map ;
config . num_ppi = DIO48E_NUM_PPI ;
config . names = dio48e_names ;
config . domain = regmap_irq_get_domain ( chip_data ) ;
2016-01-20 21:50:11 +03:00
2022-12-27 17:09:44 +03:00
return devm_i8255_regmap_register ( dev , & config ) ;
2016-01-20 21:50:11 +03:00
}
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 "
} ,
} ;
2022-08-18 19:28:12 +03:00
module_isa_driver_with_irq ( dio48e_driver , num_dio48e , num_irq ) ;
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 " ) ;