| /* |
| * Copyright (c) 2022 Vestas Wind Systems A/S |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT can_transceiver_gpio |
| |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/can/transceiver.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/logging/log.h> |
| |
| 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) |