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