svn commit: r278936 - in head/sys/dev: cardbus pccbb
Warner Losh
imp at FreeBSD.org
Wed Feb 18 05:53:06 UTC 2015
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);
}
More information about the svn-src-head
mailing list