mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-29 20:04:57 +01:00
gpioled: use hw pin inversion if available
Add hint.gpioled.%d.invmode to allow setting the pin inversion method. Accept
the following values:
- auto Use hardware pin inversion if available, else fallback to software pin
inversion.
- hw Use hardware pin inversion.
- sw Use software pin inversion.
Default is auto. This hint is ignored when no pin inversion is
requested (hint.gpioled.%d.invert=0).
Signed-off-by: Stéphane Rochoy <[email protected]>
Sponsored-by: Stormshield
Reviewed by: imp
Pull Request: https://github.com/freebsd/freebsd-src/pull/1805
This commit is contained in:
1 parent
f53228c4e3
commit
9ae3c3010a
1 file changed
+91
-17
+91
-17
@@ -55,13 +55,13 @@
|
||||
device_get_nameunit((_sc)->sc_dev), "gpioled", MTX_DEF)
|
||||
#define GPIOLED_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
|
||||
|
||||
struct gpioled_softc
|
||||
struct gpioled_softc
|
||||
{
|
||||
device_t sc_dev;
|
||||
device_t sc_busdev;
|
||||
struct mtx sc_mtx;
|
||||
struct cdev *sc_leddev;
|
||||
int sc_invert;
|
||||
int sc_softinvert;
|
||||
};
|
||||
|
||||
static void gpioled_control(void *, int);
|
||||
@@ -69,20 +69,19 @@ static int gpioled_probe(device_t);
|
||||
static int gpioled_attach(device_t);
|
||||
static int gpioled_detach(device_t);
|
||||
|
||||
static void
|
||||
static void
|
||||
gpioled_control(void *priv, int onoff)
|
||||
{
|
||||
struct gpioled_softc *sc;
|
||||
|
||||
sc = (struct gpioled_softc *)priv;
|
||||
if (onoff == -1) /* Keep the current state. */
|
||||
return;
|
||||
if (sc->sc_softinvert)
|
||||
onoff = !onoff;
|
||||
GPIOLED_LOCK(sc);
|
||||
if (GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN,
|
||||
GPIO_PIN_OUTPUT) == 0) {
|
||||
if (sc->sc_invert)
|
||||
onoff = !onoff;
|
||||
GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN,
|
||||
onoff ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
|
||||
}
|
||||
GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN,
|
||||
onoff ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
|
||||
GPIOLED_UNLOCK(sc);
|
||||
}
|
||||
|
||||
@@ -94,27 +93,102 @@ gpioled_probe(device_t dev)
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
gpioled_inv(device_t dev, uint32_t *pin_flags)
|
||||
{
|
||||
struct gpioled_softc *sc;
|
||||
int invert;
|
||||
uint32_t pin_caps;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if (resource_int_value(device_get_name(dev),
|
||||
device_get_unit(dev), "invert", &invert))
|
||||
invert = 0;
|
||||
|
||||
if (GPIOBUS_PIN_GETCAPS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN,
|
||||
&pin_caps) != 0) {
|
||||
if (bootverbose)
|
||||
device_printf(sc->sc_dev, "unable to get pin caps\n");
|
||||
return (-1);
|
||||
}
|
||||
if (pin_caps & GPIO_PIN_INVOUT)
|
||||
*pin_flags &= ~GPIO_PIN_INVOUT;
|
||||
sc->sc_softinvert = 0;
|
||||
if (invert) {
|
||||
const char *invmode;
|
||||
|
||||
if (resource_string_value(device_get_name(dev),
|
||||
device_get_unit(dev), "invmode", &invmode))
|
||||
invmode = NULL;
|
||||
|
||||
if (invmode) {
|
||||
if (!strcmp(invmode, "sw"))
|
||||
sc->sc_softinvert = 1;
|
||||
else if (!strcmp(invmode, "hw")) {
|
||||
if (pin_caps & GPIO_PIN_INVOUT)
|
||||
*pin_flags |= GPIO_PIN_INVOUT;
|
||||
else {
|
||||
device_printf(sc->sc_dev, "hardware pin inversion not supported\n");
|
||||
return (-1);
|
||||
}
|
||||
} else {
|
||||
if (strcmp(invmode, "auto") != 0)
|
||||
device_printf(sc->sc_dev, "invalid pin inversion mode\n");
|
||||
invmode = NULL;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* auto inversion mode: use hardware support if available, else fallback to
|
||||
* software emulation.
|
||||
*/
|
||||
if (invmode == NULL) {
|
||||
if (pin_caps & GPIO_PIN_INVOUT)
|
||||
*pin_flags |= GPIO_PIN_INVOUT;
|
||||
else
|
||||
sc->sc_softinvert = 1;
|
||||
}
|
||||
}
|
||||
MPASS(!invert ||
|
||||
(((*pin_flags & GPIO_PIN_INVOUT) != 0) && !sc->sc_softinvert) ||
|
||||
(((*pin_flags & GPIO_PIN_INVOUT) == 0) && sc->sc_softinvert));
|
||||
return (invert);
|
||||
}
|
||||
|
||||
static int
|
||||
gpioled_attach(device_t dev)
|
||||
{
|
||||
struct gpioled_softc *sc;
|
||||
int state;
|
||||
const char *name;
|
||||
uint32_t pin_flags;
|
||||
int invert;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->sc_dev = dev;
|
||||
sc->sc_busdev = device_get_parent(dev);
|
||||
GPIOLED_LOCK_INIT(sc);
|
||||
|
||||
state = 0;
|
||||
|
||||
if (resource_string_value(device_get_name(dev),
|
||||
if (resource_string_value(device_get_name(dev),
|
||||
device_get_unit(dev), "name", &name))
|
||||
name = NULL;
|
||||
resource_int_value(device_get_name(dev),
|
||||
device_get_unit(dev), "invert", &sc->sc_invert);
|
||||
resource_int_value(device_get_name(dev),
|
||||
device_get_unit(dev), "state", &state);
|
||||
|
||||
if (resource_int_value(device_get_name(dev),
|
||||
device_get_unit(dev), "state", &state))
|
||||
state = 0;
|
||||
|
||||
pin_flags = GPIO_PIN_OUTPUT;
|
||||
invert = gpioled_inv(dev, &pin_flags);
|
||||
if (invert < 0)
|
||||
return (ENXIO);
|
||||
device_printf(sc->sc_dev, "state %d invert %s\n",
|
||||
state, (invert ? (sc->sc_softinvert ? "sw" : "hw") : "no"));
|
||||
if (GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN,
|
||||
pin_flags) != 0) {
|
||||
if (bootverbose)
|
||||
device_printf(sc->sc_dev, "unable to set pin flags, %#x\n", pin_flags);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
sc->sc_leddev = led_create_state(gpioled_control, sc, name ? name :
|
||||
device_get_nameunit(dev), state);
|
||||
|
||||
Reference in new issue
Block a user