diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index 9f3ececce9d..ce0dddf4dcb 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -16,3 +16,5 @@ zephyr_library_sources_ifdef(CONFIG_CAN_RCAR can_rcar.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE can_handlers.c) zephyr_library_sources_ifdef(CONFIG_CAN_SHELL can_shell.c) + +add_subdirectory(transceiver) diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 58099f0381e..5b96f876a83 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -102,4 +102,6 @@ source "drivers/can/Kconfig.mcan" source "drivers/can/Kconfig.rcar" source "drivers/can/Kconfig.loopback" +source "drivers/can/transceiver/Kconfig" + endif # CAN diff --git a/drivers/can/transceiver/CMakeLists.txt b/drivers/can/transceiver/CMakeLists.txt new file mode 100644 index 00000000000..fb60eb2e120 --- /dev/null +++ b/drivers/can/transceiver/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2022 Vestas Wind Systems A/S +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_property(ALLOW_EMPTY TRUE) +zephyr_library_sources_ifdef(CONFIG_CAN_TRANSCEIVER_GPIO can_transceiver_gpio.c) diff --git a/drivers/can/transceiver/Kconfig b/drivers/can/transceiver/Kconfig new file mode 100644 index 00000000000..fa3029ba3c7 --- /dev/null +++ b/drivers/can/transceiver/Kconfig @@ -0,0 +1,21 @@ +# CAN transceiver configuration options + +# Copyright (c) 2022 Vestas Wind Systems A/S +# SPDX-License-Identifier: Apache-2.0 + +menu "CAN transceiver drivers" + +config CAN_TRANSCEIVER_INIT_PRIORITY + int "CAN transceiver driver init priority" + default 70 + help + CAN transceiver device driver initialization priority. + +config CAN_TRANSCEIVER_GPIO + bool "GPIO controlled CAN transceiver" + depends on GPIO + default $(dt_compat_enabled,can-transceiver-gpio) + help + Enable support for GPIO controlled CAN transceivers. + +endmenu diff --git a/drivers/can/transceiver/can_transceiver_gpio.c b/drivers/can/transceiver/can_transceiver_gpio.c new file mode 100644 index 00000000000..b0af4f7ff82 --- /dev/null +++ b/drivers/can/transceiver/can_transceiver_gpio.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2022 Vestas Wind Systems A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT can_transceiver_gpio + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(can_transceiver_gpio, CONFIG_CAN_LOG_LEVEL); + +/* Does any devicetree instance have an enable-gpios property? */ +#define INST_HAS_ENABLE_GPIOS_OR(inst) DT_INST_NODE_HAS_PROP(inst, enable_gpios) || +#define ANY_INST_HAS_ENABLE_GPIOS DT_INST_FOREACH_STATUS_OKAY(INST_HAS_ENABLE_GPIOS_OR) 0 + +/* Does any devicetree instance have a standby-gpios property? */ +#define INST_HAS_STANDBY_GPIOS_OR(inst) DT_INST_NODE_HAS_PROP(inst, standby_gpios) || +#define ANY_INST_HAS_STANDBY_GPIOS DT_INST_FOREACH_STATUS_OKAY(INST_HAS_STANDBY_GPIOS_OR) 0 + +struct can_transceiver_gpio_config { +#if ANY_INST_HAS_ENABLE_GPIOS + struct gpio_dt_spec enable_gpio; +#endif /* ANY_INST_HAS_ENABLE_GPIOS */ +#if ANY_INST_HAS_STANDBY_GPIOS + struct gpio_dt_spec standby_gpio; +#endif /* ANY_INST_HAS_STANDBY_GPIOS */ +}; + +static int can_transceiver_gpio_set_state(const struct device *dev, bool enabled) +{ + const struct can_transceiver_gpio_config *config = dev->config; + int err; + +#if ANY_INST_HAS_ENABLE_GPIOS + if (config->enable_gpio.port != NULL) { + err = gpio_pin_set_dt(&config->enable_gpio, enabled ? 1 : 0); + if (err != 0) { + LOG_ERR("failed to set enable GPIO pin (err %d)", err); + return -EIO; + } + } +#endif /* ANY_INST_HAS_ENABLE_GPIOS */ + +#if ANY_INST_HAS_STANDBY_GPIOS + if (config->standby_gpio.port != NULL) { + err = gpio_pin_set_dt(&config->standby_gpio, enabled ? 0 : 1); + if (err != 0) { + LOG_ERR("failed to set standby GPIO pin (err %d)", err); + return -EIO; + } + } +#endif /* ANY_INST_HAS_STANDBY_GPIOS */ + + return 0; +} + +static int can_transceiver_gpio_enable(const struct device *dev) +{ + return can_transceiver_gpio_set_state(dev, true); +} + +static int can_transceiver_gpio_disable(const struct device *dev) +{ + return can_transceiver_gpio_set_state(dev, false); +} + +static int can_transceiver_gpio_init(const struct device *dev) +{ + const struct can_transceiver_gpio_config *config = dev->config; + int err; + +#if ANY_INST_HAS_ENABLE_GPIOS + if (config->enable_gpio.port != NULL) { + if (!device_is_ready(config->enable_gpio.port)) { + LOG_ERR("enable pin GPIO device not ready"); + return -EINVAL; + } + + /* CAN transceiver is disabled during initialization */ + err = gpio_pin_configure_dt(&config->enable_gpio, GPIO_OUTPUT_INACTIVE); + if (err != 0) { + LOG_ERR("failed to configure enable GPIO pin (err %d)", err); + return err; + } + } +#endif /* ANY_INST_HAS_ENABLE_GPIOS */ + +#if ANY_INST_HAS_STANDBY_GPIOS + if (config->standby_gpio.port != NULL) { + if (!device_is_ready(config->standby_gpio.port)) { + LOG_ERR("standby pin GPIO device not ready"); + return -EINVAL; + } + + /* CAN transceiver is put in standby during initialization */ + err = gpio_pin_configure_dt(&config->standby_gpio, GPIO_OUTPUT_ACTIVE); + if (err != 0) { + LOG_ERR("failed to configure standby GPIO pin (err %d)", err); + return err; + } + } +#endif /* ANY_INST_HAS_STANDBY_GPIOS */ + + return 0; +} + +static const struct can_transceiver_driver_api can_transceiver_gpio_driver_api = { + .enable = can_transceiver_gpio_enable, + .disable = can_transceiver_gpio_disable, +}; + +#define CAN_TRANSCEIVER_GPIO_COND(inst, name) \ + IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, name##_gpios), \ + (.name##_gpio = GPIO_DT_SPEC_INST_GET(inst, name##_gpios),)) + +#define CAN_TRANSCEIVER_GPIO_INIT(inst) \ + static const struct can_transceiver_gpio_config can_transceiver_gpio_config_##inst = { \ + CAN_TRANSCEIVER_GPIO_COND(inst, enable) \ + CAN_TRANSCEIVER_GPIO_COND(inst, standby) \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, &can_transceiver_gpio_init, \ + NULL, NULL, &can_transceiver_gpio_config_##inst,\ + POST_KERNEL, CONFIG_CAN_TRANSCEIVER_INIT_PRIORITY, \ + &can_transceiver_gpio_driver_api); \ + +DT_INST_FOREACH_STATUS_OKAY(CAN_TRANSCEIVER_GPIO_INIT)