1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT nordic_npm6001_gpio
7 
8 #include <errno.h>
9 
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/drivers/gpio/gpio_utils.h>
12 #include <zephyr/drivers/i2c.h>
13 #include <zephyr/dt-bindings/gpio/nordic-npm6001-gpio.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/sys/util_macro.h>
16 #include <zephyr/toolchain.h>
17 
18 /* nPM6001 GPIO related registers */
19 #define NPM6001_GPIOOUTSET 0x69U
20 #define NPM6001_GPIOOUTCLR 0x6AU
21 #define NPM6001_GPIOIN	   0x6BU
22 #define NPM6001_GPIO0CONF  0x6CU
23 #define NPM6001_GPIO1CONF  0x6DU
24 #define NPM6001_GPIO2CONF  0x6EU
25 
26 /* GPIO(0|1|2)CONF fields */
27 #define NPM6001_CONF_DIRECTION_OUTPUT BIT(0)
28 #define NPM6001_CONF_INPUT_ENABLED    BIT(1)
29 #define NPM6001_CONF_PULLDOWN_ENABLED BIT(2)
30 #define NPM6001_CONF_DRIVE_HIGH	      BIT(5)
31 #define NPM6001_CONF_SENSE_CMOS	      BIT(6)
32 
33 #define NPM6001_PIN_MAX 2U
34 #define NPM6001_PIN_MSK 0x7U
35 
36 struct gpio_npm6001_config {
37 	struct gpio_driver_config common;
38 	struct i2c_dt_spec bus;
39 };
40 
41 struct gpio_npm6001_data {
42 	struct gpio_driver_data common;
43 };
44 
gpio_npm6001_port_get_raw(const struct device * dev,uint32_t * value)45 static int gpio_npm6001_port_get_raw(const struct device *dev, uint32_t *value)
46 {
47 	const struct gpio_npm6001_config *config = dev->config;
48 	uint8_t reg = NPM6001_GPIOIN;
49 	uint8_t val;
50 	int ret;
51 
52 	if (k_is_in_isr()) {
53 		return -EWOULDBLOCK;
54 	}
55 
56 	ret = i2c_write_read_dt(&config->bus, &reg, sizeof(reg), &val,
57 				sizeof(val));
58 	if (ret < 0) {
59 		return ret;
60 	}
61 
62 	*value = val;
63 
64 	return 0;
65 }
66 
gpio_npm6001_port_set_bits_raw(const struct device * dev,gpio_port_pins_t pins)67 static int gpio_npm6001_port_set_bits_raw(const struct device *dev,
68 					  gpio_port_pins_t pins)
69 {
70 	const struct gpio_npm6001_config *config = dev->config;
71 	uint8_t buf[2] = {NPM6001_GPIOOUTSET, (uint8_t)pins & NPM6001_PIN_MSK};
72 
73 	if (k_is_in_isr()) {
74 		return -EWOULDBLOCK;
75 	}
76 
77 	return i2c_write_dt(&config->bus, buf, sizeof(buf));
78 }
79 
gpio_npm6001_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t pins)80 static int gpio_npm6001_port_clear_bits_raw(const struct device *dev,
81 					    gpio_port_pins_t pins)
82 {
83 	const struct gpio_npm6001_config *config = dev->config;
84 	uint8_t buf[2] = {NPM6001_GPIOOUTCLR, (uint8_t)pins & NPM6001_PIN_MSK};
85 
86 	if (k_is_in_isr()) {
87 		return -EWOULDBLOCK;
88 	}
89 
90 	return i2c_write_dt(&config->bus, buf, sizeof(buf));
91 }
92 
gpio_npm6001_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)93 static inline int gpio_npm6001_configure(const struct device *dev,
94 					 gpio_pin_t pin, gpio_flags_t flags)
95 {
96 	const struct gpio_npm6001_config *config = dev->config;
97 	uint8_t buf[2] = {NPM6001_GPIO0CONF, 0U};
98 
99 	if (k_is_in_isr()) {
100 		return -EWOULDBLOCK;
101 	}
102 
103 	if (pin > NPM6001_PIN_MAX) {
104 		return -EINVAL;
105 	}
106 
107 	/* select GPIO0CONF/GPIO1CONF/GPIO2CONF */
108 	buf[0] += pin;
109 
110 	if ((flags & GPIO_OUTPUT) != 0U) {
111 		/* input buffer enabled to allow reading output level */
112 		buf[1] |= NPM6001_CONF_DIRECTION_OUTPUT |
113 			  NPM6001_CONF_INPUT_ENABLED;
114 
115 		/* open-drain/open-source not supported */
116 		if ((flags & GPIO_SINGLE_ENDED) != 0U) {
117 			return -ENOTSUP;
118 		}
119 
120 		/* drive strength (defaults to normal) */
121 		if ((flags & NPM6001_GPIO_DRIVE_MSK) ==
122 		    NPM6001_GPIO_DRIVE_HIGH) {
123 			buf[1] |= NPM6001_CONF_DRIVE_HIGH;
124 		}
125 
126 		if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) {
127 			int ret;
128 
129 			ret = gpio_npm6001_port_set_bits_raw(
130 				dev, (gpio_port_pins_t)BIT(pin));
131 			if (ret < 0) {
132 				return ret;
133 			}
134 		} else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) {
135 			int ret;
136 
137 			ret = gpio_npm6001_port_clear_bits_raw(
138 				dev, (gpio_port_pins_t)BIT(pin));
139 			if (ret < 0) {
140 				return ret;
141 			}
142 		}
143 	} else if ((flags & GPIO_INPUT) != 0U) {
144 		buf[1] |= NPM6001_CONF_INPUT_ENABLED;
145 
146 		/* pull resistor */
147 		if ((flags & GPIO_PULL_DOWN) != 0U) {
148 			buf[1] |= NPM6001_CONF_PULLDOWN_ENABLED;
149 		} else if ((flags & GPIO_PULL_UP) != 0U) {
150 			return -ENOTSUP;
151 		}
152 
153 		/* input type (defaults to schmitt trigger) */
154 		if ((flags & NPM6001_GPIO_SENSE_MSK) ==
155 		    NPM6001_GPIO_SENSE_CMOS) {
156 			buf[1] |= NPM6001_CONF_SENSE_CMOS;
157 		}
158 	} else {
159 		return -ENOTSUP;
160 	}
161 
162 	return i2c_write_dt(&config->bus, buf, sizeof(buf));
163 }
164 
gpio_npm6001_port_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)165 static int gpio_npm6001_port_set_masked_raw(const struct device *dev,
166 					    gpio_port_pins_t mask,
167 					    gpio_port_value_t value)
168 {
169 	int ret;
170 
171 	ret = gpio_npm6001_port_set_bits_raw(dev, mask & value);
172 	if (ret < 0) {
173 		return ret;
174 	}
175 
176 	return gpio_npm6001_port_clear_bits_raw(
177 		dev, mask & (~value & NPM6001_PIN_MSK));
178 }
179 
gpio_npm6001_port_toggle_bits(const struct device * dev,gpio_port_pins_t pins)180 static int gpio_npm6001_port_toggle_bits(const struct device *dev,
181 					 gpio_port_pins_t pins)
182 {
183 	uint32_t val;
184 	int ret;
185 
186 	ret = gpio_npm6001_port_get_raw(dev, &val);
187 	if (ret < 0) {
188 		return ret;
189 	}
190 
191 	return gpio_npm6001_port_set_masked_raw(dev, pins,
192 						~val & NPM6001_PIN_MSK);
193 }
194 
195 static const struct gpio_driver_api gpio_npm6001_api = {
196 	.pin_configure = gpio_npm6001_configure,
197 	.port_get_raw = gpio_npm6001_port_get_raw,
198 	.port_set_masked_raw = gpio_npm6001_port_set_masked_raw,
199 	.port_set_bits_raw = gpio_npm6001_port_set_bits_raw,
200 	.port_clear_bits_raw = gpio_npm6001_port_clear_bits_raw,
201 	.port_toggle_bits = gpio_npm6001_port_toggle_bits,
202 };
203 
gpio_npm6001_init(const struct device * dev)204 static int gpio_npm6001_init(const struct device *dev)
205 {
206 	const struct gpio_npm6001_config *config = dev->config;
207 
208 	if (!device_is_ready(config->bus.bus)) {
209 		return -ENODEV;
210 	}
211 
212 	return 0;
213 }
214 
215 #define GPIO_NPM6001_DEFINE(n)                                                 \
216 	static const struct gpio_npm6001_config gpio_npm6001_config##n = {     \
217 		.common =                                                      \
218 			{                                                      \
219 				.port_pin_mask =                               \
220 					GPIO_PORT_PIN_MASK_FROM_DT_INST(n),    \
221 			},                                                     \
222 		.bus = I2C_DT_SPEC_GET(DT_INST_PARENT(n))};                    \
223                                                                                \
224 	static struct gpio_npm6001_data gpio_npm6001_data##n;                  \
225                                                                                \
226 	DEVICE_DT_INST_DEFINE(n, &gpio_npm6001_init, NULL,                     \
227 			      &gpio_npm6001_data##n, &gpio_npm6001_config##n,  \
228 			      POST_KERNEL, CONFIG_GPIO_NPM6001_INIT_PRIORITY,  \
229 			      &gpio_npm6001_api);
230 
231 DT_INST_FOREACH_STATUS_OKAY(GPIO_NPM6001_DEFINE)
232