2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-01-19 22:23:20 +00:00
/*
* GPIO driver for Exar XR17V35X chip
*
* Copyright ( C ) 2015 Sudip Mukherjee < sudip . mukherjee @ codethink . co . uk >
*/
2020-09-19 15:32:15 +02:00
2017-01-19 22:23:20 +00:00
# include <linux/bitops.h>
# include <linux/device.h>
# include <linux/gpio/driver.h>
2020-09-19 15:36:56 +02:00
# include <linux/idr.h>
2017-01-19 22:23:20 +00: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 17:00:26 +02:00
# include <linux/regmap.h>
2017-01-19 22:23:20 +00: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
2022-09-02 16:14:34 +10:00
/*
* The Device Configuration and UART Configuration Registers
* for each UART channel take 1 KB of memory address space .
*/
# define EXAR_UART_CHANNEL_SIZE 0x400
2017-01-19 22:23:20 +00:00
# define DRIVER_NAME "gpio_exar"
static DEFINE_IDA ( ida_index ) ;
struct exar_gpio_chip {
struct gpio_chip gpio_chip ;
2020-09-28 17:00:26 +02:00
struct regmap * regmap ;
2017-01-19 22:23:20 +00:00
int index ;
char name [ 20 ] ;
2017-05-22 12:43:18 +02:00
unsigned int first_pin ;
2022-09-02 16:14:34 +10:00
/*
* The offset to the cascaded device ' s ( if existing )
* Device Configuration Registers .
*/
unsigned int cascaded_offset ;
2017-01-19 22:23:20 +00:00
} ;
2020-09-30 10:20:10 +02:00
static unsigned int
exar_offset_to_sel_addr ( struct exar_gpio_chip * exar_gpio , unsigned int offset )
{
2022-09-02 16:14:34 +10:00
unsigned int pin = exar_gpio - > first_pin + ( offset % 16 ) ;
unsigned int cascaded = offset / 16 ;
unsigned int addr = pin / 8 ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO ;
return addr + ( cascaded ? exar_gpio - > cascaded_offset : 0 ) ;
2020-09-30 10:20:10 +02:00
}
static unsigned int
exar_offset_to_lvl_addr ( struct exar_gpio_chip * exar_gpio , unsigned int offset )
{
2022-09-02 16:14:34 +10:00
unsigned int pin = exar_gpio - > first_pin + ( offset % 16 ) ;
unsigned int cascaded = offset / 16 ;
unsigned int addr = pin / 8 ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO ;
return addr + ( cascaded ? exar_gpio - > cascaded_offset : 0 ) ;
2020-09-30 10:20:10 +02:00
}
static unsigned int
exar_offset_to_bit ( struct exar_gpio_chip * exar_gpio , unsigned int offset )
{
2022-09-02 16:14:34 +10:00
unsigned int pin = exar_gpio - > first_pin + ( offset % 16 ) ;
return pin % 8 ;
2020-09-30 10:20:10 +02:00
}
2017-01-19 22:23:20 +00:00
static int exar_get_direction ( struct gpio_chip * chip , unsigned int offset )
{
2017-05-22 12:43:18 +02:00
struct exar_gpio_chip * exar_gpio = gpiochip_get_data ( chip ) ;
2020-09-30 10:20:10 +02:00
unsigned int addr = exar_offset_to_sel_addr ( exar_gpio , offset ) ;
unsigned int bit = exar_offset_to_bit ( exar_gpio , offset ) ;
2017-01-19 22:23:20 +00:00
2020-09-28 17:00:26 +02:00
if ( regmap_test_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) )
2019-11-06 10:54:12 +02:00
return GPIO_LINE_DIRECTION_IN ;
return GPIO_LINE_DIRECTION_OUT ;
2017-01-19 22:23:20 +00:00
}
static int exar_get_value ( struct gpio_chip * chip , unsigned int offset )
{
2017-05-22 12:43:18 +02:00
struct exar_gpio_chip * exar_gpio = gpiochip_get_data ( chip ) ;
2020-09-30 10:20:10 +02:00
unsigned int addr = exar_offset_to_lvl_addr ( exar_gpio , offset ) ;
unsigned int bit = exar_offset_to_bit ( exar_gpio , offset ) ;
2017-01-19 22:23:20 +00:00
2020-09-28 17:00:26 +02:00
return ! ! ( regmap_test_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) ) ;
2017-01-19 22:23:20 +00:00
}
static void exar_set_value ( struct gpio_chip * chip , unsigned int offset ,
int value )
{
2017-05-22 12:43:18 +02:00
struct exar_gpio_chip * exar_gpio = gpiochip_get_data ( chip ) ;
2020-09-30 10:20:10 +02:00
unsigned int addr = exar_offset_to_lvl_addr ( exar_gpio , offset ) ;
unsigned int bit = exar_offset_to_bit ( exar_gpio , offset ) ;
2017-01-19 22:23:20 +00:00
2020-09-28 17:00:26 +02:00
if ( value )
regmap_set_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) ;
else
regmap_clear_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) ;
2017-01-19 22:23:20 +00:00
}
2017-02-26 22:36:58 +08:00
static int exar_direction_output ( struct gpio_chip * chip , unsigned int offset ,
int value )
{
2020-09-28 17:00:26 +02: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 22:36:58 +08:00
exar_set_value ( chip , offset , value ) ;
2020-09-28 17:00:26 +02:00
regmap_clear_bits ( exar_gpio - > regmap , addr , BIT ( bit ) ) ;
return 0 ;
2017-02-26 22:36:58 +08:00
}
static int exar_direction_input ( struct gpio_chip * chip , unsigned int offset )
{
2020-09-28 17:00:26 +02: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 22:36:58 +08:00
}
2020-10-20 09:24:04 +02: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 17:00:26 +02:00
static const struct regmap_config exar_regmap_config = {
. name = " exar-gpio " ,
. reg_bits = 16 ,
. val_bits = 8 ,
} ;
2017-01-19 22:23:20 +00:00
static int gpio_exar_probe ( struct platform_device * pdev )
{
2020-09-28 16:34:37 +02:00
struct device * dev = & pdev - > dev ;
struct pci_dev * pcidev = to_pci_dev ( dev - > parent ) ;
2017-01-19 22:23:20 +00:00
struct exar_gpio_chip * exar_gpio ;
2017-05-22 12:43:18 +02:00
u32 first_pin , ngpios ;
2017-01-19 22:23:20 +00:00
void __iomem * p ;
int index , ret ;
/*
2017-05-02 08:42:40 +02:00
* The UART driver must have mapped region 0 prior to registering this
* device - use it .
2017-01-19 22:23:20 +00:00
*/
2017-05-02 08:42:40 +02:00
p = pcim_iomap_table ( pcidev ) [ 0 ] ;
2017-01-19 22:23:20 +00:00
if ( ! p )
return - ENOMEM ;
2020-09-28 16:34:37 +02:00
ret = device_property_read_u32 ( dev , " exar,first-pin " , & first_pin ) ;
2017-05-22 12:43:18 +02:00
if ( ret )
return ret ;
2020-09-28 16:34:37 +02:00
ret = device_property_read_u32 ( dev , " ngpios " , & ngpios ) ;
2017-05-22 12:43:18 +02:00
if ( ret )
return ret ;
2020-09-28 16:34:37 +02:00
exar_gpio = devm_kzalloc ( dev , sizeof ( * exar_gpio ) , GFP_KERNEL ) ;
2017-01-19 22:23:20 +00:00
if ( ! exar_gpio )
return - ENOMEM ;
2022-09-02 16:14:34 +10:00
/*
* If cascaded , secondary xr17v354 or xr17v358 have the same amount
* of MPIOs as their primaries and the last 4 bits of the primary ' s
* PCI Device ID is the number of its UART channels .
*/
if ( pcidev - > device & GENMASK ( 15 , 12 ) ) {
ngpios + = ngpios ;
exar_gpio - > cascaded_offset = ( pcidev - > device & GENMASK ( 3 , 0 ) ) *
EXAR_UART_CHANNEL_SIZE ;
}
2020-09-28 17:00:26 +02: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-19 22:23:20 +00:00
2020-09-28 14:35:51 +02:00
index = ida_alloc ( & ida_index , GFP_KERNEL ) ;
2020-09-28 17:00:26 +02:00
if ( index < 0 )
return index ;
2017-01-19 22:23:20 +00:00
2020-10-20 09:24:04 +02:00
ret = devm_add_action_or_reset ( dev , exar_devm_ida_free , exar_gpio ) ;
if ( ret )
return ret ;
2017-01-19 22:23:20 +00:00
sprintf ( exar_gpio - > name , " exar_gpio%d " , index ) ;
exar_gpio - > gpio_chip . label = exar_gpio - > name ;
2020-09-28 16:34:37 +02:00
exar_gpio - > gpio_chip . parent = dev ;
2017-01-19 22:23:20 +00: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 12:43:18 +02:00
exar_gpio - > gpio_chip . ngpio = ngpios ;
2017-01-19 22:23:20 +00:00
exar_gpio - > index = index ;
2017-05-22 12:43:18 +02:00
exar_gpio - > first_pin = first_pin ;
2017-01-19 22:23:20 +00:00
2020-09-28 16:34:37 +02:00
ret = devm_gpiochip_add_data ( dev , & exar_gpio - > gpio_chip , exar_gpio ) ;
2017-01-19 22:23:20 +00:00
if ( ret )
2020-10-20 09:24:04 +02:00
return ret ;
2017-01-19 22:23:20 +00: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 " ) ;