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