diff --git a/boards/arm/beagle_bcf/beagleconnect_freedom.dts b/boards/arm/beagle_bcf/beagleconnect_freedom.dts index 3f6fcafaa2187..9f8c84df95601 100644 --- a/boards/arm/beagle_bcf/beagleconnect_freedom.dts +++ b/boards/arm/beagle_bcf/beagleconnect_freedom.dts @@ -59,7 +59,7 @@ sens_i2c: sensor-switch { status = "okay"; - compatible = "ti,ts5a2066"; + compatible = "gpio-i2c-switch"; #address-cells = <1>; #size-cells = <0>; controller = <&i2c0>; diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index 20ea5c0cb1e38..f7f363ed8d416 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -54,6 +54,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_XILINX_AXI i2c_xilinx_axi.c) zephyr_library_sources_ifdef(CONFIG_I2C_MCHP_MSS i2c_mchp_mss.c) zephyr_library_sources_ifdef(CONFIG_I2C_SEDI i2c_sedi.c) zephyr_library_sources_ifdef(CONFIG_I2C_AMBIQ i2c_ambiq.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_I2C_SWITCH gpio_i2c_switch.c) zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1 i2c_ll_stm32_v1.c diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 5f62fbb4eb1e9..2523877c8513d 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -195,4 +195,11 @@ config I2C_RV32M1_LPI2C help Enable the RV32M1 LPI2C driver. +config GPIO_I2C_SWITCH + bool "GPIO controlled I2C bus switch" + default y + depends on DT_HAS_GPIO_I2C_SWITCH_ENABLED + help + Enable GPIO controlled I2C bus switch driver. + endif # I2C diff --git a/drivers/i2c/gpio_i2c_switch.c b/drivers/i2c/gpio_i2c_switch.c new file mode 100644 index 0000000000000..938ed11d769dc --- /dev/null +++ b/drivers/i2c/gpio_i2c_switch.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 Ayush Singh + * Copyright (c) 2021 Jason Kridner, BeagleBoard.org Foundation + * Copyright (c) 2020 Innoseis BV + * + * Based on i2c_tca9656a.c + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT gpio_i2c_switch + +#define GPIO_I2C_TOGGLE_DELAY_US 1 +#define GPIO_I2C_LOCK_TIMEOUT_US (GPIO_I2C_TOGGLE_DELAY_US * 2 + 100) + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(gpio_i2c_switch, CONFIG_I2C_LOG_LEVEL); + +struct gpio_i2c_switch_config { + const struct device *bus; + const struct gpio_dt_spec gpio; +}; + +struct gpio_i2c_switch_data { + struct k_mutex lock; +}; + +static int gpio_i2c_switch_configure(const struct device *dev, uint32_t dev_config) +{ + const struct gpio_i2c_switch_config *config = dev->config; + + return i2c_configure(config->bus, dev_config); +} + +static int gpio_i2c_switch_transfer(const struct device *dev, struct i2c_msg *msgs, + uint8_t num_msgs, uint16_t addr) +{ + int res; + struct gpio_i2c_switch_data *data = dev->data; + const struct gpio_i2c_switch_config *config = dev->config; + + res = k_mutex_lock(&data->lock, K_USEC(GPIO_I2C_LOCK_TIMEOUT_US)); + if (res != 0) { + return res; + } + + /* enable switch */ + gpio_pin_configure_dt(&config->gpio, GPIO_OUTPUT_HIGH); + k_busy_wait(GPIO_I2C_TOGGLE_DELAY_US); + + res = i2c_transfer(config->bus, msgs, num_msgs, addr); + + /* disable switch */ + gpio_pin_configure_dt(&config->gpio, GPIO_OUTPUT_LOW); + k_busy_wait(GPIO_I2C_TOGGLE_DELAY_US); + k_mutex_unlock(&data->lock); + + return res; +} + +const struct i2c_driver_api gpio_i2c_switch_api_funcs = { + .configure = gpio_i2c_switch_configure, + .transfer = gpio_i2c_switch_transfer, +}; + +static int gpio_i2c_switch_init(const struct device *dev) +{ + const struct gpio_i2c_switch_config *config = dev->config; + struct gpio_i2c_switch_data *data = dev->data; + + k_mutex_init(&data->lock); + + return gpio_pin_configure_dt(&config->gpio, GPIO_OUTPUT_LOW); +} + +#define DEFINE_GPIO_I2C_SWITCH(inst) \ + \ + static struct gpio_i2c_switch_data gpio_i2c_switch_dev_data_##inst; \ + \ + static const struct gpio_i2c_switch_config gpio_i2c_switch_dev_cfg_##inst = { \ + .bus = DEVICE_DT_GET(DT_PHANDLE(DT_DRV_INST(inst), controller)), \ + .gpio = GPIO_DT_SPEC_GET(DT_DRV_INST(inst), gpios), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, gpio_i2c_switch_init, device_pm_control_nop, \ + &gpio_i2c_switch_dev_data_##inst, &gpio_i2c_switch_dev_cfg_##inst, \ + POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, &gpio_i2c_switch_api_funcs); + +DT_INST_FOREACH_STATUS_OKAY(DEFINE_GPIO_I2C_SWITCH) diff --git a/dts/bindings/i2c/gpio-i2c-switch.yaml b/dts/bindings/i2c/gpio-i2c-switch.yaml new file mode 100644 index 0000000000000..ebd932afc710f --- /dev/null +++ b/dts/bindings/i2c/gpio-i2c-switch.yaml @@ -0,0 +1,24 @@ +# Copyright (c) 2023, Ayush Singh +# Copyright (c) 2021, Jason Kridner, BeagleBoard.org Foundation +# SPDX-License-Identifier: Apache-2.0 + +description: | + GPIO enabled analog switch to isolate devices from an I2C bus + +compatible: "gpio-i2c-switch" + +include: i2c-controller.yaml + +properties: + "#address-cells": + required: true + const: 1 + "#size-cells": + required: true + const: 0 + controller: + type: phandle + required: true + gpios: + type: phandle-array + required: true