From owner-svn-src-all@FreeBSD.ORG Wed Feb 18 05:53:06 2015 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 040EE612; Wed, 18 Feb 2015 05:53:06 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id D8B98A7; Wed, 18 Feb 2015 05:53:05 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t1I5r5RE063358; Wed, 18 Feb 2015 05:53:05 GMT (envelope-from imp@FreeBSD.org) Received: (from imp@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t1I5r5CF063356; Wed, 18 Feb 2015 05:53:05 GMT (envelope-from imp@FreeBSD.org) Message-Id: <201502180553.t1I5r5CF063356@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: imp set sender to imp@FreeBSD.org using -f From: Warner Losh Date: Wed, 18 Feb 2015 05:53:05 +0000 (UTC) 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 X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 18 Feb 2015 05:53:06 -0000 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: at device 0.0 (no driver attached) cardbus0: at device 0.1 (no driver attached) cardbus0: at device 0.2 (no driver attached) cardbus0: at device 0.3 (no driver attached) cardbus0: at device 0.4 (no driver attached) cardbus0: at device 0.5 (no driver attached) cardbus0: at device 0.6 (no driver attached) cardbus0: 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); }