mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-15 12:54:27 +00:00
Stability fixes for Intel LynxPoint XHCI controllers. Disable XHCI port
routing if we get certain errors. Poll for command completion upon command timeouts. The XHCI error events might not generate interrupts. MFC after: 1 week Reported by: Daniel Gerzo <danger@rulez.sk>, Antonis Anastasiadis <anastasiadis@datalive.gr> PR: usb/181159 Approved by: re (gjb)
This commit is contained in:
parent
e4a9863fb7
commit
4c5d13237f
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=255768
|
@ -108,6 +108,8 @@ TUNABLE_INT("hw.usb.xhci.xhci_port_route", &xhciroute);
|
||||||
SYSCTL_INT(_hw_usb_xhci, OID_AUTO, use_polling, CTLFLAG_RW | CTLFLAG_TUN,
|
SYSCTL_INT(_hw_usb_xhci, OID_AUTO, use_polling, CTLFLAG_RW | CTLFLAG_TUN,
|
||||||
&xhcipolling, 0, "Set to enable software interrupt polling for XHCI controller");
|
&xhcipolling, 0, "Set to enable software interrupt polling for XHCI controller");
|
||||||
TUNABLE_INT("hw.usb.xhci.use_polling", &xhcipolling);
|
TUNABLE_INT("hw.usb.xhci.use_polling", &xhcipolling);
|
||||||
|
#else
|
||||||
|
#define xhciroute 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define XHCI_INTR_ENDPT 1
|
#define XHCI_INTR_ENDPT 1
|
||||||
|
@ -194,16 +196,6 @@ xhci_dump_device(struct xhci_softc *sc, struct xhci_slot_ctx *psl)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t
|
|
||||||
xhci_get_port_route(void)
|
|
||||||
{
|
|
||||||
#ifdef USB_DEBUG
|
|
||||||
return (0xFFFFFFFFU ^ ((uint32_t)xhciroute));
|
|
||||||
#else
|
|
||||||
return (0xFFFFFFFFU);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t
|
uint8_t
|
||||||
xhci_use_polling(void)
|
xhci_use_polling(void)
|
||||||
{
|
{
|
||||||
|
@ -505,6 +497,11 @@ xhci_start_controller(struct xhci_softc *sc)
|
||||||
/* catch any lost interrupts */
|
/* catch any lost interrupts */
|
||||||
xhci_do_poll(&sc->sc_bus);
|
xhci_do_poll(&sc->sc_bus);
|
||||||
|
|
||||||
|
if (sc->sc_port_route != NULL) {
|
||||||
|
/* Route all ports to the XHCI by default */
|
||||||
|
sc->sc_port_route(sc->sc_bus.parent,
|
||||||
|
~xhciroute, xhciroute);
|
||||||
|
}
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -951,7 +948,7 @@ xhci_check_transfer(struct xhci_softc *sc, struct xhci_trb *trb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
xhci_check_command(struct xhci_softc *sc, struct xhci_trb *trb)
|
xhci_check_command(struct xhci_softc *sc, struct xhci_trb *trb)
|
||||||
{
|
{
|
||||||
if (sc->sc_cmd_addr == trb->qwTrb0) {
|
if (sc->sc_cmd_addr == trb->qwTrb0) {
|
||||||
|
@ -959,16 +956,19 @@ xhci_check_command(struct xhci_softc *sc, struct xhci_trb *trb)
|
||||||
sc->sc_cmd_result[0] = trb->dwTrb2;
|
sc->sc_cmd_result[0] = trb->dwTrb2;
|
||||||
sc->sc_cmd_result[1] = trb->dwTrb3;
|
sc->sc_cmd_result[1] = trb->dwTrb3;
|
||||||
cv_signal(&sc->sc_cmd_cv);
|
cv_signal(&sc->sc_cmd_cv);
|
||||||
|
return (1); /* command match */
|
||||||
}
|
}
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
xhci_interrupt_poll(struct xhci_softc *sc)
|
xhci_interrupt_poll(struct xhci_softc *sc)
|
||||||
{
|
{
|
||||||
struct usb_page_search buf_res;
|
struct usb_page_search buf_res;
|
||||||
struct xhci_hw_root *phwr;
|
struct xhci_hw_root *phwr;
|
||||||
uint64_t addr;
|
uint64_t addr;
|
||||||
uint32_t temp;
|
uint32_t temp;
|
||||||
|
int retval = 0;
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
uint8_t event;
|
uint8_t event;
|
||||||
uint8_t j;
|
uint8_t j;
|
||||||
|
@ -1008,7 +1008,7 @@ xhci_interrupt_poll(struct xhci_softc *sc)
|
||||||
xhci_check_transfer(sc, &phwr->hwr_events[i]);
|
xhci_check_transfer(sc, &phwr->hwr_events[i]);
|
||||||
break;
|
break;
|
||||||
case XHCI_TRB_EVENT_CMD_COMPLETE:
|
case XHCI_TRB_EVENT_CMD_COMPLETE:
|
||||||
xhci_check_command(sc, &phwr->hwr_events[i]);
|
retval |= xhci_check_command(sc, &phwr->hwr_events[i]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DPRINTF("Unhandled event = %u\n", event);
|
DPRINTF("Unhandled event = %u\n", event);
|
||||||
|
@ -1045,6 +1045,8 @@ xhci_interrupt_poll(struct xhci_softc *sc)
|
||||||
|
|
||||||
XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr);
|
XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr);
|
||||||
XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32));
|
XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32));
|
||||||
|
|
||||||
|
return (retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
static usb_error_t
|
static usb_error_t
|
||||||
|
@ -1132,7 +1134,15 @@ xhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb,
|
||||||
err = cv_timedwait(&sc->sc_cmd_cv, &sc->sc_bus.bus_mtx,
|
err = cv_timedwait(&sc->sc_cmd_cv, &sc->sc_bus.bus_mtx,
|
||||||
USB_MS_TO_TICKS(timeout_ms));
|
USB_MS_TO_TICKS(timeout_ms));
|
||||||
|
|
||||||
if (err) {
|
/*
|
||||||
|
* In some error cases event interrupts are not generated.
|
||||||
|
* Poll one time to see if the command has completed.
|
||||||
|
*/
|
||||||
|
if (err != 0 && xhci_interrupt_poll(sc) != 0) {
|
||||||
|
DPRINTF("Command was completed when polling\n");
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
if (err != 0) {
|
||||||
DPRINTFN(0, "Command timeout!\n");
|
DPRINTFN(0, "Command timeout!\n");
|
||||||
err = USB_ERR_TIMEOUT;
|
err = USB_ERR_TIMEOUT;
|
||||||
trb->dwTrb2 = 0;
|
trb->dwTrb2 = 0;
|
||||||
|
@ -1311,6 +1321,14 @@ xhci_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t address)
|
||||||
(address == 0), index);
|
(address == 0), index);
|
||||||
|
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
|
temp = le32toh(sc->sc_cmd_result[0]);
|
||||||
|
if (address == 0 && sc->sc_port_route != NULL &&
|
||||||
|
XHCI_TRB_2_ERROR_GET(temp) ==
|
||||||
|
XHCI_TRB_ERROR_PARAMETER) {
|
||||||
|
/* LynxPoint XHCI - ports are not switchable */
|
||||||
|
/* Un-route all ports from the XHCI */
|
||||||
|
sc->sc_port_route(sc->sc_bus.parent, 0, ~0);
|
||||||
|
}
|
||||||
DPRINTF("Could not set address "
|
DPRINTF("Could not set address "
|
||||||
"for slot %u.\n", index);
|
"for slot %u.\n", index);
|
||||||
if (address != 0)
|
if (address != 0)
|
||||||
|
|
|
@ -431,6 +431,8 @@ union xhci_hub_desc {
|
||||||
uint8_t temp[128];
|
uint8_t temp[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef int (xhci_port_route_t)(device_t, uint32_t, uint32_t);
|
||||||
|
|
||||||
struct xhci_softc {
|
struct xhci_softc {
|
||||||
struct xhci_hw_softc sc_hw;
|
struct xhci_hw_softc sc_hw;
|
||||||
/* base device */
|
/* base device */
|
||||||
|
@ -440,6 +442,8 @@ struct xhci_softc {
|
||||||
|
|
||||||
struct usb_callout sc_callout;
|
struct usb_callout sc_callout;
|
||||||
|
|
||||||
|
xhci_port_route_t *sc_port_route;
|
||||||
|
|
||||||
union xhci_hub_desc sc_hub_desc;
|
union xhci_hub_desc sc_hub_desc;
|
||||||
|
|
||||||
struct cv sc_cmd_cv;
|
struct cv sc_cmd_cv;
|
||||||
|
@ -502,7 +506,6 @@ struct xhci_softc {
|
||||||
|
|
||||||
/* prototypes */
|
/* prototypes */
|
||||||
|
|
||||||
uint32_t xhci_get_port_route(void);
|
|
||||||
uint8_t xhci_use_polling(void);
|
uint8_t xhci_use_polling(void);
|
||||||
usb_error_t xhci_halt_controller(struct xhci_softc *);
|
usb_error_t xhci_halt_controller(struct xhci_softc *);
|
||||||
usb_error_t xhci_init(struct xhci_softc *, device_t);
|
usb_error_t xhci_init(struct xhci_softc *, device_t);
|
||||||
|
|
|
@ -145,6 +145,25 @@ xhci_interrupt_poll(void *_sc)
|
||||||
usb_callout_reset(&sc->sc_callout, 1, (void *)&xhci_interrupt_poll, sc);
|
usb_callout_reset(&sc->sc_callout, 1, (void *)&xhci_interrupt_poll, sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xhci_pci_port_route(device_t self, uint32_t set, uint32_t clear)
|
||||||
|
{
|
||||||
|
uint32_t temp;
|
||||||
|
|
||||||
|
temp = pci_read_config(self, PCI_XHCI_INTEL_USB3_PSSEN, 4) |
|
||||||
|
pci_read_config(self, PCI_XHCI_INTEL_XUSB2PR, 4);
|
||||||
|
|
||||||
|
temp |= set;
|
||||||
|
temp &= ~clear;
|
||||||
|
|
||||||
|
pci_write_config(self, PCI_XHCI_INTEL_USB3_PSSEN, temp, 4);
|
||||||
|
pci_write_config(self, PCI_XHCI_INTEL_XUSB2PR, temp, 4);
|
||||||
|
|
||||||
|
device_printf(self, "Port routing mask set to 0x%08x\n", temp);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
xhci_pci_attach(device_t self)
|
xhci_pci_attach(device_t self)
|
||||||
{
|
{
|
||||||
|
@ -189,7 +208,7 @@ xhci_pci_attach(device_t self)
|
||||||
&sc->sc_irq_rid, RF_SHAREABLE | RF_ACTIVE);
|
&sc->sc_irq_rid, RF_SHAREABLE | RF_ACTIVE);
|
||||||
if (sc->sc_irq_res == NULL) {
|
if (sc->sc_irq_res == NULL) {
|
||||||
device_printf(self, "Could not allocate IRQ\n");
|
device_printf(self, "Could not allocate IRQ\n");
|
||||||
goto error;
|
/* goto error; FALLTHROUGH - use polling */
|
||||||
}
|
}
|
||||||
sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
|
sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
|
||||||
if (sc->sc_bus.bdev == NULL) {
|
if (sc->sc_bus.bdev == NULL) {
|
||||||
|
@ -216,6 +235,16 @@ xhci_pci_attach(device_t self)
|
||||||
USB_BUS_UNLOCK(&sc->sc_bus);
|
USB_BUS_UNLOCK(&sc->sc_bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* On Intel chipsets reroute ports from EHCI to XHCI controller. */
|
||||||
|
switch (pci_get_devid(self)) {
|
||||||
|
case 0x1e318086: /* Panther Point */
|
||||||
|
case 0x8c318086: /* Lynx Point */
|
||||||
|
sc->sc_port_route = &xhci_pci_port_route;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
xhci_pci_take_controller(self);
|
xhci_pci_take_controller(self);
|
||||||
|
|
||||||
err = xhci_halt_controller(sc);
|
err = xhci_halt_controller(sc);
|
||||||
|
@ -284,7 +313,6 @@ static int
|
||||||
xhci_pci_take_controller(device_t self)
|
xhci_pci_take_controller(device_t self)
|
||||||
{
|
{
|
||||||
struct xhci_softc *sc = device_get_softc(self);
|
struct xhci_softc *sc = device_get_softc(self);
|
||||||
uint32_t device_id = pci_get_devid(self);
|
|
||||||
uint32_t cparams;
|
uint32_t cparams;
|
||||||
uint32_t eecp;
|
uint32_t eecp;
|
||||||
uint32_t eec;
|
uint32_t eec;
|
||||||
|
@ -325,13 +353,5 @@ xhci_pci_take_controller(device_t self)
|
||||||
usb_pause_mtx(NULL, hz / 100); /* wait 10ms */
|
usb_pause_mtx(NULL, hz / 100); /* wait 10ms */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* On Intel chipsets reroute ports from EHCI to XHCI controller. */
|
|
||||||
if (device_id == 0x1e318086 /* Panther Point */ ||
|
|
||||||
device_id == 0x8c318086 /* Lynx Point */) {
|
|
||||||
uint32_t temp = xhci_get_port_route();
|
|
||||||
pci_write_config(self, PCI_XHCI_INTEL_USB3_PSSEN, temp, 4);
|
|
||||||
pci_write_config(self, PCI_XHCI_INTEL_XUSB2PR, temp, 4);
|
|
||||||
}
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue