Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 18 Feb 2015 05:53:05 +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: r278936 - in head/sys/dev: cardbus pccbb
Message-ID:  <201502180553.t1I5r5CF063356@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: imp
Date: Wed Feb 18 05:53:04 2015
New Revision: 278936
URL: https://svnweb.freebsd.org/changeset/base/278936

Log:
  On my Lenovo T400, a Atheros 2413 has a problem powering up
  sometimes. It will power up wrong and identify itself badly:
  
  cardbus0: <network, ethernet> at device 0.0 (no driver attached)
  cardbus0: <simple comms, UART> at device 0.1 (no driver attached)
  cardbus0: <old, non-VGA display device> at device 0.2 (no driver attached)
  cardbus0: <old, non-VGA display device> at device 0.3 (no driver attached)
  cardbus0: <old, non-VGA display device> at device 0.4 (no driver attached)
  cardbus0: <old, non-VGA display device> at device 0.5 (no driver attached)
  cardbus0: <old, non-VGA display device> at device 0.6 (no driver attached)
  cardbus0: <old, non-VGA display device> at device 0.7 (no driver attached)
  
  All the higher numbered functions (.2 and above) have a config space
  of all 0's. This smells a bit like a special debug mode, but the
  current atheros driver doesn't cope. It is unclear if this card is
  just a flake, or if we're doing something wrong in the power-up
  sequence.
  
  Put a work around into the code that tests for this rather unusual
  condition. If we power a CardBus device up, and the device says it is
  multi-function, and any of the functions have a 0 device ID, try the
  power-up sequence again.

Modified:
  head/sys/dev/cardbus/cardbus.c
  head/sys/dev/pccbb/pccbb.c

Modified: head/sys/dev/cardbus/cardbus.c
==============================================================================
--- head/sys/dev/cardbus/cardbus.c	Wed Feb 18 05:20:52 2015	(r278935)
+++ head/sys/dev/cardbus/cardbus.c	Wed Feb 18 05:53:04 2015	(r278936)
@@ -122,6 +122,7 @@ cardbus_detach(device_t cbdev)
 	cardbus_detach_card(cbdev);
 #ifdef PCI_RES_BUS
 	sc = device_get_softc(cbdev);
+	device_printf(cbdev, "Freeing up the allocatd bus\n");
 	(void)bus_release_resource(cbdev, PCI_RES_BUS, 0, sc->sc_bus);
 #endif
 	return (0);
@@ -180,6 +181,7 @@ cardbus_attach_card(device_t cbdev)
 
 	sc = device_get_softc(cbdev);
 	cardbus_detach_card(cbdev); /* detach existing cards */
+	POWER_DISABLE_SOCKET(brdev, cbdev); /* Turn the socket off first */
 	POWER_ENABLE_SOCKET(brdev, cbdev);
 	domain = pcib_get_domain(cbdev);
 	bus = pcib_get_bus(cbdev);

Modified: head/sys/dev/pccbb/pccbb.c
==============================================================================
--- head/sys/dev/pccbb/pccbb.c	Wed Feb 18 05:20:52 2015	(r278935)
+++ head/sys/dev/pccbb/pccbb.c	Wed Feb 18 05:53:04 2015	(r278936)
@@ -155,7 +155,7 @@ SYSCTL_INT(_hw_cbb, OID_AUTO, debug, CTL
 static void	cbb_insert(struct cbb_softc *sc);
 static void	cbb_removal(struct cbb_softc *sc);
 static uint32_t	cbb_detect_voltage(device_t brdev);
-static void	cbb_cardbus_reset_power(device_t brdev, device_t child, int on);
+static int	cbb_cardbus_reset_power(device_t brdev, device_t child, int on);
 static int	cbb_cardbus_io_open(device_t brdev, int win, uint32_t start,
 		    uint32_t end);
 static int	cbb_cardbus_mem_open(device_t brdev, int win,
@@ -958,12 +958,12 @@ cbb_do_power(device_t brdev)
 /* CardBus power functions						*/
 /************************************************************************/
 
-static void
+static int
 cbb_cardbus_reset_power(device_t brdev, device_t child, int on)
 {
 	struct cbb_softc *sc = device_get_softc(brdev);
-	uint32_t b;
-	int delay, count;
+	uint32_t b, h;
+	int delay, count, zero_seen, func;
 
 	/*
 	 * Asserting reset for 20ms is necessary for most bridges.  For some
@@ -1002,30 +1002,61 @@ cbb_cardbus_reset_power(device_t brdev, 
 		    0xfffffffful && --count >= 0);
 		if (count < 0)
 			device_printf(brdev, "Warning: Bus reset timeout\n");
+
+		/*
+		 * Some cards (so far just an atheros card I have) seem to
+		 * come out of reset in a funky state. They report they are
+		 * multi-function cards, but have nonsense for some of the
+		 * higher functions.  So if the card claims to be MFDEV, and
+		 * any of the higher functions' ID is 0, then we've hit the
+		 * bug and we'll try again.
+		 */
+		h = PCIB_READ_CONFIG(brdev, b, 0, 0, PCIR_HDRTYPE, 1);
+		if ((h & PCIM_MFDEV) == 0)
+			return 0;
+		zero_seen = 0;
+		for (func = 1; func < 8; func++) {
+			h = PCIB_READ_CONFIG(brdev, b, 0, func,
+			    PCIR_DEVVENDOR, 4);
+			if (h == 0)
+				zero_seen++;
+		}
+		if (!zero_seen)
+			return 0;
+		return (EINVAL);
 	}
+	return 0;
+}
+
+static int
+cbb_cardbus_power_disable_socket(device_t brdev, device_t child)
+{
+	cbb_power(brdev, CARD_OFF);
+	cbb_cardbus_reset_power(brdev, child, 0);
+	return (0);
 }
 
 static int
 cbb_cardbus_power_enable_socket(device_t brdev, device_t child)
 {
 	struct cbb_softc *sc = device_get_softc(brdev);
-	int err;
+	int err, count;
 
 	if (!CBB_CARD_PRESENT(cbb_get(sc, CBB_SOCKET_STATE)))
 		return (ENODEV);
 
-	err = cbb_do_power(brdev);
-	if (err)
-		return (err);
-	cbb_cardbus_reset_power(brdev, child, 1);
-	return (0);
-}
-
-static int
-cbb_cardbus_power_disable_socket(device_t brdev, device_t child)
-{
-	cbb_power(brdev, CARD_OFF);
-	cbb_cardbus_reset_power(brdev, child, 0);
+	count = 10;
+	do {
+		err = cbb_do_power(brdev);
+		if (err)
+			return (err);
+		err = cbb_cardbus_reset_power(brdev, child, 1);
+		if (err) {
+			device_printf(brdev, "Reset failed, trying again.\n");
+			cbb_cardbus_power_disable_socket(brdev, child);
+			pause("cbbErr1", hz / 10); /* wait 100ms */
+		}
+	} while (err != 0 && count-- > 0);
 	return (0);
 }
 



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