2019-08-12 19:11:17 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Turris Mox Moxtet GPIO expander
*
2021-04-09 23:27:04 +03:00
* Copyright ( C ) 2018 Marek Behún < kabel @ kernel . org >
2019-08-12 19:11:17 +03:00
*/
# include <linux/bitops.h>
# include <linux/gpio/driver.h>
# include <linux/moxtet.h>
# include <linux/module.h>
# define MOXTET_GPIO_NGPIOS 12
# define MOXTET_GPIO_INPUTS 4
struct moxtet_gpio_desc {
u16 in_mask ;
u16 out_mask ;
} ;
static const struct moxtet_gpio_desc descs [ ] = {
[ TURRIS_MOX_MODULE_SFP ] = {
. in_mask = GENMASK ( 2 , 0 ) ,
. out_mask = GENMASK ( 5 , 4 ) ,
} ,
} ;
struct moxtet_gpio_chip {
struct device * dev ;
struct gpio_chip gpio_chip ;
const struct moxtet_gpio_desc * desc ;
} ;
static int moxtet_gpio_get_value ( struct gpio_chip * gc , unsigned int offset )
{
struct moxtet_gpio_chip * chip = gpiochip_get_data ( gc ) ;
int ret ;
if ( chip - > desc - > in_mask & BIT ( offset ) ) {
ret = moxtet_device_read ( chip - > dev ) ;
} else if ( chip - > desc - > out_mask & BIT ( offset ) ) {
ret = moxtet_device_written ( chip - > dev ) ;
if ( ret > = 0 )
ret < < = MOXTET_GPIO_INPUTS ;
} else {
return - EINVAL ;
}
if ( ret < 0 )
return ret ;
return ! ! ( ret & BIT ( offset ) ) ;
}
static void moxtet_gpio_set_value ( struct gpio_chip * gc , unsigned int offset ,
int val )
{
struct moxtet_gpio_chip * chip = gpiochip_get_data ( gc ) ;
int state ;
state = moxtet_device_written ( chip - > dev ) ;
if ( state < 0 )
return ;
offset - = MOXTET_GPIO_INPUTS ;
if ( val )
state | = BIT ( offset ) ;
else
state & = ~ BIT ( offset ) ;
moxtet_device_write ( chip - > dev , state ) ;
}
static int moxtet_gpio_get_direction ( struct gpio_chip * gc , unsigned int offset )
{
struct moxtet_gpio_chip * chip = gpiochip_get_data ( gc ) ;
/* All lines are hard wired to be either input or output, not both. */
if ( chip - > desc - > in_mask & BIT ( offset ) )
2019-11-06 11:54:12 +03:00
return GPIO_LINE_DIRECTION_IN ;
2019-08-12 19:11:17 +03:00
else if ( chip - > desc - > out_mask & BIT ( offset ) )
2019-11-06 11:54:12 +03:00
return GPIO_LINE_DIRECTION_OUT ;
2019-08-12 19:11:17 +03:00
else
return - EINVAL ;
}
static int moxtet_gpio_direction_input ( struct gpio_chip * gc ,
unsigned int offset )
{
struct moxtet_gpio_chip * chip = gpiochip_get_data ( gc ) ;
if ( chip - > desc - > in_mask & BIT ( offset ) )
return 0 ;
else if ( chip - > desc - > out_mask & BIT ( offset ) )
return - ENOTSUPP ;
else
return - EINVAL ;
}
static int moxtet_gpio_direction_output ( struct gpio_chip * gc ,
unsigned int offset , int val )
{
struct moxtet_gpio_chip * chip = gpiochip_get_data ( gc ) ;
if ( chip - > desc - > out_mask & BIT ( offset ) )
moxtet_gpio_set_value ( gc , offset , val ) ;
else if ( chip - > desc - > in_mask & BIT ( offset ) )
return - ENOTSUPP ;
else
return - EINVAL ;
return 0 ;
}
static int moxtet_gpio_probe ( struct device * dev )
{
struct moxtet_gpio_chip * chip ;
struct device_node * nc = dev - > of_node ;
int id ;
id = to_moxtet_device ( dev ) - > id ;
if ( id > = ARRAY_SIZE ( descs ) ) {
dev_err ( dev , " %pOF Moxtet device id 0x%x is not supported by gpio-moxtet driver \n " ,
nc , id ) ;
return - ENOTSUPP ;
}
chip = devm_kzalloc ( dev , sizeof ( * chip ) , GFP_KERNEL ) ;
if ( ! chip )
return - ENOMEM ;
chip - > dev = dev ;
chip - > gpio_chip . parent = dev ;
chip - > desc = & descs [ id ] ;
dev_set_drvdata ( dev , chip ) ;
chip - > gpio_chip . label = dev_name ( dev ) ;
chip - > gpio_chip . get_direction = moxtet_gpio_get_direction ;
chip - > gpio_chip . direction_input = moxtet_gpio_direction_input ;
chip - > gpio_chip . direction_output = moxtet_gpio_direction_output ;
chip - > gpio_chip . get = moxtet_gpio_get_value ;
chip - > gpio_chip . set = moxtet_gpio_set_value ;
chip - > gpio_chip . base = - 1 ;
chip - > gpio_chip . ngpio = MOXTET_GPIO_NGPIOS ;
chip - > gpio_chip . can_sleep = true ;
chip - > gpio_chip . owner = THIS_MODULE ;
return devm_gpiochip_add_data ( dev , & chip - > gpio_chip , chip ) ;
}
static const struct of_device_id moxtet_gpio_dt_ids [ ] = {
{ . compatible = " cznic,moxtet-gpio " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , moxtet_gpio_dt_ids ) ;
static const enum turris_mox_module_id moxtet_gpio_module_table [ ] = {
TURRIS_MOX_MODULE_SFP ,
0 ,
} ;
static struct moxtet_driver moxtet_gpio_driver = {
. driver = {
. name = " moxtet-gpio " ,
. of_match_table = moxtet_gpio_dt_ids ,
. probe = moxtet_gpio_probe ,
} ,
. id_table = moxtet_gpio_module_table ,
} ;
module_moxtet_driver ( moxtet_gpio_driver ) ;
2021-04-09 23:27:04 +03:00
MODULE_AUTHOR ( " Marek Behun <kabel@kernel.org> " ) ;
2019-08-12 19:11:17 +03:00
MODULE_DESCRIPTION ( " Turris Mox Moxtet GPIO expander " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;