Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/gpio/qoriq_gpio.c
- This file was moved from sys/powerpc/mpc85xx/qoriq_gpio.c.
/*- | /*- | ||||
* Copyright (c) 2020 Alstom Group. | |||||
* Copyright (c) 2020 Semihalf. | |||||
* Copyright (c) 2015 Justin Hibbits | * Copyright (c) 2015 Justin Hibbits | ||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
Show All 37 Lines | |||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include "gpio_if.h" | #include "gpio_if.h" | ||||
#define MAXPIN (31) | #define MAXPIN (31) | ||||
#define VALID_PIN(u) ((u) >= 0 && (u) <= MAXPIN) | #define VALID_PIN(u) ((u) >= 0 && (u) <= MAXPIN) | ||||
#define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ | |||||
GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL) | |||||
#define GPIO_LOCK(sc) mtx_lock(&(sc)->sc_mtx) | #define GPIO_LOCK(sc) mtx_lock(&(sc)->sc_mtx) | ||||
#define GPIO_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) | #define GPIO_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) | ||||
#define GPIO_LOCK_INIT(sc) \ | #define GPIO_LOCK_INIT(sc) \ | ||||
mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ | mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ | ||||
"gpio", MTX_DEF) | "gpio", MTX_DEF) | ||||
#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); | #define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); | ||||
#define GPIO_GPDIR 0x0 | #define GPIO_GPDIR 0x0 | ||||
#define GPIO_GPODR 0x4 | #define GPIO_GPODR 0x4 | ||||
#define GPIO_GPDAT 0x8 | #define GPIO_GPDAT 0x8 | ||||
#define GPIO_GPIER 0xc | #define GPIO_GPIER 0xc | ||||
#define GPIO_GPIMR 0x10 | #define GPIO_GPIMR 0x10 | ||||
#define GPIO_GPICR 0x14 | #define GPIO_GPICR 0x14 | ||||
#define GPIO_GPIBE 0x18 | |||||
struct qoriq_gpio_softc { | struct qoriq_gpio_softc { | ||||
device_t dev; | device_t dev; | ||||
device_t busdev; | device_t busdev; | ||||
struct mtx sc_mtx; | struct mtx sc_mtx; | ||||
struct resource *sc_mem; /* Memory resource */ | struct resource *sc_mem; /* Memory resource */ | ||||
struct gpio_pin sc_pins[MAXPIN + 1]; | |||||
}; | }; | ||||
static device_t | static device_t | ||||
qoriq_gpio_get_bus(device_t dev) | qoriq_gpio_get_bus(device_t dev) | ||||
{ | { | ||||
struct qoriq_gpio_softc *sc; | struct qoriq_gpio_softc *sc; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
return (sc->busdev); | return (sc->busdev); | ||||
} | } | ||||
static int | static int | ||||
qoriq_gpio_pin_max(device_t dev, int *maxpin) | qoriq_gpio_pin_max(device_t dev, int *maxpin) | ||||
{ | { | ||||
*maxpin = MAXPIN; | *maxpin = MAXPIN; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Get a specific pin's capabilities. */ | /* Get a specific pin's capabilities. */ | ||||
static int | static int | ||||
qoriq_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) | qoriq_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) | ||||
{ | { | ||||
struct qoriq_gpio_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
if (!VALID_PIN(pin)) | if (!VALID_PIN(pin)) | ||||
return (EINVAL); | return (EINVAL); | ||||
*caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN); | GPIO_LOCK(sc); | ||||
*caps = sc->sc_pins[pin].gp_caps; | |||||
GPIO_UNLOCK(sc); | |||||
return (0); | return (0); | ||||
} | } | ||||
/* Get a specific pin's name. */ | /* Get a specific pin's name. */ | ||||
static int | static int | ||||
qoriq_gpio_pin_getname(device_t dev, uint32_t pin, char *name) | qoriq_gpio_pin_getname(device_t dev, uint32_t pin, char *name) | ||||
{ | { | ||||
Show All 18 Lines | qoriq_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) | ||||
if (!VALID_PIN(pin)) | if (!VALID_PIN(pin)) | ||||
return (EINVAL); | return (EINVAL); | ||||
if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == | if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == | ||||
(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) | (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) | ||||
return (EINVAL); | return (EINVAL); | ||||
GPIO_LOCK(sc); | GPIO_LOCK(sc); | ||||
if ((flags & sc->sc_pins[pin].gp_caps) != flags) { | |||||
GPIO_UNLOCK(sc); | |||||
return (EINVAL); | |||||
} | |||||
if (flags & GPIO_PIN_INPUT) { | if (flags & GPIO_PIN_INPUT) { | ||||
reg = bus_read_4(sc->sc_mem, GPIO_GPDIR); | reg = bus_read_4(sc->sc_mem, GPIO_GPDIR); | ||||
reg &= ~(1 << (31 - pin)); | reg &= ~(1 << (31 - pin)); | ||||
bus_write_4(sc->sc_mem, GPIO_GPDIR, reg); | bus_write_4(sc->sc_mem, GPIO_GPDIR, reg); | ||||
} | } | ||||
else if (flags & GPIO_PIN_OUTPUT) { | else if (flags & GPIO_PIN_OUTPUT) { | ||||
reg = bus_read_4(sc->sc_mem, GPIO_GPDIR); | reg = bus_read_4(sc->sc_mem, GPIO_GPDIR); | ||||
reg |= (1 << (31 - pin)); | reg |= (1 << (31 - pin)); | ||||
bus_write_4(sc->sc_mem, GPIO_GPDIR, reg); | bus_write_4(sc->sc_mem, GPIO_GPDIR, reg); | ||||
reg = bus_read_4(sc->sc_mem, GPIO_GPODR); | reg = bus_read_4(sc->sc_mem, GPIO_GPODR); | ||||
if (flags & GPIO_PIN_OPENDRAIN) | if (flags & GPIO_PIN_OPENDRAIN) | ||||
reg |= (1 << (31 - pin)); | reg |= (1 << (31 - pin)); | ||||
else | else | ||||
reg &= ~(1 << (31 - pin)); | reg &= ~(1 << (31 - pin)); | ||||
bus_write_4(sc->sc_mem, GPIO_GPODR, reg); | bus_write_4(sc->sc_mem, GPIO_GPODR, reg); | ||||
} | } | ||||
sc->sc_pins[pin].gp_flags = flags; | |||||
GPIO_UNLOCK(sc); | GPIO_UNLOCK(sc); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | |||||
qoriq_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *pflags) | |||||
{ | |||||
struct qoriq_gpio_softc *sc; | |||||
if (!VALID_PIN(pin)) | |||||
return (EINVAL); | |||||
sc = device_get_softc(dev); | |||||
GPIO_LOCK(sc); | |||||
*pflags = sc->sc_pins[pin].gp_flags; | |||||
GPIO_UNLOCK(sc); | |||||
return (0); | |||||
} | |||||
/* Set a specific output pin's value. */ | /* Set a specific output pin's value. */ | ||||
static int | static int | ||||
qoriq_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) | qoriq_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) | ||||
{ | { | ||||
struct qoriq_gpio_softc *sc = device_get_softc(dev); | struct qoriq_gpio_softc *sc = device_get_softc(dev); | ||||
uint32_t outvals; | uint32_t outvals; | ||||
uint8_t pinbit; | uint8_t pinbit; | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | qoriq_gpio_probe(device_t dev) | ||||
if (ofw_bus_search_compatible(dev, gpio_matches)->ocd_data == 0) | if (ofw_bus_search_compatible(dev, gpio_matches)->ocd_data == 0) | ||||
return (ENXIO); | return (ENXIO); | ||||
device_set_desc(dev, "Freescale QorIQ GPIO driver"); | device_set_desc(dev, "Freescale QorIQ GPIO driver"); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | |||||
qoriq_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins, | |||||
uint32_t change_pins, uint32_t *orig_pins) | |||||
{ | |||||
struct qoriq_gpio_softc *sc; | |||||
uint32_t hwstate; | |||||
sc = device_get_softc(dev); | |||||
if (first_pin != 0) | |||||
return (EINVAL); | |||||
GPIO_LOCK(sc); | |||||
hwstate = bus_read_4(sc->sc_mem, GPIO_GPDAT); | |||||
bus_write_4(sc->sc_mem, GPIO_GPDAT, | |||||
(hwstate & ~clear_pins) ^ change_pins); | |||||
GPIO_UNLOCK(sc); | |||||
if (orig_pins != NULL) | |||||
*orig_pins = hwstate; | |||||
return (0); | |||||
} | |||||
static int | |||||
qoriq_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, | |||||
uint32_t *pin_flags) | |||||
{ | |||||
uint32_t dir, odr, mask, reg; | |||||
struct qoriq_gpio_softc *sc; | |||||
uint32_t newflags[32]; | |||||
int i; | |||||
if (first_pin != 0 || !VALID_PIN(num_pins)) | |||||
return (EINVAL); | |||||
sc = device_get_softc(dev); | |||||
dir = odr = mask = 0; | |||||
for (i = 0; i < num_pins; i++) { | |||||
newflags[i] = 0; | |||||
mask |= (1 << i); | |||||
if (pin_flags[i] & GPIO_PIN_INPUT) { | |||||
newflags[i] = GPIO_PIN_INPUT; | |||||
dir &= ~(1 << i); | |||||
} else { | |||||
newflags[i] = GPIO_PIN_OUTPUT; | |||||
dir |= (1 << i); | |||||
if (pin_flags[i] & GPIO_PIN_OPENDRAIN) { | |||||
newflags[i] |= GPIO_PIN_OPENDRAIN; | |||||
odr |= (1 << i); | |||||
} else { | |||||
newflags[i] |= GPIO_PIN_PUSHPULL; | |||||
odr &= ~(1 << i); | |||||
} | |||||
} | |||||
} | |||||
GPIO_LOCK(sc); | |||||
reg = (bus_read_4(sc->sc_mem, GPIO_GPDIR) & ~mask) | dir; | |||||
bus_write_4(sc->sc_mem, GPIO_GPDIR, reg); | |||||
reg = (bus_read_4(sc->sc_mem, GPIO_GPODR) & ~mask) | odr; | |||||
bus_write_4(sc->sc_mem, GPIO_GPODR, reg); | |||||
for (i = 0; i < num_pins; i++) | |||||
sc->sc_pins[i].gp_flags = newflags[i]; | |||||
GPIO_UNLOCK(sc); | |||||
return (0); | |||||
} | |||||
static int | |||||
qoriq_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, | |||||
pcell_t *gpios, uint32_t *pin, uint32_t *flags) | |||||
{ | |||||
struct qoriq_gpio_softc *sc; | |||||
int err; | |||||
if (!VALID_PIN(gpios[0])) | |||||
return (EINVAL); | |||||
sc = device_get_softc(bus); | |||||
GPIO_LOCK(sc); | |||||
err = qoriq_gpio_pin_setflags(bus, gpios[0], gpios[1]); | |||||
GPIO_UNLOCK(sc); | |||||
if (err == 0) { | |||||
*pin = gpios[0]; | |||||
*flags = gpios[1]; | |||||
} | |||||
return (err); | |||||
} | |||||
static int qoriq_gpio_detach(device_t dev); | static int qoriq_gpio_detach(device_t dev); | ||||
static int | static int | ||||
qoriq_gpio_attach(device_t dev) | qoriq_gpio_attach(device_t dev) | ||||
{ | { | ||||
struct qoriq_gpio_softc *sc = device_get_softc(dev); | struct qoriq_gpio_softc *sc = device_get_softc(dev); | ||||
int rid; | int i, rid; | ||||
sc->dev = dev; | sc->dev = dev; | ||||
GPIO_LOCK_INIT(sc); | GPIO_LOCK_INIT(sc); | ||||
/* Allocate memory. */ | /* Allocate memory. */ | ||||
rid = 0; | rid = 0; | ||||
sc->sc_mem = bus_alloc_resource_any(dev, | sc->sc_mem = bus_alloc_resource_any(dev, | ||||
SYS_RES_MEMORY, &rid, RF_ACTIVE); | SYS_RES_MEMORY, &rid, RF_ACTIVE); | ||||
if (sc->sc_mem == NULL) { | if (sc->sc_mem == NULL) { | ||||
device_printf(dev, "Can't allocate memory for device output port"); | device_printf(dev, "Can't allocate memory for device output port"); | ||||
qoriq_gpio_detach(dev); | qoriq_gpio_detach(dev); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
for (i = 0; i <= MAXPIN; i++) | |||||
sc->sc_pins[i].gp_caps = DEFAULT_CAPS; | |||||
sc->busdev = gpiobus_attach_bus(dev); | sc->busdev = gpiobus_attach_bus(dev); | ||||
if (sc->busdev == NULL) { | if (sc->busdev == NULL) { | ||||
qoriq_gpio_detach(dev); | qoriq_gpio_detach(dev); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
/* | |||||
* Enable the GPIO Input Buffer for all GPIOs. | |||||
* This is safe on devices without a GPIBE register, because those | |||||
* devices ignore writes and read 0's in undefined portions of the map. | |||||
*/ | |||||
if (ofw_bus_is_compatible(dev, "fsl,qoriq-gpio")) | |||||
bus_write_4(sc->sc_mem, GPIO_GPIBE, 0xffffffff); | |||||
mw: (While not having the docs in hand) `sys/arm64/qoriq/ls1046_gpio.c` did not use GPIO_GPIBE… | |||||
Done Inline ActionsThis is required for LX2160, but may not be required for others. Every reference I have, for P1022, P5020, and LX2160, any unimplemented registers in this block are ignored, so it's not a problem on other targets. I don't yet have a Honeycomb board, so have only tested on a P1022 board thus far, that it's not broken. This only affects pins marked as input, it has no effect on other pins. This is set 'unconditionally' (only conditional on compatible property) in Linux, too. jhibbits: This is required for LX2160, but may not be required for others.
Every reference I have, for… | |||||
OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev); | OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
qoriq_gpio_detach(device_t dev) | qoriq_gpio_detach(device_t dev) | ||||
Show All 21 Lines | static device_method_t qoriq_gpio_methods[] = { | ||||
/* GPIO protocol */ | /* GPIO protocol */ | ||||
DEVMETHOD(gpio_get_bus, qoriq_gpio_get_bus), | DEVMETHOD(gpio_get_bus, qoriq_gpio_get_bus), | ||||
DEVMETHOD(gpio_pin_max, qoriq_gpio_pin_max), | DEVMETHOD(gpio_pin_max, qoriq_gpio_pin_max), | ||||
DEVMETHOD(gpio_pin_getname, qoriq_gpio_pin_getname), | DEVMETHOD(gpio_pin_getname, qoriq_gpio_pin_getname), | ||||
DEVMETHOD(gpio_pin_getcaps, qoriq_gpio_pin_getcaps), | DEVMETHOD(gpio_pin_getcaps, qoriq_gpio_pin_getcaps), | ||||
DEVMETHOD(gpio_pin_get, qoriq_gpio_pin_get), | DEVMETHOD(gpio_pin_get, qoriq_gpio_pin_get), | ||||
DEVMETHOD(gpio_pin_set, qoriq_gpio_pin_set), | DEVMETHOD(gpio_pin_set, qoriq_gpio_pin_set), | ||||
DEVMETHOD(gpio_pin_getflags, qoriq_gpio_pin_getflags), | |||||
DEVMETHOD(gpio_pin_setflags, qoriq_gpio_pin_setflags), | DEVMETHOD(gpio_pin_setflags, qoriq_gpio_pin_setflags), | ||||
DEVMETHOD(gpio_pin_toggle, qoriq_gpio_pin_toggle), | DEVMETHOD(gpio_pin_toggle, qoriq_gpio_pin_toggle), | ||||
DEVMETHOD(gpio_map_gpios, qoriq_gpio_map_gpios), | |||||
DEVMETHOD(gpio_pin_access_32, qoriq_gpio_pin_access_32), | |||||
DEVMETHOD(gpio_pin_config_32, qoriq_gpio_pin_config_32), | |||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
static driver_t qoriq_gpio_driver = { | static driver_t qoriq_gpio_driver = { | ||||
"gpio", | "gpio", | ||||
qoriq_gpio_methods, | qoriq_gpio_methods, | ||||
sizeof(struct qoriq_gpio_softc), | sizeof(struct qoriq_gpio_softc), | ||||
}; | }; | ||||
static devclass_t qoriq_gpio_devclass; | static devclass_t qoriq_gpio_devclass; | ||||
EARLY_DRIVER_MODULE(qoriq_gpio, simplebus, qoriq_gpio_driver, | EARLY_DRIVER_MODULE(qoriq_gpio, simplebus, qoriq_gpio_driver, | ||||
qoriq_gpio_devclass, NULL, NULL, | qoriq_gpio_devclass, NULL, NULL, | ||||
BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); | BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); |
(While not having the docs in hand) sys/arm64/qoriq/ls1046_gpio.c did not use GPIO_GPIBE - this could be a bug, but: