]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/nctgpio/nctgpio.c
Fix Machine Check Exception on Page Size Change.
[FreeBSD/FreeBSD.git] / sys / dev / nctgpio / nctgpio.c
1 /*-
2  * Copyright (c) 2016 Daniel Wyatt <Daniel.Wyatt@gmail.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  *
28  */
29
30 /*
31  * Nuvoton GPIO driver.
32  *
33  */
34
35 #include <sys/cdefs.h>
36
37 #include <sys/param.h>
38 #include <sys/kernel.h>
39 #include <sys/systm.h>
40 #include <sys/bus.h>
41 #include <sys/eventhandler.h>
42 #include <sys/lock.h>
43
44 #include <sys/module.h>
45 #include <sys/rman.h>
46 #include <sys/gpio.h>
47
48 #include <isa/isavar.h>
49
50 #include <machine/bus.h>
51 #include <machine/resource.h>
52
53 #include <dev/gpio/gpiobusvar.h>
54
55 #include "gpio_if.h"
56
57 /*
58  * Global configuration registers (CR).
59  */
60 #define NCT_CR_LDN                      0x07    /* Logical Device Number */
61 #define NCT_CR_CHIP_ID                  0x20    /* Chip ID */
62 #define NCT_CR_CHIP_ID_H                0x20    /* Chip ID (high byte) */
63 #define NCT_CR_CHIP_ID_L                0x21    /* Chip ID (low byte) */
64 #define NCT_CR_OPT_1                    0x26    /* Global Options (1) */
65
66 /* Logical Device Numbers. */
67 #define NCT_LDN_GPIO                    0x07
68 #define NCT_LDN_GPIO_CFG                0x08
69 #define NCT_LDN_GPIO_MODE               0x0f
70
71 /* Logical Device 7 */
72 #define NCT_LD7_GPIO_ENABLE             0x30
73 #define NCT_LD7_GPIO0_IOR               0xe0
74 #define NCT_LD7_GPIO0_DAT               0xe1
75 #define NCT_LD7_GPIO0_INV               0xe2
76 #define NCT_LD7_GPIO0_DST               0xe3
77 #define NCT_LD7_GPIO1_IOR               0xe4
78 #define NCT_LD7_GPIO1_DAT               0xe5
79 #define NCT_LD7_GPIO1_INV               0xe6
80 #define NCT_LD7_GPIO1_DST               0xe7
81
82 /* Logical Device F */
83 #define NCT_LDF_GPIO0_OUTCFG            0xe0
84 #define NCT_LDF_GPIO1_OUTCFG            0xe1
85
86 #define NCT_EXTFUNC_ENTER               0x87
87 #define NCT_EXTFUNC_EXIT                0xaa
88
89 #define NCT_MAX_PIN                     15
90 #define NCT_IS_VALID_PIN(_p)    ((_p) >= 0 && (_p) <= NCT_MAX_PIN)
91
92 #define NCT_PIN_BIT(_p)         (1 << ((_p) % 8))
93
94 #define NCT_GPIO_CAPS   (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
95         GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \
96         GPIO_PIN_INVIN | GPIO_PIN_INVOUT)
97
98 struct nct_softc {
99         device_t                        dev;
100         device_t                        busdev;
101         struct mtx                      mtx;
102         struct resource                 *portres;
103         int                             rid;
104         struct gpio_pin                 pins[NCT_MAX_PIN + 1];
105 };
106
107 #define GPIO_LOCK_INIT(_sc)     mtx_init(&(_sc)->mtx,           \
108                 device_get_nameunit(dev), NULL, MTX_DEF)
109 #define GPIO_LOCK_DESTROY(_sc)          mtx_destroy(&(_sc)->mtx)
110 #define GPIO_LOCK(_sc)          mtx_lock(&(_sc)->mtx)
111 #define GPIO_UNLOCK(_sc)        mtx_unlock(&(_sc)->mtx)
112 #define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED)
113 #define GPIO_ASSERT_UNLOCKED(_sc)       mtx_assert(&(_sc)->mtx, MA_NOTOWNED)
114
115 #define NCT_BARRIER_WRITE(_sc)  \
116         bus_barrier((_sc)->portres, 0, 2, BUS_SPACE_BARRIER_WRITE)
117
118 #define NCT_BARRIER_READ_WRITE(_sc)     \
119         bus_barrier((_sc)->portres, 0, 2, \
120                 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)
121
122 static void     ext_cfg_enter(struct nct_softc *);
123 static void     ext_cfg_exit(struct nct_softc *);
124
125 /*
126  * Potential Extended Function Enable Register addresses.
127  * Same address as EFIR.
128  */
129 uint8_t probe_addrs[] = {0x2e, 0x4e};
130
131 struct nuvoton_vendor_device_id {
132         uint16_t                chip_id;
133         const char *            descr;
134 } nct_devs[] = {
135         {
136                 .chip_id        = 0x1061,
137                 .descr          = "Nuvoton NCT5104D",
138         },
139         {
140                 .chip_id        = 0xc452,
141                 .descr          = "Nuvoton NCT5104D (PC-Engines APU)",
142         },
143         {
144                 .chip_id        = 0xc453,
145                 .descr          = "Nuvoton NCT5104D (PC-Engines APU3)",
146         },
147 };
148
149 static void
150 write_cfg_reg_1(struct nct_softc *sc, uint8_t reg, uint8_t value)
151 {
152         GPIO_ASSERT_LOCKED(sc);
153         bus_write_1(sc->portres, 0, reg);
154         NCT_BARRIER_WRITE(sc);
155         bus_write_1(sc->portres, 1, value);
156         NCT_BARRIER_WRITE(sc);
157 }
158
159 static uint8_t
160 read_cfg_reg_1(struct nct_softc *sc, uint8_t reg)
161 {
162         uint8_t value;
163
164         GPIO_ASSERT_LOCKED(sc);
165         bus_write_1(sc->portres, 0, reg);
166         NCT_BARRIER_READ_WRITE(sc);
167         value = bus_read_1(sc->portres, 1);
168         NCT_BARRIER_READ_WRITE(sc);
169         
170         return (value);
171 }
172
173 static uint16_t
174 read_cfg_reg_2(struct nct_softc *sc, uint8_t reg)
175 {
176         uint16_t value;
177
178         value = read_cfg_reg_1(sc, reg) << 8;
179         value |= read_cfg_reg_1(sc, reg + 1);
180
181         return (value);
182 }
183
184 /*
185  * Enable extended function mode.
186  *
187  */
188 static void
189 ext_cfg_enter(struct nct_softc *sc)
190 {
191         GPIO_ASSERT_LOCKED(sc);
192         bus_write_1(sc->portres, 0, NCT_EXTFUNC_ENTER);
193         NCT_BARRIER_WRITE(sc);
194         bus_write_1(sc->portres, 0, NCT_EXTFUNC_ENTER);
195         NCT_BARRIER_WRITE(sc);
196 }
197
198 /*
199  * Disable extended function mode.
200  *
201  */
202 static void
203 ext_cfg_exit(struct nct_softc *sc)
204 {
205         GPIO_ASSERT_LOCKED(sc);
206         bus_write_1(sc->portres, 0, NCT_EXTFUNC_EXIT);
207         NCT_BARRIER_WRITE(sc);
208 }
209
210 /*
211  * Select a Logical Device.
212  */
213 static void
214 select_ldn(struct nct_softc *sc, uint8_t ldn)
215 {
216         write_cfg_reg_1(sc, NCT_CR_LDN, ldn);
217 }
218
219 /*
220  * Get the GPIO Input/Output register address
221  * for a pin.
222  */
223 static uint8_t
224 nct_ior_addr(uint32_t pin_num)
225 {
226         uint8_t addr;
227
228         addr = NCT_LD7_GPIO0_IOR;
229         if (pin_num > 7)
230                 addr = NCT_LD7_GPIO1_IOR;
231
232         return (addr);
233 }
234
235 /*
236  * Get the GPIO Data register address for a pin.
237  */
238 static uint8_t
239 nct_dat_addr(uint32_t pin_num)
240 {
241         uint8_t addr;
242
243         addr = NCT_LD7_GPIO0_DAT;
244         if (pin_num > 7)
245                 addr = NCT_LD7_GPIO1_DAT;
246
247         return (addr);
248 }
249
250 /*
251  * Get the GPIO Inversion register address
252  * for a pin.
253  */
254 static uint8_t
255 nct_inv_addr(uint32_t pin_num)
256 {
257         uint8_t addr;
258
259         addr = NCT_LD7_GPIO0_INV;
260         if (pin_num > 7)
261                 addr = NCT_LD7_GPIO1_INV;
262
263         return (addr);
264 }
265
266 /*
267  * Get the GPIO Output Configuration/Mode
268  * register address for a pin.
269  */
270 static uint8_t
271 nct_outcfg_addr(uint32_t pin_num)
272 {
273         uint8_t addr;
274
275         addr = NCT_LDF_GPIO0_OUTCFG;
276         if (pin_num > 7)
277                 addr = NCT_LDF_GPIO1_OUTCFG;
278
279         return (addr);
280 }
281
282 /*
283  * Set a pin to output mode.
284  */
285 static void
286 nct_set_pin_is_output(struct nct_softc *sc, uint32_t pin_num)
287 {
288         uint8_t reg;
289         uint8_t ior;
290
291         reg = nct_ior_addr(pin_num);
292         select_ldn(sc, NCT_LDN_GPIO);
293         ior = read_cfg_reg_1(sc, reg);
294         ior &= ~(NCT_PIN_BIT(pin_num));
295         write_cfg_reg_1(sc, reg, ior);
296 }
297
298 /*
299  * Set a pin to input mode.
300  */
301 static void
302 nct_set_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
303 {
304         uint8_t reg;
305         uint8_t ior;
306
307         reg = nct_ior_addr(pin_num);
308         select_ldn(sc, NCT_LDN_GPIO);
309         ior = read_cfg_reg_1(sc, reg);
310         ior |= NCT_PIN_BIT(pin_num);
311         write_cfg_reg_1(sc, reg, ior);
312 }
313
314 /*
315  * Check whether a pin is configured as an input.
316  */
317 static bool
318 nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
319 {
320         uint8_t reg;
321         uint8_t ior;
322
323         reg = nct_ior_addr(pin_num);
324         select_ldn(sc, NCT_LDN_GPIO);
325         ior = read_cfg_reg_1(sc, reg);
326
327         return (ior & NCT_PIN_BIT(pin_num));
328 }
329
330 /*
331  * Write a value to an output pin.
332  */
333 static void
334 nct_write_pin(struct nct_softc *sc, uint32_t pin_num, uint8_t data)
335 {
336         uint8_t reg;
337         uint8_t value;
338
339         reg = nct_dat_addr(pin_num);
340         select_ldn(sc, NCT_LDN_GPIO);
341         value = read_cfg_reg_1(sc, reg);
342         if (data)
343                 value |= NCT_PIN_BIT(pin_num);
344         else
345                 value &= ~(NCT_PIN_BIT(pin_num));
346
347         write_cfg_reg_1(sc, reg, value);
348 }
349
350 static bool
351 nct_read_pin(struct nct_softc *sc, uint32_t pin_num)
352 {
353         uint8_t reg;
354
355         reg = nct_dat_addr(pin_num);
356         select_ldn(sc, NCT_LDN_GPIO);
357
358         return (read_cfg_reg_1(sc, reg) & NCT_PIN_BIT(pin_num));
359 }
360
361 static void
362 nct_set_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
363 {
364         uint8_t reg;
365         uint8_t inv;
366
367         reg = nct_inv_addr(pin_num);
368         select_ldn(sc, NCT_LDN_GPIO);
369         inv = read_cfg_reg_1(sc, reg);
370         inv |= (NCT_PIN_BIT(pin_num));
371         write_cfg_reg_1(sc, reg, inv);
372 }
373
374 static void
375 nct_set_pin_not_inverted(struct nct_softc *sc, uint32_t pin_num)
376 {
377         uint8_t reg;
378         uint8_t inv;
379
380         reg = nct_inv_addr(pin_num);
381         select_ldn(sc, NCT_LDN_GPIO);
382         inv = read_cfg_reg_1(sc, reg);
383         inv &= ~(NCT_PIN_BIT(pin_num));
384         write_cfg_reg_1(sc, reg, inv);
385 }
386
387 static bool
388 nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
389 {
390         uint8_t reg;
391         uint8_t inv;
392
393         reg = nct_inv_addr(pin_num);
394         select_ldn(sc, NCT_LDN_GPIO);
395         inv = read_cfg_reg_1(sc, reg);
396
397         return (inv & NCT_PIN_BIT(pin_num));
398 }
399
400 static void
401 nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num)
402 {
403         uint8_t reg;
404         uint8_t outcfg;
405
406         reg = nct_outcfg_addr(pin_num);
407         select_ldn(sc, NCT_LDN_GPIO_MODE);
408         outcfg = read_cfg_reg_1(sc, reg);
409         outcfg |= (NCT_PIN_BIT(pin_num));
410         write_cfg_reg_1(sc, reg, outcfg);
411 }
412
413 static void
414 nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num)
415 {
416         uint8_t reg;
417         uint8_t outcfg;
418
419         reg = nct_outcfg_addr(pin_num);
420         select_ldn(sc, NCT_LDN_GPIO_MODE);
421         outcfg = read_cfg_reg_1(sc, reg);
422         outcfg &= ~(NCT_PIN_BIT(pin_num));
423         write_cfg_reg_1(sc, reg, outcfg);
424 }
425
426 static bool
427 nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num)
428 {
429         uint8_t reg;
430         uint8_t outcfg;
431
432         reg = nct_outcfg_addr(pin_num);
433         select_ldn(sc, NCT_LDN_GPIO_MODE);
434         outcfg = read_cfg_reg_1(sc, reg);
435
436         return (outcfg & NCT_PIN_BIT(pin_num));
437 }
438
439 static void
440 nct_identify(driver_t *driver, device_t parent)
441 {
442         if (device_find_child(parent, driver->name, 0) != NULL)
443                 return;
444
445         BUS_ADD_CHILD(parent, 0, driver->name, 0);
446 }
447
448 static int
449 nct_probe(device_t dev)
450 {
451         int i, j;
452         int rc;
453         struct nct_softc *sc;
454         uint16_t chipid;
455
456         /* Make sure we do not claim some ISA PNP device. */
457         if (isa_get_logicalid(dev) != 0)
458                 return (ENXIO);
459
460         sc = device_get_softc(dev);
461
462         for (i = 0; i < nitems(probe_addrs); i++) {
463                 sc->rid = 0;
464                 sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
465                         probe_addrs[i], probe_addrs[i] + 1, 2, RF_ACTIVE);
466                 if (sc->portres == NULL)
467                         continue;
468
469                 GPIO_LOCK_INIT(sc);
470
471                 GPIO_ASSERT_UNLOCKED(sc);
472                 GPIO_LOCK(sc);
473                 ext_cfg_enter(sc);
474                 chipid = read_cfg_reg_2(sc, NCT_CR_CHIP_ID);
475                 ext_cfg_exit(sc);
476                 GPIO_UNLOCK(sc);
477
478                 GPIO_LOCK_DESTROY(sc);
479
480                 bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
481                 bus_delete_resource(dev, SYS_RES_IOPORT, sc->rid);
482
483                 for (j = 0; j < nitems(nct_devs); j++) {
484                         if (chipid == nct_devs[j].chip_id) {
485                                 rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, probe_addrs[i], 2);
486                                 if (rc != 0) {
487                                         device_printf(dev, "bus_set_resource failed for address 0x%02X\n", probe_addrs[i]);
488                                         continue;
489                                 }
490                                 device_set_desc(dev, nct_devs[j].descr);
491                                 return (BUS_PROBE_DEFAULT);
492                         }
493                 }
494         }
495         return (ENXIO);
496 }
497
498 static int
499 nct_attach(device_t dev)
500 {
501         struct nct_softc *sc;
502         int i;
503
504         sc = device_get_softc(dev);
505
506         sc->rid = 0;
507         sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
508                 0ul, ~0ul, 2, RF_ACTIVE);
509         if (sc->portres == NULL) {
510                 device_printf(dev, "cannot allocate ioport\n");
511                 return (ENXIO);
512         }
513
514         GPIO_LOCK_INIT(sc);
515
516         GPIO_ASSERT_UNLOCKED(sc);
517         GPIO_LOCK(sc);
518         ext_cfg_enter(sc);
519         select_ldn(sc, NCT_LDN_GPIO);
520         /* Enable gpio0 and gpio1. */
521         write_cfg_reg_1(sc, NCT_LD7_GPIO_ENABLE,
522                 read_cfg_reg_1(sc, NCT_LD7_GPIO_ENABLE) | 0x03);
523
524         for (i = 0; i <= NCT_MAX_PIN; i++) {
525                 struct gpio_pin *pin;
526
527                 pin = &sc->pins[i];
528                 pin->gp_pin = i;
529                 pin->gp_caps = NCT_GPIO_CAPS;
530                 pin->gp_flags = 0;
531
532                 snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%02o", i);
533                 pin->gp_name[GPIOMAXNAME - 1] = '\0';
534
535                 if (nct_pin_is_input(sc, i))
536                         pin->gp_flags |= GPIO_PIN_INPUT;
537                 else
538                         pin->gp_flags |= GPIO_PIN_OUTPUT;
539
540                 if (nct_pin_is_opendrain(sc, i))
541                         pin->gp_flags |= GPIO_PIN_OPENDRAIN;
542                 else
543                         pin->gp_flags |= GPIO_PIN_PUSHPULL;
544
545                 if (nct_pin_is_inverted(sc, i))
546                         pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
547         }
548         GPIO_UNLOCK(sc);
549
550         sc->busdev = gpiobus_attach_bus(dev);
551         if (sc->busdev == NULL) {
552                 GPIO_ASSERT_UNLOCKED(sc);
553                 GPIO_LOCK(sc);
554                 ext_cfg_exit(sc);
555                 GPIO_UNLOCK(sc);
556                 bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
557                 GPIO_LOCK_DESTROY(sc);
558
559                 return (ENXIO);
560         }
561
562         return (0);
563 }
564
565 static int
566 nct_detach(device_t dev)
567 {
568         struct nct_softc *sc;
569
570         sc = device_get_softc(dev);
571         gpiobus_detach_bus(dev);
572
573         GPIO_ASSERT_UNLOCKED(sc);
574         GPIO_LOCK(sc);
575         ext_cfg_exit(sc);
576         GPIO_UNLOCK(sc);
577
578         /* Cleanup resources. */
579         bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
580
581         GPIO_LOCK_DESTROY(sc);
582
583         return (0);
584 }
585
586 static device_t
587 nct_gpio_get_bus(device_t dev)
588 {
589         struct nct_softc *sc;
590
591         sc = device_get_softc(dev);
592
593         return (sc->busdev);
594 }
595
596 static int
597 nct_gpio_pin_max(device_t dev, int *npins)
598 {
599         *npins = NCT_MAX_PIN;
600
601         return (0);
602 }
603
604 static int
605 nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
606 {
607         struct nct_softc *sc;
608
609         if (!NCT_IS_VALID_PIN(pin_num))
610                 return (EINVAL);
611
612         sc = device_get_softc(dev);
613         GPIO_ASSERT_UNLOCKED(sc);
614         GPIO_LOCK(sc);
615         nct_write_pin(sc, pin_num, pin_value);
616         GPIO_UNLOCK(sc);
617
618         return (0);
619 }
620
621 static int
622 nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
623 {
624         struct nct_softc *sc;
625
626         if (!NCT_IS_VALID_PIN(pin_num))
627                 return (EINVAL);
628
629         sc = device_get_softc(dev);
630         GPIO_ASSERT_UNLOCKED(sc);
631         GPIO_LOCK(sc);
632         *pin_value = nct_read_pin(sc, pin_num);
633         GPIO_UNLOCK(sc);
634
635         return (0);
636 }
637
638 static int
639 nct_gpio_pin_toggle(device_t dev, uint32_t pin_num)
640 {
641         struct nct_softc *sc;
642
643         if (!NCT_IS_VALID_PIN(pin_num))
644                 return (EINVAL);
645
646         sc = device_get_softc(dev);
647         GPIO_ASSERT_UNLOCKED(sc);
648         GPIO_LOCK(sc);
649         if (nct_read_pin(sc, pin_num))
650                 nct_write_pin(sc, pin_num, 0);
651         else
652                 nct_write_pin(sc, pin_num, 1);
653
654         GPIO_UNLOCK(sc);
655
656         return (0);
657 }
658
659 static int
660 nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
661 {
662         struct nct_softc *sc;
663
664         if (!NCT_IS_VALID_PIN(pin_num))
665                 return (EINVAL);
666
667         sc = device_get_softc(dev);
668         GPIO_ASSERT_UNLOCKED(sc);
669         GPIO_LOCK(sc);
670         *caps = sc->pins[pin_num].gp_caps;
671         GPIO_UNLOCK(sc);
672
673         return (0);
674 }
675
676 static int
677 nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
678 {
679         struct nct_softc *sc;
680
681         if (!NCT_IS_VALID_PIN(pin_num))
682                 return (EINVAL);
683
684         sc = device_get_softc(dev);
685         GPIO_ASSERT_UNLOCKED(sc);
686         GPIO_LOCK(sc);
687         *flags = sc->pins[pin_num].gp_flags;
688         GPIO_UNLOCK(sc);
689
690         return (0);
691 }
692
693 static int
694 nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
695 {
696         struct nct_softc *sc;
697
698         if (!NCT_IS_VALID_PIN(pin_num))
699                 return (EINVAL);
700
701         sc = device_get_softc(dev);
702         GPIO_ASSERT_UNLOCKED(sc);
703         GPIO_LOCK(sc);
704         memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
705         GPIO_UNLOCK(sc);
706
707         return (0);
708 }
709
710 static int
711 nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
712 {
713         struct nct_softc *sc;
714         struct gpio_pin *pin;
715
716         if (!NCT_IS_VALID_PIN(pin_num))
717                 return (EINVAL);
718
719         sc = device_get_softc(dev);
720         pin = &sc->pins[pin_num];
721         if ((flags & pin->gp_caps) != flags)
722                 return (EINVAL);
723
724         GPIO_ASSERT_UNLOCKED(sc);
725         GPIO_LOCK(sc);
726         if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
727                 if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
728                         (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
729                                 GPIO_UNLOCK(sc);
730                                 return (EINVAL);
731                 }
732
733                 if (flags & GPIO_PIN_INPUT)
734                         nct_set_pin_is_input(sc, pin_num);
735                 else
736                         nct_set_pin_is_output(sc, pin_num);
737         }
738
739         if (flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
740                 if (flags & GPIO_PIN_INPUT) {
741                         GPIO_UNLOCK(sc);
742                         return (EINVAL);
743                 }
744
745                 if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) ==
746                         (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
747                                 GPIO_UNLOCK(sc);
748                                 return (EINVAL);
749                 }
750
751                 if (flags & GPIO_PIN_OPENDRAIN)
752                         nct_set_pin_opendrain(sc, pin_num);
753                 else
754                         nct_set_pin_pushpull(sc, pin_num);
755         }
756
757         if (flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
758                 if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) !=
759                         (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
760                                 GPIO_UNLOCK(sc);
761                                 return (EINVAL);
762                 }
763
764                 if (flags & GPIO_PIN_INVIN)
765                         nct_set_pin_is_inverted(sc, pin_num);
766                 else
767                         nct_set_pin_not_inverted(sc, pin_num);
768         }
769
770         pin->gp_flags = flags;
771         GPIO_UNLOCK(sc);
772
773         return (0);
774 }
775
776 static device_method_t nct_methods[] = {
777         /* Device interface */
778         DEVMETHOD(device_identify,      nct_identify),
779         DEVMETHOD(device_probe,         nct_probe),
780         DEVMETHOD(device_attach,        nct_attach),
781         DEVMETHOD(device_detach,        nct_detach),
782
783         /* GPIO */
784         DEVMETHOD(gpio_get_bus,                 nct_gpio_get_bus),
785         DEVMETHOD(gpio_pin_max,                 nct_gpio_pin_max),
786         DEVMETHOD(gpio_pin_get,                 nct_gpio_pin_get),
787         DEVMETHOD(gpio_pin_set,                 nct_gpio_pin_set),
788         DEVMETHOD(gpio_pin_toggle,              nct_gpio_pin_toggle),
789         DEVMETHOD(gpio_pin_getname,             nct_gpio_pin_getname),
790         DEVMETHOD(gpio_pin_getcaps,             nct_gpio_pin_getcaps),
791         DEVMETHOD(gpio_pin_getflags,    nct_gpio_pin_getflags),
792         DEVMETHOD(gpio_pin_setflags,    nct_gpio_pin_setflags),
793
794         DEVMETHOD_END
795 };
796
797 static driver_t nct_isa_driver = {
798         "gpio",
799         nct_methods,
800         sizeof(struct nct_softc)
801 };
802
803 static devclass_t nct_devclass;
804
805 DRIVER_MODULE(nctgpio, isa, nct_isa_driver, nct_devclass, NULL, NULL);
806 MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1);