1 /*
2 * Copyright (c) 2022 SILA Embedded Solutions GmbH
3 * Copyright (c) 2024 SILA Embedded Solutions GmbH
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT rohm_bd8lb600fs_gpio
9
10 #include <zephyr/kernel.h>
11 #include <zephyr/device.h>
12 #include <zephyr/init.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/drivers/gpio/gpio_utils.h>
15 #include <zephyr/drivers/mfd/bd8lb600fs.h>
16 #include <zephyr/logging/log.h>
17
18 LOG_MODULE_REGISTER(gpio_bd8lb600fs, CONFIG_GPIO_LOG_LEVEL);
19
20 struct bd8lb600fs_gpio_config {
21 /* gpio_driver_config needs to be first */
22 struct gpio_driver_config common;
23 const struct device *parent_dev;
24 int gpios_count;
25 };
26
27 struct bd8lb600fs_gpio_data {
28 /* gpio_driver_data needs to be first */
29 struct gpio_driver_data data;
30 /* each bit is one output channel, bit 0 = channel 1, ... */
31 uint32_t state;
32 /* each bit defines if the output channel is configured, see state */
33 uint32_t configured;
34 struct k_mutex lock;
35 };
36
bd8lb600fs_gpio_pin_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)37 static int bd8lb600fs_gpio_pin_configure(const struct device *dev, gpio_pin_t pin,
38 gpio_flags_t flags)
39 {
40 const struct bd8lb600fs_gpio_config *config = dev->config;
41 struct bd8lb600fs_gpio_data *data = dev->data;
42
43 /* cannot execute a bus operation in an ISR context */
44 if (k_is_in_isr()) {
45 return -EWOULDBLOCK;
46 }
47
48 if (pin >= config->gpios_count) {
49 LOG_ERR("invalid pin number %i", pin);
50 return -EINVAL;
51 }
52
53 if ((flags & GPIO_INPUT) != 0) {
54 LOG_ERR("cannot configure pin as input");
55 return -ENOTSUP;
56 }
57
58 if ((flags & GPIO_OUTPUT) == 0) {
59 LOG_ERR("pin must be configured as an output");
60 return -ENOTSUP;
61 }
62
63 if ((flags & GPIO_SINGLE_ENDED) == 0) {
64 LOG_ERR("pin must be configured as single ended");
65 return -ENOTSUP;
66 }
67
68 if ((flags & GPIO_LINE_OPEN_DRAIN) == 0) {
69 LOG_ERR("pin must be configured as open drain");
70 return -ENOTSUP;
71 }
72
73 if ((flags & GPIO_PULL_UP) != 0) {
74 LOG_ERR("pin cannot have a pull up configured");
75 return -ENOTSUP;
76 }
77
78 if ((flags & GPIO_PULL_DOWN) != 0) {
79 LOG_ERR("pin cannot have a pull down configured");
80 return -ENOTSUP;
81 }
82
83 k_mutex_lock(&data->lock, K_FOREVER);
84
85 if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
86 WRITE_BIT(data->state, pin, 0);
87 } else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
88 WRITE_BIT(data->state, pin, 1);
89 }
90
91 WRITE_BIT(data->configured, pin, 1);
92
93 int result = mfd_bd8lb600fs_set_outputs(config->parent_dev, data->state);
94
95 k_mutex_unlock(&data->lock);
96
97 return result;
98 }
99
bd8lb600fs_gpio_port_get_raw(const struct device * dev,uint32_t * value)100 static int bd8lb600fs_gpio_port_get_raw(const struct device *dev, uint32_t *value)
101 {
102 LOG_ERR("input pins are not available");
103 return -ENOTSUP;
104 }
105
bd8lb600fs_gpio_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)106 static int bd8lb600fs_gpio_port_set_masked_raw(const struct device *dev, uint32_t mask,
107 uint32_t value)
108 {
109 const struct bd8lb600fs_gpio_config *config = dev->config;
110 struct bd8lb600fs_gpio_data *data = dev->data;
111
112 /* cannot execute a bus operation in an ISR context */
113 if (k_is_in_isr()) {
114 return -EWOULDBLOCK;
115 }
116
117 k_mutex_lock(&data->lock, K_FOREVER);
118 data->state = (data->state & ~mask) | (mask & value);
119
120 int result = mfd_bd8lb600fs_set_outputs(config->parent_dev, data->state);
121
122 k_mutex_unlock(&data->lock);
123
124 return result;
125 }
126
bd8lb600fs_gpio_port_set_bits_raw(const struct device * dev,uint32_t mask)127 static int bd8lb600fs_gpio_port_set_bits_raw(const struct device *dev, uint32_t mask)
128 {
129 return bd8lb600fs_gpio_port_set_masked_raw(dev, mask, mask);
130 }
131
bd8lb600fs_gpio_port_clear_bits_raw(const struct device * dev,uint32_t mask)132 static int bd8lb600fs_gpio_port_clear_bits_raw(const struct device *dev, uint32_t mask)
133 {
134 return bd8lb600fs_gpio_port_set_masked_raw(dev, mask, 0);
135 }
136
bd8lb600fs_gpio_port_toggle_bits(const struct device * dev,uint32_t mask)137 static int bd8lb600fs_gpio_port_toggle_bits(const struct device *dev, uint32_t mask)
138 {
139 const struct bd8lb600fs_gpio_config *config = dev->config;
140 struct bd8lb600fs_gpio_data *data = dev->data;
141
142 /* cannot execute a bus operation in an ISR context */
143 if (k_is_in_isr()) {
144 return -EWOULDBLOCK;
145 }
146
147 k_mutex_lock(&data->lock, K_FOREVER);
148 data->state ^= mask;
149
150 int result = mfd_bd8lb600fs_set_outputs(config->parent_dev, data->state);
151
152 k_mutex_unlock(&data->lock);
153
154 return result;
155 }
156
157 static DEVICE_API(gpio, api_table) = {
158 .pin_configure = bd8lb600fs_gpio_pin_configure,
159 .port_get_raw = bd8lb600fs_gpio_port_get_raw,
160 .port_set_masked_raw = bd8lb600fs_gpio_port_set_masked_raw,
161 .port_set_bits_raw = bd8lb600fs_gpio_port_set_bits_raw,
162 .port_clear_bits_raw = bd8lb600fs_gpio_port_clear_bits_raw,
163 .port_toggle_bits = bd8lb600fs_gpio_port_toggle_bits,
164 };
165
bd8lb600fs_gpio_init(const struct device * dev)166 static int bd8lb600fs_gpio_init(const struct device *dev)
167 {
168 const struct bd8lb600fs_gpio_config *config = dev->config;
169 struct bd8lb600fs_gpio_data *data = dev->data;
170
171 if (!device_is_ready(config->parent_dev)) {
172 LOG_ERR("MFD parent is not ready");
173 return -ENODEV;
174 }
175
176 int result = k_mutex_init(&data->lock);
177
178 if (result != 0) {
179 LOG_ERR("unable to initialize mutex");
180 return result;
181 }
182
183 return 0;
184 }
185
186 #define BD8LB600FS_GPIO_INIT(inst) \
187 static const struct bd8lb600fs_gpio_config bd8lb600fs_##inst##_config = { \
188 .common = \
189 { \
190 .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \
191 }, \
192 .parent_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
193 .gpios_count = DT_INST_PROP(inst, ngpios), \
194 }; \
195 \
196 static struct bd8lb600fs_gpio_data bd8lb600fs_##inst##_data = { \
197 .state = 0x00, \
198 .configured = 0x00, \
199 }; \
200 \
201 DEVICE_DT_INST_DEFINE(inst, bd8lb600fs_gpio_init, NULL, &bd8lb600fs_##inst##_data, \
202 &bd8lb600fs_##inst##_config, POST_KERNEL, \
203 CONFIG_GPIO_BD8LB600FS_INIT_PRIORITY, &api_table);
204
205 DT_INST_FOREACH_STATUS_OKAY(BD8LB600FS_GPIO_INIT)
206