2020-07-09 16:48:29 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for PCA9570 I2C GPO expander
*
* Copyright ( C ) 2020 Sungbo Eo < mans0n @ gorani . run >
*
* Based on gpio - tpic2810 . c
* Copyright ( C ) 2015 Texas Instruments Incorporated - http : //www.ti.com/
* Andrew F . Davis < afd @ ti . com >
*/
# include <linux/gpio/driver.h>
# include <linux/i2c.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/property.h>
2022-10-26 18:15:43 +03:00
# define SLG7XL45106_GPO_REG 0xDB
2022-10-26 18:15:42 +03:00
/**
2022-12-30 13:00:44 +03:00
* struct pca9570_chip_data - GPIO platformdata
2022-10-26 18:15:42 +03:00
* @ ngpio : no of gpios
* @ command : Command to be sent
*/
2022-12-30 13:00:44 +03:00
struct pca9570_chip_data {
2022-10-26 18:15:42 +03:00
u16 ngpio ;
u32 command ;
} ;
2020-07-09 16:48:29 +03:00
/**
* struct pca9570 - GPIO driver data
* @ chip : GPIO controller chip
2023-08-17 22:44:49 +03:00
* @ chip_data : GPIO controller platform data
2020-07-09 16:48:29 +03:00
* @ lock : Protects write sequences
* @ out : Buffer for device register
*/
struct pca9570 {
struct gpio_chip chip ;
2022-12-30 13:00:44 +03:00
const struct pca9570_chip_data * chip_data ;
2020-07-09 16:48:29 +03:00
struct mutex lock ;
u8 out ;
} ;
static int pca9570_read ( struct pca9570 * gpio , u8 * value )
{
struct i2c_client * client = to_i2c_client ( gpio - > chip . parent ) ;
int ret ;
2022-12-30 13:00:44 +03:00
if ( gpio - > chip_data - > command ! = 0 )
ret = i2c_smbus_read_byte_data ( client , gpio - > chip_data - > command ) ;
2022-10-26 18:15:43 +03:00
else
ret = i2c_smbus_read_byte ( client ) ;
2020-07-09 16:48:29 +03:00
if ( ret < 0 )
return ret ;
* value = ret ;
return 0 ;
}
static int pca9570_write ( struct pca9570 * gpio , u8 value )
{
struct i2c_client * client = to_i2c_client ( gpio - > chip . parent ) ;
2022-12-30 13:00:44 +03:00
if ( gpio - > chip_data - > command ! = 0 )
return i2c_smbus_write_byte_data ( client , gpio - > chip_data - > command , value ) ;
2022-10-26 18:15:43 +03:00
2020-07-09 16:48:29 +03:00
return i2c_smbus_write_byte ( client , value ) ;
}
static int pca9570_get_direction ( struct gpio_chip * chip ,
unsigned offset )
{
/* This device always output */
return GPIO_LINE_DIRECTION_OUT ;
}
static int pca9570_get ( struct gpio_chip * chip , unsigned offset )
{
struct pca9570 * gpio = gpiochip_get_data ( chip ) ;
u8 buffer ;
int ret ;
ret = pca9570_read ( gpio , & buffer ) ;
if ( ret )
return ret ;
return ! ! ( buffer & BIT ( offset ) ) ;
}
static void pca9570_set ( struct gpio_chip * chip , unsigned offset , int value )
{
struct pca9570 * gpio = gpiochip_get_data ( chip ) ;
u8 buffer ;
int ret ;
mutex_lock ( & gpio - > lock ) ;
buffer = gpio - > out ;
if ( value )
buffer | = BIT ( offset ) ;
else
buffer & = ~ BIT ( offset ) ;
ret = pca9570_write ( gpio , buffer ) ;
if ( ret )
goto out ;
gpio - > out = buffer ;
out :
mutex_unlock ( & gpio - > lock ) ;
}
static int pca9570_probe ( struct i2c_client * client )
{
struct pca9570 * gpio ;
gpio = devm_kzalloc ( & client - > dev , sizeof ( * gpio ) , GFP_KERNEL ) ;
if ( ! gpio )
return - ENOMEM ;
gpio - > chip . label = client - > name ;
gpio - > chip . parent = & client - > dev ;
gpio - > chip . owner = THIS_MODULE ;
gpio - > chip . get_direction = pca9570_get_direction ;
gpio - > chip . get = pca9570_get ;
gpio - > chip . set = pca9570_set ;
gpio - > chip . base = - 1 ;
2022-12-30 13:00:44 +03:00
gpio - > chip_data = device_get_match_data ( & client - > dev ) ;
gpio - > chip . ngpio = gpio - > chip_data - > ngpio ;
2020-07-09 16:48:29 +03:00
gpio - > chip . can_sleep = true ;
mutex_init ( & gpio - > lock ) ;
/* Read the current output level */
pca9570_read ( gpio , & gpio - > out ) ;
i2c_set_clientdata ( client , gpio ) ;
return devm_gpiochip_add_data ( & client - > dev , & gpio - > chip , gpio ) ;
}
2022-12-30 13:00:44 +03:00
static const struct pca9570_chip_data pca9570_gpio = {
2022-10-26 18:15:42 +03:00
. ngpio = 4 ,
} ;
2022-12-30 13:00:44 +03:00
static const struct pca9570_chip_data pca9571_gpio = {
2022-10-26 18:15:42 +03:00
. ngpio = 8 ,
} ;
2022-12-30 13:00:44 +03:00
static const struct pca9570_chip_data slg7xl45106_gpio = {
2022-10-26 18:15:43 +03:00
. ngpio = 8 ,
. command = SLG7XL45106_GPO_REG ,
} ;
2020-07-09 16:48:29 +03:00
static const struct i2c_device_id pca9570_id_table [ ] = {
2022-10-26 18:15:42 +03:00
{ " pca9570 " , ( kernel_ulong_t ) & pca9570_gpio } ,
{ " pca9571 " , ( kernel_ulong_t ) & pca9571_gpio } ,
2022-10-26 18:15:43 +03:00
{ " slg7xl45106 " , ( kernel_ulong_t ) & slg7xl45106_gpio } ,
2020-07-09 16:48:29 +03:00
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( i2c , pca9570_id_table ) ;
static const struct of_device_id pca9570_of_match_table [ ] = {
2022-10-26 18:15:43 +03:00
{ . compatible = " dlg,slg7xl45106 " , . data = & slg7xl45106_gpio } ,
2022-10-26 18:15:42 +03:00
{ . compatible = " nxp,pca9570 " , . data = & pca9570_gpio } ,
{ . compatible = " nxp,pca9571 " , . data = & pca9571_gpio } ,
2020-07-09 16:48:29 +03:00
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , pca9570_of_match_table ) ;
static struct i2c_driver pca9570_driver = {
. driver = {
. name = " pca9570 " ,
. of_match_table = pca9570_of_match_table ,
} ,
2023-05-20 20:47:35 +03:00
. probe = pca9570_probe ,
2020-07-09 16:48:29 +03:00
. id_table = pca9570_id_table ,
} ;
module_i2c_driver ( pca9570_driver ) ;
MODULE_AUTHOR ( " Sungbo Eo <mans0n@gorani.run> " ) ;
MODULE_DESCRIPTION ( " GPIO expander driver for PCA9570 " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;