make msi interrupts available to child devices in a pci card driver
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 27 Sep 2023 14:11:44 UTC
Hi, I would like to know how to make msi interrupts available to child devices in a pci card driver? I'm writing a driver for the OCP TimeCard. Apart from the timers, it also has 16650 UARTs and IIC devices. All of them are memory mapped in a single BAR. It also has 32 MSI interrupts, with each "function/device" connected to a specific MSI interrupt. I would like to add a child device for each of these "functions" and let each of them have their own interrupt, but I'm struggling to figure out the correct way to make the interrupts available to the child device. The memory mapping, using rman and creating subregions for the different devices is working and I can get a UART device connected in polled mode. The basic process in the card's attach() is much like what the puc(4) driver does (leaving error checking etc. out): <snip> bar->b_rid = PCIR_BAR(0); bar->b_type = SYS_RES_MEMORY; bar->b_res = bus_alloc_resource_any(sc->sc_dev, bar->b_type, &bar->b_rid, RF_ACTIVE); sc->sc_iomem.rm_type = RMAN_ARRAY; error = rman_init(&sc->sc_iomem); error = rman_manage_region(&sc->sc_iomem, start, end); for (idx = 0; idx < nports; idx++) { port->p_rres = rman_reserve_resource(&sc->sc_iomem, start, end, size, 0, NULL); bsh = rman_get_bushandle(bar->b_res); bst = rman_get_bustag(bar->b_res); port->p_rres = bus_space_subregion(bst, bsh, ofs, size, &bsh); rman_set_bushandle(port->p_rres, bsh); rman_set_bustag(port->p_rres, bst); port->p_dev = device_add_child(dev, NULL, -1); error = device_probe_and_attach(port->p_dev); </snip> I can also allocate MSI interrupts for the driver itself as a test, with no problem: <snip> sc->sc_msi =32; pci_alloc_msi(dev, &sc->sc_msi); printf("msi irqs %d\n", sc->sc_msi); for (idx = 0; idx < sc->sc_msi; idx++) { iport->pp_irid = idx + 1; iport->pp_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &iport->pp_irid, RF_ACTIVE); bus_setup_intr(dev, iport->pp_ires, INTR_TYPE_TTY | INTR_MPSAFE, timecard_intr, NULL, iport, &iport->p_ihandle); error = pci_enable_busmaster(dev); </snip> But I have not been able to find a way, similar to the memory to make it available to a child device, that works. It might be because the child is not a direct child of the pci driver. One hackish way that I could get to work was if I used my dev instead of the child's inside the timecard_bus_alloc_resource() and timecard_bus_setup_intr() methods, something like this: <snip> static struct resource * timecard_bus_release_resource(device_t dev, device_t child, int type, int rid, struct resource *res) { if (type == SYS_RES_IRQ) return BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, type, rid, res); } static int timecard_bus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, driver_filter_t *filt, void (*ihand)(void *), void *arg, void **cookiep) { return bus_setup_intr(dev, res, flags, filt, ihand, arg, cookiep); } </snip> That works, but does not feel quite right and then the interrupt is not "owned" by the child, but by the timecard driver: <snip> # vmstat -ia | grep timecard irq41: timecard0 10408538 175 </snip> Regards John