1 /*
2  * Copyright (c), 2023 Basalte bv
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_sc18im704_gpio
8 
9 #include <errno.h>
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 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(gpio_sc18im, CONFIG_GPIO_LOG_LEVEL);
18 
19 #include "i2c/i2c_sc18im704.h"
20 
21 #define GPIO_SC18IM_MAX_PINS		8
22 
23 /* After reset the GPIO config registers are 0x55 */
24 #define GPIO_SC18IM_DEFAULT_CONF	0x55
25 
26 #define GPIO_SC18IM_CONF_INPUT		0x01
27 #define GPIO_SC18IM_CONF_PUSH_PULL	0x02
28 #define GPIO_SC18IM_CONF_OPEN_DRAIN	0x03
29 #define GPIO_SC18IM_CONF_MASK		0x03
30 
31 struct gpio_sc18im_config {
32 	/* gpio_driver_config needs to be first */
33 	struct gpio_driver_config common;
34 
35 	const struct device *bridge;
36 };
37 
38 struct gpio_sc18im_data {
39 	/* gpio_driver_data needs to be first */
40 	struct gpio_driver_data common;
41 
42 	uint8_t conf1;
43 	uint8_t conf2;
44 	uint8_t output_state;
45 };
46 
gpio_sc18im_port_set_raw(const struct device * port,uint8_t mask,uint8_t value,uint8_t toggle)47 static int gpio_sc18im_port_set_raw(const struct device *port,
48 				    uint8_t mask, uint8_t value, uint8_t toggle)
49 {
50 	const struct gpio_sc18im_config *cfg = port->config;
51 	struct gpio_sc18im_data *data = port->data;
52 	uint8_t buf[] = {
53 		SC18IM704_CMD_WRITE_GPIO,
54 		data->output_state,
55 		SC18IM704_CMD_STOP,
56 	};
57 	int ret;
58 
59 	if (k_is_in_isr()) {
60 		return -EWOULDBLOCK;
61 	}
62 
63 	buf[1] &= ~mask;
64 	buf[1] |= (value & mask);
65 	buf[1] ^= toggle;
66 
67 	ret = sc18im704_transfer(cfg->bridge, buf, sizeof(buf), NULL, 0);
68 	if (ret < 0) {
69 		LOG_ERR("Failed to write GPIO state (%d)", ret);
70 		return ret;
71 	}
72 
73 	data->output_state = buf[1];
74 
75 	return 0;
76 }
77 
gpio_sc18im_pin_configure(const struct device * port,gpio_pin_t pin,gpio_flags_t flags)78 static int gpio_sc18im_pin_configure(const struct device *port, gpio_pin_t pin,
79 				     gpio_flags_t flags)
80 {
81 	const struct gpio_sc18im_config *cfg = port->config;
82 	struct gpio_sc18im_data *data = port->data;
83 	uint8_t pin_conf;
84 	int ret;
85 	uint8_t buf[] = {
86 		SC18IM704_CMD_WRITE_REG,
87 		0x00,
88 		0x00,
89 		SC18IM704_CMD_STOP,
90 	};
91 
92 	if (pin >= GPIO_SC18IM_MAX_PINS) {
93 		return -EINVAL;
94 	}
95 
96 	if (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) {
97 		return -ENOTSUP;
98 	}
99 
100 	if (flags & GPIO_INPUT) {
101 		pin_conf = GPIO_SC18IM_CONF_INPUT;
102 	} else if (flags & GPIO_OUTPUT) {
103 		if (flags & GPIO_SINGLE_ENDED) {
104 			if (flags & GPIO_LINE_OPEN_DRAIN) {
105 				pin_conf = GPIO_SC18IM_CONF_OPEN_DRAIN;
106 			} else {
107 				/* Open-drain is the only supported single-ended mode */
108 				return -ENOTSUP;
109 			}
110 		} else {
111 			/* Default to push/pull */
112 			pin_conf = GPIO_SC18IM_CONF_PUSH_PULL;
113 		}
114 	} else {
115 		/* Neither input nor output mode is selected */
116 		return -ENOTSUP;
117 	}
118 
119 	ret = sc18im704_claim(cfg->bridge);
120 	if (ret < 0) {
121 		LOG_ERR("Failed to claim bridge (%d)", ret);
122 		return ret;
123 	}
124 
125 	if (pin < 4) {
126 		/* Shift the config to the pin offset */
127 		data->conf1 &= ~(GPIO_SC18IM_CONF_MASK << (pin * 2));
128 		data->conf1 |= pin_conf << (pin * 2);
129 
130 		buf[1] = SC18IM704_REG_GPIO_CONF1;
131 		buf[2] = data->conf1;
132 	} else {
133 		/* Shift the config to the pin offset */
134 		data->conf2 &= ~(GPIO_SC18IM_CONF_MASK << ((pin - 4) * 2));
135 		data->conf2 |= pin_conf << ((pin - 4) * 2);
136 
137 		buf[1] = SC18IM704_REG_GPIO_CONF2;
138 		buf[2] = data->conf2;
139 	}
140 
141 	ret = sc18im704_transfer(cfg->bridge, buf, sizeof(buf), NULL, 0);
142 	if (ret < 0) {
143 		LOG_ERR("Failed to configure GPIO (%d)", ret);
144 	}
145 
146 	if (ret == 0 && flags & GPIO_OUTPUT) {
147 		if (flags & GPIO_OUTPUT_INIT_HIGH) {
148 			gpio_sc18im_port_set_raw(port, BIT(pin), BIT(pin), 0);
149 		}
150 		if (flags & GPIO_OUTPUT_INIT_LOW) {
151 			gpio_sc18im_port_set_raw(port, BIT(pin), 0, 0);
152 		}
153 	}
154 
155 	sc18im704_release(cfg->bridge);
156 
157 	return ret;
158 }
159 
160 #ifdef CONFIG_GPIO_GET_CONFIG
gpio_sc18im_pin_get_config(const struct device * port,gpio_pin_t pin,gpio_flags_t * flags)161 static int gpio_sc18im_pin_get_config(const struct device *port, gpio_pin_t pin,
162 				      gpio_flags_t *flags)
163 {
164 	struct gpio_sc18im_data *data = port->data;
165 	uint8_t conf;
166 
167 	if (pin >= GPIO_SC18IM_MAX_PINS) {
168 		return -EINVAL;
169 	}
170 
171 	if (pin < 4) {
172 		conf = (data->conf1 >> (2 * pin)) & GPIO_SC18IM_CONF_MASK;
173 	} else {
174 		conf = (data->conf2 >> (2 * (pin - 4))) & GPIO_SC18IM_CONF_MASK;
175 	}
176 
177 	switch (conf) {
178 	case GPIO_SC18IM_CONF_PUSH_PULL:
179 		*flags = GPIO_OUTPUT | GPIO_PUSH_PULL;
180 		break;
181 	case GPIO_SC18IM_CONF_OPEN_DRAIN:
182 		*flags = GPIO_OUTPUT | GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN;
183 		break;
184 	case GPIO_SC18IM_CONF_INPUT:
185 	default:
186 		*flags = GPIO_INPUT;
187 		break;
188 	}
189 
190 	return 0;
191 }
192 #endif
193 
gpio_sc18im_port_get_raw(const struct device * port,gpio_port_value_t * value)194 static int gpio_sc18im_port_get_raw(const struct device *port, gpio_port_value_t *value)
195 {
196 	const struct gpio_sc18im_config *cfg = port->config;
197 
198 	uint8_t buf[] = {
199 		SC18IM704_CMD_READ_GPIO,
200 		SC18IM704_CMD_STOP,
201 	};
202 	uint8_t data;
203 	int ret;
204 
205 	if (k_is_in_isr()) {
206 		return -EWOULDBLOCK;
207 	}
208 
209 	ret = sc18im704_transfer(cfg->bridge, buf, sizeof(buf), &data, 1);
210 	if (ret < 0) {
211 		LOG_ERR("Failed to read GPIO state (%d)", ret);
212 		return ret;
213 	}
214 
215 	*value = data;
216 
217 	return 0;
218 }
219 
gpio_sc18im_port_set_masked_raw(const struct device * port,gpio_port_pins_t mask,gpio_port_value_t value)220 static int gpio_sc18im_port_set_masked_raw(const struct device *port,
221 					   gpio_port_pins_t mask,
222 					   gpio_port_value_t value)
223 {
224 	return gpio_sc18im_port_set_raw(port, (uint8_t)mask, (uint8_t)value, 0);
225 }
226 
gpio_sc18im_port_set_bits_raw(const struct device * port,gpio_port_pins_t pins)227 static int gpio_sc18im_port_set_bits_raw(const struct device *port, gpio_port_pins_t pins)
228 {
229 	return gpio_sc18im_port_set_raw(port, (uint8_t)pins, (uint8_t)pins, 0);
230 }
231 
gpio_sc18im_port_clear_bits_raw(const struct device * port,gpio_port_pins_t pins)232 static int gpio_sc18im_port_clear_bits_raw(const struct device *port, gpio_port_pins_t pins)
233 {
234 	return gpio_sc18im_port_set_raw(port, (uint8_t)pins, 0, 0);
235 }
236 
gpio_sc18im_port_toggle_bits(const struct device * port,gpio_port_pins_t pins)237 static int gpio_sc18im_port_toggle_bits(const struct device *port, gpio_port_pins_t pins)
238 {
239 	return gpio_sc18im_port_set_raw(port, 0, 0, (uint8_t)pins);
240 }
241 
gpio_sc18im_init(const struct device * dev)242 static int gpio_sc18im_init(const struct device *dev)
243 {
244 	const struct gpio_sc18im_config *cfg = dev->config;
245 
246 	if (!device_is_ready(cfg->bridge)) {
247 		LOG_ERR("Parent device not ready");
248 		return -ENODEV;
249 	}
250 
251 	return 0;
252 }
253 
254 static const struct gpio_driver_api gpio_sc18im_driver_api = {
255 	.pin_configure = gpio_sc18im_pin_configure,
256 #ifdef CONFIG_GPIO_GET_CONFIG
257 	.pin_get_config = gpio_sc18im_pin_get_config,
258 #endif
259 	.port_get_raw = gpio_sc18im_port_get_raw,
260 	.port_set_masked_raw = gpio_sc18im_port_set_masked_raw,
261 	.port_set_bits_raw = gpio_sc18im_port_set_bits_raw,
262 	.port_clear_bits_raw = gpio_sc18im_port_clear_bits_raw,
263 	.port_toggle_bits = gpio_sc18im_port_toggle_bits,
264 };
265 
266 #define CHECK_COMPAT(node)									\
267 	COND_CODE_1(DT_NODE_HAS_COMPAT(node, nxp_sc18im704_i2c), (DEVICE_DT_GET(node)), ())
268 
269 #define GPIO_SC18IM704_I2C_SIBLING(n)								\
270 	DT_FOREACH_CHILD_STATUS_OKAY(DT_INST_PARENT(n), CHECK_COMPAT)
271 
272 #define GPIO_SC18IM704_DEFINE(n)								\
273 	static const struct gpio_sc18im_config gpio_sc18im_config_##n = {			\
274 		.common = {									\
275 			.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),			\
276 		},										\
277 		.bridge = GPIO_SC18IM704_I2C_SIBLING(n),					\
278 	};											\
279 	static struct gpio_sc18im_data gpio_sc18im_data_##n = {					\
280 		.conf1 = GPIO_SC18IM_DEFAULT_CONF,						\
281 		.conf2 = GPIO_SC18IM_DEFAULT_CONF,						\
282 	};											\
283 												\
284 	DEVICE_DT_INST_DEFINE(n, gpio_sc18im_init, NULL,					\
285 			      &gpio_sc18im_data_##n, &gpio_sc18im_config_##n,			\
286 			      POST_KERNEL, CONFIG_GPIO_SC18IM704_INIT_PRIORITY,			\
287 			      &gpio_sc18im_driver_api);
288 
289 DT_INST_FOREACH_STATUS_OKAY(GPIO_SC18IM704_DEFINE);
290