1 /*
2 * Copyright (c) 2020 Nuvoton Technology Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nuvoton_npcx_gpio
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/dt-bindings/gpio/nuvoton-npcx-gpio.h>
13 #include <soc.h>
14
15 #include <zephyr/drivers/gpio/gpio_utils.h>
16 #include "soc_gpio.h"
17 #include "soc_miwu.h"
18
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(gpio_npcx, LOG_LEVEL_ERR);
21
22 /* GPIO module instances */
23 #define NPCX_GPIO_DEV(inst) DEVICE_DT_INST_GET(inst),
24 static const struct device *const gpio_devs[] = {
25 DT_INST_FOREACH_STATUS_OKAY(NPCX_GPIO_DEV)
26 };
27
28 /* Driver config */
29 struct gpio_npcx_config {
30 /* gpio_driver_config needs to be first */
31 struct gpio_driver_config common;
32 /* GPIO controller base address */
33 uintptr_t base;
34 /* IO port */
35 int port;
36 /* Mapping table between gpio bits and wui */
37 struct npcx_wui wui_maps[NPCX_GPIO_PORT_PIN_NUM];
38 /* Mapping table between gpio bits and lvol */
39 struct npcx_lvol lvol_maps[NPCX_GPIO_PORT_PIN_NUM];
40 };
41
42 /* Driver data */
43 struct gpio_npcx_data {
44 /* gpio_driver_data needs to be first */
45 struct gpio_driver_data common;
46 };
47
48 /* Driver convenience defines */
49 #define HAL_INSTANCE(dev) \
50 ((struct gpio_reg *)((const struct gpio_npcx_config *)(dev)->config)->base)
51
52 /* Platform specific GPIO functions */
npcx_get_gpio_dev(int port)53 const struct device *npcx_get_gpio_dev(int port)
54 {
55 if (port >= ARRAY_SIZE(gpio_devs)) {
56 return NULL;
57 }
58
59 return gpio_devs[port];
60 }
61
npcx_gpio_enable_io_pads(const struct device * dev,int pin)62 void npcx_gpio_enable_io_pads(const struct device *dev, int pin)
63 {
64 const struct gpio_npcx_config *const config = dev->config;
65 const struct npcx_wui *io_wui = &config->wui_maps[pin];
66
67 if (io_wui->table == NPCX_MIWU_TABLE_NONE) {
68 LOG_ERR("Cannot enable GPIO(%x, %d) pad", config->port, pin);
69 return;
70 }
71
72 /*
73 * If this pin is configured as a GPIO interrupt source, do not
74 * implement bypass. Or ec cannot wake up via this event.
75 */
76 if (pin < NPCX_GPIO_PORT_PIN_NUM && !npcx_miwu_irq_get_state(io_wui)) {
77 npcx_miwu_io_enable(io_wui);
78 }
79 }
80
npcx_gpio_disable_io_pads(const struct device * dev,int pin)81 void npcx_gpio_disable_io_pads(const struct device *dev, int pin)
82 {
83 const struct gpio_npcx_config *const config = dev->config;
84 const struct npcx_wui *io_wui = &config->wui_maps[pin];
85
86 if (io_wui->table == NPCX_MIWU_TABLE_NONE) {
87 LOG_ERR("Cannot disable GPIO(%x, %d) pad", config->port, pin);
88 return;
89 }
90
91 /*
92 * If this pin is configured as a GPIO interrupt source, do not
93 * implement bypass. Or ec cannot wake up via this event.
94 */
95 if (pin < NPCX_GPIO_PORT_PIN_NUM && !npcx_miwu_irq_get_state(io_wui)) {
96 npcx_miwu_io_disable(io_wui);
97 }
98 }
99
100 /* GPIO api functions */
gpio_npcx_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)101 static int gpio_npcx_config(const struct device *dev,
102 gpio_pin_t pin, gpio_flags_t flags)
103 {
104 const struct gpio_npcx_config *const config = dev->config;
105 const struct npcx_lvol *lvol = &config->lvol_maps[pin];
106 struct gpio_reg *const inst = HAL_INSTANCE(dev);
107 uint32_t mask = BIT(pin);
108
109 /* Don't support simultaneous in/out mode */
110 if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) {
111 return -ENOTSUP;
112 }
113
114 /* Don't support "open source" mode */
115 if (((flags & GPIO_SINGLE_ENDED) != 0) &&
116 ((flags & GPIO_LINE_OPEN_DRAIN) == 0)) {
117 return -ENOTSUP;
118 }
119
120 /*
121 * Configure pin as input, if requested. Output is configured only
122 * after setting all other attributes, so as not to create a
123 * temporary incorrect logic state 0:input 1:output
124 */
125 if ((flags & GPIO_OUTPUT) == 0) {
126 inst->PDIR &= ~mask;
127 }
128
129 /* Does this IO pad support low-voltage input (1.8V) detection? */
130 if (lvol->ctrl != NPCX_DT_LVOL_CTRL_NONE) {
131 gpio_flags_t volt = flags & NPCX_GPIO_VOLTAGE_MASK;
132
133 /*
134 * If this IO pad is configured for low-voltage input detection,
135 * the related drive type must select to open-drain also.
136 */
137 if (volt == NPCX_GPIO_VOLTAGE_1P8) {
138 flags |= GPIO_OPEN_DRAIN;
139 npcx_lvol_set_detect_level(lvol->ctrl, lvol->bit, true);
140 } else {
141 npcx_lvol_set_detect_level(lvol->ctrl, lvol->bit, false);
142 }
143 }
144
145 /* Select open drain 0:push-pull 1:open-drain */
146 if ((flags & GPIO_OPEN_DRAIN) != 0) {
147 inst->PTYPE |= mask;
148 } else {
149 inst->PTYPE &= ~mask;
150 }
151
152 /* Select pull-up/down of GPIO 0:pull-up 1:pull-down */
153 if ((flags & GPIO_PULL_UP) != 0) {
154 inst->PPUD &= ~mask;
155 inst->PPULL |= mask;
156 } else if ((flags & GPIO_PULL_DOWN) != 0) {
157 inst->PPUD |= mask;
158 inst->PPULL |= mask;
159 } else {
160 /* disable pull down/up */
161 inst->PPULL &= ~mask;
162 }
163
164 /* Set level 0:low 1:high */
165 if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
166 inst->PDOUT |= mask;
167 } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
168 inst->PDOUT &= ~mask;
169 }
170
171 /* Configure pin as output, if requested 0:input 1:output */
172 if ((flags & GPIO_OUTPUT) != 0) {
173 inst->PDIR |= mask;
174 }
175
176 return 0;
177 }
178
179 #ifdef CONFIG_GPIO_GET_CONFIG
gpio_npcx_pin_get_config(const struct device * port,gpio_pin_t pin,gpio_flags_t * out_flags)180 static int gpio_npcx_pin_get_config(const struct device *port, gpio_pin_t pin,
181 gpio_flags_t *out_flags)
182 {
183 const struct gpio_npcx_config *const config = port->config;
184 const struct npcx_lvol *lvol = &config->lvol_maps[pin];
185 struct gpio_reg *const inst = HAL_INSTANCE(port);
186 uint32_t mask = BIT(pin);
187 gpio_flags_t flags = 0;
188
189 /* 0:input 1:output */
190 if (inst->PDIR & mask) {
191 flags |= GPIO_OUTPUT;
192
193 /* 0:push-pull 1:open-drain */
194 if (inst->PTYPE & mask) {
195 flags |= GPIO_OPEN_DRAIN;
196 }
197
198 /* 0:low 1:high */
199 if (inst->PDOUT & mask) {
200 flags |= GPIO_OUTPUT_HIGH;
201 } else {
202 flags |= GPIO_OUTPUT_LOW;
203 }
204 } else {
205 flags |= GPIO_INPUT;
206
207 /* 0:disabled 1:enabled pull */
208 if (inst->PPULL & mask) {
209 /* 0:pull-up 1:pull-down */
210 if (inst->PPUD & mask) {
211 flags |= GPIO_PULL_DOWN;
212 } else {
213 flags |= GPIO_PULL_UP;
214 }
215 }
216 }
217
218 /* Enable low-voltage detection? */
219 if (lvol->ctrl != NPCX_DT_LVOL_CTRL_NONE &&
220 npcx_lvol_get_detect_level(lvol->ctrl, lvol->bit)) {
221 flags |= NPCX_GPIO_VOLTAGE_1P8;
222 };
223
224 *out_flags = flags;
225
226 return 0;
227 }
228 #endif
229
gpio_npcx_port_get_raw(const struct device * dev,gpio_port_value_t * value)230 static int gpio_npcx_port_get_raw(const struct device *dev,
231 gpio_port_value_t *value)
232 {
233 struct gpio_reg *const inst = HAL_INSTANCE(dev);
234
235 /* Get raw bits of GPIO input registers */
236 *value = inst->PDIN;
237
238 return 0;
239 }
240
gpio_npcx_port_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)241 static int gpio_npcx_port_set_masked_raw(const struct device *dev,
242 gpio_port_pins_t mask,
243 gpio_port_value_t value)
244 {
245 struct gpio_reg *const inst = HAL_INSTANCE(dev);
246 uint8_t out = inst->PDOUT;
247
248 inst->PDOUT = ((out & ~mask) | (value & mask));
249
250 return 0;
251 }
252
gpio_npcx_port_set_bits_raw(const struct device * dev,gpio_port_pins_t mask)253 static int gpio_npcx_port_set_bits_raw(const struct device *dev,
254 gpio_port_pins_t mask)
255 {
256 struct gpio_reg *const inst = HAL_INSTANCE(dev);
257
258 /* Set raw bits of GPIO output registers */
259 inst->PDOUT |= mask;
260
261 return 0;
262 }
263
gpio_npcx_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t mask)264 static int gpio_npcx_port_clear_bits_raw(const struct device *dev,
265 gpio_port_pins_t mask)
266 {
267 struct gpio_reg *const inst = HAL_INSTANCE(dev);
268
269 /* Clear raw bits of GPIO output registers */
270 inst->PDOUT &= ~mask;
271
272 return 0;
273 }
274
gpio_npcx_port_toggle_bits(const struct device * dev,gpio_port_pins_t mask)275 static int gpio_npcx_port_toggle_bits(const struct device *dev,
276 gpio_port_pins_t mask)
277 {
278 struct gpio_reg *const inst = HAL_INSTANCE(dev);
279
280 /* Toggle raw bits of GPIO output registers */
281 inst->PDOUT ^= mask;
282
283 return 0;
284 }
285
gpio_npcx_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)286 static int gpio_npcx_pin_interrupt_configure(const struct device *dev,
287 gpio_pin_t pin,
288 enum gpio_int_mode mode,
289 enum gpio_int_trig trig)
290 {
291 const struct gpio_npcx_config *const config = dev->config;
292
293 if (config->wui_maps[pin].table == NPCX_MIWU_TABLE_NONE) {
294 LOG_ERR("Cannot configure GPIO(%x, %d)", config->port, pin);
295 return -EINVAL;
296 }
297
298 LOG_DBG("pin_int_conf (%d, %d) match (%d, %d, %d)!!!",
299 config->port, pin, config->wui_maps[pin].table,
300 config->wui_maps[pin].group,
301 config->wui_maps[pin].bit);
302 #ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT
303 if (mode == GPIO_INT_MODE_DISABLE_ONLY) {
304 npcx_miwu_irq_disable(&config->wui_maps[pin]);
305 return 0;
306 } else if (mode == GPIO_INT_MODE_ENABLE_ONLY) {
307 npcx_miwu_irq_enable(&config->wui_maps[pin]);
308 return 0;
309 }
310 #endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */
311
312 /* Disable irq of wake-up input io-pads before configuring them */
313 npcx_miwu_irq_disable(&config->wui_maps[pin]);
314
315 /* Configure and enable interrupt? */
316 if (mode != GPIO_INT_MODE_DISABLED) {
317 enum miwu_int_mode miwu_mode;
318 enum miwu_int_trig miwu_trig;
319 int ret = 0;
320
321 /* Determine interrupt is level or edge mode? */
322 if (mode == GPIO_INT_MODE_EDGE) {
323 miwu_mode = NPCX_MIWU_MODE_EDGE;
324 } else {
325 miwu_mode = NPCX_MIWU_MODE_LEVEL;
326 }
327
328 /* Determine trigger mode is low, high or both? */
329 if (trig == GPIO_INT_TRIG_LOW) {
330 miwu_trig = NPCX_MIWU_TRIG_LOW;
331 } else if (trig == GPIO_INT_TRIG_HIGH) {
332 miwu_trig = NPCX_MIWU_TRIG_HIGH;
333 } else if (trig == GPIO_INT_TRIG_BOTH) {
334 miwu_trig = NPCX_MIWU_TRIG_BOTH;
335 } else {
336 LOG_ERR("Invalid interrupt trigger type %d", trig);
337 return -EINVAL;
338 }
339
340 /* Call MIWU routine to setup interrupt configuration */
341 ret = npcx_miwu_interrupt_configure(&config->wui_maps[pin],
342 miwu_mode, miwu_trig);
343 if (ret != 0) {
344 LOG_ERR("Configure MIWU interrupt failed");
345 return ret;
346 }
347
348 /* Enable it after configuration is completed */
349 npcx_miwu_irq_enable(&config->wui_maps[pin]);
350 }
351
352 return 0;
353 }
354
gpio_npcx_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)355 static int gpio_npcx_manage_callback(const struct device *dev,
356 struct gpio_callback *callback, bool set)
357 {
358 const struct gpio_npcx_config *const config = dev->config;
359 struct miwu_callback *miwu_cb = (struct miwu_callback *)callback;
360 int pin = find_lsb_set(callback->pin_mask) - 1;
361
362 /* pin_mask should not be zero */
363 if (pin < 0) {
364 return -EINVAL;
365 }
366
367 /* Has the IO pin valid MIWU input source? */
368 if (config->wui_maps[pin].table == NPCX_MIWU_TABLE_NONE) {
369 LOG_ERR("Cannot manage GPIO(%x, %d) callback!", config->port,
370 pin);
371 return -EINVAL;
372 }
373
374 /* Initialize WUI information in unused bits field */
375 npcx_miwu_init_gpio_callback(miwu_cb, &config->wui_maps[pin],
376 config->port);
377
378 /* Insert or remove a IO callback which being called in MIWU ISRs */
379 return npcx_miwu_manage_callback(miwu_cb, set);
380 }
381
382 /* GPIO driver registration */
383 static const struct gpio_driver_api gpio_npcx_driver = {
384 .pin_configure = gpio_npcx_config,
385 #ifdef CONFIG_GPIO_GET_CONFIG
386 .pin_get_config = gpio_npcx_pin_get_config,
387 #endif
388 .port_get_raw = gpio_npcx_port_get_raw,
389 .port_set_masked_raw = gpio_npcx_port_set_masked_raw,
390 .port_set_bits_raw = gpio_npcx_port_set_bits_raw,
391 .port_clear_bits_raw = gpio_npcx_port_clear_bits_raw,
392 .port_toggle_bits = gpio_npcx_port_toggle_bits,
393 .pin_interrupt_configure = gpio_npcx_pin_interrupt_configure,
394 .manage_callback = gpio_npcx_manage_callback,
395 };
396
gpio_npcx_init(const struct device * dev)397 int gpio_npcx_init(const struct device *dev)
398 {
399 ARG_UNUSED(dev);
400
401 return 0;
402 }
403
404 #define NPCX_GPIO_DEVICE_INIT(inst) \
405 static const struct gpio_npcx_config gpio_npcx_cfg_##inst = { \
406 .common = { \
407 .port_pin_mask = \
408 GPIO_PORT_PIN_MASK_FROM_NGPIOS(NPCX_GPIO_PORT_PIN_NUM),\
409 }, \
410 .base = DT_INST_REG_ADDR(inst), \
411 .port = inst, \
412 .wui_maps = NPCX_DT_WUI_ITEMS_LIST(inst), \
413 .lvol_maps = NPCX_DT_LVOL_ITEMS_LIST(inst), \
414 }; \
415 BUILD_ASSERT(NPCX_DT_WUI_ITEMS_LEN(inst) == NPCX_GPIO_PORT_PIN_NUM, \
416 "size of prop. wui-maps must equal to pin number!"); \
417 BUILD_ASSERT(NPCX_DT_LVOL_ITEMS_LEN(inst) == NPCX_GPIO_PORT_PIN_NUM, \
418 "size of prop. lvol-maps must equal to pin number!"); \
419 \
420 static struct gpio_npcx_data gpio_npcx_data_##inst; \
421 \
422 DEVICE_DT_INST_DEFINE(inst, \
423 gpio_npcx_init, \
424 NULL, \
425 &gpio_npcx_data_##inst, \
426 &gpio_npcx_cfg_##inst, \
427 PRE_KERNEL_1, \
428 CONFIG_GPIO_INIT_PRIORITY, \
429 &gpio_npcx_driver);
430
431 DT_INST_FOREACH_STATUS_OKAY(NPCX_GPIO_DEVICE_INIT)
432