Index: uspace/drv/bus/usb/xhci/commands.c
===================================================================
--- uspace/drv/bus/usb/xhci/commands.c	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/drv/bus/usb/xhci/commands.c	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -86,6 +86,4 @@
 	list_initialize(&cr->cmd_list);
 
-	cr->state = XHCI_CR_STATE_OPEN;
-
 	return EOK;
 }
@@ -251,4 +249,29 @@
 		fibril_condvar_wait(&cr->stopped_cv, &cr->guard);
 
+	fibril_mutex_unlock(&cr->guard);
+}
+
+/**
+ * Mark the command ring as stopped. NAK new commands, abort running, do not
+ * touch the HC as it's probably broken.
+ */
+void xhci_nuke_command_ring(xhci_hc_t *hc)
+{
+	xhci_cmd_ring_t *cr = get_cmd_ring(hc);
+	fibril_mutex_lock(&cr->guard);
+	// Prevent others from starting CR again.
+	cr_set_state(cr, XHCI_CR_STATE_CLOSED);
+	fibril_mutex_unlock(&cr->guard);
+}
+
+/**
+ * Mark the command ring as working again.
+ */
+void xhci_start_command_ring(xhci_hc_t *hc)
+{
+	xhci_cmd_ring_t *cr = get_cmd_ring(hc);
+	fibril_mutex_lock(&cr->guard);
+	// Prevent others from starting CR again.
+	cr_set_state(cr, XHCI_CR_STATE_OPEN);
 	fibril_mutex_unlock(&cr->guard);
 }
Index: uspace/drv/bus/usb/xhci/commands.h
===================================================================
--- uspace/drv/bus/usb/xhci/commands.h	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/drv/bus/usb/xhci/commands.h	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -128,4 +128,5 @@
 extern void xhci_fini_commands(xhci_hc_t *);
 
+extern void xhci_nuke_command_ring(xhci_hc_t *);
 extern void xhci_stop_command_ring(xhci_hc_t *);
 extern void xhci_abort_command_ring(xhci_hc_t *);
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -259,5 +259,5 @@
 int hc_init_memory(xhci_hc_t *hc, ddf_dev_t *device)
 {
-	int err;
+	int err = ENOMEM;
 
 	if (dma_buffer_alloc(&hc->dcbaa_dma, (1 + hc->max_slots) * sizeof(uint64_t)))
@@ -265,6 +265,10 @@
 	hc->dcbaa = hc->dcbaa_dma.virt;
 
+	hc->event_worker = joinable_fibril_create(&event_worker, hc);
+	if (!hc->event_worker)
+		goto err_dcbaa;
+
 	if ((err = xhci_event_ring_init(&hc->event_ring, 1)))
-		goto err_dcbaa;
+		goto err_worker;
 
 	if ((err = xhci_scratchpad_alloc(hc)))
@@ -277,16 +281,8 @@
 		goto err_cmd;
 
-	hc->event_worker = joinable_fibril_create(&event_worker, hc);
-	if (!hc->event_worker)
-		goto err_bus;
-
 	xhci_sw_ring_init(&hc->sw_ring, PAGE_SIZE / sizeof(xhci_trb_t));
 
-	joinable_fibril_start(hc->event_worker);
-
 	return EOK;
 
-err_bus:
-	xhci_bus_fini(&hc->bus);
 err_cmd:
 	xhci_fini_commands(hc);
@@ -295,4 +291,6 @@
 err_event_ring:
 	xhci_event_ring_fini(&hc->event_ring);
+err_worker:
+	joinable_fibril_destroy(hc->event_worker);
 err_dcbaa:
 	hc->dcbaa = NULL;
@@ -468,5 +466,5 @@
  * Initialize the HC: section 4.2
  */
-int hc_start(xhci_hc_t *hc, bool irq)
+int hc_start(xhci_hc_t *hc)
 {
 	int err;
@@ -489,4 +487,6 @@
 
 	XHCI_REG_SET(hc->op_regs, XHCI_OP_EWE, 1);
+
+	xhci_event_ring_reset(&hc->event_ring);
 
 	xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0];
@@ -499,5 +499,5 @@
 	XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_HI, UPPER32(erstptr));
 
-	if (irq) {
+	if (hc->base.irq_cap > 0) {
 		XHCI_REG_SET(intr0, XHCI_INTR_IE, 1);
 		XHCI_REG_SET(hc->op_regs, XHCI_OP_INTE, 1);
@@ -506,9 +506,64 @@
 	XHCI_REG_SET(hc->op_regs, XHCI_OP_HSEE, 1);
 
+	xhci_sw_ring_restart(&hc->sw_ring);
+	joinable_fibril_start(hc->event_worker);
+
+	xhci_start_command_ring(hc);
+
 	XHCI_REG_SET(hc->op_regs, XHCI_OP_RS, 1);
 
-	xhci_rh_startup(&hc->rh);
+	/* RH needs to access port states on startup */
+	xhci_rh_start(&hc->rh);
 
 	return EOK;
+}
+
+static void hc_stop(xhci_hc_t *hc)
+{
+	/* Stop the HC in hardware. */
+	XHCI_REG_CLR(hc->op_regs, XHCI_OP_RS, 1);
+
+	/*
+	 * Wait until the HC is halted - it shall take at most 16 ms.
+	 * Note that we ignore the return value here.
+	 */
+	xhci_reg_wait(&hc->op_regs->usbsts, XHCI_REG_MASK(XHCI_OP_HCH),
+	    XHCI_REG_MASK(XHCI_OP_HCH));
+
+	/* Make sure commands will not block other fibrils. */
+	xhci_nuke_command_ring(hc);
+
+	/* Stop the event worker fibril to restart it */
+	xhci_sw_ring_stop(&hc->sw_ring);
+	joinable_fibril_join(hc->event_worker);
+
+	/* Then, disconnect all roothub devices, which shall trigger
+	 * disconnection of everything */
+	xhci_rh_stop(&hc->rh);
+}
+
+static void hc_reinitialize(xhci_hc_t *hc)
+{
+	/* Stop everything. */
+	hc_stop(hc);
+
+	usb_log_info("HC stopped. Starting again...");
+
+	/* The worker fibrils need to be started again */
+	joinable_fibril_recreate(hc->event_worker);
+	joinable_fibril_recreate(hc->rh.event_worker);
+
+	/* Now, the HC shall be stopped and software shall be clean. */
+	hc_start(hc);
+}
+
+static bool hc_is_broken(xhci_hc_t *hc)
+{
+	const uint32_t usbcmd = XHCI_REG_RD_FIELD(&hc->op_regs->usbcmd, 32);
+	const uint32_t usbsts = XHCI_REG_RD_FIELD(&hc->op_regs->usbsts, 32);
+
+	return !(usbcmd & XHCI_REG_MASK(XHCI_OP_RS))
+	    ||  (usbsts & XHCI_REG_MASK(XHCI_OP_HCE))
+	    ||  (usbsts & XHCI_REG_MASK(XHCI_OP_HSE));
 }
 
@@ -622,5 +677,4 @@
 	hc->event_handler = 0;
 
-	/* Update the ERDP to make room in the ring. */
 	uint64_t erdp = hc->event_ring.dequeue_ptr;
 	erdp |= XHCI_REG_MASK(XHCI_INTR_ERDP_EHB);
@@ -646,6 +700,12 @@
 
 	if (status & XHCI_REG_MASK(XHCI_OP_HSE)) {
-		usb_log_error("Host controller error occured. Bad things gonna happen...");
-		status &= ~XHCI_REG_MASK(XHCI_OP_HSE);
+		usb_log_error("Host system error occured. Aren't we supposed to be dead already?");
+		return;
+	}
+
+	if (status & XHCI_REG_MASK(XHCI_OP_HCE)) {
+		usb_log_error("Host controller error occured. Reinitializing...");
+		hc_reinitialize(hc);
+		return;
 	}
 
@@ -676,8 +736,8 @@
 void hc_fini(xhci_hc_t *hc)
 {
-	xhci_sw_ring_stop(&hc->sw_ring);
-	joinable_fibril_join(hc->event_worker);
+	hc_stop(hc);
+
 	xhci_sw_ring_fini(&hc->sw_ring);
-
+	joinable_fibril_destroy(hc->event_worker);
 	xhci_bus_fini(&hc->bus);
 	xhci_event_ring_fini(&hc->event_ring);
@@ -882,4 +942,7 @@
 	xhci_hc_t * const hc = bus_to_hc(dev->base.bus);
 
+	if (hc_is_broken(hc))
+		return EOK;
+
 	/* Issue configure endpoint command (sec 4.3.5) with the DC flag. */
 	return xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT,
@@ -930,5 +993,9 @@
 {
 	xhci_device_t * const dev = xhci_ep_to_dev(ep);
+	xhci_hc_t * const hc = bus_to_hc(dev->base.bus);
 	const unsigned dci = endpoint_dci(ep);
+
+	if (hc_is_broken(hc))
+		return EOK;
 
 	/* Issue configure endpoint command (sec 4.3.5). */
@@ -938,5 +1005,4 @@
 		return err;
 
-	xhci_hc_t * const hc = bus_to_hc(dev->base.bus);
 	xhci_input_ctx_t *ictx = ictx_dma_buf.virt;
 	XHCI_INPUT_CTRL_CTX_DROP_SET(*XHCI_GET_CTRL_CTX(ictx, hc), dci);
@@ -992,4 +1058,8 @@
 	const unsigned dci = endpoint_dci(ep);
 	xhci_hc_t * const hc = bus_to_hc(dev->base.bus);
+
+	if (hc_is_broken(hc))
+		return EOK;
+
 	return xhci_cmd_sync_inline(hc, STOP_ENDPOINT,
 		.slot_id = dev->slot_id,
Index: uspace/drv/bus/usb/xhci/hc.h
===================================================================
--- uspace/drv/bus/usb/xhci/hc.h	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -116,5 +116,5 @@
 extern int hc_claim(xhci_hc_t *, ddf_dev_t *);
 extern int hc_irq_code_gen(irq_code_t *, xhci_hc_t *, const hw_res_list_parsed_t *, int *);
-extern int hc_start(xhci_hc_t *, bool);
+extern int hc_start(xhci_hc_t *);
 extern void hc_fini(xhci_hc_t *);
 
Index: uspace/drv/bus/usb/xhci/main.c
===================================================================
--- uspace/drv/bus/usb/xhci/main.c	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/drv/bus/usb/xhci/main.c	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -83,5 +83,5 @@
 {
 	xhci_hc_t *hc = hcd_to_hc(hcd);
-	return hc_start(hc, hcd->irq_cap >= 0);
+	return hc_start(hc);
 }
 
Index: uspace/drv/bus/usb/xhci/rh.c
===================================================================
--- uspace/drv/bus/usb/xhci/rh.c	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/drv/bus/usb/xhci/rh.c	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -113,6 +113,4 @@
 	xhci_sw_ring_init(&rh->event_ring, rh->max_ports);
 
-	joinable_fibril_start(rh->event_worker);
-
 	return EOK;
 }
@@ -290,6 +288,9 @@
 }
 
-void xhci_rh_startup(xhci_rh_t *rh)
-{
+void xhci_rh_start(xhci_rh_t *rh)
+{
+	xhci_sw_ring_restart(&rh->event_ring);
+	joinable_fibril_start(rh->event_worker);
+
 	/* The reset changed status of all ports, and SW originated reason does
 	 * not cause an interrupt.
@@ -311,4 +312,20 @@
 }
 
+/**
+ * Disconnect all devices on all ports. On contrary to ordinary disconnect, this
+ * function waits until the disconnection routine is over.
+ */
+void xhci_rh_stop(xhci_rh_t *rh)
+{
+	xhci_sw_ring_stop(&rh->event_ring);
+	joinable_fibril_join(rh->event_worker);
+
+	for (uint8_t i = 0; i < rh->max_ports; ++i) {
+		rh_port_t * const port = &rh->ports[i];
+		usb_port_disabled(&port->base, &rh_remove_device);
+		usb_port_fini(&port->base);
+	}
+}
+
 static int rh_worker(void *arg)
 {
Index: uspace/drv/bus/usb/xhci/rh.h
===================================================================
--- uspace/drv/bus/usb/xhci/rh.h	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/drv/bus/usb/xhci/rh.h	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -85,5 +85,6 @@
 
 extern void xhci_rh_set_ports_protocol(xhci_rh_t *, unsigned, unsigned, unsigned);
-extern void xhci_rh_startup(xhci_rh_t *);
+extern void xhci_rh_start(xhci_rh_t *);
+extern void xhci_rh_stop(xhci_rh_t *rh);
 
 #endif
Index: uspace/drv/bus/usb/xhci/trb_ring.c
===================================================================
--- uspace/drv/bus/usb/xhci/trb_ring.c	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/drv/bus/usb/xhci/trb_ring.c	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -188,5 +188,5 @@
 static uintptr_t trb_ring_enqueue_phys(xhci_trb_ring_t *ring)
 {
-	uintptr_t trb_id = ring->enqueue_trb - segment_begin(ring->enqueue_segment);
+	size_t trb_id = ring->enqueue_trb - segment_begin(ring->enqueue_segment);
 	return ring->enqueue_segment->phys + trb_id * sizeof(xhci_trb_t);
 }
@@ -346,4 +346,15 @@
 	}
 
+	fibril_mutex_initialize(&ring->guard);
+
+	usb_log_debug("Initialized event ring.");
+	return EOK;
+}
+
+void xhci_event_ring_reset(xhci_event_ring_t *ring)
+{
+	list_foreach(ring->segments, segments_link, trb_segment_t, segment)
+		memset(segment->trb_storage, 0, sizeof(segment->trb_storage));
+
 	trb_segment_t * const segment = get_first_segment(&ring->segments);
 	ring->dequeue_segment = segment;
@@ -351,9 +362,4 @@
 	ring->dequeue_ptr = segment->phys;
 	ring->ccs = 1;
-
-	fibril_mutex_initialize(&ring->guard);
-
-	usb_log_debug("Initialized event ring.");
-	return EOK;
 }
 
@@ -432,11 +438,9 @@
 	ring->end = ring->begin + size;
 
-	ring->enqueue = ring->dequeue = ring->begin;
-
 	fibril_mutex_initialize(&ring->guard);
 	fibril_condvar_initialize(&ring->enqueued_cv);
 	fibril_condvar_initialize(&ring->dequeued_cv);
 
-	ring->running = true;
+	xhci_sw_ring_restart(ring);
 }
 
@@ -486,4 +490,11 @@
 }
 
+void xhci_sw_ring_restart(xhci_sw_ring_t *ring)
+{
+	ring->enqueue = ring->dequeue = ring->begin;
+	memset(ring->begin, 0, sizeof(xhci_trb_t) * (ring->end - ring->begin));
+	ring->running = true;
+}
+
 void xhci_sw_ring_fini(xhci_sw_ring_t *ring)
 {
Index: uspace/drv/bus/usb/xhci/trb_ring.h
===================================================================
--- uspace/drv/bus/usb/xhci/trb_ring.h	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/drv/bus/usb/xhci/trb_ring.h	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -109,4 +109,5 @@
 extern int xhci_event_ring_init(xhci_event_ring_t *, size_t);
 extern void xhci_event_ring_fini(xhci_event_ring_t *);
+extern void xhci_event_ring_reset(xhci_event_ring_t *);
 extern int xhci_event_ring_dequeue(xhci_event_ring_t *, xhci_trb_t *);
 
@@ -130,4 +131,5 @@
 extern int xhci_sw_ring_dequeue(xhci_sw_ring_t *, xhci_trb_t *);
 
+extern void xhci_sw_ring_restart(xhci_sw_ring_t *);
 extern void xhci_sw_ring_stop(xhci_sw_ring_t *);
 extern void xhci_sw_ring_fini(xhci_sw_ring_t *);
Index: uspace/lib/usbhost/include/usb/host/utility.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/utility.h	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/lib/usbhost/include/usb/host/utility.h	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -63,4 +63,5 @@
 void joinable_fibril_join(joinable_fibril_t *);
 void joinable_fibril_destroy(joinable_fibril_t *);
+errno_t joinable_fibril_recreate(joinable_fibril_t *);
 
 
Index: uspace/lib/usbhost/src/utility.c
===================================================================
--- uspace/lib/usbhost/src/utility.c	(revision 53fdf8cbac47a41c493c58222638a856360e9a84)
+++ uspace/lib/usbhost/src/utility.c	(revision 19f004817f021dca631beaf412028483298418a7)
@@ -297,4 +297,5 @@
 {
 	joinable_fibril_t *jf = arg;
+
 	jf->worker(jf->arg);
 
@@ -315,10 +316,4 @@
 		return NULL;
 
-	jf->fid = fibril_create(joinable_fibril_worker, jf);
-	if (!jf->fid) {
-		free(jf);
-		return NULL;
-	}
-
 	jf->worker = worker;
 	jf->arg = arg;
@@ -326,7 +321,11 @@
 	fibril_condvar_initialize(&jf->dead_cv);
 
+	if (joinable_fibril_recreate(jf)) {
+		free(jf);
+		return NULL;
+	}
+
 	return jf;
 }
-
 
 /**
@@ -353,4 +352,17 @@
 		fibril_condvar_wait(&jf->dead_cv, &jf->guard);
 	fibril_mutex_unlock(&jf->guard);
+
+	jf->fid = 0;
+}
+
+/**
+ * Reinitialize a joinable fibril.
+ */
+errno_t joinable_fibril_recreate(joinable_fibril_t *jf)
+{
+	assert(!jf->fid);
+
+	jf->fid = fibril_create(joinable_fibril_worker, jf);
+	return jf->fid ? EOK : ENOMEM;
 }
 
