zephyr/drivers/w1/w1_ds2477_85_common.c
Thomas Stranger 81bf4f98f7 drivers/w1: driver for ds2485 1-Wire master
This commit introduces the 1-wire master driver for maxim ds2485.

The ds2485 master has nearly the same (1-wire) feature set and
i2c-interface as the ds2477.
Therefore the common parts are extracted, but to avoid
any nda troubles only the ds2485 specific part is included.

Compared to older 1-wire masters, the ds2485 supports higher level
commands, supporting multi byte operations, search next, automatic crc
calculation.

In this driver only basic read and write operations are supported,
further hardware features are not yet utilized by the driver.

Signed-off-by: Thomas Stranger <thomas.stranger@outlook.com>
2022-09-09 14:11:30 +00:00

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;
}