/*- * Copyright (c) 2018 Stormshield * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "lewisburg_gpiocm.h" #include "p2sb.h" #define PADBAR 0x00c #define PADCFG0_GPIORXDIS (1<<9) #define PADCFG0_GPIOTXDIS (1<<8) #define PADCFG0_GPIORXSTATE (1<<1) #define PADCFG0_GPIOTXSTATE (1<<0) #define MAX_PAD_PER_GROUP 24 #define LBGGPIOCM_READ(sc, reg) p2sb_port_read_4(sc->p2sb, sc->port, reg) #define LBGGPIOCM_WRITE(sc, reg, val) \ p2sb_port_write_4(sc->p2sb, sc->port, reg, val) #define LBGGPIOCM_LOCK(sc) p2sb_lock(sc->p2sb) #define LBGGPIOCM_UNLOCK(sc) p2sb_unlock(sc->p2sb) struct lbggroup { int groupid; int npins; int pins_off; device_t dev; char grpname; }; struct lbgcommunity { uint8_t npins; const char *name; uint32_t pad_off; struct lbggroup groups[3]; int ngroups; const char *grpnames; }; #define LBG_COMMUNITY(n, np, g) \ { \ .name = n, \ .npins = np, \ .grpnames = g, \ } static struct lbgcommunity lbg_communities[] = { LBG_COMMUNITY("LewisBurg GPIO Community 0", 72, "ABF"), LBG_COMMUNITY("LewisBurg GPIO Community 1", 61, "CDE"), LBG_COMMUNITY("LewisBurg GPIO Community 2", 0, ""), LBG_COMMUNITY("LewisBurg GPIO Community 3", 12, "I"), LBG_COMMUNITY("LewisBurg GPIO Community 4", 36, "JK"), LBG_COMMUNITY("LewisBurg GPIO Community 5", 66, "GHL"), }; struct lbggpiocm_softc { int port; device_t p2sb; struct lbgcommunity *community; }; static struct lbggroup *lbggpiocm_get_group(struct lbggpiocm_softc *sc, device_t child); static __inline struct lbggroup * lbggpiocm_get_group(struct lbggpiocm_softc *sc, device_t child) { int i; for (i = 0; i < sc->community->ngroups; ++i) if (sc->community->groups[i].dev == child) return (&sc->community->groups[i]); return (NULL); } static __inline uint32_t lbggpiocm_getpad(struct lbggpiocm_softc *sc, uint32_t pin) { if (pin >= sc->community->npins) return (0); return (sc->community->pad_off + 2 * 4 * pin); } int lbggpiocm_get_group_npins(device_t dev, device_t child) { struct lbggpiocm_softc *sc = device_get_softc(dev); struct lbggroup *group; group = lbggpiocm_get_group(sc, child); if (group != NULL) return (group->npins); return (-1); } char lbggpiocm_get_group_name(device_t dev, device_t child) { struct lbggpiocm_softc *sc = device_get_softc(dev); struct lbggroup *group; group = lbggpiocm_get_group(sc, child); if (group != NULL) return (group->grpname); return ('\0'); } static int lbggpiocm_pin2cpin(struct lbggpiocm_softc *sc, device_t child, uint32_t pin) { struct lbggroup *group; group = lbggpiocm_get_group(sc, child); if (group != NULL) return (pin + group->pins_off); return (-1); } int lbggpiocm_pin_setflags(device_t dev, device_t child, uint32_t pin, uint32_t flags) { struct lbggpiocm_softc *sc = device_get_softc(dev); uint32_t padreg, padval; int rpin; if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) return (EINVAL); if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 0) return (EINVAL); rpin = lbggpiocm_pin2cpin(sc, child, pin); if (rpin < 0) return (EINVAL); padreg = lbggpiocm_getpad(sc, rpin); LBGGPIOCM_LOCK(sc); padval = LBGGPIOCM_READ(sc, padreg); if (flags & GPIO_PIN_INPUT) { padval &= ~PADCFG0_GPIORXDIS; padval |= PADCFG0_GPIOTXDIS; } else if (flags & GPIO_PIN_OUTPUT) { padval &= ~PADCFG0_GPIOTXDIS; padval |= PADCFG0_GPIORXDIS; } LBGGPIOCM_WRITE(sc, padreg, padval); LBGGPIOCM_UNLOCK(sc); return (0); } int lbggpiocm_pin_get(device_t dev, device_t child, uint32_t pin, uint32_t *value) { struct lbggpiocm_softc *sc = device_get_softc(dev); uint32_t padreg, val; int rpin; if (value == NULL) return (EINVAL); rpin = lbggpiocm_pin2cpin(sc, child, pin); if (rpin < 0) return (EINVAL); padreg = lbggpiocm_getpad(sc, rpin); LBGGPIOCM_LOCK(sc); val = LBGGPIOCM_READ(sc, padreg); LBGGPIOCM_UNLOCK(sc); if (!(val & PADCFG0_GPIOTXDIS)) *value = !!(val & PADCFG0_GPIOTXSTATE); else *value = !!(val & PADCFG0_GPIORXSTATE); return (0); } int lbggpiocm_pin_set(device_t dev, device_t child, uint32_t pin, uint32_t value) { struct lbggpiocm_softc *sc = device_get_softc(dev); uint32_t padreg, padcfg; int rpin; rpin = lbggpiocm_pin2cpin(sc, child, pin); if (rpin < 0) return (EINVAL); padreg = lbggpiocm_getpad(sc, rpin); LBGGPIOCM_LOCK(sc); padcfg = LBGGPIOCM_READ(sc, padreg); if (value) padcfg |= PADCFG0_GPIOTXSTATE; else padcfg &= ~PADCFG0_GPIOTXSTATE; LBGGPIOCM_WRITE(sc, padreg, padcfg); LBGGPIOCM_UNLOCK(sc); return (0); } int lbggpiocm_pin_toggle(device_t dev, device_t child, uint32_t pin) { struct lbggpiocm_softc *sc = device_get_softc(dev); uint32_t padreg, padcfg; int rpin; rpin = lbggpiocm_pin2cpin(sc, child, pin); if (rpin < 0) return (EINVAL); padreg = lbggpiocm_getpad(sc, rpin); LBGGPIOCM_LOCK(sc); padcfg = LBGGPIOCM_READ(sc, padreg); padcfg ^= PADCFG0_GPIOTXSTATE; LBGGPIOCM_WRITE(sc, padreg, padcfg); LBGGPIOCM_UNLOCK(sc); return (0); } static int lbggpiocm_probe(device_t dev) { struct lbggpiocm_softc *sc = device_get_softc(dev); int unit; sc->p2sb = device_get_parent(dev); unit = device_get_unit(dev); KASSERT(unit < nitems(lbg_communities), ("Wrong number of devices or communities")); sc->port = p2sb_get_port(sc->p2sb, unit); sc->community = &lbg_communities[unit]; if (sc->port < 0) return (ENXIO); device_set_desc(dev, sc->community->name); return (BUS_PROBE_DEFAULT); } static int lbggpiocm_attach(device_t dev) { uint32_t npins; struct lbggpiocm_softc *sc; struct lbggroup *group; int i; sc = device_get_softc(dev); if (sc->community->npins == 0) return (ENXIO); LBGGPIOCM_LOCK(sc); sc->community->pad_off = LBGGPIOCM_READ(sc, PADBAR); LBGGPIOCM_UNLOCK(sc); npins = sc->community->npins; for (i = 0; i < nitems(sc->community->groups) && npins > 0; ++i) { group = &sc->community->groups[i]; group->groupid = i; group->grpname = sc->community->grpnames[i]; group->pins_off = i * MAX_PAD_PER_GROUP; group->npins = npins < MAX_PAD_PER_GROUP ? npins : MAX_PAD_PER_GROUP; npins -= group->npins; group->dev = device_add_child(dev, "gpio", -1); } sc->community->ngroups = i; bus_attach_children(dev); return (0); } static int lbggpiocm_detach(device_t dev) { int error; error = device_delete_children(dev); if (error) return (error); return (bus_generic_detach(dev)); } static device_method_t lbggpiocm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, lbggpiocm_probe), DEVMETHOD(device_attach, lbggpiocm_attach), DEVMETHOD(device_detach, lbggpiocm_detach), DEVMETHOD_END }; static driver_t lbggpiocm_driver = { "lbggpiocm", lbggpiocm_methods, sizeof(struct lbggpiocm_softc) }; DRIVER_MODULE(lbggpiocm, p2sb, lbggpiocm_driver, NULL, NULL);