Edit

IABSD.fr/src/sys/dev/acpi/amdgpio.c

Branch :

  • Show log

    Commit

  • Author : krw
    Date : 2020-06-20 18:06:22
    Hash : c0937d19
    Message : Shifts (<<) of more than 32 bits must be done on 64-bit values. COVERITY 1491295 ok kettenis@

  • sys/dev/acpi/amdgpio.c
  • /*	$OpenBSD: amdgpio.c,v 1.4 2020/06/20 18:06:22 krw Exp $	*/
    /*
     * Copyright (c) 2016 Mark Kettenis
     * Copyright (c) 2019 James Hastings
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    #include <sys/param.h>
    #include <sys/malloc.h>
    #include <sys/systm.h>
    
    #include <dev/acpi/acpireg.h>
    #include <dev/acpi/acpivar.h>
    #include <dev/acpi/acpidev.h>
    #include <dev/acpi/amltypes.h>
    #include <dev/acpi/dsdt.h>
    
    #define AMDGPIO_CONF_LEVEL		0x00000100
    #define AMDGPIO_CONF_ACTLO		0x00000200
    #define AMDGPIO_CONF_ACTBOTH		0x00000400
    #define AMDGPIO_CONF_MASK		0x00000600
    #define AMDGPIO_CONF_INT_EN		0x00000800
    #define AMDGPIO_CONF_INT_MASK		0x00001000
    #define AMDGPIO_CONF_RXSTATE		0x00010000
    #define AMDGPIO_CONF_TXSTATE		0x00400000
    #define AMDGPIO_CONF_TXSTATE_EN		0x00800000
    #define AMDGPIO_CONF_INT_STS		0x10000000
    #define AMDGPIO_IRQ_MASTER_EOI		0x20000000
    #define AMDGPIO_IRQ_BITS		46
    #define AMDGPIO_IRQ_PINS		4
    
    #define AMDGPIO_IRQ_MASTER		0xfc
    #define AMDGPIO_IRQ_STS			0x2f8
    
    struct amdgpio_intrhand {
    	int (*ih_func)(void *);
    	void *ih_arg;
    };
    
    struct amdgpio_softc {
    	struct device sc_dev;
    	struct acpi_softc *sc_acpi;
    	struct aml_node *sc_node;
    
    	bus_space_tag_t sc_memt;
    	bus_space_handle_t sc_memh;
    	int sc_irq_flags;
    	void *sc_ih;
    
    	int sc_npins;
    	struct amdgpio_intrhand *sc_pin_ih;
    
    	struct acpi_gpio sc_gpio;
    };
    
    int	amdgpio_match(struct device *, void *, void *);
    void	amdgpio_attach(struct device *, struct device *, void *);
    
    struct cfattach amdgpio_ca = {
    	sizeof(struct amdgpio_softc), amdgpio_match, amdgpio_attach
    };
    
    struct cfdriver amdgpio_cd = {
    	NULL, "amdgpio", DV_DULL
    };
    
    const char *amdgpio_hids[] = {
    	"AMDI0030",
    	"AMD0030",
    	NULL
    };
    
    int	amdgpio_read_pin(void *, int);
    void	amdgpio_write_pin(void *, int, int);
    void	amdgpio_intr_establish(void *, int, int, int (*)(), void *);
    int	amdgpio_pin_intr(struct amdgpio_softc *, int);
    int	amdgpio_intr(void *);
    
    int
    amdgpio_match(struct device *parent, void *match, void *aux)
    {
    	struct acpi_attach_args *aaa = aux;
    	struct cfdata *cf = match;
    
    	return acpi_matchhids(aaa, amdgpio_hids, cf->cf_driver->cd_name);
    }
    
    void
    amdgpio_attach(struct device *parent, struct device *self, void *aux)
    {
    	struct acpi_attach_args *aaa = aux;
    	struct amdgpio_softc *sc = (struct amdgpio_softc *)self;
    	int64_t uid;
    
    	sc->sc_acpi = (struct acpi_softc *)parent;
    	sc->sc_node = aaa->aaa_node;
    	printf(" %s", sc->sc_node->name);
    
    	if (aaa->aaa_naddr < 1) {
    		printf(": no registers\n");
    		return;
    	}
    
    	if (aaa->aaa_nirq < 1) {
    		printf(": no interrupt\n");
    		return;
    	}
    
    	if (aml_evalinteger(sc->sc_acpi, sc->sc_node, "_UID", 0, NULL, &uid)) {
    		printf(": can't find uid\n");
    		return;
    	}
    
    	printf(" uid %lld", uid);
    
    	switch (uid) {
    	case 0:
    		sc->sc_npins = 184;
    		break;
    	default:
    		printf("\n");
    		return;
    	}
    
    	printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]);
    	printf(" irq %d", aaa->aaa_irq[0]);
    
    	sc->sc_memt = aaa->aaa_bst[0];
    	if (bus_space_map(sc->sc_memt, aaa->aaa_addr[0], aaa->aaa_size[0],
    	    0, &sc->sc_memh)) {
    		printf(": can't map registers\n");
    		return;
    	}
    
    	sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih),
    	    M_DEVBUF, M_WAITOK | M_ZERO);
    
    	sc->sc_ih = acpi_intr_establish(aaa->aaa_irq[0], aaa->aaa_irq_flags[0],
    	    IPL_BIO, amdgpio_intr, sc, sc->sc_dev.dv_xname);
    	if (sc->sc_ih == NULL) {
    		printf(": can't establish interrupt\n");
    		goto unmap;
    	}
    
    	sc->sc_gpio.cookie = sc;
    	sc->sc_gpio.read_pin = amdgpio_read_pin;
    	sc->sc_gpio.write_pin = amdgpio_write_pin;
    	sc->sc_gpio.intr_establish = amdgpio_intr_establish;
    	sc->sc_node->gpio = &sc->sc_gpio;
    
    	printf(", %d pins\n", sc->sc_npins);
    
    	acpi_register_gpio(sc->sc_acpi, sc->sc_node);
    	return;
    
    unmap:
    	free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih));
    	bus_space_unmap(sc->sc_memt, sc->sc_memh, aaa->aaa_size[0]);
    }
    
    int
    amdgpio_read_pin(void *cookie, int pin)
    {
    	struct amdgpio_softc *sc = cookie;
    	uint32_t reg;
    
    	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, pin * 4);
    
    	return !!(reg & AMDGPIO_CONF_RXSTATE);
    }
    
    void
    amdgpio_write_pin(void *cookie, int pin, int value)
    {
    	struct amdgpio_softc *sc = cookie;
    	uint32_t reg;
    
    	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, pin * 4);
    	reg |= AMDGPIO_CONF_TXSTATE_EN;
    	if (value)
    		reg |= AMDGPIO_CONF_TXSTATE;
    	else
    		reg &= ~AMDGPIO_CONF_TXSTATE;
    	bus_space_write_4(sc->sc_memt, sc->sc_memh, pin * 4, reg);
    }
    
    void
    amdgpio_intr_establish(void *cookie, int pin, int flags,
        int (*func)(void *), void *arg)
    {
    	struct amdgpio_softc *sc = cookie;
    	uint32_t reg;
    
    	KASSERT(pin >= 0 && pin != 63 && pin < sc->sc_npins);
    
    	sc->sc_pin_ih[pin].ih_func = func;
    	sc->sc_pin_ih[pin].ih_arg = arg;
    
    	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, pin * 4);
    	reg &= ~(AMDGPIO_CONF_MASK | AMDGPIO_CONF_LEVEL |
    	    AMDGPIO_CONF_TXSTATE_EN);
    	if ((flags & LR_GPIO_MODE) == 0)
    		reg |= AMDGPIO_CONF_LEVEL;
    	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTLO)
    		reg |= AMDGPIO_CONF_ACTLO;
    	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTBOTH)
    		reg |= AMDGPIO_CONF_ACTBOTH;
    	reg |= (AMDGPIO_CONF_INT_MASK | AMDGPIO_CONF_INT_EN);
    	bus_space_write_4(sc->sc_memt, sc->sc_memh, pin * 4, reg);
    }
    
    int
    amdgpio_pin_intr(struct amdgpio_softc *sc, int pin)
    {
    	uint32_t reg;
    	int rc = 0;
    
    	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, pin * 4);
    	if (reg & AMDGPIO_CONF_INT_STS) {
    		if (sc->sc_pin_ih[pin].ih_func) {
    			sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg);
    
    			/* Clear interrupt */
    			reg = bus_space_read_4(sc->sc_memt, sc->sc_memh,
    			    pin * 4);
    			bus_space_write_4(sc->sc_memt, sc->sc_memh,
    			    pin * 4, reg);
    			rc = 1;
    		} else {
    			/* Mask unhandled interrupt */
    			reg &= ~(AMDGPIO_CONF_INT_MASK | AMDGPIO_CONF_INT_EN);
    			bus_space_write_4(sc->sc_memt, sc->sc_memh,
    			    pin * 4, reg);
    		}
    	}
    
    	return rc;
    }
    
    int
    amdgpio_intr(void *arg)
    {
    	struct amdgpio_softc *sc = arg;
    	uint64_t status;
    	uint32_t reg;
    	int rc = 0, pin = 0;
    	int i, j;
    
    	status = bus_space_read_4(sc->sc_memt, sc->sc_memh,
    	    AMDGPIO_IRQ_STS + 4);
    	status <<= 32;
    	status |= bus_space_read_4(sc->sc_memt, sc->sc_memh,
    	    AMDGPIO_IRQ_STS);
    
    	/* One status bit for every four pins */
    	for (i = 0; i < AMDGPIO_IRQ_BITS; i++, pin += 4) {
    		if (status & (1ULL << i)) {
    			for (j = 0; j < AMDGPIO_IRQ_PINS; j++) {
    				if (amdgpio_pin_intr(sc, pin + j))
    					rc = 1;
    			}
    		}
    	}
    
    	/* Signal end of interrupt */
    	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh,
    	    AMDGPIO_IRQ_MASTER);
    	reg |= AMDGPIO_IRQ_MASTER_EOI;
    	bus_space_write_4(sc->sc_memt, sc->sc_memh,
    	    AMDGPIO_IRQ_MASTER, reg);
    
    	return rc;
    }