zephyr/drivers/w1/w1_zephyr_gpio.c
Fredrik Gihl 649d9d31af drivers: w1: Ensure DQ pin is both input and output.
Driver did not work on ESP32c3, by specifying pin to be output and
input solve the issue. Improve documentation, to highlight that driver
is requiring open-drain support.

Signed-off-by: Fredrik Gihl <fgihl@hotmail.com>
2024-05-29 07:34:29 +02:00

329 lines
8 KiB
C

/*
* Copyright (c) 2023 Hudson C. Dalpra
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT zephyr_w1_gpio
/**
* @brief 1-Wire Bus Master driver using Zephyr GPIO interface.
*
* This file contains the implementation of the 1-Wire Bus Master driver using
* the Zephyr GPIO interface. The driver is based on GPIO bit-banging and
* follows the timing specifications for 1-Wire communication.
*
* The driver supports both standard speed and overdrive speed modes.
*
* This driver is heavily based on the w1_zephyr_serial.c driver and the
* technical documentation from Maxim Integrated.
*
* - w1_zephyr_serial.c: drivers/w1/w1_zephyr_serial.c
* - Maxim Integrated 1-Wire Communication Through Software:
* https://www.analog.com/en/technical-articles/1wire-communication-through-software.html
*/
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/w1.h>
#include <zephyr/device.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(w1_gpio, CONFIG_W1_LOG_LEVEL);
/*
* The time critical sections are used to ensure that the timing
* between communication operations is correct.
*/
#if defined(CONFIG_W1_ZEPHYR_GPIO_TIME_CRITICAL)
#define W1_GPIO_ENTER_CRITICAL() irq_lock()
#define W1_GPIO_EXIT_CRITICAL(key) irq_unlock(key)
#define W1_GPIO_WAIT_US(us) k_busy_wait(us)
#else
#define W1_GPIO_ENTER_CRITICAL() 0u
#define W1_GPIO_EXIT_CRITICAL(key) (void)key
#define W1_GPIO_WAIT_US(us) k_usleep(us)
#endif
/*
* Standard timing between communication operations:
*/
#define W1_GPIO_TIMING_STD_A 6u
#define W1_GPIO_TIMING_STD_B 64u
#define W1_GPIO_TIMING_STD_C 60u
#define W1_GPIO_TIMING_STD_D 10u
#define W1_GPIO_TIMING_STD_E 9u
#define W1_GPIO_TIMING_STD_F 55u
#define W1_GPIO_TIMING_STD_G 0u
#define W1_GPIO_TIMING_STD_H 480u
#define W1_GPIO_TIMING_STD_I 70u
#define W1_GPIO_TIMING_STD_J 410u
/*
* Overdrive timing between communication operations:
*
* Not completely correct since the overdrive communication requires
* delays of 2.5us, 7.5us and 8.5us.
* The delays are approximated by flooring the values.
*/
#define W1_GPIO_TIMING_OD_A 1u
#define W1_GPIO_TIMING_OD_B 7u
#define W1_GPIO_TIMING_OD_C 7u
#define W1_GPIO_TIMING_OD_D 2u
#define W1_GPIO_TIMING_OD_E 1u
#define W1_GPIO_TIMING_OD_F 7u
#define W1_GPIO_TIMING_OD_G 2u
#define W1_GPIO_TIMING_OD_H 70u
#define W1_GPIO_TIMING_OD_I 8u
#define W1_GPIO_TIMING_OD_J 40u
struct w1_gpio_timing {
uint16_t a;
uint16_t b;
uint16_t c;
uint16_t d;
uint16_t e;
uint16_t f;
uint16_t g;
uint16_t h;
uint16_t i;
uint16_t j;
};
struct w1_gpio_config {
/** w1 master config, common to all drivers */
struct w1_master_config master_config;
/** GPIO device used for 1-Wire communication */
const struct gpio_dt_spec spec;
};
struct w1_gpio_data {
/** w1 master data, common to all drivers */
struct w1_master_data master_data;
/** timing parameters for 1-Wire communication */
const struct w1_gpio_timing *timing;
/** overdrive speed mode active */
bool overdrive_active;
};
static const struct w1_gpio_timing std = {
.a = W1_GPIO_TIMING_STD_A,
.b = W1_GPIO_TIMING_STD_B,
.c = W1_GPIO_TIMING_STD_C,
.d = W1_GPIO_TIMING_STD_D,
.e = W1_GPIO_TIMING_STD_E,
.f = W1_GPIO_TIMING_STD_F,
.g = W1_GPIO_TIMING_STD_G,
.h = W1_GPIO_TIMING_STD_H,
.i = W1_GPIO_TIMING_STD_I,
.j = W1_GPIO_TIMING_STD_J,
};
static const struct w1_gpio_timing od = {
.a = W1_GPIO_TIMING_OD_A,
.b = W1_GPIO_TIMING_OD_B,
.c = W1_GPIO_TIMING_OD_C,
.d = W1_GPIO_TIMING_OD_D,
.e = W1_GPIO_TIMING_OD_E,
.f = W1_GPIO_TIMING_OD_F,
.g = W1_GPIO_TIMING_OD_G,
.h = W1_GPIO_TIMING_OD_H,
.i = W1_GPIO_TIMING_OD_I,
.j = W1_GPIO_TIMING_OD_J,
};
static int w1_gpio_reset_bus(const struct device *dev)
{
const struct w1_gpio_config *cfg = dev->config;
const struct w1_gpio_data *data = dev->data;
const struct gpio_dt_spec *spec = &cfg->spec;
const struct w1_gpio_timing *timing = data->timing;
int ret = 0;
unsigned int key = W1_GPIO_ENTER_CRITICAL();
W1_GPIO_WAIT_US(timing->g);
ret = gpio_pin_set_dt(spec, 0);
if (ret < 0) {
goto out;
}
W1_GPIO_WAIT_US(timing->h);
ret = gpio_pin_set_dt(spec, 1);
if (ret < 0) {
goto out;
}
W1_GPIO_WAIT_US(timing->i);
ret = gpio_pin_get_dt(spec);
if (ret < 0) {
goto out;
}
ret ^= 0x01;
W1_GPIO_WAIT_US(timing->j);
out:
W1_GPIO_EXIT_CRITICAL(key);
return ret;
}
static int w1_gpio_read_bit(const struct device *dev)
{
const struct w1_gpio_config *cfg = dev->config;
const struct w1_gpio_data *data = dev->data;
const struct gpio_dt_spec *spec = &cfg->spec;
const struct w1_gpio_timing *timing = data->timing;
int ret = 0;
unsigned int key = W1_GPIO_ENTER_CRITICAL();
ret = gpio_pin_set_dt(spec, 0);
if (ret < 0) {
goto out;
}
W1_GPIO_WAIT_US(timing->a);
ret = gpio_pin_set_dt(spec, 1);
if (ret < 0) {
goto out;
}
W1_GPIO_WAIT_US(timing->e);
ret = gpio_pin_get_dt(spec);
if (ret < 0) {
goto out;
}
ret &= 0x01;
W1_GPIO_WAIT_US(timing->f);
out:
W1_GPIO_EXIT_CRITICAL(key);
return ret;
}
static int w1_gpio_write_bit(const struct device *dev, const bool bit)
{
const struct w1_gpio_config *cfg = dev->config;
const struct w1_gpio_data *data = dev->data;
const struct gpio_dt_spec *spec = &cfg->spec;
const struct w1_gpio_timing *timing = data->timing;
int ret = 0;
unsigned int key = W1_GPIO_ENTER_CRITICAL();
ret = gpio_pin_set_dt(spec, 0);
if (ret < 0) {
goto out;
}
W1_GPIO_WAIT_US(bit ? timing->a : timing->c);
ret = gpio_pin_set_dt(spec, 1);
if (ret < 0) {
goto out;
}
W1_GPIO_WAIT_US(bit ? timing->b : timing->d);
out:
W1_GPIO_EXIT_CRITICAL(key);
return ret;
}
static int w1_gpio_read_byte(const struct device *dev)
{
int ret = 0;
int byte = 0x00;
for (int i = 0; i < 8; i++) {
ret = w1_gpio_read_bit(dev);
if (ret < 0) {
return ret;
}
byte >>= 1;
if (ret) {
byte |= 0x80;
}
}
return byte;
}
static int w1_gpio_write_byte(const struct device *dev, const uint8_t byte)
{
int ret = 0;
uint8_t write = byte;
for (int i = 0; i < 8; i++) {
ret = w1_gpio_write_bit(dev, write & 0x01);
if (ret < 0) {
return ret;
}
write >>= 1;
}
return ret;
}
static int w1_gpio_configure(const struct device *dev, enum w1_settings_type type, uint32_t value)
{
struct w1_gpio_data *data = dev->data;
switch (type) {
case W1_SETTING_SPEED:
data->overdrive_active = (value != 0);
data->timing = data->overdrive_active ? &od : &std;
return 0;
default:
return -ENOTSUP;
}
}
static int w1_gpio_init(const struct device *dev)
{
const struct w1_gpio_config *cfg = dev->config;
const struct gpio_dt_spec *spec = &cfg->spec;
struct w1_gpio_data *data = dev->data;
if (gpio_is_ready_dt(spec)) {
int ret = gpio_pin_configure_dt(spec, GPIO_OUTPUT_INACTIVE | GPIO_OPEN_DRAIN |
GPIO_INPUT);
if (ret < 0) {
LOG_ERR("Failed to configure GPIO port %s pin %d", spec->port->name,
spec->pin);
return ret;
}
} else {
LOG_ERR("GPIO port %s is not ready", spec->port->name);
return -ENODEV;
}
data->timing = &std;
data->overdrive_active = false;
LOG_DBG("w1-gpio initialized, with %d slave devices", cfg->master_config.slave_count);
return 0;
}
static const struct w1_driver_api w1_gpio_driver_api = {
.reset_bus = w1_gpio_reset_bus,
.read_bit = w1_gpio_read_bit,
.write_bit = w1_gpio_write_bit,
.read_byte = w1_gpio_read_byte,
.write_byte = w1_gpio_write_byte,
.configure = w1_gpio_configure,
};
#define W1_ZEPHYR_GPIO_INIT(inst) \
static const struct w1_gpio_config w1_gpio_cfg_##inst = { \
.master_config.slave_count = W1_INST_SLAVE_COUNT(inst), \
.spec = GPIO_DT_SPEC_INST_GET(inst, gpios)}; \
static struct w1_gpio_data w1_gpio_data_##inst = {}; \
DEVICE_DT_INST_DEFINE(inst, &w1_gpio_init, NULL, &w1_gpio_data_##inst, \
&w1_gpio_cfg_##inst, POST_KERNEL, CONFIG_W1_INIT_PRIORITY, \
&w1_gpio_driver_api);
DT_INST_FOREACH_STATUS_OKAY(W1_ZEPHYR_GPIO_INIT)