2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-01-20 01:23:20 +03:00
/*
* GPIO driver for Exar XR17V35X chip
*
* Copyright ( C ) 2015 Sudip Mukherjee < sudip . mukherjee @ codethink . co . uk >
*/
2020-09-19 16:32:15 +03:00
2017-01-20 01:23:20 +03:00
# include <linux/bitops.h>
# include <linux/device.h>
# include <linux/gpio/driver.h>
2020-09-19 16:36:56 +03:00
# include <linux/idr.h>
2017-01-20 01:23:20 +03:00
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/platform_device.h>
2020-09-28 18:00:26 +03:00
# include <linux/regmap.h>
2017-01-20 01:23:20 +03:00
# define EXAR_OFFSET_MPIOLVL_LO 0x90
# define EXAR_OFFSET_MPIOSEL_LO 0x93
# define EXAR_OFFSET_MPIOLVL_HI 0x96
# define EXAR_OFFSET_MPIOSEL_HI 0x99
# define DRIVER_NAME "gpio_exar"
static DEFINE_IDA ( ida_index ) ;
struct exar_gpio_chip {
struct gpio_chip gpio_chip ;
2020-09-28 18:00:26 +03:00
struct regmap * regmap ;
2017-01-20 01:23:20 +03:00
int index ;
char name [ 20 ] ;
2017-05-22 13:43:18 +03:00
unsigned int first_pin ;
2017-01-20 01:23:20 +03:00
} ;
2020-09-30 11:20:10 +03:00
static unsigned int
exar_offset_to_sel_addr ( struct exar_gpio_chip * exar_gpio , unsigned int offset )
{
return ( offset + exar_gpio - > first_pin ) / 8 ? EXAR_OFFSET_MPIOSEL_HI
: EXAR_OFFSET_MPIOSEL_LO ;
}
static unsigned int
exar_offset_to_lvl_addr ( struct exar_gpio_chip * exar_gpio , unsigned int offset )
{
return ( offset + exar_gpio - > first_pin ) / 8 ? EXAR_OFFSET_MPIOLVL_HI
: EXAR_OFFSET_MPIOLVL_LO ;
}
static unsigned int
exar_offset_to_bit ( struct exar_gpio_chip * exar_gpio , unsigned int offset )
{
return ( offset + exar_gpio - > first_pin ) % 8 ;
}
2017-01-20 01:23:20 +03:00
static int exar_get_direction ( struct gpio_chip * chip , unsigned int offset )
{
2017-05-22 13:43:18 +03:00
struct exar_gpio_chip * exar_gpio = gpiochip_get_data ( chip ) ;
2020-09-30 11:20:10 +03:00
unsigned int addr = exar_offset_to_sel_addr ( exar_gpio , offset ) ;
unsigned int bit = exar_offset_to_bit ( exar_gpio , offset ) ;
2017-01-20 01:23:20 +03:00
2020-09-28 18:00:26 +03:00
if ( regmap_test_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) )
2019-11-06 11:54:12 +03:00
return GPIO_LINE_DIRECTION_IN ;
return GPIO_LINE_DIRECTION_OUT ;
2017-01-20 01:23:20 +03:00
}
static int exar_get_value ( struct gpio_chip * chip , unsigned int offset )
{
2017-05-22 13:43:18 +03:00
struct exar_gpio_chip * exar_gpio = gpiochip_get_data ( chip ) ;
2020-09-30 11:20:10 +03:00
unsigned int addr = exar_offset_to_lvl_addr ( exar_gpio , offset ) ;
unsigned int bit = exar_offset_to_bit ( exar_gpio , offset ) ;
2017-01-20 01:23:20 +03:00
2020-09-28 18:00:26 +03:00
return ! ! ( regmap_test_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) ) ;
2017-01-20 01:23:20 +03:00
}
static void exar_set_value ( struct gpio_chip * chip , unsigned int offset ,
int value )
{
2017-05-22 13:43:18 +03:00
struct exar_gpio_chip * exar_gpio = gpiochip_get_data ( chip ) ;
2020-09-30 11:20:10 +03:00
unsigned int addr = exar_offset_to_lvl_addr ( exar_gpio , offset ) ;
unsigned int bit = exar_offset_to_bit ( exar_gpio , offset ) ;
2017-01-20 01:23:20 +03:00
2020-09-28 18:00:26 +03:00
if ( value )
regmap_set_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) ;
else
regmap_clear_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) ;
2017-01-20 01:23:20 +03:00
}
2017-02-26 17:36:58 +03:00
static int exar_direction_output ( struct gpio_chip * chip , unsigned int offset ,
int value )
{
2020-09-28 18:00:26 +03:00
struct exar_gpio_chip * exar_gpio = gpiochip_get_data ( chip ) ;
unsigned int addr = exar_offset_to_sel_addr ( exar_gpio , offset ) ;
unsigned int bit = exar_offset_to_bit ( exar_gpio , offset ) ;
2017-02-26 17:36:58 +03:00
exar_set_value ( chip , offset , value ) ;
2020-09-28 18:00:26 +03:00
regmap_clear_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) ;
return 0 ;
2017-02-26 17:36:58 +03:00
}
static int exar_direction_input ( struct gpio_chip * chip , unsigned int offset )
{
2020-09-28 18:00:26 +03:00
struct exar_gpio_chip * exar_gpio = gpiochip_get_data ( chip ) ;
unsigned int addr = exar_offset_to_sel_addr ( exar_gpio , offset ) ;
unsigned int bit = exar_offset_to_bit ( exar_gpio , offset ) ;
regmap_set_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) ;
return 0 ;
2017-02-26 17:36:58 +03:00
}
2020-10-20 10:24:04 +03:00
static void exar_devm_ida_free ( void * data )
{
struct exar_gpio_chip * exar_gpio = data ;
ida_free ( & ida_index , exar_gpio - > index ) ;
}
2020-09-28 18:00:26 +03:00
static const struct regmap_config exar_regmap_config = {
. name = " exar-gpio " ,
. reg_bits = 16 ,
. val_bits = 8 ,
} ;
2017-01-20 01:23:20 +03:00
static int gpio_exar_probe ( struct platform_device * pdev )
{
2020-09-28 17:34:37 +03:00
struct device * dev = & pdev - > dev ;
struct pci_dev * pcidev = to_pci_dev ( dev - > parent ) ;
2017-01-20 01:23:20 +03:00
struct exar_gpio_chip * exar_gpio ;
2017-05-22 13:43:18 +03:00
u32 first_pin , ngpios ;
2017-01-20 01:23:20 +03:00
void __iomem * p ;
int index , ret ;
/*
2017-05-02 09:42:40 +03:00
* The UART driver must have mapped region 0 prior to registering this
* device - use it .
2017-01-20 01:23:20 +03:00
*/
2017-05-02 09:42:40 +03:00
p = pcim_iomap_table ( pcidev ) [ 0 ] ;
2017-01-20 01:23:20 +03:00
if ( ! p )
return - ENOMEM ;
2020-09-28 17:34:37 +03:00
ret = device_property_read_u32 ( dev , " exar,first-pin " , & first_pin ) ;
2017-05-22 13:43:18 +03:00
if ( ret )
return ret ;
2020-09-28 17:34:37 +03:00
ret = device_property_read_u32 ( dev , " ngpios " , & ngpios ) ;
2017-05-22 13:43:18 +03:00
if ( ret )
return ret ;
2020-09-28 17:34:37 +03:00
exar_gpio = devm_kzalloc ( dev , sizeof ( * exar_gpio ) , GFP_KERNEL ) ;
2017-01-20 01:23:20 +03:00
if ( ! exar_gpio )
return - ENOMEM ;
2020-09-28 18:00:26 +03:00
/*
* We don ' t need to check the return values of mmio regmap operations ( unless
* the regmap has a clock attached which is not the case here ) .
*/
exar_gpio - > regmap = devm_regmap_init_mmio ( dev , p , & exar_regmap_config ) ;
if ( IS_ERR ( exar_gpio - > regmap ) )
return PTR_ERR ( exar_gpio - > regmap ) ;
2017-01-20 01:23:20 +03:00
2020-09-28 15:35:51 +03:00
index = ida_alloc ( & ida_index , GFP_KERNEL ) ;
2020-09-28 18:00:26 +03:00
if ( index < 0 )
return index ;
2017-01-20 01:23:20 +03:00
2020-10-20 10:24:04 +03:00
ret = devm_add_action_or_reset ( dev , exar_devm_ida_free , exar_gpio ) ;
if ( ret )
return ret ;
2017-01-20 01:23:20 +03:00
sprintf ( exar_gpio - > name , " exar_gpio%d " , index ) ;
exar_gpio - > gpio_chip . label = exar_gpio - > name ;
2020-09-28 17:34:37 +03:00
exar_gpio - > gpio_chip . parent = dev ;
2017-01-20 01:23:20 +03:00
exar_gpio - > gpio_chip . direction_output = exar_direction_output ;
exar_gpio - > gpio_chip . direction_input = exar_direction_input ;
exar_gpio - > gpio_chip . get_direction = exar_get_direction ;
exar_gpio - > gpio_chip . get = exar_get_value ;
exar_gpio - > gpio_chip . set = exar_set_value ;
exar_gpio - > gpio_chip . base = - 1 ;
2017-05-22 13:43:18 +03:00
exar_gpio - > gpio_chip . ngpio = ngpios ;
2017-01-20 01:23:20 +03:00
exar_gpio - > index = index ;
2017-05-22 13:43:18 +03:00
exar_gpio - > first_pin = first_pin ;
2017-01-20 01:23:20 +03:00
2020-09-28 17:34:37 +03:00
ret = devm_gpiochip_add_data ( dev , & exar_gpio - > gpio_chip , exar_gpio ) ;
2017-01-20 01:23:20 +03:00
if ( ret )
2020-10-20 10:24:04 +03:00
return ret ;
2017-01-20 01:23:20 +03:00
platform_set_drvdata ( pdev , exar_gpio ) ;
return 0 ;
}
static struct platform_driver gpio_exar_driver = {
. probe = gpio_exar_probe ,
. driver = {
. name = DRIVER_NAME ,
} ,
} ;
module_platform_driver ( gpio_exar_driver ) ;
MODULE_ALIAS ( " platform: " DRIVER_NAME ) ;
MODULE_DESCRIPTION ( " Exar GPIO driver " ) ;
MODULE_AUTHOR ( " Sudip Mukherjee <sudip.mukherjee@codethink.co.uk> " ) ;
MODULE_LICENSE ( " GPL " ) ;