Diff below adds GPIO support to bcmgpio(4).  It also adds the bits to
attach gpio(4) such that GPIO pins can be controlled from userland.
This makes sense on boards like the Raspberry Pi and the
implementation makes sure that pins used by kernel drivers can't be
touched.

ok?


Index: arch/arm64/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v
retrieving revision 1.161
diff -u -p -r1.161 GENERIC
--- arch/arm64/conf/GENERIC     25 Apr 2020 22:28:12 -0000      1.161
+++ arch/arm64/conf/GENERIC     26 Apr 2020 10:51:19 -0000
@@ -148,6 +148,7 @@ bcmclock*   at fdt? early 1
 bcmdmac*       at fdt? early 1
 bcmdog*                at fdt?
 bcmgpio*       at fdt? early 1
+gpio*          at bcmgpio?
 bcmintc*       at fdt? early 1
 bcmirng*       at fdt?
 bcmmbox*       at fdt? early 1
Index: dev/fdt/bcm2835_gpio.c
===================================================================
RCS file: /cvs/src/sys/dev/fdt/bcm2835_gpio.c,v
retrieving revision 1.2
diff -u -p -r1.2 bcm2835_gpio.c
--- dev/fdt/bcm2835_gpio.c      25 Apr 2020 22:15:00 -0000      1.2
+++ dev/fdt/bcm2835_gpio.c      26 Apr 2020 10:51:20 -0000
@@ -17,16 +17,21 @@
 
 #include <sys/param.h>
 #include <sys/device.h>
+#include <sys/gpio.h>
 #include <sys/malloc.h>
 #include <sys/systm.h>
 
 #include <machine/bus.h>
 #include <machine/fdt.h>
 
+#include <dev/gpio/gpiovar.h>
 #include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_gpio.h>
 #include <dev/ofw/ofw_pinctrl.h>
 #include <dev/ofw/fdt.h>
 
+#include "gpio.h"
+
 /* Registers */
 #define GPFSEL(n)              (0x00 + ((n) * 4))
 #define  GPFSEL_MASK           0x7
@@ -38,6 +43,9 @@
 #define  GPFSEL_ALT3           0x7
 #define  GPFSEL_ALT4           0x3
 #define  GPFSEL_ALT5           0x2
+#define GPSET(n)               (0x1c + ((n) * 4))
+#define GPCLR(n)               (0x28 + ((n) * 4))
+#define GPLEV(n)               (0x34 + ((n) * 4))
 #define GPPUD                  0x94
 #define  GPPUD_PUD             0x3
 #define  GPPUD_PUD_OFF         0x0
@@ -47,6 +55,8 @@
 #define GPPULL(n)              (0xe4 + ((n) * 4))
 #define  GPPULL_MASK           0x3
 
+#define BCMGPIO_MAX_PINS       58
+
 #define HREAD4(sc, reg)                                                        
\
        (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
 #define HWRITE4(sc, reg, val)                                          \
@@ -58,6 +68,13 @@ struct bcmgpio_softc {
        bus_space_handle_t      sc_ioh;
 
        void    (*sc_config_pull)(struct bcmgpio_softc *, int, int);
+       int                     sc_num_pins;
+
+       struct gpio_controller  sc_gc;
+
+       struct gpio_chipset_tag sc_gpio_tag;
+       gpio_pin_t              sc_gpio_pins[BCMGPIO_MAX_PINS];
+       int                     sc_gpio_claimed[BCMGPIO_MAX_PINS];
 };
 
 int    bcmgpio_match(struct device *, void *, void *);
@@ -74,6 +91,10 @@ struct cfdriver bcmgpio_cd = {
 void   bcm2711_config_pull(struct bcmgpio_softc *, int, int);
 void   bcm2835_config_pull(struct bcmgpio_softc *, int, int);
 int    bcmgpio_pinctrl(uint32_t, void *);
+void   bcmgpio_config_pin(void *, uint32_t *, int);
+int    bcmgpio_get_pin(void *, uint32_t *);
+void   bcmgpio_set_pin(void *, uint32_t *, int);
+void   bcmgpio_attach_gpio(struct device *);
 
 int
 bcmgpio_match(struct device *parent, void *match, void *aux)
@@ -104,12 +125,24 @@ bcmgpio_attach(struct device *parent, st
 
        printf("\n");
 
-       if (OF_is_compatible(faa->fa_node, "brcm,bcm2711-gpio"))
+       if (OF_is_compatible(faa->fa_node, "brcm,bcm2711-gpio")) {
            sc->sc_config_pull = bcm2711_config_pull;
-       else
+           sc->sc_num_pins = 58;
+       } else {
            sc->sc_config_pull = bcm2835_config_pull;
+           sc->sc_num_pins = 54;
+       }
 
        pinctrl_register(faa->fa_node, bcmgpio_pinctrl, sc);
+
+       sc->sc_gc.gc_node = faa->fa_node;
+       sc->sc_gc.gc_cookie = sc;
+       sc->sc_gc.gc_config_pin = bcmgpio_config_pin;
+       sc->sc_gc.gc_get_pin = bcmgpio_get_pin;
+       sc->sc_gc.gc_set_pin = bcmgpio_set_pin;
+       gpio_controller_register(&sc->sc_gc);
+
+       config_mountroot(self, bcmgpio_attach_gpio);
 }
 
 void
@@ -186,6 +219,7 @@ bcmgpio_pinctrl(uint32_t phandle, void *
                bcmgpio_config_func(sc, pins[i], func);
                if (plen > 0 && i < plen / sizeof(uint32_t))
                        sc->sc_config_pull(sc, pins[i], pull[i]);
+               sc->sc_gpio_claimed[pins[i]] = 1;
        }
 
        free(pull, M_TEMP, plen);
@@ -196,4 +230,176 @@ fail:
        free(pull, M_TEMP, plen);
        free(pins, M_TEMP, len);
        return -1;
+}
+
+void
+bcmgpio_config_pin(void *cookie, uint32_t *cells, int config)
+{
+       struct bcmgpio_softc *sc = cookie;
+       uint32_t pin = cells[0];
+
+       if (pin >= sc->sc_num_pins)
+               return;
+
+       if (config & GPIO_CONFIG_OUTPUT)
+               bcmgpio_config_func(sc, pin, GPFSEL_GPIO_OUT);
+       else
+               bcmgpio_config_func(sc, pin, GPFSEL_GPIO_OUT);
+       if (config & GPIO_CONFIG_PULL_UP)
+               sc->sc_config_pull(sc, pin, GPPUD_PUD_UP);
+       else if (config & GPIO_CONFIG_PULL_DOWN)
+               sc->sc_config_pull(sc, pin, GPPUD_PUD_DOWN);
+       else
+               sc->sc_config_pull(sc, pin, GPPUD_PUD_OFF);
+}
+
+int
+bcmgpio_get_pin(void *cookie, uint32_t *cells)
+{
+       struct bcmgpio_softc *sc = cookie;
+       uint32_t pin = cells[0];
+       uint32_t flags = cells[1];
+       uint32_t reg;
+       int val;
+
+       if (pin >= sc->sc_num_pins)
+               return 0;
+
+       reg = HREAD4(sc, GPLEV(pin / 32));
+       val = (reg >> (pin % 32)) & 1;
+       if (flags & GPIO_ACTIVE_LOW)
+               val = !val;
+       return val;
+}
+
+void
+bcmgpio_set_pin(void *cookie, uint32_t *cells, int val)
+{
+       struct bcmgpio_softc *sc = cookie;
+       uint32_t pin = cells[0];
+       uint32_t flags = cells[1];
+
+       if (pin >= sc->sc_num_pins)
+               return;
+
+       if (flags & GPIO_ACTIVE_LOW)
+               val = !val;
+       if (val)
+               HWRITE4(sc, GPSET(pin / 32), (1 << (pin % 32)));
+       else
+               HWRITE4(sc, GPCLR(pin / 32), (1 << (pin % 32)));
+}
+
+/*
+ * GPIO support code
+ */
+
+int    bcmgpio_pin_read(void *, int);
+void   bcmgpio_pin_write(void *, int, int);
+void   bcmgpio_pin_ctl(void *, int, int);
+
+static const struct gpio_chipset_tag bcmgpio_gpio_tag = {
+       .gp_pin_read = bcmgpio_pin_read,
+       .gp_pin_write = bcmgpio_pin_write,
+       .gp_pin_ctl = bcmgpio_pin_ctl
+};
+
+int
+bcmgpio_pin_read(void *cookie, int pin)
+{
+       struct bcmgpio_softc *sc = cookie;
+       uint32_t cells[2];
+
+       cells[0] = pin;
+       cells[1] = 0;
+
+       return bcmgpio_get_pin(sc, cells) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
+}
+
+void
+bcmgpio_pin_write(void *cookie, int pin, int val)
+{
+       struct bcmgpio_softc *sc = cookie;
+       uint32_t cells[2];
+
+       cells[0] = pin;
+       cells[1] = 0;
+
+       bcmgpio_set_pin(sc, cells, val);
+}
+
+void
+bcmgpio_pin_ctl(void *cookie, int pin, int flags)
+{
+       struct bcmgpio_softc *sc = cookie;
+       uint32_t cells[2];
+       uint32_t config;
+
+       cells[0] = pin;
+       cells[1] = 0;
+
+       config = 0;
+       if (ISSET(flags, GPIO_PIN_OUTPUT))
+               config |= GPIO_CONFIG_OUTPUT;
+       if (ISSET(flags, GPIO_PIN_PULLUP))
+               config |= GPIO_CONFIG_PULL_UP;
+       if (ISSET(flags, GPIO_PIN_PULLDOWN))
+               config |= GPIO_CONFIG_PULL_DOWN;
+
+       bcmgpio_config_pin(sc, cells, config);
+}
+
+void
+bcmgpio_attach_gpio(struct device *parent)
+{
+       struct bcmgpio_softc *sc = (struct bcmgpio_softc *)parent;
+       struct gpiobus_attach_args gba;
+       uint32_t reg;
+       int func, state, flags;
+       int pin;
+
+       for (pin = 0; pin < sc->sc_num_pins; pin++) {
+               /* Skip pins claimed by other devices. */
+               if (sc->sc_gpio_claimed[pin])
+                       continue;
+
+               /* Get pin configuration. */
+               reg = HREAD4(sc, GPFSEL(pin / 10));
+               func = (reg >> ((pin % 10) * 3)) & GPFSEL_MASK;
+
+               switch (func) {
+               case GPFSEL_GPIO_IN:
+                       flags = GPIO_PIN_SET | GPIO_PIN_INPUT;
+                       break;
+               case GPFSEL_GPIO_OUT:
+                       flags = GPIO_PIN_SET | GPIO_PIN_OUTPUT;
+                       break;
+               default:
+                       /* Ignore pins with an assigned function. */
+                       continue;
+               }
+
+               /* Get pin state. */
+               reg = HREAD4(sc, GPLEV(pin / 32));
+               state = (reg >> (pin % 32)) & 1;
+
+               sc->sc_gpio_pins[pin].pin_caps =
+                   GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
+                   GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN;
+               sc->sc_gpio_pins[pin].pin_flags = flags;
+               sc->sc_gpio_pins[pin].pin_state = state;
+               sc->sc_gpio_pins[pin].pin_num = pin;
+       }
+
+       memcpy(&sc->sc_gpio_tag, &bcmgpio_gpio_tag, sizeof(bcmgpio_gpio_tag));
+       sc->sc_gpio_tag.gp_cookie = sc;
+
+       gba.gba_name = "gpio";
+       gba.gba_gc = &sc->sc_gpio_tag;
+       gba.gba_pins = &sc->sc_gpio_pins[0];
+       gba.gba_npins = sc->sc_num_pins;
+
+#if NGPIO > 0
+       config_found(&sc->sc_dev, &gba, gpiobus_print);
+#endif
 }
Index: dev/fdt/files.fdt
===================================================================
RCS file: /cvs/src/sys/dev/fdt/files.fdt,v
retrieving revision 1.128
diff -u -p -r1.128 files.fdt
--- dev/fdt/files.fdt   25 Apr 2020 22:28:12 -0000      1.128
+++ dev/fdt/files.fdt   26 Apr 2020 10:51:20 -0000
@@ -97,7 +97,7 @@ device        bcmdog
 attach bcmdog at fdt
 file   dev/fdt/bcm2835_dog.c           bcmdog
 
-device bcmgpio
+device bcmgpio: gpiobus
 attach bcmgpio at fdt
 file   dev/fdt/bcm2835_gpio.c          bcmgpio
 
Index: dev/ofw/ofw_gpio.h
===================================================================
RCS file: /cvs/src/sys/dev/ofw/ofw_gpio.h,v
retrieving revision 1.3
diff -u -p -r1.3 ofw_gpio.h
--- dev/ofw/ofw_gpio.h  29 Sep 2019 04:25:08 -0000      1.3
+++ dev/ofw/ofw_gpio.h  26 Apr 2020 10:51:20 -0000
@@ -37,6 +37,8 @@ void  gpio_controller_register(struct gpi
 
 #define GPIO_CONFIG_INPUT      0x0000
 #define GPIO_CONFIG_OUTPUT     0x0001
+#define GPIO_CONFIG_PULL_UP    0x0010
+#define GPIO_CONFIG_PULL_DOWN  0x0020
 #define GPIO_CONFIG_MD0                0x1000
 #define GPIO_CONFIG_MD1                0x2000
 #define GPIO_CONFIG_MD2                0x4000

Reply via email to