1 /*
2  * Copyright (c) 2023 Grinn
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT adi_ad559x_gpio
7 
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/gpio/gpio_utils.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/sys/byteorder.h>
12 
13 #include <zephyr/drivers/mfd/ad559x.h>
14 
15 #define AD559X_GPIO_RD_POINTER 0x60
16 
17 struct gpio_ad559x_config {
18 	/* gpio_driver_config needs to be first */
19 	struct gpio_driver_config common;
20 	const struct device *mfd_dev;
21 };
22 
23 struct gpio_ad559x_data {
24 	/* gpio_driver_data needs to be first */
25 	struct gpio_driver_data common;
26 	uint8_t gpio_val;
27 	uint8_t gpio_out;
28 	uint8_t gpio_in;
29 	uint8_t gpio_pull_down;
30 };
31 
gpio_ad559x_port_get_raw(const struct device * dev,uint32_t * value)32 static int gpio_ad559x_port_get_raw(const struct device *dev, uint32_t *value)
33 {
34 	const struct gpio_ad559x_config *config = dev->config;
35 	struct gpio_ad559x_data *drv_data = dev->data;
36 	uint16_t data;
37 	int ret;
38 
39 	if (k_is_in_isr()) {
40 		return -EWOULDBLOCK;
41 	}
42 
43 	if (mfd_ad559x_has_pointer_byte_map(config->mfd_dev)) {
44 		ret = mfd_ad559x_read_reg(config->mfd_dev, AD559X_GPIO_RD_POINTER, 0, &data);
45 		/* LSB contains port information. Clear the MSB. */
46 		data &= BIT_MASK(AD559X_PIN_MAX);
47 	} else {
48 		ret = mfd_ad559x_read_reg(config->mfd_dev, AD559X_REG_GPIO_INPUT_EN,
49 					  drv_data->gpio_in, &data);
50 	}
51 
52 	if (ret < 0) {
53 		return ret;
54 	}
55 
56 	*value = (uint32_t)data;
57 
58 	return 0;
59 }
60 
gpio_ad559x_port_set_bits_raw(const struct device * dev,gpio_port_pins_t pins)61 static int gpio_ad559x_port_set_bits_raw(const struct device *dev,
62 					  gpio_port_pins_t pins)
63 {
64 	struct gpio_ad559x_data *data = dev->data;
65 	const struct gpio_ad559x_config *config = dev->config;
66 
67 	if (k_is_in_isr()) {
68 		return -EWOULDBLOCK;
69 	}
70 
71 	data->gpio_val |= (uint8_t)pins;
72 
73 	return mfd_ad559x_write_reg(config->mfd_dev, AD559X_REG_GPIO_SET, data->gpio_val);
74 }
75 
gpio_ad559x_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t pins)76 static int gpio_ad559x_port_clear_bits_raw(const struct device *dev,
77 					    gpio_port_pins_t pins)
78 {
79 	struct gpio_ad559x_data *data = dev->data;
80 	const struct gpio_ad559x_config *config = dev->config;
81 
82 	if (k_is_in_isr()) {
83 		return -EWOULDBLOCK;
84 	}
85 
86 	data->gpio_val &= ~(uint8_t)pins;
87 
88 	return mfd_ad559x_write_reg(config->mfd_dev, AD559X_REG_GPIO_SET, data->gpio_val);
89 }
90 
gpio_ad559x_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)91 static inline int gpio_ad559x_configure(const struct device *dev,
92 					 gpio_pin_t pin, gpio_flags_t flags)
93 {
94 	struct gpio_ad559x_data *data = dev->data;
95 	const struct gpio_ad559x_config *config = dev->config;
96 	uint8_t val;
97 	int ret;
98 
99 	if (k_is_in_isr()) {
100 		return -EWOULDBLOCK;
101 	}
102 
103 	if (pin >= AD559X_PIN_MAX) {
104 		return -EINVAL;
105 	}
106 
107 	val = BIT(pin);
108 	if ((flags & GPIO_OUTPUT) != 0U) {
109 		data->gpio_in &= ~val;
110 		data->gpio_out |= val;
111 
112 		if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) {
113 			ret = gpio_ad559x_port_set_bits_raw(
114 				dev, (gpio_port_pins_t)BIT(pin));
115 			if (ret < 0) {
116 				return ret;
117 			}
118 		} else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) {
119 			ret = gpio_ad559x_port_clear_bits_raw(
120 				dev, (gpio_port_pins_t)BIT(pin));
121 			if (ret < 0) {
122 				return ret;
123 			}
124 		}
125 
126 		ret = mfd_ad559x_write_reg(config->mfd_dev,
127 					   AD559X_REG_GPIO_OUTPUT_EN, data->gpio_out);
128 		if (ret < 0) {
129 			return ret;
130 		}
131 
132 		ret = mfd_ad559x_write_reg(config->mfd_dev,
133 					   AD559X_REG_GPIO_INPUT_EN, data->gpio_in);
134 	} else if ((flags & GPIO_INPUT) != 0U) {
135 		data->gpio_in |= val;
136 		data->gpio_out &= ~val;
137 
138 		if ((flags & GPIO_PULL_DOWN) != 0U) {
139 			data->gpio_pull_down |= val;
140 
141 			ret = mfd_ad559x_write_reg(config->mfd_dev,
142 						   AD559X_REG_GPIO_PULLDOWN,
143 						   data->gpio_pull_down);
144 			if (ret < 0) {
145 				return ret;
146 			}
147 		} else if ((flags & GPIO_PULL_UP) != 0U) {
148 			return -ENOTSUP;
149 		}
150 
151 		ret = mfd_ad559x_write_reg(config->mfd_dev,
152 					   AD559X_REG_GPIO_OUTPUT_EN, data->gpio_out);
153 		if (ret < 0) {
154 			return ret;
155 		}
156 
157 		ret = mfd_ad559x_write_reg(config->mfd_dev,
158 					   AD559X_REG_GPIO_INPUT_EN, data->gpio_in);
159 	} else {
160 		return -ENOTSUP;
161 	}
162 
163 	return ret;
164 }
165 
gpio_ad559x_port_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)166 static int gpio_ad559x_port_set_masked_raw(const struct device *dev,
167 					    gpio_port_pins_t mask,
168 					    gpio_port_value_t value)
169 {
170 	ARG_UNUSED(dev);
171 	ARG_UNUSED(mask);
172 	ARG_UNUSED(value);
173 
174 	return -ENOTSUP;
175 }
176 
gpio_ad559x_port_toggle_bits(const struct device * dev,gpio_port_pins_t pins)177 static int gpio_ad559x_port_toggle_bits(const struct device *dev,
178 					 gpio_port_pins_t pins)
179 {
180 	ARG_UNUSED(dev);
181 	ARG_UNUSED(pins);
182 
183 	return -ENOTSUP;
184 }
185 
gpio_ad559x_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)186 static int gpio_ad559x_pin_interrupt_configure(const struct device *dev,
187 						gpio_pin_t pin,
188 						enum gpio_int_mode mode,
189 						enum gpio_int_trig trig)
190 {
191 	ARG_UNUSED(dev);
192 	ARG_UNUSED(pin);
193 	ARG_UNUSED(mode);
194 	ARG_UNUSED(trig);
195 
196 	return -ENOTSUP;
197 }
198 
199 static const struct gpio_driver_api gpio_ad559x_api = {
200 	.pin_configure = gpio_ad559x_configure,
201 	.port_get_raw = gpio_ad559x_port_get_raw,
202 	.port_set_masked_raw = gpio_ad559x_port_set_masked_raw,
203 	.port_set_bits_raw = gpio_ad559x_port_set_bits_raw,
204 	.port_clear_bits_raw = gpio_ad559x_port_clear_bits_raw,
205 	.port_toggle_bits = gpio_ad559x_port_toggle_bits,
206 	.pin_interrupt_configure = gpio_ad559x_pin_interrupt_configure,
207 };
208 
gpio_ad559x_init(const struct device * dev)209 static int gpio_ad559x_init(const struct device *dev)
210 {
211 	const struct gpio_ad559x_config *config = dev->config;
212 
213 	if (!device_is_ready(config->mfd_dev)) {
214 		return -ENODEV;
215 	}
216 
217 	return 0;
218 }
219 
220 #define GPIO_AD559X_DEFINE(inst)							\
221 	static const struct gpio_ad559x_config gpio_ad559x_config##inst = {		\
222 		.common = {								\
223 			.port_pin_mask =						\
224 			GPIO_PORT_PIN_MASK_FROM_DT_INST(inst),				\
225 		},									\
226 		.mfd_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)),				\
227 	};										\
228 											\
229 	static struct gpio_ad559x_data gpio_ad559x_data##inst;				\
230 											\
231 	DEVICE_DT_INST_DEFINE(inst, gpio_ad559x_init, NULL,				\
232 			      &gpio_ad559x_data##inst, &gpio_ad559x_config##inst,	\
233 			      POST_KERNEL, CONFIG_MFD_INIT_PRIORITY,			\
234 			      &gpio_ad559x_api);
235 
236 DT_INST_FOREACH_STATUS_OKAY(GPIO_AD559X_DEFINE)
237