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"