/* $OpenBSD: rpone.c,v 1.1 2025/08/20 21:40:34 kettenis Exp $ */ /* * Copyright (c) 2025 Mark Kettenis * * 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 #include #include #include #include #include #include #include #define RP1_MSIX_BAR (PCI_MAPREG_START + 0x00) #define RP1_PERIPH_BAR (PCI_MAPREG_START + 0x04) #define RP1_MSIX_VECTORS 61 #define MSIX_CFG(x) (0x108008 + 4 * (x)) #define MSIX_CFG_SET(x) (MSIX_CFG(x) + 0x800) #define MSIX_CFG_CLR(x) (MSIX_CFG(x) + 0xc00) #define MSIX_CFG_ENABLE (1 << 0) #define MSIX_CFG_TEST (1 << 1) #define MSIX_CFG_IACK (1 << 2) #define MSIX_CFG_IACK_EN (1 << 3) struct rpone_vector { int (*rv_func)(void *); void *rv_arg; int rv_vec; int rv_type; pci_intr_handle_t rv_ih; void *rv_sc; }; struct rpone_softc { struct device sc_dev; pci_chipset_tag_t sc_pc; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; struct bus_space sc_bus; bus_addr_t sc_base; bus_size_t sc_size; struct rpone_vector *sc_vec; int sc_nvec; struct interrupt_controller sc_ic; }; int rpone_match(struct device *, void *, void *); void rpone_attach(struct device *, struct device *, void *); const struct cfattach rpone_ca = { sizeof(struct rpone_softc), rpone_match, rpone_attach, }; struct cfdriver rpone_cd = { NULL, "rpone", DV_DULL }; static const struct pci_matchid rpone_devices[] = { { PCI_VENDOR_RPI, PCI_PRODUCT_RPI_RP1 }, }; void rpone_late(struct device *); void *rpone_intr_establish(void *, int *, int, struct cpu_info *, int (*)(void *), void *, char *); int rpone_bs_map(bus_space_tag_t, bus_addr_t, bus_size_t, int, bus_space_handle_t *); int rpone_bs_memmap(bus_space_tag_t, bus_addr_t, bus_size_t, int, bus_space_handle_t *); int rpone_match(struct device *parent, void *match, void *aux) { return (pci_matchbyid(aux, rpone_devices, nitems(rpone_devices))); } void rpone_attach(struct device *parent, struct device *self, void *aux) { struct rpone_softc *sc = (struct rpone_softc *)self; struct pci_attach_args *pa = aux; struct fdt_attach_args faa; pcireg_t memtype; int node, vec; sc->sc_pc = pa->pa_pc; memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, RP1_PERIPH_BAR); if (pci_mapreg_map(pa, RP1_PERIPH_BAR, memtype, 0, &sc->sc_iot, &sc->sc_ioh, &sc->sc_base, &sc->sc_size, 0) != 0) { printf(": can't map registers\n"); return; } sc->sc_nvec = pci_intr_msix_count(pa); if (sc->sc_nvec == 0) { printf(": no msix vectors\n"); return; } sc->sc_vec = mallocarray(sc->sc_nvec, sizeof(struct rpone_vector), M_DEVBUF, M_WAITOK); for (vec = 0; vec < sc->sc_nvec; vec++) { if (pci_intr_map_msix(pa, vec, &sc->sc_vec[vec].rv_ih)) { printf(": can't map msix vector\n"); return; } } printf("\n"); node = PCITAG_NODE(pa->pa_tag); if (node == 0) { printf("%s: can't find device tree node\n", sc->sc_dev.dv_xname); return; } sc->sc_ic.ic_node = node; sc->sc_ic.ic_cookie = sc; sc->sc_ic.ic_establish = rpone_intr_establish; sc->sc_ic.ic_barrier = intr_barrier; fdt_intr_register(&sc->sc_ic); memcpy(&sc->sc_bus, sc->sc_iot, sizeof(sc->sc_bus)); sc->sc_bus.bus_private = sc; sc->sc_bus._space_map = rpone_bs_map; memset(&faa, 0, sizeof(faa)); faa.fa_node = node; faa.fa_iot = &sc->sc_bus; faa.fa_dmat = pa->pa_dmat; faa.fa_acells = 3; faa.fa_scells = 2; config_found(self, &faa, NULL); } int rpone_intr(void *arg) { struct rpone_vector *rv = arg; struct rpone_softc *sc = rv->rv_sc; int handled; handled = rv->rv_func(rv->rv_arg); /* ACK level-triggered interrupts. */ if (rv->rv_type == 4) { bus_space_write_4(sc->sc_iot, sc->sc_ioh, MSIX_CFG_SET(rv->rv_vec), MSIX_CFG_IACK); } return handled; } void * rpone_intr_establish(void *cookie, int *cells, int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name) { struct rpone_softc *sc = cookie; struct rpone_vector *rv; uint32_t vec = cells[0]; uint32_t type = cells[1]; if (vec >= sc->sc_nvec) return NULL; rv = &sc->sc_vec[vec]; rv->rv_func = func; rv->rv_arg = arg; rv->rv_vec = vec; rv->rv_type = type; rv->rv_sc = sc; if (type == 4) { bus_space_write_4(sc->sc_iot, sc->sc_ioh, MSIX_CFG_SET(vec), MSIX_CFG_IACK_EN); } else { bus_space_write_4(sc->sc_iot, sc->sc_ioh, MSIX_CFG_CLR(vec), MSIX_CFG_IACK_EN); } bus_space_write_4(sc->sc_iot, sc->sc_ioh, MSIX_CFG_SET(vec), MSIX_CFG_ENABLE); return pci_intr_establish(sc->sc_pc, rv->rv_ih, level, rpone_intr, rv, name); } int rpone_bs_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size, int flag, bus_space_handle_t *bshp) { struct rpone_softc *sc = t->bus_private; if (bpa >= sc->sc_size) return ENXIO; return bus_space_map(sc->sc_iot, bpa + sc->sc_base, size, flag, bshp); }