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.
This commit is contained in:
Warner Losh 2015-02-18 05:53:04 +00:00
parent 03a5f9f0fd
commit 8e5f761422
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=278936
2 changed files with 53 additions and 20 deletions

View File

@ -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);

View File

@ -155,7 +155,7 @@ SYSCTL_INT(_hw_cbb, OID_AUTO, debug, CTLFLAG_RWTUN, &cbb_debug, 0,
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,23 +1002,30 @@ cbb_cardbus_reset_power(device_t brdev, device_t child, int on)
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);
}
}
static int
cbb_cardbus_power_enable_socket(device_t brdev, device_t child)
{
struct cbb_softc *sc = device_get_softc(brdev);
int err;
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);
return 0;
}
static int
@ -1029,6 +1036,30 @@ cbb_cardbus_power_disable_socket(device_t brdev, device_t child)
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, count;
if (!CBB_CARD_PRESENT(cbb_get(sc, CBB_SOCKET_STATE)))
return (ENODEV);
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);
}
/************************************************************************/
/* CardBus Resource */
/************************************************************************/