Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 14 Jan 2015 05:41:34 +0000 (UTC)
From:      Warner Losh <imp@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r277164 - head/sys/dev/pccbb
Message-ID:  <201501140541.t0E5fYQN070078@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: imp
Date: Wed Jan 14 05:41:33 2015
New Revision: 277164
URL: https://svnweb.freebsd.org/changeset/base/277164

Log:
  Various interrelated fixes to make suspend / resume work better. We now
  can suspend / resume and unload / load cbb and cardbus without errors
  on my Lenovo T400, which wasn't possible before. Cards suspending
  and resuming in the CardBus slot not yet tested.
  o Enable memory cycles to the bridge early (as part of the new
    cbb_pci_bridge_init). This fixes the Bad VCC errors which were
    caused by the code accessing the device registers with this
    cleared. The suspend / resume process clears it.
  o Refactor suspend / resume into bus specific code (though the ISA
    code is just stubbed). This isn't strictly necessary, but makes
    the initializaiton code more uniform and should be more bullet
    proof in the face of variant behavior among cardbus bridges.
  o Fixup comments in the power-up sequence to reflect reality. These
    comments were written for one regime of power-up, but not updated
    as things were revised.
  o Add a paranoid small delay (100ms) to cover noisy cards powering
    down.
  o Fix some debugging prints to be easier to grep from dmesg.
  
  Sponsored by: Netflix

Modified:
  head/sys/dev/pccbb/pccbb.c
  head/sys/dev/pccbb/pccbb_isa.c
  head/sys/dev/pccbb/pccbb_pci.c
  head/sys/dev/pccbb/pccbbvar.h

Modified: head/sys/dev/pccbb/pccbb.c
==============================================================================
--- head/sys/dev/pccbb/pccbb.c	Wed Jan 14 05:41:31 2015	(r277163)
+++ head/sys/dev/pccbb/pccbb.c	Wed Jan 14 05:41:33 2015	(r277164)
@@ -477,7 +477,7 @@ cbb_event_thread(void *arg)
 		 */
 		mtx_lock(&Giant);
 		status = cbb_get(sc, CBB_SOCKET_STATE);
-		DPRINTF(("Status is 0x%x\n", status));
+		DEVPRINTF((sc->dev, "Status is %#x\n", status));
 		if (!CBB_CARD_PRESENT(status)) {
 			not_a_card = 0;		/* We know card type */
 			cbb_removal(sc);
@@ -800,13 +800,14 @@ cbb_power(device_t brdev, int volts)
 	if (on) {
 		mtx_lock(&sc->mtx);
 		cnt = sc->powerintr;
+
 		/*
 		 * We have a shortish timeout of 500ms here.  Some bridges do
-		 * not generate a POWER_CYCLE event for 16-bit cards.  In
-		 * those cases, we have to cope the best we can, and having
-		 * only a short delay is better than the alternatives.  Others
-		 * raise the power cycle a smidge before it is really ready.
-		 * We deal with those below.
+		 * not generate a POWER_CYCLE event for 16-bit cards.  In those
+		 * cases, we have to cope the best we can, and having only a
+		 * short delay is better than the alternatives.  Others raise
+		 * the power cycle a smidge before it is really ready.  We deal
+		 * with those below.
 		 */
 		sane = 10;
 		while (!(cbb_get(sc, CBB_SOCKET_STATE) & CBB_STATE_POWER_CYCLE) &&
@@ -816,19 +817,18 @@ cbb_power(device_t brdev, int volts)
 
 		/*
 		 * Relax for 100ms.  Some bridges appear to assert this signal
-		 * right away, but before the card has stabilized.  Other
-		 * cards need need more time to cope up reliabily.
-		 * Experiments with troublesome setups show this to be a
-		 * "cheap" way to enhance reliabilty.  We need not do this for
-		 * "off" since we don't touch the card after we turn it off.
+		 * right away, but before the card has stabilized.  Other cards
+		 * need need more time to cope up reliabily.  Experiments with
+		 * troublesome setups show this to be a "cheap" way to enhance
+		 * reliabilty.
 		 */
 		pause("cbbPwr", min(hz / 10, 1));
 
 		/*
-		 * The TOPIC95B requires a little bit extra time to get its
-		 * act together, so delay for an additional 100ms.  Also as
-		 * documented below, it doesn't seem to set the POWER_CYCLE
-		 * bit, so don't whine if it never came on.
+		 * The TOPIC95B requires a little bit extra time to get its act
+		 * together, so delay for an additional 100ms.  Also as
+		 * documented below, it doesn't seem to set the POWER_CYCLE bit,
+		 * so don't whine if it never came on.
 		 */
 		if (sc->chipset == CB_TOPIC95)
 			pause("cbb95B", hz / 10);
@@ -838,26 +838,27 @@ cbb_power(device_t brdev, int volts)
 
 	/*
 	 * After the power is good, we can turn off the power interrupt.
-	 * However, the PC Card standard says that we must delay turning the
-	 * CD bit back on for a bit to allow for bouncyness on power down
-	 * (recall that we don't wait above for a power down, since we don't
-	 * get an interrupt for that).  We're called either from the suspend
-	 * code in which case we don't want to turn card change on again, or
-	 * we're called from the card insertion code, in which case the cbb
-	 * thread will turn it on for us before it waits to be woken by a
-	 * change event.
+	 * However, the PC Card standard says that we must delay turning the CD
+	 * bit back on for a bit to allow for bouncyness on power down. We just
+	 * pause a little below to cover that. Most bridges don't seem to need
+	 * this delay.
 	 *
-	 * NB: Topic95B doesn't set the power cycle bit.  we assume that
-	 * both it and the TOPIC95 behave the same.
+	 * NB: Topic95B doesn't set the power cycle bit.  We assume that
+	 * both it and the TOPIC95 behave the same, though despite efforts
+	 * to find one, the author never could locate a laptop with a TOPIC95
+	 * in it.
 	 */
 	cbb_clrb(sc, CBB_SOCKET_MASK, CBB_SOCKET_MASK_POWER);
 	status = cbb_get(sc, CBB_SOCKET_STATE);
 	if (on && sc->chipset != CB_TOPIC95) {
 		if ((status & CBB_STATE_POWER_CYCLE) == 0)
 			device_printf(sc->dev, "Power not on?\n");
+	} else {
+		pause("cbbDwn", hz / 10);
 	}
 	if (status & CBB_STATE_BAD_VCC_REQ) {
-		device_printf(sc->dev, "Bad Vcc requested\n");	
+		device_printf(sc->dev, "Bad Vcc requested status %#x %dV\n",
+		    status, volts);	
 		/*
 		 * Turn off the power, and try again.  Retrigger other
 		 * active interrupts via force register.  From NetBSD
@@ -1563,61 +1564,6 @@ cbb_write_ivar(device_t brdev, device_t 
 }
 
 int
-cbb_suspend(device_t self)
-{
-	int			error = 0;
-	struct cbb_softc	*sc = device_get_softc(self);
-
-	error = bus_generic_suspend(self);
-	if (error != 0)
-		return (error);
-	cbb_set(sc, CBB_SOCKET_MASK, 0);	/* Quiet hardware */
-	sc->cardok = 0;				/* Card is bogus now */
-	return (0);
-}
-
-int
-cbb_resume(device_t self)
-{
-	int	error = 0;
-	struct cbb_softc *sc = (struct cbb_softc *)device_get_softc(self);
-	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.
-	 *
-	 * For most drivers, the PCI layer handles this saving. However, since
-	 * there's much black magic and arcane art hidden in these few lines of
-	 * code that would be difficult to transition into the PCI
-	 * layer. chipinit was several years of trial and error to write.
-	 */
-	pci_write_config(self, CBBR_SOCKBASE, rman_get_start(sc->base_res), 4);
-	DEVPRINTF((self, "PCI Memory allocated: %08lx\n",
-	    rman_get_start(sc->base_res)));
-
-	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(self);
-
-	return (error);
-}
-
-int
 cbb_child_present(device_t parent, device_t child)
 {
 	struct cbb_softc *sc = (struct cbb_softc *)device_get_softc(parent);

Modified: head/sys/dev/pccbb/pccbb_isa.c
==============================================================================
--- head/sys/dev/pccbb/pccbb_isa.c	Wed Jan 14 05:41:31 2015	(r277163)
+++ head/sys/dev/pccbb/pccbb_isa.c	Wed Jan 14 05:41:33 2015	(r277164)
@@ -203,13 +203,27 @@ cbb_isa_attach(device_t dev)
 	return (ENOMEM);
 }
 
+static int
+cbb_isa_suspend(device_t dev)
+{
+
+	return 0;
+}
+
+static int
+cbb_isa_resume(device_t dev)
+{
+
+	return 0;
+}
+
 static device_method_t cbb_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,			cbb_isa_probe),
 	DEVMETHOD(device_attach,		cbb_isa_attach),
 	DEVMETHOD(device_detach,		cbb_detach),
-	DEVMETHOD(device_suspend,		cbb_suspend),
-	DEVMETHOD(device_resume,		cbb_resume),
+	DEVMETHOD(device_suspend,		cbb_isa_suspend),
+	DEVMETHOD(device_resume,		cbb_isa_resume),
 
 	/* bus methods */
 	DEVMETHOD(bus_read_ivar,		cbb_read_ivar),

Modified: head/sys/dev/pccbb/pccbb_pci.c
==============================================================================
--- head/sys/dev/pccbb/pccbb_pci.c	Wed Jan 14 05:41:31 2015	(r277163)
+++ head/sys/dev/pccbb/pccbb_pci.c	Wed Jan 14 05:41:33 2015	(r277164)
@@ -301,6 +301,41 @@ cbb_print_config(device_t dev)
 	printf("\n");
 }
 
+static void
+cbb_pci_bridge_init(device_t brdev)
+{
+	struct cbb_softc *sc = (struct cbb_softc *)device_get_softc(brdev);
+	u_int32_t membase, irq;
+
+	if (pci_get_powerstate(brdev) != PCI_POWERSTATE_D0) {
+		/* Reset the power state. */
+		device_printf(brdev, "chip is in D%d power mode "
+		    "-- setting to D0\n", pci_get_powerstate(brdev));
+		pci_set_powerstate(brdev, PCI_POWERSTATE_D0);
+	}
+	membase = rman_get_start(sc->base_res);
+	irq = rman_get_start(sc->irq_res);
+
+	pci_write_config(brdev, CBBR_SOCKBASE, membase, 4);
+	pci_write_config(brdev, PCIR_INTLINE, irq, 4);
+	PCI_ENABLE_IO(device_get_parent(brdev), brdev, SYS_RES_MEMORY);
+
+	exca_init(&sc->exca[0], brdev, sc->bst, sc->bsh, CBB_EXCA_OFFSET);
+	sc->chipinit(sc);
+
+	/* 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));
+}
+
 static int
 cbb_pci_attach(device_t brdev)
 {
@@ -345,11 +380,9 @@ cbb_pci_attach(device_t brdev)
 
 	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);
@@ -427,17 +460,7 @@ cbb_pci_attach(device_t brdev)
 		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));
+	cbb_pci_bridge_init(brdev);
 
 	if (bootverbose)
 		cbb_print_config(brdev);
@@ -877,14 +900,45 @@ cbb_write_config(device_t brdev, u_int b
 	    b, s, f, reg, val, width);
 }
 
+static int
+cbb_pci_suspend(device_t brdev)
+{
+	int			error = 0;
+	struct cbb_softc	*sc = device_get_softc(brdev);
+
+	error = bus_generic_suspend(brdev);
+	if (error != 0)
+		return (error);
+	cbb_set(sc, CBB_SOCKET_MASK, 0);	/* Quiet hardware */
+	sc->cardok = 0;				/* Card is bogus now */
+	return (0);
+}
+
+static int
+cbb_pci_resume(device_t brdev)
+{
+	int	error = 0;
+	struct cbb_softc *sc = (struct cbb_softc *)device_get_softc(brdev);
+
+	/* Reinitialize the hardware, ala attach */
+	cbb_pci_bridge_init(brdev);
+
+	/* Signal the thread to wakeup to see if we have any cards to work with. */
+	wakeup(&sc->intrhand);
+
+	error = bus_generic_resume(brdev);
+
+	return (error);
+}
+
 static device_method_t cbb_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,			cbb_pci_probe),
 	DEVMETHOD(device_attach,		cbb_pci_attach),
 	DEVMETHOD(device_detach,		cbb_detach),
 	DEVMETHOD(device_shutdown,		cbb_pci_shutdown),
-	DEVMETHOD(device_suspend,		cbb_suspend),
-	DEVMETHOD(device_resume,		cbb_resume),
+	DEVMETHOD(device_suspend,		cbb_pci_suspend),
+	DEVMETHOD(device_resume,		cbb_pci_resume),
 
 	/* bus methods */
 	DEVMETHOD(bus_read_ivar,		cbb_read_ivar),

Modified: head/sys/dev/pccbb/pccbbvar.h
==============================================================================
--- head/sys/dev/pccbb/pccbbvar.h	Wed Jan 14 05:41:31 2015	(r277163)
+++ head/sys/dev/pccbb/pccbbvar.h	Wed Jan 14 05:41:33 2015	(r277164)
@@ -134,11 +134,9 @@ int	cbb_read_ivar(device_t brdev, device
 	    uintptr_t *result);
 int	cbb_release_resource(device_t brdev, device_t child,
 	    int type, int rid, struct resource *r);
-int	cbb_resume(device_t self);
 int	cbb_setup_intr(device_t dev, device_t child, struct resource *irq,
 	    int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg,
 	    void **cookiep);
-int	cbb_suspend(device_t self);
 int	cbb_teardown_intr(device_t dev, device_t child, struct resource *irq,
 	    void *cookie);
 int	cbb_write_ivar(device_t brdev, device_t child, int which,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201501140541.t0E5fYQN070078>