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:
parent
03a5f9f0fd
commit
8e5f761422
@ -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);
|
||||
|
@ -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 */
|
||||
/************************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user