svn commit: r212238 - head/sys/dev/acpica

Alexander Motin mav at FreeBSD.org
Sun Sep 5 19:24:33 UTC 2010


Author: mav
Date: Sun Sep  5 19:24:32 2010
New Revision: 212238
URL: http://svn.freebsd.org/changeset/base/212238

Log:
  Several improvements to HPET driver:
   - Add special check for case when time expires before being programmed.
  This fixes interrupt loss and respectively timer death on attempt to
  program very short interval. Increase minimal supported period to more
  realistic value.
   - Add support for hint.hpet.X.allowed_irqs tunable, allowing manually
  specify which interrupts driver allowed to use. Unluckily, many BIOSes
  program wrong allowed interrupts mask, so driver tries to stay on safe
  side by not using unshareable ISA IRQs. This option gives control over
  this limitation, allowing more per-CPU timers to be provided, when FSB
  interrupts are not supported. Value of this tunable is bitmask.
   - Do not use regular interrupts on virtual machines. QEMU and VirtualBox
  do not support them properly, that may cause problems. Stay safe by default.
  Same time both QEMU and VirtualBox work fine in legacy_route mode.
  VirtualBox also works fine if manually specify allowed ISA IRQs with above.

Modified:
  head/sys/dev/acpica/acpi_hpet.c

Modified: head/sys/dev/acpica/acpi_hpet.c
==============================================================================
--- head/sys/dev/acpica/acpi_hpet.c	Sun Sep  5 18:56:24 2010	(r212237)
+++ head/sys/dev/acpica/acpi_hpet.c	Sun Sep  5 19:24:32 2010	(r212238)
@@ -74,6 +74,7 @@ struct hpet_softc {
 	int			irq;
 	int			useirq;
 	int			legacy_route;
+	uint32_t		allowed_irqs;
 	struct resource		*mem_res;
 	struct resource		*intr_res;
 	void			*intr_handle;
@@ -146,7 +147,7 @@ hpet_start(struct eventtimer *et,
 	struct hpet_timer *mt = (struct hpet_timer *)et->et_priv;
 	struct hpet_timer *t;
 	struct hpet_softc *sc = mt->sc;
-	uint32_t fdiv;
+	uint32_t fdiv, cmp;
 
 	t = (mt->pcpu_master < 0) ? mt : &sc->t[mt->pcpu_slaves[curcpu]];
 	if (period != NULL) {
@@ -164,23 +165,31 @@ hpet_start(struct eventtimer *et,
 			fdiv += sc->freq * first->sec;
 	} else
 		fdiv = t->div;
+	if (t->irq < 0)
+		bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
+	t->caps |= HPET_TCNF_INT_ENB;
 	t->last = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
+restart:
+	cmp = t->last + fdiv;
 	if (t->mode == 1 && (t->caps & HPET_TCAP_PER_INT)) {
 		t->caps |= HPET_TCNF_TYPE;
 		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num),
 		    t->caps | HPET_TCNF_VAL_SET);
-		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
-		    t->last + fdiv);
-		bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num));
-		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
-		    t->div);
+		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num), cmp);
+		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num), t->div);
 	} else {
-		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num),
-		    t->last + fdiv);
+		t->caps &= ~HPET_TCNF_TYPE;
+		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
+		bus_write_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num), cmp);
+	}
+	if (fdiv < 5000) {
+		bus_read_4(sc->mem_res, HPET_TIMER_COMPARATOR(t->num));
+		t->last = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
+		if ((int32_t)(t->last - cmp) < 0) {
+			fdiv *= 2;
+			goto restart;
+		}
 	}
-	t->caps |= HPET_TCNF_INT_ENB;
-	bus_write_4(sc->mem_res, HPET_ISR, 1 << t->num);
-	bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(t->num), t->caps);
 	return (0);
 }
 
@@ -321,7 +330,7 @@ hpet_attach(device_t dev)
 	int i, j, num_msi, num_timers, num_percpu_et, num_percpu_t, cur_cpu;
 	int pcpu_master;
 	static int maxhpetet = 0;
-	uint32_t val, val2, cvectors;
+	uint32_t val, val2, cvectors, dvectors;
 	uint16_t vendor, rev;
 
 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
@@ -438,10 +447,9 @@ hpet_attach(device_t dev)
 		sc->t[1].vectors = 0;
 	}
 
-	num_msi = 0;
-	sc->useirq = 0;
-	/* Find common legacy IRQ vectors for all timers. */
-	cvectors = 0xffff0000;
+	/* Check what IRQs we want use. */
+	/* By default allow any PCI IRQs. */
+	sc->allowed_irqs = 0xffff0000;
 	/*
 	 * HPETs in AMD chipsets before SB800 have problems with IRQs >= 16
 	 * Lower are also not always working for different reasons.
@@ -450,7 +458,25 @@ hpet_attach(device_t dev)
 	 * interrupt loss. Avoid legacy IRQs for AMD.
 	 */
 	if (vendor == HPET_VENDID_AMD)
-		cvectors = 0x00000000;
+		sc->allowed_irqs = 0x00000000;
+	/*
+	 * Neither QEMU nor VirtualBox report supported IRQs correctly.
+	 * The only way to use HPET there is to specify IRQs manually
+	 * and/or use legacy_route. Legacy_route mode work on both.
+	 */
+	if (vm_guest)
+		sc->allowed_irqs = 0x00000000;
+	/* Let user override. */
+	resource_int_value(device_get_name(dev), device_get_unit(dev),
+	     "allowed_irqs", &sc->allowed_irqs);
+
+	num_msi = 0;
+	sc->useirq = 0;
+	/* Find IRQ vectors for all timers. */
+	cvectors = sc->allowed_irqs & 0xffff0000;
+	dvectors = sc->allowed_irqs & 0x0000ffff;
+	if (sc->legacy_route)
+		dvectors &= 0x0000fefe;
 	for (i = 0; i < num_timers; i++) {
 		t = &sc->t[i];
 		if (sc->legacy_route && i < 2)
@@ -465,6 +491,10 @@ hpet_attach(device_t dev)
 			}
 		}
 #endif
+		else if (dvectors & t->vectors) {
+			t->irq = ffs(dvectors & t->vectors) - 1;
+			dvectors &= ~(1 << t->irq);
+		}
 		if (t->irq >= 0) {
 			if (!(t->intr_res =
 			    bus_alloc_resource(dev, SYS_RES_IRQ, &t->intr_rid,
@@ -495,7 +525,7 @@ hpet_attach(device_t dev)
 	if (sc->legacy_route)
 		hpet_enable(sc);
 	/* Group timers for per-CPU operation. */
-	num_percpu_et = min(num_msi / mp_ncpus, 2);
+	num_percpu_et = min(num_msi / mp_ncpus, 1);
 	num_percpu_t = num_percpu_et * mp_ncpus;
 	pcpu_master = 0;
 	cur_cpu = CPU_FIRST();
@@ -510,7 +540,8 @@ hpet_attach(device_t dev)
 			bus_bind_intr(dev, t->intr_res, cur_cpu);
 			cur_cpu = CPU_NEXT(cur_cpu);
 			num_percpu_t--;
-		}
+		} else if (t->irq >= 0)
+			bus_bind_intr(dev, t->intr_res, CPU_FIRST());
 	}
 	bus_write_4(sc->mem_res, HPET_ISR, 0xffffffff);
 	sc->irq = -1;
@@ -545,7 +576,7 @@ hpet_attach(device_t dev)
 			/* Legacy route doesn't need more configuration. */
 		} else
 #ifdef DEV_APIC
-		if (t->irq >= 0) {
+		if ((t->caps & HPET_TCAP_FSB_INT_DEL) && t->irq >= 0) {
 			uint64_t addr;
 			uint32_t data;	
 			
@@ -561,7 +592,9 @@ hpet_attach(device_t dev)
 				t->irq = -2;
 		} else
 #endif
-		if (sc->irq >= 0 && (t->vectors & (1 << sc->irq)))
+		if (t->irq >= 0)
+			t->caps |= (t->irq << 9);
+		else if (sc->irq >= 0 && (t->vectors & (1 << sc->irq)))
 			t->caps |= (sc->irq << 9) | HPET_TCNF_INT_TYPE;
 		bus_write_4(sc->mem_res, HPET_TIMER_CAP_CNF(i), t->caps);
 		/* Skip event timers without set up IRQ. */
@@ -585,7 +618,7 @@ hpet_attach(device_t dev)
 			t->et.et_quality -= 10;
 		t->et.et_frequency = sc->freq;
 		t->et.et_min_period.sec = 0;
-		t->et.et_min_period.frac = 0x00004000LLU << 32;
+		t->et.et_min_period.frac = 0x00008000LLU << 32;
 		t->et.et_max_period.sec = 0xfffffffeLLU / sc->freq;
 		t->et.et_max_period.frac =
 		    ((0xfffffffeLLU << 32) / sc->freq) << 32;


More information about the svn-src-all mailing list