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.
328 lines
7.9 KiB
328 lines
7.9 KiB
/* |
|
* 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 article from Analog Devices. |
|
* |
|
* - w1_zephyr_serial.c: drivers/w1/w1_zephyr_serial.c |
|
* - Analog Devices 1-Wire Communication Through Software: |
|
* https://www.analog.com/en/resources/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 DEVICE_API(w1, 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)
|
|
|