164 lines
3.8 KiB
C
164 lines
3.8 KiB
C
/*
|
|
* 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 */
|