@ -1,10 +1,16 @@
@@ -1,10 +1,16 @@
/**
* Copyright ( c ) 2022 Ithinx GmbH
* Copyright ( c )
* 2022 Ithinx GmbH
* 2023 Amrith Venkat Kesavamoorthi < amrith @ mr - beam . org >
* 2023 Mr Beam Lasers GmbH .
*
* SPDX - License - Identifier : Apache - 2.0
*
* @ see https : //www.nxp.com/docs/en/data-sheet/PCF8575.pdf
* @ see https : //www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf
*/
# define DT_DRV_COMPAT nxp_pcf8574
# define DT_DRV_COMPAT nxp_pcf857x
# include <zephyr/drivers/gpio/gpio_utils.h>
@ -12,28 +18,31 @@
@@ -12,28 +18,31 @@
# include <zephyr/drivers/i2c.h>
# include <zephyr/kernel.h>
# include <zephyr/logging/log.h>
LOG_MODULE_REGISTER ( pcf8574 , CONFIG_GPIO_LOG_LEVEL ) ;
# include <zephyr/sys/byteorder.h>
LOG_MODULE_REGISTER ( pcf857x , CONFIG_GPIO_LOG_LEVEL ) ;
struct pcf8574_pins_cfg {
uint8 _t configured_as_outputs ; /* 0 for input, 1 for output */
uint8 _t outputs_state ;
struct pcf857x _pins_cfg {
uint16 _t configured_as_outputs ; /* 0 for input, 1 for output */
uint16 _t outputs_state ;
} ;
/** Runtime driver data of the pcf8574 */
struct pcf8574 _drv_data {
/** Runtime driver data of the pcf857x */
struct pcf857x _drv_data {
/* gpio_driver_data needs to be first */
struct gpio_driver_data common ;
struct pcf8574 _pins_cfg pins_cfg ;
struct pcf857x _pins_cfg pins_cfg ;
sys_slist_t callbacks ;
struct k_sem lock ;
struct k_work work ;
const struct device * dev ;
struct gpio_callback int_gpio_cb ;
uint8_t input_port_last ;
uint16_t input_port_last ;
int num_bytes ;
} ;
/** Configuration data*/
struct pcf8574 _drv_cfg {
struct pcf857x _drv_cfg {
/* gpio_driver_config needs to be first */
struct gpio_driver_config common ;
struct i2c_dt_spec i2c ;
@ -41,52 +50,53 @@ struct pcf8574_drv_cfg {
@@ -41,52 +50,53 @@ struct pcf8574_drv_cfg {
} ;
/**
* @ brief Reads the value of the pins from pcf8574 respectively from a connected device .
* @ brief Reads the value of the pins from pcf857x respectively from a connected device .
*
* @ param dev Pointer to the device structure of the driver instance .
* @ param value Pointer to the input value . It contains the received Byte .
* @ param value Pointer to the input value . It contains the received Bytes ( receives 2 Bytes for P0
* and P1 ) .
*
* @ retval 0 If successful .
* @ retval Negative value for error code .
*/
static int pcf8574 _process_input ( const struct device * dev , gpio_port_value_t * value )
static int pcf857x _process_input ( const struct device * dev , gpio_port_value_t * value )
{
const struct pcf8574 _drv_cfg * drv_cfg = dev - > config ;
struct pcf8574 _drv_data * drv_data = dev - > data ;
const struct pcf857x _drv_cfg * drv_cfg = dev - > config ;
struct pcf857x _drv_data * drv_data = dev - > data ;
int rc = 0 ;
uint8_t rx_buf ;
uint8_t rx_buf [ 2 ] = { 0 } ;
rc = i2c_read_dt ( & drv_cfg - > i2c , & rx_buf , sizeof ( rx_buf ) ) ;
rc = i2c_read_dt ( & drv_cfg - > i2c , rx_buf , drv_data - > num_bytes ) ;
if ( rc ! = 0 ) {
LOG_ERR ( " %s: failed to read from device: %d " , dev - > name , rc ) ;
return - EIO ;
}
if ( value ) {
* value = rx_buf ;
* value = sys_get_le16 ( rx_buf ) ; /*format P17-P10..P07-P00 (bit15-bit8..bit7-bit0)*/
}
drv_data - > input_port_last = rx_buf ;
drv_data - > input_port_last = sys_get_le16 ( rx_buf ) ;
return rc ;
}
/** Register the read-task as work*/
static void pcf8574 _work_handler ( struct k_work * work )
static void pcf857x _work_handler ( struct k_work * work )
{
struct pcf8574 _drv_data * drv_data = CONTAINER_OF ( work , struct pcf8574 _drv_data , work ) ;
struct pcf857x _drv_data * drv_data = CONTAINER_OF ( work , struct pcf857x _drv_data , work ) ;
k_sem_take ( & drv_data - > lock , K_FOREVER ) ;
uint32_t changed_pins ;
uint8 _t input_port_last_temp = drv_data - > input_port_last ;
int rc = pcf8574 _process_input ( drv_data - > dev , & changed_pins ) ;
uint16 _t input_port_last_temp = drv_data - > input_port_last ;
int rc = pcf857x _process_input ( drv_data - > dev , & changed_pins ) ;
if ( rc ) {
LOG_ERR ( " Failed to read interrupt sources: %d " , rc ) ;
}
k_sem_give ( & drv_data - > lock ) ;
if ( input_port_last_temp ! = ( uint8 _t ) changed_pins & & ! rc ) {
if ( input_port_last_temp ! = ( uint16 _t ) changed_pins & & ! rc ) {
/** Find changed bits*/
changed_pins ^ = input_port_last_temp ;
@ -94,15 +104,15 @@ static void pcf8574_work_handler(struct k_work *work)
@@ -94,15 +104,15 @@ static void pcf8574_work_handler(struct k_work *work)
}
}
/** Callback for interrupt through some level changes on pcf8574 pins*/
static void pcf8574 _int_gpio_handler ( const struct device * dev , struct gpio_callback * gpio_cb ,
/** Callback for interrupt through some level changes on pcf857x pins*/
static void pcf857x _int_gpio_handler ( const struct device * dev , struct gpio_callback * gpio_cb ,
uint32_t pins )
{
ARG_UNUSED ( dev ) ;
ARG_UNUSED ( pins ) ;
struct pcf8574 _drv_data * drv_data =
CONTAINER_OF ( gpio_cb , struct pcf8574 _drv_data , int_gpio_cb ) ;
struct pcf857x _drv_data * drv_data =
CONTAINER_OF ( gpio_cb , struct pcf857x _drv_data , int_gpio_cb ) ;
k_work_submit ( & drv_data - > work ) ;
}
@ -116,16 +126,16 @@ static void pcf8574_int_gpio_handler(const struct device *dev, struct gpio_callb
@@ -116,16 +126,16 @@ static void pcf8574_int_gpio_handler(const struct device *dev, struct gpio_callb
* @ retval 0 If successful .
* @ retval Negative value for error code .
*/
static int pcf8574 _port_get_raw ( const struct device * dev , gpio_port_value_t * value )
static int pcf857x _port_get_raw ( const struct device * dev , gpio_port_value_t * value )
{
struct pcf8574 _drv_data * drv_data = dev - > data ;
struct pcf857x _drv_data * drv_data = dev - > data ;
int rc ;
if ( k_is_in_isr ( ) ) {
return - EWOULDBLOCK ;
}
if ( ( ~ drv_data - > pins_cfg . configured_as_outputs & ( uint8 _t ) * value ) ! = ( uint8 _t ) * value ) {
if ( ( ~ drv_data - > pins_cfg . configured_as_outputs & ( uint16 _t ) * value ) ! = ( uint16 _t ) * value ) {
LOG_ERR ( " Pin(s) is/are configured as output which should be input. " ) ;
return - EOPNOTSUPP ;
}
@ -136,7 +146,7 @@ static int pcf8574_port_get_raw(const struct device *dev, gpio_port_value_t *val
@@ -136,7 +146,7 @@ static int pcf8574_port_get_raw(const struct device *dev, gpio_port_value_t *val
* Reading of the input port also clears the generated interrupt ,
* thus the configured callbacks must be fired also here if needed .
*/
rc = pcf8574 _process_input ( dev , value ) ;
rc = pcf857x _process_input ( dev , value ) ;
k_sem_give ( & drv_data - > lock ) ;
@ -148,19 +158,20 @@ static int pcf8574_port_get_raw(const struct device *dev, gpio_port_value_t *val
@@ -148,19 +158,20 @@ static int pcf8574_port_get_raw(const struct device *dev, gpio_port_value_t *val
*
* @ param dev A pointer to the device structure
* @ param mask A mask of bits to set some bits to LOW or HIGH
* @ param value The value which is written via i2c to the pfc8574 ' s output pins
* @ param value The value which is written via i2c to the pcf857x ' s output pins
* @ param toggle A way to toggle some bits with xor
*
* @ retval 0 If successful .
* @ retval Negative value for error code .
*/
static int pcf8574 _port_set_raw ( const struct device * dev , uint8_t mask , uint8 _t value ,
uint8 _t toggle )
static int pcf857x _port_set_raw ( const struct device * dev , uint16_t mask , uint16 _t value ,
uint16 _t toggle )
{
const struct pcf8574 _drv_cfg * drv_cfg = dev - > config ;
struct pcf8574 _drv_data * drv_data = dev - > data ;
const struct pcf857x _drv_cfg * drv_cfg = dev - > config ;
struct pcf857x _drv_data * drv_data = dev - > data ;
int rc = 0 ;
uint8_t tx_buf ;
uint16_t tx_buf ;
uint8_t tx_buf_p [ 2 ] ;
if ( k_is_in_isr ( ) ) {
return - EWOULDBLOCK ;
@ -174,14 +185,13 @@ static int pcf8574_port_set_raw(const struct device *dev, uint8_t mask, uint8_t
@@ -174,14 +185,13 @@ static int pcf8574_port_set_raw(const struct device *dev, uint8_t mask, uint8_t
tx_buf = ( drv_data - > pins_cfg . outputs_state & ~ mask ) ;
tx_buf | = ( value & mask ) ;
tx_buf ^ = toggle ;
sys_put_le16 ( tx_buf , tx_buf_p ) ;
rc = i2c_write_dt ( & drv_cfg - > i2c , & tx_buf , sizeof ( tx_buf ) ) ;
rc = i2c_write_dt ( & drv_cfg - > i2c , tx_buf_p , drv_data - > num_bytes ) ;
if ( rc ! = 0 ) {
LOG_ERR ( " %s: failed to write output port: %d " , dev - > name , rc ) ;
return - EIO ;
}
k_sem_take ( & drv_data - > lock , K_FOREVER ) ;
drv_data - > pins_cfg . outputs_state = tx_buf ;
k_sem_give ( & drv_data - > lock ) ;
@ -190,9 +200,9 @@ static int pcf8574_port_set_raw(const struct device *dev, uint8_t mask, uint8_t
@@ -190,9 +200,9 @@ static int pcf8574_port_set_raw(const struct device *dev, uint8_t mask, uint8_t
}
/**
* @ brief This function fills a dummy because the pfc8574 has no pins to configure .
* @ brief This function fills a dummy because the pcf857x has no pins to configure .
* You can use it to set some pins permanent to HIGH or LOW until reset . It uses the port_set_raw
* function to set the pins of pcf8574 directly .
* function to set the pins of pcf857x directly .
*
* @ param dev Pointer to the device structure for the driver instance .
* @ param pin The bit in the io register which is set to high
@ -201,12 +211,12 @@ static int pcf8574_port_set_raw(const struct device *dev, uint8_t mask, uint8_t
@@ -201,12 +211,12 @@ static int pcf8574_port_set_raw(const struct device *dev, uint8_t mask, uint8_t
* @ retval 0 If successful .
* @ retval Negative value for error .
*/
static int pcf8574 _pin_configure ( const struct device * dev , gpio_pin_t pin , gpio_flags_t flags )
static int pcf857x _pin_configure ( const struct device * dev , gpio_pin_t pin , gpio_flags_t flags )
{
struct pcf8574 _drv_data * drv_data = dev - > data ;
struct pcf857x _drv_data * drv_data = dev - > data ;
int ret = 0 ;
uint8 _t temp_pins = drv_data - > pins_cfg . outputs_state ;
uint8 _t temp_outputs = drv_data - > pins_cfg . configured_as_outputs ;
uint16 _t temp_pins = drv_data - > pins_cfg . outputs_state ;
uint16 _t temp_outputs = drv_data - > pins_cfg . configured_as_outputs ;
if ( flags & ( GPIO_PULL_UP | GPIO_PULL_DOWN | GPIO_DISCONNECTED | GPIO_SINGLE_ENDED ) ) {
return - ENOTSUP ;
@ -225,7 +235,7 @@ static int pcf8574_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_
@@ -225,7 +235,7 @@ static int pcf8574_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_
temp_pins & = ~ ( 1 < < pin ) ;
}
ret = pcf8574 _port_set_raw ( dev , drv_data - > pins_cfg . configured_as_outputs , temp_pins , 0 ) ;
ret = pcf857x _port_set_raw ( dev , drv_data - > pins_cfg . configured_as_outputs , temp_pins , 0 ) ;
if ( ret = = 0 ) {
k_sem_take ( & drv_data - > lock , K_FOREVER ) ;
@ -238,7 +248,7 @@ static int pcf8574_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_
@@ -238,7 +248,7 @@ static int pcf8574_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_
}
/**
* @ brief Sets a value to the pins of pcf8574
* @ brief Sets a value to the pins of pcf857x
*
* @ param dev Pointer to the device structure for the driver instance .
* @ param mask The bit mask which bits should be set
@ -247,24 +257,24 @@ static int pcf8574_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_
@@ -247,24 +257,24 @@ static int pcf8574_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_
* @ retval 0 If successful .
* @ retval Negative value for error .
*/
static int pcf8574 _port_set_masked_raw ( const struct device * dev , gpio_port_pins_t mask ,
static int pcf857x _port_set_masked_raw ( const struct device * dev , gpio_port_pins_t mask ,
gpio_port_value_t value )
{
return pcf8574 _port_set_raw ( dev , ( uint8 _t ) mask , ( uint8 _t ) value , 0 ) ;
return pcf857x _port_set_raw ( dev , ( uint16 _t ) mask , ( uint16 _t ) value , 0 ) ;
}
/**
* @ brief Sets some output pins of the pcf8574
* @ brief Sets some output pins of the pcf857x
*
* @ param dev Pointer to the device structure for the driver instance .
* @ param pins The pin ( s ) which will be set in a range from 0 to 7
* @ param pins The pin ( s ) which will be set in a range from P17 - P10 . . P07 - P00
*
* @ retval 0 If successful .
* @ retval Negative value for error .
*/
static int pcf8574 _port_set_bits_raw ( const struct device * dev , gpio_port_pins_t pins )
static int pcf857x _port_set_bits_raw ( const struct device * dev , gpio_port_pins_t pins )
{
return pcf8574 _port_set_raw ( dev , ( uint8 _t ) pins , ( uint8 _t ) pins , 0 ) ;
return pcf857x _port_set_raw ( dev , ( uint16 _t ) pins , ( uint16 _t ) pins , 0 ) ;
}
/**
@ -276,9 +286,9 @@ static int pcf8574_port_set_bits_raw(const struct device *dev, gpio_port_pins_t
@@ -276,9 +286,9 @@ static int pcf8574_port_set_bits_raw(const struct device *dev, gpio_port_pins_t
* @ retval 0 If successful .
* @ retval Negative value for error .
*/
static int pcf8574 _port_clear_bits_raw ( const struct device * dev , gpio_port_pins_t pins )
static int pcf857x _port_clear_bits_raw ( const struct device * dev , gpio_port_pins_t pins )
{
return pcf8574 _port_set_raw ( dev , ( uint8 _t ) pins , 0 , 0 ) ;
return pcf857x _port_set_raw ( dev , ( uint16 _t ) pins , 0 , 0 ) ;
}
/**
@ -290,16 +300,16 @@ static int pcf8574_port_clear_bits_raw(const struct device *dev, gpio_port_pins_
@@ -290,16 +300,16 @@ static int pcf8574_port_clear_bits_raw(const struct device *dev, gpio_port_pins_
* @ retval 0 If successful .
* @ retval Negative value for error .
*/
static int pcf8574 _port_toggle_bits ( const struct device * dev , gpio_port_pins_t pins )
static int pcf857x _port_toggle_bits ( const struct device * dev , gpio_port_pins_t pins )
{
return pcf8574 _port_set_raw ( dev , 0 , 0 , ( uint8 _t ) pins ) ;
return pcf857x _port_set_raw ( dev , 0 , 0 , ( uint16 _t ) pins ) ;
}
/* Each pin gives an interrupt at pcf8574 . In this function the configuration is checked. */
static int pcf8574 _pin_interrupt_configure ( const struct device * dev , gpio_pin_t pin ,
/* Each pin gives an interrupt at pcf857x . In this function the configuration is checked. */
static int pcf857x _pin_interrupt_configure ( const struct device * dev , gpio_pin_t pin ,
enum gpio_int_mode mode , enum gpio_int_trig trig )
{
const struct pcf8574 _drv_cfg * drv_cfg = dev - > config ;
const struct pcf857x _drv_cfg * drv_cfg = dev - > config ;
if ( ! drv_cfg - > gpio_int . port ) {
return - ENOTSUP ;
@ -314,19 +324,19 @@ static int pcf8574_pin_interrupt_configure(const struct device *dev, gpio_pin_t
@@ -314,19 +324,19 @@ static int pcf8574_pin_interrupt_configure(const struct device *dev, gpio_pin_t
}
/** Register the callback in the callback list */
static int pcf8574 _manage_callback ( const struct device * dev , struct gpio_callback * callback ,
static int pcf857x _manage_callback ( const struct device * dev , struct gpio_callback * callback ,
bool set )
{
struct pcf8574 _drv_data * drv_data = dev - > data ;
struct pcf857x _drv_data * drv_data = dev - > data ;
return gpio_manage_callback ( & drv_data - > callbacks , callback , set ) ;
}
/** Initialize the pcf8574 */
static int pcf8574 _init ( const struct device * dev )
/** Initialize the pcf857x */
static int pcf857x _init ( const struct device * dev )
{
const struct pcf8574 _drv_cfg * drv_cfg = dev - > config ;
struct pcf8574 _drv_data * drv_data = dev - > data ;
const struct pcf857x _drv_cfg * drv_cfg = dev - > config ;
struct pcf857x _drv_data * drv_data = dev - > data ;
int rc ;
if ( ! device_is_ready ( drv_cfg - > i2c . bus ) ) {
@ -353,7 +363,7 @@ static int pcf8574_init(const struct device *dev)
@@ -353,7 +363,7 @@ static int pcf8574_init(const struct device *dev)
return - EIO ;
}
gpio_init_callback ( & drv_data - > int_gpio_cb , pcf8574 _int_gpio_handler ,
gpio_init_callback ( & drv_data - > int_gpio_cb , pcf857x _int_gpio_handler ,
BIT ( drv_cfg - > gpio_int . pin ) ) ;
rc = gpio_add_callback ( drv_cfg - > gpio_int . port , & drv_data - > int_gpio_cb ) ;
if ( rc ! = 0 ) {
@ -365,20 +375,20 @@ static int pcf8574_init(const struct device *dev)
@@ -365,20 +375,20 @@ static int pcf8574_init(const struct device *dev)
return 0 ;
}
/** Realizes the functions of gpio.h for pcf8574 */
static const struct gpio_driver_api pcf8574 _drv_api = {
. pin_configure = pcf8574 _pin_configure ,
. port_get_raw = pcf8574 _port_get_raw ,
. port_set_masked_raw = pcf8574 _port_set_masked_raw ,
. port_set_bits_raw = pcf8574 _port_set_bits_raw ,
. port_clear_bits_raw = pcf8574 _port_clear_bits_raw ,
. port_toggle_bits = pcf8574 _port_toggle_bits ,
. pin_interrupt_configure = pcf8574 _pin_interrupt_configure ,
. manage_callback = pcf8574 _manage_callback ,
/** Realizes the functions of gpio.h for pcf857x */
static const struct gpio_driver_api pcf857x _drv_api = {
. pin_configure = pcf857x _pin_configure ,
. port_get_raw = pcf857x _port_get_raw ,
. port_set_masked_raw = pcf857x _port_set_masked_raw ,
. port_set_bits_raw = pcf857x _port_set_bits_raw ,
. port_clear_bits_raw = pcf857x _port_clear_bits_raw ,
. port_toggle_bits = pcf857x _port_toggle_bits ,
. pin_interrupt_configure = pcf857x _pin_interrupt_configure ,
. manage_callback = pcf857x _manage_callback ,
} ;
# define GPIO_PCF8574 _INST(idx) \
static const struct pcf8574_drv_cfg pcf8574 _cfg # # idx = { \
# define GPIO_PCF857X _INST(idx) \
static const struct pcf857x_drv_cfg pcf857x _cfg # # idx = { \
. common = \
{ \
. port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST ( idx ) , \
@ -386,12 +396,13 @@ static const struct gpio_driver_api pcf8574_drv_api = {
@@ -386,12 +396,13 @@ static const struct gpio_driver_api pcf8574_drv_api = {
. gpio_int = GPIO_DT_SPEC_INST_GET_OR ( idx , int_gpios , { 0 } ) , \
. i2c = I2C_DT_SPEC_INST_GET ( idx ) , \
} ; \
static struct pcf8574_drv_data pcf8574 _data # # idx = { \
. lock = Z_SEM_INITIALIZER ( pcf8574 _data # # idx . lock , 1 , 1 ) , \
. work = Z_WORK_INITIALIZER ( pcf8574 _work_handler ) , \
static struct pcf857x_drv_data pcf857x _data # # idx = { \
. lock = Z_SEM_INITIALIZER ( pcf857x _data # # idx . lock , 1 , 1 ) , \
. work = Z_WORK_INITIALIZER ( pcf857x _work_handler ) , \
. dev = DEVICE_DT_INST_GET ( idx ) , \
. num_bytes = DT_INST_ENUM_IDX ( idx , ngpios ) + 1 , \
} ; \
DEVICE_DT_INST_DEFINE ( idx , pcf8574 _init , NULL , & pcf8574 _data # # idx , & pcf8574 _cfg # # idx , \
POST_KERNEL , CONFIG_GPIO_PCF8574_INIT_PRIORITY , & pcf8574 _drv_api ) ;
DEVICE_DT_INST_DEFINE ( idx , pcf857x _init , NULL , & pcf857x _data # # idx , & pcf857x _cfg # # idx , \
POST_KERNEL , CONFIG_GPIO_PCF857X_INIT_PRIORITY , & pcf857x _drv_api ) ;
DT_INST_FOREACH_STATUS_OKAY ( GPIO_PCF8574 _INST ) ;
DT_INST_FOREACH_STATUS_OKAY ( GPIO_PCF857X _INST ) ;