You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
3.8 KiB
163 lines
3.8 KiB
/* |
|
* 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 */
|
|
|