コード例 #1
0
ファイル: pccbb_pci.c プロジェクト: embedclub/freebsd
static int
cbb_pci_resume(device_t brdev)
{
	int	error = 0;
	struct cbb_softc *sc = (struct cbb_softc *)device_get_softc(brdev);
	uint32_t tmp;

	/*
	 * In the APM and early ACPI era, BIOSes saved the PCI config
	 * registers. As chips became more complicated, that functionality moved
	 * into the ACPI code / tables. We must therefore, restore the settings
	 * we made here to make sure the device come back. Transitions to Dx
	 * from D0 and back to D0 cause the bridge to lose its config space, so
	 * all the bus mappings and such are preserved.
	 *
	 * The PCI layer handles standard PCI registers like the
	 * command register and BARs, but cbb-specific registers are
	 * handled here.
	 */
	sc->chipinit(sc);

	/* reset interrupt -- Do we really need to do this? */
	tmp = cbb_get(sc, CBB_SOCKET_EVENT);
	cbb_set(sc, CBB_SOCKET_EVENT, tmp);

	/* CSC Interrupt: Card detect interrupt on */
	cbb_setb(sc, CBB_SOCKET_MASK, CBB_SOCKET_MASK_CD);

	/* Signal the thread to wakeup. */
	wakeup(&sc->intrhand);

	error = bus_generic_resume(brdev);

	return (error);
}
コード例 #2
0
ファイル: pccbb_pci.c プロジェクト: AhmadTux/freebsd
static int
cbb_pci_filt(void *arg)
{
	struct cbb_softc *sc = arg;
	uint32_t sockevent;
	uint8_t csc;
	int retval = FILTER_STRAY;

	/*
	 * Some chips also require us to read the old ExCA registe for card
	 * status change when we route CSC vis PCI.  This isn't supposed to be
	 * required, but it clears the interrupt state on some chipsets.
	 * Maybe there's a setting that would obviate its need.  Maybe we
	 * should test the status bits and deal with them, but so far we've
	 * not found any machines that don't also give us the socket status
	 * indication above.
	 *
	 * This call used to be unconditional.  However, further research
	 * suggests that we hit this condition when the card READY interrupt
	 * fired.  So now we only read it for 16-bit cards, and we only claim
	 * the interrupt if READY is set.  If this still causes problems, then
	 * the next step would be to read this if we have a 16-bit card *OR*
	 * we have no card.  We treat the READY signal as if it were the power
	 * completion signal.  Some bridges may double signal things here, bit
	 * signalling twice should be OK since we only sleep on the powerintr
	 * in one place and a double wakeup would be benign there.
	 */
	if (sc->flags & CBB_16BIT_CARD) {
		csc = exca_getb(&sc->exca[0], EXCA_CSC);
		if (csc & EXCA_CSC_READY) {
			atomic_add_int(&sc->powerintr, 1);
			wakeup((void *)&sc->powerintr);
			retval = FILTER_HANDLED;
		}
	}

	/*
	 * Read the socket event.  Sometimes, the theory goes, the PCI bus is
	 * so loaded that it cannot satisfy the read request, so we get
	 * garbage back from the following read.  We have to filter out the
	 * garbage so that we don't spontaneously reset the card under high
	 * load.  PCI isn't supposed to act like this.  No doubt this is a bug
	 * in the PCI bridge chipset (or cbb brige) that's being used in
	 * certain amd64 laptops today.  Work around the issue by assuming
	 * that any bits we don't know about being set means that we got
	 * garbage.
	 */
	sockevent = cbb_get(sc, CBB_SOCKET_EVENT);
	if (sockevent != 0 && (sockevent & ~CBB_SOCKET_EVENT_VALID_MASK) == 0) {
		/*
		 * If anything has happened to the socket, we assume that the
		 * card is no longer OK, and we shouldn't call its ISR.  We
		 * set cardok as soon as we've attached the card.  This helps
		 * in a noisy eject, which happens all too often when users
		 * are ejecting their PC Cards.
		 *
		 * We use this method in preference to checking to see if the
		 * card is still there because the check suffers from a race
		 * condition in the bouncing case.
		 */
#define DELTA (CBB_SOCKET_MASK_CD)
		if (sockevent & DELTA) {
			cbb_clrb(sc, CBB_SOCKET_MASK, DELTA);
			cbb_set(sc, CBB_SOCKET_EVENT, DELTA);
			sc->cardok = 0;
			cbb_disable_func_intr(sc);
			wakeup(&sc->intrhand);
		}
#undef DELTA

		/*
		 * Wakeup anybody waiting for a power interrupt.  We have to
		 * use atomic_add_int for wakups on other cores.
		 */
		if (sockevent & CBB_SOCKET_EVENT_POWER) {
			cbb_clrb(sc, CBB_SOCKET_MASK, CBB_SOCKET_EVENT_POWER);
			cbb_set(sc, CBB_SOCKET_EVENT, CBB_SOCKET_EVENT_POWER);
			atomic_add_int(&sc->powerintr, 1);
			wakeup((void *)&sc->powerintr);
		}

		/*
		 * Status change interrupts aren't presently used in the
		 * rest of the driver.  For now, just ACK them.
		 */
		if (sockevent & CBB_SOCKET_EVENT_CSTS)
			cbb_set(sc, CBB_SOCKET_EVENT, CBB_SOCKET_EVENT_CSTS);
		retval = FILTER_HANDLED;
	}
	return retval;
}
コード例 #3
0
ファイル: pccbb_pci.c プロジェクト: AhmadTux/freebsd
static int
cbb_pci_attach(device_t brdev)
{
	static int curr_bus_number = 2; /* XXX EVILE BAD (see below) */
	struct cbb_softc *sc = (struct cbb_softc *)device_get_softc(brdev);
	struct sysctl_ctx_list *sctx;
	struct sysctl_oid *soid;
	int rid;
	device_t parent;
	uint32_t pribus;

	parent = device_get_parent(brdev);
	mtx_init(&sc->mtx, device_get_nameunit(brdev), "cbb", MTX_DEF);
	sc->chipset = cbb_chipset(pci_get_devid(brdev), NULL);
	sc->dev = brdev;
	sc->cbdev = NULL;
	sc->exca[0].pccarddev = NULL;
	sc->domain = pci_get_domain(brdev);
	sc->secbus = pci_read_config(brdev, PCIR_SECBUS_2, 1);
	sc->subbus = pci_read_config(brdev, PCIR_SUBBUS_2, 1);
	sc->pribus = pcib_get_bus(parent);
	SLIST_INIT(&sc->rl);
	cbb_powerstate_d0(brdev);

	rid = CBBR_SOCKBASE;
	sc->base_res = bus_alloc_resource_any(brdev, SYS_RES_MEMORY, &rid,
	    RF_ACTIVE);
	if (!sc->base_res) {
		device_printf(brdev, "Could not map register memory\n");
		mtx_destroy(&sc->mtx);
		return (ENOMEM);
	} else {
		DEVPRINTF((brdev, "Found memory at %08lx\n",
		    rman_get_start(sc->base_res)));
	}

	sc->bst = rman_get_bustag(sc->base_res);
	sc->bsh = rman_get_bushandle(sc->base_res);
	exca_init(&sc->exca[0], brdev, sc->bst, sc->bsh, CBB_EXCA_OFFSET);
	sc->exca[0].flags |= EXCA_HAS_MEMREG_WIN;
	sc->exca[0].chipset = EXCA_CARDBUS;
	sc->chipinit = cbb_chipinit;
	sc->chipinit(sc);

	/*Sysctls*/
	sctx = device_get_sysctl_ctx(brdev);
	soid = device_get_sysctl_tree(brdev);
	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "domain",
	    CTLFLAG_RD, &sc->domain, 0, "Domain number");
	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "pribus",
	    CTLFLAG_RD, &sc->pribus, 0, "Primary bus number");
	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "secbus",
	    CTLFLAG_RD, &sc->secbus, 0, "Secondary bus number");
	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "subbus",
	    CTLFLAG_RD, &sc->subbus, 0, "Subordinate bus number");
#if 0
	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "memory",
	    CTLFLAG_RD, &sc->subbus, 0, "Memory window open");
	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "premem",
	    CTLFLAG_RD, &sc->subbus, 0, "Prefetch memroy window open");
	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "io1",
	    CTLFLAG_RD, &sc->subbus, 0, "io range 1 open");
	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "io2",
	    CTLFLAG_RD, &sc->subbus, 0, "io range 2 open");
#endif

	/*
	 * This is a gross hack.  We should be scanning the entire pci
	 * tree, assigning bus numbers in a way such that we (1) can
	 * reserve 1 extra bus just in case and (2) all sub busses
	 * are in an appropriate range.
	 */
	DEVPRINTF((brdev, "Secondary bus is %d\n", sc->secbus));
	pribus = pci_read_config(brdev, PCIR_PRIBUS_2, 1);
	if (sc->secbus == 0 || sc->pribus != pribus) {
		if (curr_bus_number <= sc->pribus)
			curr_bus_number = sc->pribus + 1;
		if (pribus != sc->pribus) {
			DEVPRINTF((brdev, "Setting primary bus to %d\n",
			    sc->pribus));
			pci_write_config(brdev, PCIR_PRIBUS_2, sc->pribus, 1);
		}
		sc->secbus = curr_bus_number++;
		sc->subbus = curr_bus_number++;
		DEVPRINTF((brdev, "Secondary bus set to %d subbus %d\n",
		    sc->secbus, sc->subbus));
		pci_write_config(brdev, PCIR_SECBUS_2, sc->secbus, 1);
		pci_write_config(brdev, PCIR_SUBBUS_2, sc->subbus, 1);
	}

	/* attach children */
	sc->cbdev = device_add_child(brdev, "cardbus", -1);
	if (sc->cbdev == NULL)
		DEVPRINTF((brdev, "WARNING: cannot add cardbus bus.\n"));
	else if (device_probe_and_attach(sc->cbdev) != 0)
		DEVPRINTF((brdev, "WARNING: cannot attach cardbus bus!\n"));

	sc->exca[0].pccarddev = device_add_child(brdev, "pccard", -1);
	if (sc->exca[0].pccarddev == NULL)
		DEVPRINTF((brdev, "WARNING: cannot add pccard bus.\n"));
	else if (device_probe_and_attach(sc->exca[0].pccarddev) != 0)
		DEVPRINTF((brdev, "WARNING: cannot attach pccard bus.\n"));

	/* Map and establish the interrupt. */
	rid = 0;
	sc->irq_res = bus_alloc_resource_any(brdev, SYS_RES_IRQ, &rid,
	    RF_SHAREABLE | RF_ACTIVE);
	if (sc->irq_res == NULL) {
		device_printf(brdev, "Unable to map IRQ...\n");
		goto err;
	}

	if (bus_setup_intr(brdev, sc->irq_res, INTR_TYPE_AV | INTR_MPSAFE,
	    cbb_pci_filt, NULL, sc, &sc->intrhand)) {
		device_printf(brdev, "couldn't establish interrupt\n");
		goto err;
	}

	/* reset 16-bit pcmcia bus */
	exca_clrb(&sc->exca[0], EXCA_INTR, EXCA_INTR_RESET);

	/* turn off power */
	cbb_power(brdev, CARD_OFF);

	/* CSC Interrupt: Card detect interrupt on */
	cbb_setb(sc, CBB_SOCKET_MASK, CBB_SOCKET_MASK_CD);

	/* reset interrupt */
	cbb_set(sc, CBB_SOCKET_EVENT, cbb_get(sc, CBB_SOCKET_EVENT));

	if (bootverbose)
		cbb_print_config(brdev);

	/* Start the thread */
	if (kproc_create(cbb_event_thread, sc, &sc->event_thread, 0, 0,
	    "%s event thread", device_get_nameunit(brdev))) {
		device_printf(brdev, "unable to create event thread.\n");
		panic("cbb_create_event_thread");
	}
	sc->sc_root_token = root_mount_hold(device_get_nameunit(sc->dev));
	return (0);
err:
	if (sc->irq_res)
		bus_release_resource(brdev, SYS_RES_IRQ, 0, sc->irq_res);
	if (sc->base_res) {
		bus_release_resource(brdev, SYS_RES_MEMORY, CBBR_SOCKBASE,
		    sc->base_res);
	}
	mtx_destroy(&sc->mtx);
	return (ENOMEM);
}