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);
 }
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to