Thu Apr 23 20:33:57 2020 UTC ()
Implement a HPET based DELAY().


(ad)
diff -r1.13 -r1.14 src/sys/dev/ic/hpet.c
diff -r1.4 -r1.5 src/sys/dev/ic/hpetvar.h

cvs diff -r1.13 -r1.14 src/sys/dev/ic/hpet.c (expand / switch to context diff)
--- src/sys/dev/ic/hpet.c 2011/10/31 12:47:15 1.13
+++ src/sys/dev/ic/hpet.c 2020/04/23 20:33:57 1.14
@@ -1,4 +1,4 @@
-/* $NetBSD: hpet.c,v 1.13 2011/10/31 12:47:15 yamt Exp $ */
+/* $NetBSD: hpet.c,v 1.14 2020/04/23 20:33:57 ad Exp $ */
 
 /*
  * Copyright (c) 2006 Nicolas Joly
@@ -33,7 +33,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hpet.c,v 1.13 2011/10/31 12:47:15 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hpet.c,v 1.14 2020/04/23 20:33:57 ad Exp $");
 
 #include <sys/systm.h>
 #include <sys/device.h>
@@ -43,6 +43,7 @@
 #include <sys/timetc.h>
 
 #include <sys/bus.h>
+#include <sys/lock.h>
 
 #include <dev/ic/hpetreg.h>
 #include <dev/ic/hpetvar.h>
@@ -50,9 +51,12 @@
 static u_int	hpet_get_timecount(struct timecounter *);
 static bool	hpet_resume(device_t, const pmf_qual_t *);
 
+static struct hpet_softc *hpet0 __read_mostly;
+
 int
 hpet_detach(device_t dv, int flags)
 {
+#if 0 /* XXX DELAY() is based off this, detaching is not a good idea. */
 	struct hpet_softc *sc = device_private(dv);
 	int rc;
 
@@ -64,6 +68,9 @@
 	bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, sc->sc_config);
 
 	return 0;
+#else
+	return EBUSY;
+#endif
 }
 
 void
@@ -84,8 +91,8 @@
 	tc->tc_counter_mask = 0xffffffff;
 
 	/* Get frequency */
-	val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_PERIOD);
-	if (val == 0 || val > HPET_PERIOD_MAX) {
+	sc->sc_period = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_PERIOD);
+	if (sc->sc_period == 0 || sc->sc_period > HPET_PERIOD_MAX) {
 		aprint_error_dev(dv, "invalid timer period\n");
 		return;
 	}
@@ -104,7 +111,7 @@
 		}
 	}
 
-	tmp = (1000000000000000ULL * 2) / val;
+	tmp = (1000000000000000ULL * 2) / sc->sc_period;
 	tc->tc_frequency = (tmp / 2) + (tmp & 1);
 
 	/* Enable timer */
@@ -120,6 +127,9 @@
 
 	if (!pmf_device_register(dv, NULL, hpet_resume))
 		aprint_error_dev(dv, "couldn't establish power handler\n");
+
+	if (device_unit(dv) == 0)
+		hpet0 = sc;
 }
 
 static u_int
@@ -141,6 +151,38 @@
 	bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, val);
 
 	return true;
+}
+
+bool
+hpet_delay_p(void)
+{
+
+	return hpet0 != NULL;
+}
+
+void
+hpet_delay(unsigned int us)
+{
+	struct hpet_softc *sc;
+	uint32_t ntick, otick;
+	int64_t delta;
+
+	/*
+	 * Read timer before slow division.  Assume that each read of the
+	 * HPET costs ~500ns.  Aim for the middle and subtract 750ns for
+	 * overhead.
+	 */
+	sc = hpet0;
+	otick = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
+	delta = (((int64_t)us * 1000000000) - 750000000) / sc->sc_period;
+
+	while (delta > 0) {
+		SPINLOCK_BACKOFF_HOOK;
+		ntick = bus_space_read_4(sc->sc_memt, sc->sc_memh,
+		    HPET_MCOUNT_LO);
+		delta -= (uint32_t)(ntick - otick);
+		otick = ntick;
+	}
 }
 
 MODULE(MODULE_CLASS_DRIVER, hpet, NULL);

cvs diff -r1.4 -r1.5 src/sys/dev/ic/hpetvar.h (expand / switch to context diff)
--- src/sys/dev/ic/hpetvar.h 2011/06/14 16:33:51 1.4
+++ src/sys/dev/ic/hpetvar.h 2020/04/23 20:33:57 1.5
@@ -1,4 +1,4 @@
-/* $NetBSD: hpetvar.h,v 1.4 2011/06/14 16:33:51 jruoho Exp $ */
+/* $NetBSD: hpetvar.h,v 1.5 2020/04/23 20:33:57 ad Exp $ */
 
 /*
  * Copyright (c) 2006 Nicolas Joly
@@ -31,6 +31,8 @@
 #ifndef _DEV_IC_HPETVAR_H_
 #define _DEV_IC_HPETVAR_H_
 
+#include <sys/timetc.h>
+
 struct hpet_softc {
 	bus_size_t		sc_mems;
 	bus_space_tag_t		sc_memt;
@@ -38,10 +40,13 @@
 
 	bool			sc_mapped;
 	uint32_t		sc_config;
+	int32_t			sc_period;
 	struct			timecounter sc_tc;
 };
 
 void	hpet_attach_subr(device_t);
 int	hpet_detach(device_t, int flags);
+void	hpet_delay(unsigned int);
+bool	hpet_delay_p(void);
 
 #endif /* _DEV_IC_HPETVAR_H_ */