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