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