drivers: w1: add zephyr-gpio driver

The zephyr-gpio w1 driver introduced in this commit implements
all routines for the w1 api on top of the zephyr gpio driver.
W1 bit read, write, and reset operations are executed by
bit-banging the selected gpio.

Signed-off-by: Hudson C. Dalpra <hudson@bduncanltd.com>
This commit is contained in:
Hudson C. Dalpra 2023-11-30 16:33:09 +13:00 committed by Carles Cufí
parent 8f6be9661e
commit 410684c7b0
10 changed files with 453 additions and 0 deletions

View file

@ -14,6 +14,7 @@ zephyr_library_sources_ifdef(CONFIG_W1_DS2484 w1_ds2484.c)
zephyr_library_sources_ifdef(CONFIG_W1_DS2485 w1_ds2485.c)
zephyr_library_sources_ifdef(CONFIG_W1_DS2477_85_COMMON w1_ds2477_85_common.c)
zephyr_library_sources_ifdef(CONFIG_W1_TEST w1_test.c)
zephyr_library_sources_ifdef(CONFIG_W1_ZEPHYR_GPIO w1_zephyr_gpio.c)
zephyr_library_sources_ifdef(CONFIG_W1_ZEPHYR_SERIAL w1_zephyr_serial.c)
# network functions:

View file

@ -44,6 +44,7 @@ rsource "Kconfig.ds2484"
rsource "Kconfig.ds2477_85"
rsource "Kconfig.ds2485"
rsource "Kconfig.test"
rsource "Kconfig.zephyr_gpio"
rsource "Kconfig.zephyr_serial"
config W1_NET

View file

@ -0,0 +1,28 @@
# Configuration options for the Zephyr GPIO 1-Wire Master driver
# Copyright (c) 2023 Hudson C. Dalpra
# SPDX-License-Identifier: Apache-2.0
config W1_ZEPHYR_GPIO
bool "1-wire GPIO"
default y
depends on DT_HAS_ZEPHYR_W1_GPIO_ENABLED
help
This option enables the Zephyr GPIO 1-Wire master driver.
The bus reset, and bit read and write operations are executed
via byte read and write operations on top of the Zephyr
GPIO driver interface.
if W1_ZEPHYR_GPIO
config W1_ZEPHYR_GPIO_TIME_CRITICAL
bool "Force time critical operations"
default y
help
This option forces the 1-Wire GPIO driver to use time critical
operations for bus reset, and bit read and write operations.
Time critical communications operations are not interrupted while
being generated.
endif # W1_ZEPHYR_GPIO

326
drivers/w1/w1_zephyr_gpio.c Normal file
View file

@ -0,0 +1,326 @@
/*
* 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) ^ 0x01;
if (ret < 0) {
goto out;
}
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) & 0x01;
if (ret < 0) {
goto out;
}
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_PULL_UP);
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)

View file

@ -0,0 +1,29 @@
# Copyright (c) 2023 Hudson C. Dalpra
# SPDX-License-Identifier: Apache-2.0
description: |
Zephyr W1 GPIO node
This defines a one-wire driver through GPIO bit-banging.
For example:
/ {
w1: w1 {
compatible = "zephyr,w1-gpio";
gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN | GPIO_PULL_UP)>;
};
};
Above:
- w1 is pin 13 on gpio0. The gpio is active when the pin is high, is
configured as an open-drain, and has a pull-up resistor.
compatible: "zephyr,w1-gpio"
include: [w1-master.yaml]
properties:
gpios:
type: phandle-array
required: true

View file

@ -0,0 +1,9 @@
# Copyright (c) 2023 Hudson C. Dalpra
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(build_all)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2023 Hudson C. Dalpra
*
* SPDX-License-Identifier: Apache-2.0
*
* Application overlay for testing driver builds
*
* Names in this file should be chosen in a way that won't conflict
* with real-world devicetree nodes, to allow these tests to run on
* (and be extended to test) real hardware.
*/
/ {
test {
#address-cells = <1>;
#size-cells = <1>;
test_gpio: gpio@deadbeef {
compatible = "vnd,gpio";
gpio-controller;
reg = <0xdeadbeef 0x1000>;
#gpio-cells = <0x2>;
status = "okay";
};
test_w1_gpio: test_w1_gpio {
compatible = "zephyr,w1-gpio";
gpios = <&test_gpio 0 0>;
};
};
};

View file

@ -0,0 +1,7 @@
# Copyright (c) 2023 Hudson C. Dalpra
# SPDX-License-Identifier: Apache-2.0
CONFIG_TEST=y
CONFIG_TEST_USERSPACE=y
CONFIG_GPIO=y
CONFIG_W1=y

View file

@ -0,0 +1,10 @@
/*
* Copyright (c) 2023 Hudson C. Dalpra
*
* SPDX-License-Identifier: Apache-2.0
*/
int main(void)
{
return 0;
}

View file

@ -0,0 +1,11 @@
# Copyright (c) 2023 Hudson C. Dalpra
# SPDX-License-Identifier: Apache-2.0
tests:
drivers.w1.build:
build_only: true
tags:
- drivers
- w1
integration_platforms:
- native_posix