Ejemplo n.º 1
0
/* BHNDB_ROUTE_INTERRUPTS() */
static int
bhndb_pci_route_interrupts(device_t dev, device_t child)
{
	struct bhndb_pci_softc	*sc;
	struct bhnd_core_info	 core;
	uint32_t		 core_bit;
	uint32_t		 intmask;

	sc = device_get_softc(dev);

	if (sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC)
		return (bhndb_pci_route_siba_interrupts(sc, child));

	core = bhnd_get_core_info(child);
	if (core.core_idx > BHNDB_PCI_SBIM_COREIDX_MAX) {
		/* This should never be an issue in practice */
		device_printf(dev, "cannot route interrupts to high core "
		    "index %u\n", core.core_idx);
		return (ENXIO);
	}

	BHNDB_PCI_LOCK(sc);

	core_bit = (1<<core.core_idx) << BHNDB_PCI_SBIM_SHIFT;
	intmask = pci_read_config(sc->parent, BHNDB_PCI_INT_MASK, 4);
	intmask |= core_bit;
	pci_write_config(sc->parent, BHNDB_PCI_INT_MASK, intmask, 4);

	BHNDB_PCI_UNLOCK(sc);

	return (0);
}
Ejemplo n.º 2
0
/**
 * Initialize the full bridge configuration.
 * 
 * This is called during the DEVICE_ATTACH() process by the bridged bhndb(4)
 * bus, prior to probe/attachment of child cores.
 * 
 * At this point, we can introspect the enumerated cores, find our host
 * bridge device, and apply any bridge-level hardware workarounds required
 * for proper operation of the bridged device cores.
 */
static int
bhndb_pci_init_full_config(device_t dev, device_t child,
    const struct bhndb_hw_priority *prio_table)
{
	struct bhnd_core_info		 core;
	const struct bhndb_pci_id	*id;
	struct bhndb_pci_softc		*sc;
	struct bhndb_region		*pcir;
	bhnd_addr_t			 pcir_addr;
	bhnd_size_t			 pcir_size;
	int				 error;

	sc = device_get_softc(dev);

	/* Let bhndb perform full discovery and initialization of the
	 * available register windows and bridge resources. */
	if ((error = bhndb_generic_init_full_config(dev, child, prio_table)))
		return (error);

	/* 
	 * Identify our PCI bridge core, its register family, and any
	 * applicable hardware quirks.
	 */
	KASSERT(sc->bhndb.hostb_dev,
	    ("missing hostb device\n"));

	core = bhnd_get_core_info(sc->bhndb.hostb_dev);
	id = bhndb_pci_find_core_id(&core);
	if (id == NULL) {
		device_printf(dev, "%s %s hostb core is not recognized\n",
		    bhnd_vendor_name(core.vendor), bhnd_core_name(&core));
	}

	sc->regfmt = id->regfmt;

	/* Now that we've identified the PCI bridge core, we can determine the
	 * full set of device quirks */
	sc->quirks = bhndb_pci_discover_quirks(sc, id);

	/*
	 * Determine and save a reference to the bhndb resource and offset
	 * at which the bridge core's device registers are mapped.
	 * 
	 * All known bhnd(4) hardware provides a fixed static mapping of
	 * the PCI core's registers. If this changes in the future -- which
	 * is unlikely -- this driver will need to be adjusted to use
	 * dynamic register windows.
	 */

	/* Find base address and size of the PCI core's register block. */
	error = bhnd_get_region_addr(sc->bhndb.hostb_dev, BHND_PORT_DEVICE, 0,
	    0, &pcir_addr, &pcir_size);
	if (error) {
		device_printf(dev,
		    "failed to locate PCI core registers\n");
		return (error);
	}

	/* Find the bhndb_region that statically maps this block */
	pcir = bhndb_find_resource_region(sc->bhndb.bus_res, pcir_addr,
	    pcir_size);
	if (pcir == NULL || pcir->static_regwin == NULL) {
		device_printf(dev,
		    "missing static PCI core register window\n");
		return (ENXIO);
	}

	/* Save borrowed reference to the mapped PCI core registers */
	sc->mem_off = pcir->static_regwin->win_offset;
	sc->mem_res = bhndb_find_regwin_resource(sc->bhndb.bus_res,
	    pcir->static_regwin);
	if (sc->mem_res == NULL || !(rman_get_flags(sc->mem_res) & RF_ACTIVE)) {
		device_printf(dev,
		    "no active resource maps the PCI core register window\n");
		return (ENXIO);
	}

	/* Configure a direct bhnd_resource wrapper that we can pass to
	 * bhnd_resource APIs */
	sc->bhnd_mem_res = (struct bhnd_resource) {
		.res = sc->mem_res,
		.direct = true
	};

	/*
	 * Attach MMIO device (if this is a PCIe device), which is used for
	 * access to the PCIe SerDes required by the quirk workarounds.
	 */
	if (sc->pci_devclass == BHND_DEVCLASS_PCIE) {
		sc->mdio = device_add_child(dev, 
		    devclass_get_name(bhnd_mdio_pci_devclass), 0);
		if (sc->mdio == NULL)
			return (ENXIO);

		if ((error = device_probe_and_attach(sc->mdio))) {
			device_printf(dev, "failed to attach MDIO device\n");
			return (error);
		}
	}

	/* Apply any early one-time quirk workarounds */
	if ((error = bhndb_pci_wars_early_once(sc)))
		return (error);

	/* Apply attach-time quirk workarounds, required before the bridged
	 * bhnd(4) bus itself performs a full attach(). */
	if ((error = bhndb_pci_wars_hwup(sc)))
		return (error);

	return (0);
}

/**
 * Apply any hardware workarounds that must be executed prior to attempting
 * register access on the bridged chipset.
 * 
 * This must be called very early in attach() or resume(), after the basic
 * set of applicable device quirks has been determined.
 */
static int
bhndb_pci_wars_register_access(struct bhndb_pci_softc *sc)
{
	int error;

	if (BHNDB_PCI_QUIRK(sc, EXT_CLOCK_GATING)) {
		if ((error = bhndb_enable_pci_clocks(sc))) {
			device_printf(sc->dev, "failed to enable clocks\n");
			return (error);
		}
	}

	return (0);
}