Ejemplo n.º 1
0
/**
 * Apply any hardware workarounds that are required upon detach or suspend
 * of the bridge device.
 */
static int
bhndb_pci_wars_hwdown(struct bhndb_pci_softc *sc)
{
	int error;
	
	/* Reduce L1 timer for better power savings.
	 * TODO: We could enable/disable this on demand for better power
	 * savings if we tie this to HT clock request handling */
	if (BHNDB_PCIE_QUIRK(sc, L1_TIMER_PERF)) {
		uint32_t pmt;
		pmt = bhndb_pcie_read_proto_reg(sc, BHND_PCIE_DLLP_PMTHRESHREG);
		pmt &= ~BHND_PCIE_ASPMTIMER_EXTEND;
		bhndb_pcie_write_proto_reg(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
	}

	/* Disable clocks */
	if (BHNDB_PCI_QUIRK(sc, EXT_CLOCK_GATING)) {
		if ((error = bhndb_disable_pci_clocks(sc))) {
			device_printf(sc->dev, "failed to disable clocks\n");
			return (error);
		}
	}

	return (0);
}
Ejemplo n.º 2
0
static int
bhndb_pci_detach(device_t dev)
{
	struct bhndb_pci_softc	*sc;
	int			 error;

	sc = device_get_softc(dev);

	/* Attempt to detach our children */
	if ((error = bus_generic_detach(dev)))
		return (error);

	/* Perform generic bridge detach */
	if ((error = bhndb_generic_detach(dev)))
		return (error);

	/* Disable clocks (if required by this hardware) */
	if ((error = bhndb_disable_pci_clocks(sc->dev)))
		return (error);

	/* Free our interrupt resources */
	bhndb_free_intr_isrc(sc->isrc);

	/* Release MSI interrupts */
	if (sc->msi_count > 0)
		pci_release_msi(sc->parent);

	/* Disable PCI bus mastering */
	pci_disable_busmaster(sc->parent);

	BHNDB_PCI_LOCK_DESTROY(sc);

	return (0);
}
Ejemplo n.º 3
0
/** 
 * Default bhndb_pci implementation of device_probe().
 * 
 * Verifies that the parent is a PCI/PCIe device.
 */
static int
bhndb_pci_probe(device_t dev)
{
	struct bhndb_pci_probe	*probe;
	struct bhndb_pci_core	*entry;
	bhnd_devclass_t		 hostb_devclass;
	device_t		 parent, parent_bus;
	devclass_t		 pci, bus_devclass;
	int			 error;

	probe = NULL;

	/* Our parent must be a PCI/PCIe device. */
	pci = devclass_find("pci");
	parent = device_get_parent(dev);
	parent_bus = device_get_parent(parent);
	if (parent_bus == NULL)
		return (ENXIO);

	/* The bus device class may inherit from 'pci' */
	for (bus_devclass = device_get_devclass(parent_bus);
	    bus_devclass != NULL;
	    bus_devclass = devclass_get_parent(bus_devclass))
	{
		if (bus_devclass == pci)
			break;
	}

	if (bus_devclass != pci)
		return (ENXIO);

	/* Enable clocks */
	if ((error = bhndb_enable_pci_clocks(dev)))
		return (error);

	/* Identify the chip and enumerate the bridged cores */
	hostb_devclass = bhndb_expected_pci_devclass(dev);
	if ((error = bhndb_pci_probe_alloc(&probe, dev, hostb_devclass)))
		goto cleanup;

	/* Look for a matching core table entry */
	if ((entry = bhndb_pci_find_core(&probe->hostb_core)) == NULL) {
		error = ENXIO;
		goto cleanup;
	}

	device_set_desc(dev, "PCI-BHND bridge");

	/* fall-through */
	error = BUS_PROBE_DEFAULT;

cleanup:
	if (probe != NULL)
		bhndb_pci_probe_free(probe);

	bhndb_disable_pci_clocks(dev);

	return (error);
}
Ejemplo n.º 4
0
static int
bhndb_pci_suspend(device_t dev)
{
	struct bhndb_pci_softc	*sc;
	int			 error;

	sc = device_get_softc(dev);
	
	/* Disable clocks (if supported by this hardware) */
	if ((error = bhndb_disable_pci_clocks(sc->dev)))
		return (error);

	/* Perform suspend */
	return (bhndb_generic_suspend(dev));
}
Ejemplo n.º 5
0
static int
bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child,
	bhnd_clock clock)
{
	struct bhndb_pci_softc *sc = device_get_softc(dev);

	/* Only supported on PCI devices */
	if (bhndb_is_pcie_attached(sc->dev))
		return (ENODEV);

	/* Only HT is supported */
	if (clock != BHND_CLOCK_HT)
		return (ENXIO);

	return (bhndb_disable_pci_clocks(sc->dev));
}
Ejemplo n.º 6
0
static int
bhndb_pci_attach(device_t dev)
{
	struct bhndb_pci_softc	*sc;
	struct bhnd_chipid	 cid;
	struct bhnd_core_info	*cores, hostb_core;
	bhnd_erom_class_t	*erom_class;
	struct bhndb_pci_probe	*probe;
	u_int			 ncores;
	int			 irq_rid;
	int			 error;

	sc = device_get_softc(dev);
	sc->dev = dev;
	sc->parent = device_get_parent(dev);
	sc->pci_devclass = bhndb_expected_pci_devclass(dev);
	sc->pci_quirks = 0;
	sc->set_regwin = NULL;

	BHNDB_PCI_LOCK_INIT(sc);

	probe = NULL;
	cores = NULL;

	/* Enable PCI bus mastering */
	pci_enable_busmaster(sc->parent);

	/* Enable clocks (if required by this hardware) */
	if ((error = bhndb_enable_pci_clocks(sc->dev)))
		goto cleanup;

	/* Identify the chip and enumerate the bridged cores */
	error = bhndb_pci_probe_alloc(&probe, dev, sc->pci_devclass);
	if (error)
		goto cleanup;

	sc->pci_quirks = bhndb_pci_get_core_quirks(&probe->cid,
	    &probe->hostb_core);

	/* Select the appropriate register window handler */
	if (probe->cid.chip_type == BHND_CHIPTYPE_SIBA) {
		sc->set_regwin = bhndb_pci_compat_setregwin;
	} else {
		sc->set_regwin = bhndb_pci_fast_setregwin;
	}

	/*
	 * Fix up our PCI base address in the SPROM shadow, if necessary.
	 * 
	 * This must be done prior to accessing any static register windows
	 * that map the PCI core.
	 */
	if ((error = bhndb_pci_srsh_pi_war(sc, probe)))
		goto cleanup;

	/* Set up PCI interrupt handling */
	if (bhndb_pci_alloc_msi(sc, &sc->msi_count) == 0) {
		/* MSI uses resource IDs starting at 1 */
		irq_rid = 1;

		device_printf(dev, "Using MSI interrupts on %s\n",
		    device_get_nameunit(sc->parent));
	} else {
		sc->msi_count = 0;
		irq_rid = 0;

		device_printf(dev, "Using INTx interrupts on %s\n",
		    device_get_nameunit(sc->parent));
	}

	sc->isrc = bhndb_alloc_intr_isrc(sc->parent, irq_rid, 0, RM_MAX_END, 1,
	    RF_SHAREABLE | RF_ACTIVE);
	if (sc->isrc == NULL) {
		device_printf(sc->dev, "failed to allocate interrupt "
		    "resource\n");
		error = ENXIO;
		goto cleanup;
	}

	/*
	 * Copy out the probe results and then free our probe state, releasing
	 * its exclusive ownership of host bridge resources.
	 * 
	 * This must be done prior to full configuration of the bridge via
	 * bhndb_attach().
	 */
	cid = probe->cid;
	erom_class = probe->erom_class;
	hostb_core = probe->hostb_core;

	error = bhndb_pci_probe_copy_core_table(probe, &cores, &ncores);
	if (error) {
		cores = NULL;
		goto cleanup;
	}

	bhndb_pci_probe_free(probe);
	probe = NULL;

	/* Perform bridge attach */
	error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class);
	if (error)
		goto cleanup;

	/* Add any additional child devices */
	if ((error = bhndb_pci_add_children(sc)))
		goto cleanup;

	/* Probe and attach our children */
	if ((error = bus_generic_attach(dev)))
		goto cleanup;

	bhndb_pci_probe_free_core_table(cores);

	return (0);

cleanup:
	device_delete_children(dev);

	if (sc->isrc != NULL)
		bhndb_free_intr_isrc(sc->isrc);

	if (sc->msi_count > 0)
		pci_release_msi(sc->parent);

	if (cores != NULL)
		bhndb_pci_probe_free_core_table(cores);

	if (probe != NULL)
		bhndb_pci_probe_free(probe);

	bhndb_disable_pci_clocks(sc->dev);

	pci_disable_busmaster(sc->parent);

	BHNDB_PCI_LOCK_DESTROY(sc);

	return (error);
}