blob: 5e2a3dfb1bd962636a31453ba9bcb5302097b69e [file]
/* Copyright 2025 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/shell/shell.h>
#include <zephyr/sys/util.h>
#define DT_DRV_COMPAT cros_i2c_mux
LOG_MODULE_REGISTER(i2c_mux, LOG_LEVEL_INF);
struct i2c_mux_config {
const struct gpio_dt_spec en_gpio;
const struct gpio_dt_spec *a_gpio;
uint8_t a_gpio_count;
uint8_t port_count;
};
static int i2c_mux_select(const struct device *dev, uint8_t port)
{
const struct i2c_mux_config *cfg = dev->config;
int ret;
if (port >= cfg->port_count) {
return -EINVAL;
}
for (uint8_t i = 0; i < cfg->a_gpio_count; i++) {
const struct gpio_dt_spec *gpio = &cfg->a_gpio[i];
uint8_t val = IS_BIT_SET(port, i) != 0 ? 1 : 0;
ret = gpio_pin_set_dt(gpio, val);
if (ret < 0) {
LOG_ERR("gpio set failed: %d", ret);
return ret;
}
}
return 0;
}
static const struct i2c_mux_api {
/* empty, just for use in shell_device_filter */
} i2c_mux_api;
static bool i2c_mux_device_filter(const struct device *dev)
{
return dev->api == &i2c_mux_api;
}
static void device_name_get(size_t idx, struct shell_static_entry *entry)
{
const struct device *dev =
shell_device_filter(idx, i2c_mux_device_filter);
entry->syntax = (dev != NULL) ? dev->name : NULL;
entry->handler = NULL;
entry->help = NULL;
entry->subcmd = NULL;
}
SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
static int cmd_i2c_mux(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
int err = 0;
int port;
dev = device_get_binding(argv[1]);
if (dev == NULL) {
shell_error(sh, "Device %s not available", argv[1]);
return -ENODEV;
}
port = shell_strtol(argv[2], 0, &err);
if (err) {
shell_error(sh, "Invalid argument: %s", argv[2]);
return err;
}
err = i2c_mux_select(dev, port);
if (err < 0) {
shell_error(sh, "i2c_mux_select error: %d", err);
return err;
}
return 0;
}
SHELL_CMD_ARG_REGISTER(i2c_mux, &dsub_device_name, "I2C multiplexer control",
cmd_i2c_mux, 3, 0);
static int i2c_mux_init(const struct device *dev)
{
const struct i2c_mux_config *cfg = dev->config;
int ret;
ret = gpio_pin_configure_dt(&cfg->en_gpio, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
LOG_ERR("gpio configuration failed: %d", ret);
return ret;
}
for (uint8_t i = 0; i < cfg->a_gpio_count; i++) {
const struct gpio_dt_spec *gpio = &cfg->a_gpio[i];
ret = gpio_pin_configure_dt(gpio, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("gpio configuration failed: %d", ret);
return ret;
}
}
return 0;
}
#define I2C_MUX_INIT(n) \
const struct gpio_dt_spec i2c_mux_a_gpio_##n[] = { \
DT_INST_FOREACH_PROP_ELEM_SEP(n, a_gpios, \
GPIO_DT_SPEC_GET_BY_IDX, (, )) \
}; \
\
static const struct i2c_mux_config i2c_mux_cfg_##n = { \
.en_gpio = GPIO_DT_SPEC_INST_GET(n, en_gpios), \
.a_gpio = i2c_mux_a_gpio_##n, \
.a_gpio_count = ARRAY_SIZE(i2c_mux_a_gpio_##n), \
.port_count = DT_INST_PROP(n, port_count), \
}; \
\
DEVICE_DT_INST_DEFINE(n, i2c_mux_init, NULL(n), NULL, \
&i2c_mux_cfg_##n, POST_KERNEL, \
CONFIG_I2C_INIT_PRIORITY, &i2c_mux_api);
DT_INST_FOREACH_STATUS_OKAY(I2C_MUX_INIT)