310 lines
7.4 KiB
C
310 lines
7.4 KiB
C
/*
|
|
* Copyright (c) 2022 Thomas Stranger
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @brief Common functions for Maxim DS2477,DS2485 1-Wire Masters
|
|
*/
|
|
|
|
#include "w1_ds2477_85_common.h"
|
|
|
|
#include <zephyr/drivers/i2c.h>
|
|
#include <zephyr/drivers/w1.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/kernel.h>
|
|
|
|
LOG_MODULE_REGISTER(w1_ds2477_85, CONFIG_W1_LOG_LEVEL);
|
|
|
|
int ds2477_85_write_port_config(const struct device *dev, uint8_t reg, uint16_t value)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
uint8_t buf[5] = {CMD_WR_W1_PORT_CFG, CMD_WR_W1_PORT_CFG_LEN, reg};
|
|
int ret;
|
|
|
|
__ASSERT_NO_MSG(reg <= PORT_REG_COUNT);
|
|
|
|
sys_put_le16(value, &buf[3]);
|
|
ret = i2c_write_dt(&cfg->i2c_spec, buf, (CMD_WR_W1_PORT_CFG_LEN + CMD_OVERHEAD_LEN));
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
k_usleep(cfg->t_op_us);
|
|
|
|
ret = i2c_read_dt(&cfg->i2c_spec, buf, 2);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
if ((buf[0] != 1) || (buf[1] != DS2477_88_RES_SUCCESS)) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ds2477_85_read_port_config(const struct device *dev, uint8_t reg, uint16_t *value)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
uint8_t buf[4] = {CMD_RD_W1_PORT_CFG, CMD_RD_W1_PORT_CFG_LEN, reg};
|
|
int ret;
|
|
|
|
__ASSERT_NO_MSG(value != NULL && reg <= PORT_REG_COUNT);
|
|
|
|
ret = i2c_write_dt(&cfg->i2c_spec, buf, (CMD_RD_W1_PORT_CFG_LEN + CMD_OVERHEAD_LEN));
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
k_usleep(cfg->t_op_us);
|
|
|
|
ret = i2c_read_dt(&cfg->i2c_spec, buf, 4);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
if ((buf[0] != 3) || (buf[1] != DS2477_88_RES_SUCCESS)) {
|
|
return -EIO;
|
|
}
|
|
|
|
*value = sys_get_le16(&buf[2]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ds2477_85_reset_master(const struct device *dev)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
uint8_t buf[2] = {CMD_MASTER_RESET};
|
|
int ret;
|
|
|
|
ret = i2c_write_dt(&cfg->i2c_spec, buf, 1);
|
|
if (ret < 0) {
|
|
LOG_ERR("initiate reset failed");
|
|
return ret;
|
|
}
|
|
|
|
k_usleep(cfg->t_op_us);
|
|
|
|
ret = i2c_read_dt(&cfg->i2c_spec, buf, 2);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
if ((buf[0] != 1) || (buf[1] != DS2477_88_RES_SUCCESS)) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ds2477_85_reset_bus(const struct device *dev)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
struct w1_ds2477_85_data *data = dev->data;
|
|
uint16_t delay_us = cfg->mode_timing[data->master_reg.od_active].t_reset;
|
|
uint8_t tx_data;
|
|
uint8_t rx_data;
|
|
int ret;
|
|
|
|
tx_data = data->master_reg.od_active ? BIT(3) : BIT(7);
|
|
ret = cfg->w1_script_cmd(dev, delay_us, SCRIPT_OW_RESET, &tx_data, 1, &rx_data, 1);
|
|
|
|
switch (ret) {
|
|
case DS2477_88_RES_COMM_FAILURE:
|
|
/* presence not detected */
|
|
return 0;
|
|
case DS2477_88_RES_SUCCESS:
|
|
/* at least 1 device present */
|
|
return 1;
|
|
default:
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
int ds2477_85_read_bit(const struct device *dev)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
struct w1_ds2477_85_data *data = dev->data;
|
|
uint16_t delay_us = cfg->mode_timing[data->master_reg.od_active].t_slot;
|
|
uint8_t rx_data;
|
|
int ret;
|
|
|
|
ret = cfg->w1_script_cmd(dev, delay_us, SCRIPT_OW_READ_BIT, NULL, 0, &rx_data, 1);
|
|
if (ret != DS2477_88_RES_SUCCESS) {
|
|
return -EIO;
|
|
}
|
|
|
|
return (rx_data & BIT(5)) ? 1 : 0;
|
|
}
|
|
|
|
int ds2477_85_write_bit(const struct device *dev, const bool bit)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
struct w1_ds2477_85_data *data = dev->data;
|
|
uint16_t delay_us = cfg->mode_timing[data->master_reg.od_active].t_slot;
|
|
uint8_t tx_data = (uint8_t)bit;
|
|
uint8_t rx_data;
|
|
int ret;
|
|
|
|
ret = cfg->w1_script_cmd(dev, delay_us, SCRIPT_OW_WRITE_BIT, &tx_data, 1, &rx_data, 1);
|
|
if (ret != DS2477_88_RES_SUCCESS) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ds2477_85_read_byte(const struct device *dev)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
struct w1_ds2477_85_data *data = dev->data;
|
|
uint16_t delay_us = cfg->mode_timing[data->master_reg.od_active].t_slot * 8;
|
|
uint8_t rx_data;
|
|
int ret;
|
|
|
|
ret = cfg->w1_script_cmd(dev, delay_us, SCRIPT_OW_READ_BYTE, NULL, 0, &rx_data, 1);
|
|
if (ret != DS2477_88_RES_SUCCESS) {
|
|
return -EIO;
|
|
}
|
|
|
|
return rx_data;
|
|
}
|
|
|
|
int ds2477_85_write_byte(const struct device *dev, const uint8_t tx_byte)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
struct w1_ds2477_85_data *data = dev->data;
|
|
uint16_t delay_us = cfg->mode_timing[data->master_reg.od_active].t_slot * 8;
|
|
uint8_t rx_data;
|
|
int ret;
|
|
|
|
ret = cfg->w1_script_cmd(dev, delay_us, SCRIPT_OW_WRITE_BYTE, &tx_byte, 1, &rx_data, 1);
|
|
if (ret != DS2477_88_RES_SUCCESS) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ds2477_85_write_block(const struct device *dev, const uint8_t *buffer, size_t tx_len)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
struct w1_ds2477_85_data *data = dev->data;
|
|
int w1_timing = cfg->mode_timing[data->master_reg.od_active].t_slot * 8 * tx_len;
|
|
uint8_t buf[3] = {CMD_WR_BLOCK, (tx_len + CMD_WR_BLOCK_LEN), 0};
|
|
struct i2c_msg tx_msg[2] = {
|
|
{.buf = buf, .len = (CMD_WR_BLOCK_LEN + CMD_OVERHEAD_LEN), .flags = I2C_MSG_WRITE},
|
|
{.buf = (uint8_t *)buffer, .len = tx_len, .flags = (I2C_MSG_WRITE | I2C_MSG_STOP)},
|
|
};
|
|
int ret;
|
|
|
|
__ASSERT_NO_MSG(tx_len <= MAX_BLOCK_LEN);
|
|
|
|
if (tx_len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
ret = i2c_transfer_dt(&cfg->i2c_spec, tx_msg, 2);
|
|
if (ret < 0) {
|
|
LOG_ERR("write block fail: %x", ret);
|
|
return ret;
|
|
}
|
|
|
|
k_usleep(cfg->t_op_us + (cfg->t_seq_us * tx_len) + w1_timing);
|
|
|
|
ret = i2c_read_dt(&cfg->i2c_spec, buf, 2);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
if ((buf[0] != 1) || (buf[1] != DS2477_88_RES_SUCCESS)) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ds2477_85_read_block(const struct device *dev, uint8_t *buffer, size_t rx_len)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
struct w1_ds2477_85_data *data = dev->data;
|
|
int w1_timing = cfg->mode_timing[data->master_reg.od_active].t_slot * 8 * rx_len;
|
|
uint8_t buf[3] = {CMD_RD_BLOCK, CMD_RD_BLOCK_LEN, rx_len};
|
|
struct i2c_msg rx_msg[2] = {
|
|
{.buf = buf, .len = 2, .flags = I2C_MSG_READ},
|
|
{.buf = buffer, .len = rx_len, .flags = (I2C_MSG_READ | I2C_MSG_STOP)}
|
|
};
|
|
int ret;
|
|
|
|
__ASSERT_NO_MSG(rx_len <= MAX_BLOCK_LEN);
|
|
|
|
if (rx_len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
ret = i2c_write_dt(&cfg->i2c_spec, buf, (CMD_RD_BLOCK_LEN + CMD_OVERHEAD_LEN));
|
|
if (ret < 0) {
|
|
LOG_ERR("read block fail: %x", ret);
|
|
return ret;
|
|
}
|
|
|
|
k_usleep(cfg->t_op_us + (cfg->t_seq_us * rx_len) + w1_timing);
|
|
|
|
ret = i2c_transfer_dt(&cfg->i2c_spec, rx_msg, 2);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
if (buf[1] != DS2477_88_RES_SUCCESS) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ds2477_85_configure(const struct device *dev, enum w1_settings_type type, uint32_t value)
|
|
{
|
|
struct w1_ds2477_85_data *data = dev->data;
|
|
|
|
switch (type) {
|
|
case W1_SETTING_SPEED:
|
|
__ASSERT_NO_MSG(value <= 1);
|
|
data->master_reg.od_active = value;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ds2477_85_write_port_config(dev, PORT_REG_MASTER_CONFIGURATION,
|
|
data->master_reg.value);
|
|
}
|
|
|
|
int w1_ds2477_85_init(const struct device *dev)
|
|
{
|
|
const struct w1_ds2477_85_config *cfg = dev->config;
|
|
struct w1_ds2477_85_data *data = dev->data;
|
|
|
|
data->master_reg.apu = cfg->apu;
|
|
|
|
if (ds2477_85_write_port_config(dev, PORT_REG_MASTER_CONFIGURATION,
|
|
data->master_reg.value) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (ds2477_85_write_port_config(dev, PORT_REG_RPUP_BUF, cfg->rpup_buf) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (ds2477_85_write_port_config(dev, PORT_REG_PDSLEW, cfg->pdslew) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
LOG_DBG("cfg: rpup_buf=%02x, pdslew:%02x", cfg->rpup_buf, cfg->pdslew);
|
|
|
|
/* RPUP/BUF configuration is applied after a bus reset */
|
|
(void)ds2477_85_reset_bus(dev);
|
|
|
|
LOG_DBG("w1-ds2477/85 init; %d slave devices",
|
|
cfg->master_config.slave_count);
|
|
|
|
return 0;
|
|
}
|