drivers: gpio: pca95xx: Reduce data transfer over I2C

When accessing GPIO by pin, access only the required port register
(instead of systematically accessing both PORT0 and PORT1).

Signed-off-by: Vincent Geneves <vgeneves@kalray.eu>
diff --git a/drivers/gpio/gpio_pca95xx.c b/drivers/gpio/gpio_pca95xx.c
index 3baccc4..5330b01 100644
--- a/drivers/gpio/gpio_pca95xx.c
+++ b/drivers/gpio/gpio_pca95xx.c
@@ -28,6 +28,14 @@
 #include <zephyr/logging/log.h>
 LOG_MODULE_REGISTER(gpio_pca95xx);
 
+#if CONFIG_LITTLE_ENDIAN
+#define LOW_BYTE_LE16_IDX	0
+#define HIGH_BYTE_LE16_IDX	1
+#else
+#define LOW_BYTE_LE16_IDX	1
+#define HIGH_BYTE_LE16_IDX	0
+#endif
+
 /* Register definitions */
 #define REG_INPUT_PORT0			0x00
 #define REG_INPUT_PORT1			0x01
@@ -36,7 +44,7 @@
 #define REG_POL_INV_PORT0		0x04
 #define REG_POL_INV_PORT1		0x05
 #define REG_CONF_PORT0			0x06
-#define REG_CONG_PORT1			0x07
+#define REG_CONF_PORT1			0x07
 #define REG_OUT_DRV_STRENGTH_PORT0_L	0x40
 #define REG_OUT_DRV_STRENGTH_PORT0_H	0x41
 #define REG_OUT_DRV_STRENGTH_PORT1_L	0x42
@@ -108,6 +116,37 @@
 #endif
 };
 
+static int read_port_reg(const struct device *dev, uint8_t reg, uint8_t pin,
+			 uint16_t *cache, uint16_t *buf)
+{
+	const struct gpio_pca95xx_config * const config = dev->config;
+	uint8_t b_buf;
+	int ret;
+
+	if (pin >= 8)
+		reg++;
+
+	ret = i2c_reg_read_byte_dt(&config->bus, reg, &b_buf);
+	if (ret != 0) {
+		LOG_ERR("PCA95XX[0x%X]: error reading register 0x%X (%d)",
+			config->bus.addr, reg, ret);
+		return ret;
+	}
+
+	if (pin < 8) {
+		((uint8_t *)cache)[LOW_BYTE_LE16_IDX] = b_buf;
+	} else {
+		((uint8_t *)cache)[HIGH_BYTE_LE16_IDX] = b_buf;
+	}
+
+	*buf = *cache;
+
+	LOG_DBG("PCA95XX[0x%X]: Read: REG[0x%X] = 0x%X",
+		config->bus.addr, reg, b_buf);
+
+	return 0;
+}
+
 /**
  * @brief Read both port 0 and port 1 registers of certain register function.
  *
@@ -145,6 +184,36 @@
 	return 0;
 }
 
+
+static int write_port_reg(const struct device *dev, uint8_t reg, uint8_t pin,
+			  uint16_t *cache, uint16_t value)
+{
+	const struct gpio_pca95xx_config * const config = dev->config;
+	uint8_t buf[2];
+	int ret;
+
+	if (pin < 8) {
+		buf[1] = value;
+	} else {
+		buf[1] = value >> 8;
+		reg++;
+	}
+	buf[0] = reg;
+
+	LOG_DBG("PCA95XX[0x%X]: Write: REG[0x%X] = 0x%X", config->bus.addr,
+			reg, buf[1]);
+
+	ret = i2c_write_dt(&config->bus, buf, sizeof(buf));
+	if (ret == 0) {
+		*cache = value;
+	} else {
+		LOG_ERR("PCA95XX[0x%X]: error writing to register 0x%X "
+			"(%d)", config->bus.addr, reg, ret);
+	}
+
+	return ret;
+}
+
 /**
  * @brief Write both port 0 and port 1 registers of certain register function.
  *
@@ -182,6 +251,16 @@
 	return ret;
 }
 
+static inline int update_input_reg(const struct device *dev, uint8_t pin,
+				   uint16_t *buf)
+{
+	struct gpio_pca95xx_drv_data * const drv_data =
+		(struct gpio_pca95xx_drv_data * const)dev->data;
+
+	return read_port_reg(dev, REG_INPUT_PORT0, pin,
+			     &drv_data->reg_cache.input, buf);
+}
+
 static inline int update_input_regs(const struct device *dev, uint16_t *buf)
 {
 	struct gpio_pca95xx_drv_data * const drv_data =
@@ -191,6 +270,16 @@
 			      &drv_data->reg_cache.input, buf);
 }
 
+static inline int update_output_reg(const struct device *dev, uint8_t pin,
+				    uint16_t value)
+{
+	struct gpio_pca95xx_drv_data * const drv_data =
+		(struct gpio_pca95xx_drv_data * const)dev->data;
+
+	return write_port_reg(dev, REG_OUTPUT_PORT0, pin,
+			      &drv_data->reg_cache.output, value);
+}
+
 static inline int update_output_regs(const struct device *dev, uint16_t value)
 {
 	struct gpio_pca95xx_drv_data * const drv_data =
@@ -200,43 +289,45 @@
 			       &drv_data->reg_cache.output, value);
 }
 
-static inline int update_direction_regs(const struct device *dev,
+static inline int update_direction_reg(const struct device *dev, uint8_t pin,
 					uint16_t value)
 {
 	struct gpio_pca95xx_drv_data * const drv_data =
 		(struct gpio_pca95xx_drv_data * const)dev->data;
 
-	return write_port_regs(dev, REG_CONF_PORT0,
-			       &drv_data->reg_cache.dir, value);
+	return write_port_reg(dev, REG_CONF_PORT0, pin,
+			      &drv_data->reg_cache.dir, value);
 }
 
-static inline int update_pul_sel_regs(const struct device *dev,
-				      uint16_t value)
+static inline int update_pul_sel_reg(const struct device *dev, uint8_t pin,
+					uint16_t value)
 {
 	struct gpio_pca95xx_drv_data * const drv_data =
 		(struct gpio_pca95xx_drv_data * const)dev->data;
 
-	return write_port_regs(dev, REG_PUD_SEL_PORT0,
-			       &drv_data->reg_cache.pud_sel, value);
+	return write_port_reg(dev, REG_PUD_SEL_PORT0, pin,
+			      &drv_data->reg_cache.pud_sel, value);
 }
 
-static inline int update_pul_en_regs(const struct device *dev, uint16_t value)
+static inline int update_pul_en_reg(const struct device *dev, uint8_t pin,
+					uint8_t value)
 {
 	struct gpio_pca95xx_drv_data * const drv_data =
 		(struct gpio_pca95xx_drv_data * const)dev->data;
 
-	return write_port_regs(dev, REG_PUD_EN_PORT0,
-			       &drv_data->reg_cache.pud_en, value);
+	return write_port_reg(dev, REG_PUD_EN_PORT0, pin,
+			      &drv_data->reg_cache.pud_en, value);
 }
 
 #ifdef CONFIG_GPIO_PCA95XX_INTERRUPT
-static inline int update_int_mask_regs(const struct device *dev, uint16_t value)
+static inline int update_int_mask_reg(const struct device *dev, uint8_t pin,
+					uint16_t value)
 {
 	struct gpio_pca95xx_drv_data * const drv_data =
 		(struct gpio_pca95xx_drv_data * const)dev->data;
 
-	return write_port_regs(dev, REG_INT_MASK_PORT0,
-			       &drv_data->reg_cache.int_mask, value);
+	return write_port_reg(dev, REG_INT_MASK_PORT0, pin,
+			      &drv_data->reg_cache.int_mask, value);
 }
 #endif /* CONFIG_GPIO_PCA95XX_INTERRUPT */
 
@@ -264,7 +355,7 @@
 		} else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) {
 			reg_out &= ~BIT(pin);
 		}
-		ret = update_output_regs(dev, reg_out);
+		ret = update_output_reg(dev, pin, reg_out);
 		if (ret != 0) {
 			return ret;
 		}
@@ -273,7 +364,7 @@
 		reg_dir |= BIT(pin);
 	}
 
-	ret = update_direction_regs(dev, reg_dir);
+	ret = update_direction_reg(dev, pin, reg_dir);
 
 	return ret;
 }
@@ -319,7 +410,7 @@
 		/* pull down == 0, pull up == 1 */
 		WRITE_BIT(reg_pud, pin, (flags & GPIO_PULL_UP) != 0U);
 
-		ret = update_pul_sel_regs(dev, reg_pud);
+		ret = update_pul_sel_reg(dev, pin, reg_pud);
 		if (ret) {
 			return ret;
 		}
@@ -331,7 +422,7 @@
 	WRITE_BIT(reg_pud, pin,
 		  (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0U);
 
-	ret = update_pul_en_regs(dev, reg_pud);
+	ret = update_pul_en_reg(dev, pin, reg_pud);
 
 	return ret;
 }
@@ -394,8 +485,7 @@
 	return ret;
 }
 
-static int gpio_pca95xx_port_get_raw(const struct device *dev,
-				     uint32_t *value)
+static int gpio_pca95xx_port_get_raw(const struct device *dev, uint32_t *value)
 {
 	struct gpio_pca95xx_drv_data * const drv_data =
 		(struct gpio_pca95xx_drv_data * const)dev->data;
@@ -587,7 +677,7 @@
 		reg_out = drv_data->reg_cache.int_mask;
 		WRITE_BIT(reg_out, pin, (mode == GPIO_INT_MODE_DISABLED));
 
-		ret = update_int_mask_regs(dev, reg_out);
+		ret = update_int_mask_reg(dev, pin, reg_out);
 		if (ret != 0) {
 			LOG_ERR("PCA95XX[0x%X]: failed to update int mask (%d)",
 				config->bus.addr, ret);