blob: 0d8bda63425f70691559119a8c3c07e86d1e32c8 [file] [log] [blame]
/*
* Copyright (c) 2024, Croxel Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/pinctrl.h>
#include <nrfx_twi.h>
#include "i2c_nrfx_twi_common.h"
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(i2c_nrfx_twi);
int i2c_nrfx_twi_init(const struct device *dev)
{
const struct i2c_nrfx_twi_config *config = dev->config;
nrfx_err_t result = nrfx_twi_init(&config->twi, &config->config,
config->event_handler, (void *)dev);
if (result != NRFX_SUCCESS) {
LOG_ERR("Failed to initialize device: %s",
dev->name);
return -EBUSY;
}
return 0;
}
int i2c_nrfx_twi_configure(const struct device *dev, uint32_t dev_config)
{
const struct i2c_nrfx_twi_config *config = dev->config;
struct i2c_nrfx_twi_common_data *data = dev->data;
nrfx_twi_t const *inst = &config->twi;
if (I2C_ADDR_10_BITS & dev_config) {
return -EINVAL;
}
switch (I2C_SPEED_GET(dev_config)) {
case I2C_SPEED_STANDARD:
nrf_twi_frequency_set(inst->p_twi, NRF_TWI_FREQ_100K);
break;
case I2C_SPEED_FAST:
nrf_twi_frequency_set(inst->p_twi, NRF_TWI_FREQ_400K);
break;
default:
LOG_ERR("unsupported speed");
return -EINVAL;
}
data->dev_config = dev_config;
return 0;
}
int i2c_nrfx_twi_recover_bus(const struct device *dev)
{
const struct i2c_nrfx_twi_config *config = dev->config;
uint32_t scl_pin;
uint32_t sda_pin;
nrfx_err_t err;
scl_pin = nrf_twi_scl_pin_get(config->twi.p_twi);
sda_pin = nrf_twi_sda_pin_get(config->twi.p_twi);
err = nrfx_twi_bus_recover(scl_pin, sda_pin);
return (err == NRFX_SUCCESS ? 0 : -EBUSY);
}
int i2c_nrfx_twi_msg_transfer(const struct device *dev, uint8_t flags,
uint8_t *buf, size_t buf_len,
uint16_t i2c_addr, bool more_msgs)
{
const struct i2c_nrfx_twi_config *config = dev->config;
int ret = 0;
uint32_t xfer_flags = 0;
nrfx_err_t res;
nrfx_twi_xfer_desc_t cur_xfer = {
.p_primary_buf = buf,
.primary_length = buf_len,
.address = i2c_addr,
.type = (flags & I2C_MSG_READ) ?
NRFX_TWI_XFER_RX : NRFX_TWI_XFER_TX,
};
if (flags & I2C_MSG_ADDR_10_BITS) {
LOG_ERR("10-bit I2C Addr devices not supported");
ret = -ENOTSUP;
} else if (!(flags & I2C_MSG_STOP)) {
/* - if the transfer consists of more messages
* and the I2C repeated START is not requested
* to appear before the next message, suspend
* the transfer after the current message,
* so that it can be resumed with the next one,
* resulting in the two messages merged into
* a continuous transfer on the bus
*/
if (more_msgs) {
xfer_flags |= NRFX_TWI_FLAG_SUSPEND;
/* - otherwise, just finish the transfer without
* generating the STOP condition, unless the current
* message is an RX request, for which such feature
* is not supported
*/
} else if (flags & I2C_MSG_READ) {
ret = -ENOTSUP;
} else {
xfer_flags |= NRFX_TWI_FLAG_TX_NO_STOP;
}
}
if (!ret) {
res = nrfx_twi_xfer(&config->twi, &cur_xfer, xfer_flags);
switch (res) {
case NRFX_SUCCESS:
break;
case NRFX_ERROR_BUSY:
ret = -EBUSY;
break;
default:
ret = -EIO;
break;
}
}
return ret;
}
#ifdef CONFIG_PM_DEVICE
int twi_nrfx_pm_action(const struct device *dev, enum pm_device_action action)
{
const struct i2c_nrfx_twi_config *config = dev->config;
struct i2c_nrfx_twi_common_data *data = dev->data;
int ret = 0;
switch (action) {
case PM_DEVICE_ACTION_RESUME:
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
return ret;
}
i2c_nrfx_twi_init(dev);
if (data->dev_config) {
i2c_nrfx_twi_configure(dev, data->dev_config);
}
break;
case PM_DEVICE_ACTION_SUSPEND:
nrfx_twi_uninit(&config->twi);
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
if (ret < 0) {
return ret;
}
break;
default:
ret = -ENOTSUP;
}
return ret;
}
#endif /* CONFIG_PM_DEVICE */