1 /*
2 * Copyright (c) 2022 Vestas Wind Systems A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT can_transceiver_gpio
8
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/can/transceiver.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/logging/log.h>
13
14 LOG_MODULE_REGISTER(can_transceiver_gpio, CONFIG_CAN_LOG_LEVEL);
15
16 /* Does any devicetree instance have an enable-gpios property? */
17 #define INST_HAS_ENABLE_GPIOS_OR(inst) DT_INST_NODE_HAS_PROP(inst, enable_gpios) ||
18 #define ANY_INST_HAS_ENABLE_GPIOS DT_INST_FOREACH_STATUS_OKAY(INST_HAS_ENABLE_GPIOS_OR) 0
19
20 /* Does any devicetree instance have a standby-gpios property? */
21 #define INST_HAS_STANDBY_GPIOS_OR(inst) DT_INST_NODE_HAS_PROP(inst, standby_gpios) ||
22 #define ANY_INST_HAS_STANDBY_GPIOS DT_INST_FOREACH_STATUS_OKAY(INST_HAS_STANDBY_GPIOS_OR) 0
23
24 struct can_transceiver_gpio_config {
25 #if ANY_INST_HAS_ENABLE_GPIOS
26 struct gpio_dt_spec enable_gpio;
27 #endif /* ANY_INST_HAS_ENABLE_GPIOS */
28 #if ANY_INST_HAS_STANDBY_GPIOS
29 struct gpio_dt_spec standby_gpio;
30 #endif /* ANY_INST_HAS_STANDBY_GPIOS */
31 };
32
can_transceiver_gpio_set_state(const struct device * dev,bool enabled)33 static int can_transceiver_gpio_set_state(const struct device *dev, bool enabled)
34 {
35 const struct can_transceiver_gpio_config *config = dev->config;
36 int err;
37
38 #if ANY_INST_HAS_ENABLE_GPIOS
39 if (config->enable_gpio.port != NULL) {
40 err = gpio_pin_set_dt(&config->enable_gpio, enabled ? 1 : 0);
41 if (err != 0) {
42 LOG_ERR("failed to set enable GPIO pin (err %d)", err);
43 return -EIO;
44 }
45 }
46 #endif /* ANY_INST_HAS_ENABLE_GPIOS */
47
48 #if ANY_INST_HAS_STANDBY_GPIOS
49 if (config->standby_gpio.port != NULL) {
50 err = gpio_pin_set_dt(&config->standby_gpio, enabled ? 0 : 1);
51 if (err != 0) {
52 LOG_ERR("failed to set standby GPIO pin (err %d)", err);
53 return -EIO;
54 }
55 }
56 #endif /* ANY_INST_HAS_STANDBY_GPIOS */
57
58 return 0;
59 }
60
can_transceiver_gpio_enable(const struct device * dev,can_mode_t mode)61 static int can_transceiver_gpio_enable(const struct device *dev, can_mode_t mode)
62 {
63 ARG_UNUSED(mode);
64
65 return can_transceiver_gpio_set_state(dev, true);
66 }
67
can_transceiver_gpio_disable(const struct device * dev)68 static int can_transceiver_gpio_disable(const struct device *dev)
69 {
70 return can_transceiver_gpio_set_state(dev, false);
71 }
72
can_transceiver_gpio_init(const struct device * dev)73 static int can_transceiver_gpio_init(const struct device *dev)
74 {
75 const struct can_transceiver_gpio_config *config = dev->config;
76 int err;
77
78 #if ANY_INST_HAS_ENABLE_GPIOS
79 if (config->enable_gpio.port != NULL) {
80 if (!gpio_is_ready_dt(&config->enable_gpio)) {
81 LOG_ERR("enable pin GPIO device not ready");
82 return -EINVAL;
83 }
84
85 /* CAN transceiver is disabled during initialization */
86 err = gpio_pin_configure_dt(&config->enable_gpio, GPIO_OUTPUT_INACTIVE);
87 if (err != 0) {
88 LOG_ERR("failed to configure enable GPIO pin (err %d)", err);
89 return err;
90 }
91 }
92 #endif /* ANY_INST_HAS_ENABLE_GPIOS */
93
94 #if ANY_INST_HAS_STANDBY_GPIOS
95 if (config->standby_gpio.port != NULL) {
96 if (!gpio_is_ready_dt(&config->standby_gpio)) {
97 LOG_ERR("standby pin GPIO device not ready");
98 return -EINVAL;
99 }
100
101 /* CAN transceiver is put in standby during initialization */
102 err = gpio_pin_configure_dt(&config->standby_gpio, GPIO_OUTPUT_ACTIVE);
103 if (err != 0) {
104 LOG_ERR("failed to configure standby GPIO pin (err %d)", err);
105 return err;
106 }
107 }
108 #endif /* ANY_INST_HAS_STANDBY_GPIOS */
109
110 return 0;
111 }
112
113 static DEVICE_API(can_transceiver, can_transceiver_gpio_driver_api) = {
114 .enable = can_transceiver_gpio_enable,
115 .disable = can_transceiver_gpio_disable,
116 };
117
118 #define CAN_TRANSCEIVER_GPIO_COND(inst, name) \
119 IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, name##_gpios), \
120 (.name##_gpio = GPIO_DT_SPEC_INST_GET(inst, name##_gpios),))
121
122 #define CAN_TRANSCEIVER_GPIO_INIT(inst) \
123 BUILD_ASSERT(DT_INST_NODE_HAS_PROP(inst, enable_gpios) || \
124 DT_INST_NODE_HAS_PROP(inst, standby_gpios), \
125 "Missing GPIO property on " \
126 DT_NODE_FULL_NAME(DT_DRV_INST(inst))); \
127 \
128 static const struct can_transceiver_gpio_config can_transceiver_gpio_config_##inst = { \
129 CAN_TRANSCEIVER_GPIO_COND(inst, enable) \
130 CAN_TRANSCEIVER_GPIO_COND(inst, standby) \
131 }; \
132 \
133 DEVICE_DT_INST_DEFINE(inst, &can_transceiver_gpio_init, \
134 NULL, NULL, &can_transceiver_gpio_config_##inst,\
135 POST_KERNEL, CONFIG_CAN_TRANSCEIVER_INIT_PRIORITY, \
136 &can_transceiver_gpio_driver_api); \
137
138 DT_INST_FOREACH_STATUS_OKAY(CAN_TRANSCEIVER_GPIO_INIT)
139