svn commit: r273598 - in head: include sys/dev/acpica

Rui Paulo rpaulo at FreeBSD.org
Fri Oct 24 18:39:16 UTC 2014


Author: rpaulo
Date: Fri Oct 24 18:39:15 2014
New Revision: 273598
URL: https://svnweb.freebsd.org/changeset/base/273598

Log:
  HPET: create /dev/hpetN as a way to access HPET from userland.
  
  In some cases, TSC is broken and special applications might benefit
  from memory mapping HPET and reading the registers to count time.
  Most often the main HPET counter is 32-bit only[1], so this only gives
  the application a 300 second window based on the default HPET
  interval.
  Other applications, such as Intel's DPDK, expect /dev/hpet to be
  present and use it to count time as well.
  
  Although we have an almost userland version of gettimeofday() which
  uses rdtsc in userland, it's not always possible to use it, depending
  on how broken the multi-socket hardware is.
  
  Install the acpi_hpet.h so that applications can use the HPET register
  definitions.
  
  [1] I haven't found a system where HPET's main counter uses more than
  32 bit.  There seems to be a discrepancy in the Intel documentation
  (claiming it's a 64-bit counter) and the actual implementation (a
  32-bit counter in a 64-bit memory area).
  
  MFC after:	1 week
  Relnotes:	yes

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

Modified: head/include/Makefile
==============================================================================
--- head/include/Makefile	Fri Oct 24 17:40:32 2014	(r273597)
+++ head/include/Makefile	Fri Oct 24 18:39:15 2014	(r273598)
@@ -159,6 +159,8 @@ copies:
 	cd ${.CURDIR}/../sys/dev/acpica; \
 	${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 acpiio.h \
 	    ${DESTDIR}${INCLUDEDIR}/dev/acpica
+	${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 acpi_hpet.h \
+	    ${DESTDIR}${INCLUDEDIR}/dev/acpica
 	cd ${.CURDIR}/../sys/dev/agp; \
 	${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 agpreg.h \
 	    ${DESTDIR}${INCLUDEDIR}/dev/agp
@@ -243,7 +245,7 @@ symlinks:
 	done
 .endfor
 	cd ${.CURDIR}/../sys/dev/acpica; \
-	for h in acpiio.h; do \
+	for h in acpiio.h acpi_hpet.h; do \
 		ln -fs ../../../../sys/dev/acpica/$$h \
 		    ${DESTDIR}${INCLUDEDIR}/dev/acpica; \
 	done

Modified: head/sys/dev/acpica/acpi_hpet.c
==============================================================================
--- head/sys/dev/acpica/acpi_hpet.c	Fri Oct 24 17:40:32 2014	(r273597)
+++ head/sys/dev/acpica/acpi_hpet.c	Fri Oct 24 18:39:15 2014	(r273598)
@@ -35,11 +35,13 @@ __FBSDID("$FreeBSD$");
 #include "opt_apic.h"
 #endif
 #include <sys/param.h>
+#include <sys/conf.h>
 #include <sys/bus.h>
 #include <sys/kernel.h>
 #include <sys/module.h>
 #include <sys/proc.h>
 #include <sys/rman.h>
+#include <sys/mman.h>
 #include <sys/time.h>
 #include <sys/smp.h>
 #include <sys/sysctl.h>
@@ -106,6 +108,23 @@ struct hpet_softc {
 		char			name[8];
 	} 			t[32];
 	int			num_timers;
+	struct cdev		*pdev;
+	int			mmap_allow;
+	int			mmap_allow_write;
+	int			devinuse;
+};
+
+static d_open_t hpet_open;
+static d_close_t hpet_close;
+static d_mmap_t hpet_mmap;
+
+static struct cdevsw hpet_cdevsw = {
+	.d_version =	D_VERSION,
+	.d_flags =	D_TRACKCLOSE,
+	.d_name =	"hpet",
+	.d_open =	hpet_open,
+	.d_close =	hpet_close,
+	.d_mmap =	hpet_mmap,
 };
 
 static u_int hpet_get_timecount(struct timecounter *tc);
@@ -317,6 +336,47 @@ hpet_find_irq_rid(device_t dev, u_long s
 	}
 }
 
+static int
+hpet_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
+{
+	struct hpet_softc *sc;
+
+	sc = cdev->si_drv1;
+	if (!sc->mmap_allow)
+		return (EPERM);
+	if (atomic_cmpset_32(&sc->devinuse, 0, 1) == 0)
+		return (EBUSY);
+	else
+		return (0);
+}
+
+static int
+hpet_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
+{
+	struct hpet_softc *sc;
+
+	sc = cdev->si_drv1;
+	sc->devinuse = 0;
+
+	return (0);
+}
+
+static int
+hpet_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
+    int nprot, vm_memattr_t *memattr)
+{
+	struct hpet_softc *sc;
+
+	sc = cdev->si_drv1;
+	if (offset > rman_get_size(sc->mem_res))
+		return (EINVAL);
+	if (!sc->mmap_allow_write && (nprot & PROT_WRITE))
+		return (EPERM);
+	*paddr = rman_get_start(sc->mem_res) + offset;
+
+	return (0);
+}
+
 /* Discover the HPET via the ACPI table of the same name. */
 static void 
 hpet_identify(driver_t *driver, device_t parent)
@@ -701,6 +761,26 @@ hpet_attach(device_t dev)
 			maxhpetet++;
 		}
 	}
+
+	sc->pdev = make_dev(&hpet_cdevsw, 0, UID_ROOT, GID_WHEEL,
+	    0600, "hpet%d", device_get_unit(dev));
+	if (sc->pdev) {
+		sc->pdev->si_drv1 = sc;
+		sc->mmap_allow = 1;
+		TUNABLE_INT_FETCH("hw.acpi.hpet.mmap_allow",
+		    &sc->mmap_allow);
+		sc->mmap_allow_write = 1;
+		TUNABLE_INT_FETCH("hw.acpi.hpet.mmap_allow_write",
+		    &sc->mmap_allow_write);
+		SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+		    OID_AUTO, "mmap_allow",
+		    CTLFLAG_RW, &sc->mmap_allow, 0,
+		    "Allow userland to memory map HPET");
+	} else
+		device_printf(dev, "could not create /dev/hpet%d\n",
+		    device_get_unit(dev));
+
 	return (0);
 }
 


More information about the svn-src-head mailing list