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, ®, 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