blob: 615e754fc95a5778f0e0b6272cc90748533239f0 [file] [edit]
/*
* Copyright (c) 2026, tinyvision.ai
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nxp_sc18is606
#include <zephyr/logging/log.h>
#include "mfd_sc18is606.h"
LOG_MODULE_REGISTER(nxp_sc18is606, CONFIG_MFD_LOG_LEVEL);
int nxp_sc18is606_transfer(const struct device *dev, const uint8_t *tx_data, uint8_t tx_len,
uint8_t *rx_data, uint8_t rx_len, uint8_t *id_buf)
{
struct sc18is606_data *data = dev->data;
const struct sc18is606_config *info = dev->config;
int ret;
ret = k_mutex_lock(&data->bridge_lock, K_FOREVER);
if (ret < 0) {
return ret;
}
if (tx_data != NULL) {
if (id_buf != NULL) {
struct i2c_msg tx_msg[2] = {
{
.buf = id_buf,
.len = 1,
.flags = I2C_MSG_WRITE,
},
{
.buf = (uint8_t *)tx_data,
.len = tx_len,
.flags = I2C_MSG_WRITE,
},
};
ret = i2c_transfer_dt(&info->i2c_controller, tx_msg, 2);
} else {
struct i2c_msg tx_msg[1] = {{
.buf = (uint8_t *)tx_data,
.len = tx_len,
.flags = I2C_MSG_WRITE,
}};
ret = i2c_transfer_dt(&info->i2c_controller, tx_msg, 1);
}
if (ret != 0) {
LOG_ERR("SPI write failed: %d", ret);
goto out;
}
}
/*If interrupt pin is used wait before next transaction*/
if (info->int_gpios.port) {
ret = k_sem_take(&data->int_sem, K_MSEC(5));
if (ret != 0) {
LOG_WRN("Interrupt semaphore timedout, proceeding with read");
}
}
if (rx_data != NULL) {
/*What is the time*/
k_timepoint_t end;
/*Set a deadline in a second*/
end = sys_timepoint_calc(K_MSEC(1));
do {
ret = i2c_read(info->i2c_controller.bus, rx_data, rx_len,
info->i2c_controller.addr);
if (ret >= 0) {
break;
}
} while (!sys_timepoint_expired(end)); /*Keep reading while in the deadline*/
if (ret < 0) {
LOG_ERR("Failed to read data (%d)", ret);
goto out;
}
}
ret = 0;
out:
k_mutex_unlock(&data->bridge_lock);
return ret;
}
static void sc18is606_int_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
struct sc18is606_data *data = CONTAINER_OF(cb, struct sc18is606_data, int_cb);
k_sem_give(&data->int_sem);
}
static int int_gpios_setup(const struct device *dev)
{
struct sc18is606_data *data = dev->data;
const struct sc18is606_config *cfg = dev->config;
int ret;
if (!gpio_is_ready_dt(&cfg->int_gpios)) {
LOG_ERR("SC18IS606 Int GPIO not ready");
return -ENODEV;
}
ret = gpio_pin_configure_dt(&cfg->int_gpios, GPIO_INPUT);
if (ret != 0U) {
LOG_ERR("Failed to configure SC18IS606 int gpio (%d)", ret);
return ret;
}
ret = k_sem_init(&data->int_sem, 0, 1);
if (ret != 0U) {
LOG_ERR("Failed to Initialize Interrupt Semaphore (%d)", ret);
return ret;
}
gpio_init_callback(&data->int_cb, sc18is606_int_isr, BIT(cfg->int_gpios.pin));
ret = gpio_add_callback(cfg->int_gpios.port, &data->int_cb);
if (ret != 0U) {
LOG_ERR("Failed to assign the Interrupt callback (%d)", ret);
return ret;
}
ret = gpio_pin_interrupt_configure_dt(&cfg->int_gpios, GPIO_INT_EDGE_TO_ACTIVE);
if (ret != 0U) {
LOG_ERR("Failed to configure the GPIO interrupt edge (%d)", ret);
return ret;
}
return ret;
}
static int sc18is606_init(const struct device *dev)
{
const struct sc18is606_config *cfg = dev->config;
int ret;
if (!device_is_ready(cfg->i2c_controller.bus)) {
LOG_ERR("I2C controller %s not found", cfg->i2c_controller.bus->name);
return -ENODEV;
}
LOG_DBG("Using I2C controller: %s", cfg->i2c_controller.bus->name);
if (cfg->reset_gpios.port) {
if (!gpio_is_ready_dt(&cfg->reset_gpios)) {
LOG_ERR("SC18IS606 Reset GPIO not ready");
return -ENODEV;
}
ret = gpio_pin_configure_dt(&cfg->reset_gpios, GPIO_OUTPUT_ACTIVE);
if (ret != 0U) {
LOG_ERR("Failed to configure SC18IS606 reset GPIO (%d)", ret);
return ret;
}
ret = gpio_pin_set_dt(&cfg->reset_gpios, 0);
if (ret != 0U) {
LOG_ERR("Failed to reset Bridge via Reset pin (%d)", ret);
return ret;
}
}
if (cfg->int_gpios.port) {
ret = int_gpios_setup(dev);
if (ret != 0U) {
LOG_ERR("Could not set up device int_gpios (%d)", ret);
return ret;
}
}
LOG_DBG("SC18IS606 initialized");
return 0;
}
#define MFD_SC18IS606_DEFINE(inst) \
static const struct sc18is606_config sc18is606_config_##inst = { \
.i2c_controller = I2C_DT_SPEC_GET(DT_DRV_INST(inst)), \
.reset_gpios = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(inst), reset_gpios, {0}), \
.int_gpios = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(inst), int_gpios, {0}), \
}; \
\
static struct sc18is606_data data##inst; \
\
DEVICE_DT_INST_DEFINE(inst, sc18is606_init, NULL, &data##inst, &sc18is606_config_##inst, \
POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, NULL);
DT_INST_FOREACH_STATUS_OKAY(MFD_SC18IS606_DEFINE)