PERFORCE change 101529 for review

Warner Losh imp at FreeBSD.org
Fri Jul 14 07:16:43 UTC 2006


http://perforce.freebsd.org/chv.cgi?CH=101529

Change 101529 by imp at imp_lighthouse on 2006/07/14 07:15:44

	Start to implement a pps driver connected to TC1.  This allows
	us to run ntp with a better refclock.  Also use a timecounter
	for TC1.  We can likely enable much of this for non-tsc users
	too, since it will give us a clock resolution of 133ns in the
	timing rather than 30517.5ns, which would be 200 times better
	than the SCK.

Affected files ...

.. //depot/projects/arm/src/sys/arm/at91/at91_tc.c#6 edit

Differences ...

==== //depot/projects/arm/src/sys/arm/at91/at91_tc.c#6 (text+ko) ====

@@ -30,13 +30,21 @@
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/module.h>
 #include <sys/mutex.h>
+#include <sys/poll.h>
+#include <sys/rman.h>
+#include <sys/selinfo.h>
+#include <sys/taskqueue.h>
 #include <sys/time.h>
+#include <sys/timepps.h>
 #include <sys/timetc.h>
-#include <sys/rman.h>
+#include <sys/uio.h>
 
 #include <machine/bus.h>
 #include <machine/cpu.h>
@@ -55,13 +63,23 @@
 	struct resource *irq_res;	/* IRQ resource */
 	struct at91_tc_softc *sc;
 	bus_size_t offset;
+#ifdef AT91_TSC
+	struct pps_state pps;
+	struct selinfo selp;
+	struct cdev *pdev;
+	struct cdev *cdev;
+	uint32_t pps_seen;
+	uint32_t pps_read;
+	struct task task;
+	int	cnonblock;
+#endif
 };
 
 struct at91_tc_softc {
 	struct resource	*mem_res;	/* Memory resource */
 	device_t dev;
 	struct at91_tc_counter tc[MAX_COUNTER];
-	struct mtx sc_mtx;		/* basically a perimeter lock */
+	struct mtx mtx;		/* basically a perimeter lock */
 	struct cdev *cdev;
 	int overflows;
 };
@@ -75,13 +93,40 @@
 #define WR4sc(sc, off, val) \
 	bus_write_4(sc->mem_res, (off), (val))
 
-#define AT91_TC_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
-#define	AT91_TC_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
-#define AT91_TC_LOCK_INIT(_sc) \
-	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), "tc", MTX_DEF)
-#define AT91_TC_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
-#define AT91_TC_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
-#define AT91_TC_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+#define AT91_TC_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED);
+#define AT91_TC_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
+
+#ifdef AT91_TSC
+static	d_open_t	at91_tc_open;
+static	d_close_t	at91_tc_close;
+static	d_ioctl_t	at91_tc_ioctl;
+
+static	d_open_t	at91_tc_ctl_open;
+static	d_close_t	at91_tc_ctl_close;
+static	d_read_t	at91_tc_ctl_read;
+static	d_ioctl_t	at91_tc_ctl_ioctl;
+static	d_poll_t	at91_tc_ctl_poll;
+
+static task_fn_t at91_tc_tq_wakeup;
+
+static struct cdevsw pps_cdevsw = {
+	.d_version =	D_VERSION,
+	.d_open =	at91_tc_open,
+	.d_close =	at91_tc_close,
+	.d_ioctl =	at91_tc_ioctl,
+	.d_name =	"pps",
+};
+
+static struct cdevsw pps_ctl_cdevsw = {
+	.d_version =	D_VERSION,
+	.d_open =	at91_tc_ctl_open,
+	.d_read =	at91_tc_ctl_read,
+	.d_close =	at91_tc_ctl_close,
+	.d_ioctl =	at91_tc_ctl_ioctl,
+	.d_poll =	at91_tc_ctl_poll,
+	.d_name =	"pps",
+};
+#endif
 
 /* helper routines */
 static int at91_tc_activate(device_t dev);
@@ -103,7 +148,7 @@
 static struct timecounter at91_tc_timecounter = {
 	at91_tc_get_timecount, /* get_timecount */
 	NULL, /* no poll_pps */
-	0xfffffu, /* counter_mask */
+	0xffffu, /* counter_mask */
 //	5000000, /* frequency */
 	60000000 / 8, /* frequency */
 	"5MHz", /* name */
@@ -132,7 +177,7 @@
 	err = at91_tc_activate(dev);
 	if (err)
 		goto out;
-	AT91_TC_LOCK_INIT(sc);
+	mtx_init(&sc->mtx, device_get_nameunit(sc->dev), "tc", MTX_DEF);
 
 	sc->tc[0].offset = TC_TC0_OFFSET;
 	sc->tc[1].offset = TC_TC1_OFFSET;
@@ -143,7 +188,6 @@
 		// On the TSC board, we have 5MHz going into TIOA2.  Setup
 		// TC1's XC1 to use this.
 		WR4sc(sc, TC_BMR, TC_BMR_XC1_TIOA2);
-	printf("BMR: %x\n", RD4sc(sc, TC_BMR));
 #endif
 	for (i = 0; i < MAX_COUNTER; i++)
 		at91_tc_counter_init(sc, &sc->tc[i], i);
@@ -212,10 +256,10 @@
 {
 #ifdef AT91_TSC
 	int rid;
+	struct cdev *d;
 #endif
 
 	tc->sc = sc;
-	printf("tc%d offset %x\n", unit, (uint32_t)tc->offset);
 #ifdef AT91_TSC
 	// Only TC1 is needed.  All others are disabled.
 	if (unit != 1 || device_get_unit(sc->dev) != 0) {
@@ -226,7 +270,7 @@
 #ifdef AT91_TSC
 	}
 
-	rid = 0;
+	rid = unit;
 	tc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
 	    RF_ACTIVE);
 	if (tc->irq_res == NULL)
@@ -235,45 +279,212 @@
 	    at91_tc_counter_isr, tc, &tc->intrhand) != 0)
 		return;
 
+	TASK_INIT(&tc->task, 0, at91_tc_tq_wakeup, tc);
+	rid = 0;
+	d = make_dev(&pps_cdevsw, unit, UID_ROOT, GID_WHEEL, 0600,
+	    "pps%d", rid);
+	d->si_drv1 = tc;
+	d->si_drv2 = (void *)0;
+	tc->pdev = d;
+	tc->pps.ppscap = PPS_CAPTUREASSERT;
+	pps_init(&tc->pps);
+	d = make_dev(&pps_ctl_cdevsw, unit, UID_ROOT, GID_WHEEL, 0600,
+	    "pps%d.ctl", rid);
+	d->si_drv1 = tc;
+	d->si_drv2 = (void *)1;
+	tc->cdev = d;
+
 	// Setup TC1 into Capture Mode (WAVE=0), clocked by our external
-	// 5MHz, loading RA on rise and RB on falling edge
-//	WR4(tc, TC_CMR, TC_CMR_XC1 | TC_CMR_BURST_NONE | TC_CMR_ETRGEDG_NONE |
-//	    TC_CMR_LDRA_RISE | TC_CMR_LDRB_FALL);
-	WR4(tc, TC_CMR, TC_CMR_TIMER_CLOCK2 | TC_CMR_BURST_NONE | TC_CMR_ETRGEDG_NONE |
+	// 5MHz, loading RA on rise and RB on falling edge.  For some reason,
+	// we seem to need to enable both of these captures and throw away
+	// the one we don't need, otherwise neither happens.
+	WR4(tc, TC_CMR,
+#if 0
+	    TC_CMR_XC1 |
+#else
+	    TC_CMR_TIMER_CLOCK2 |
+#endif
+	    TC_CMR_BURST_NONE | TC_CMR_ETRGEDG_NONE |
 	    TC_CMR_LDRA_RISE | TC_CMR_LDRB_FALL);
-	printf("CMR: %x\n", RD4(tc, TC_CMR));
 	WR4(tc, TC_IDR, 0xffffffff);
 	WR4(tc, TC_IER, TC_SR_COVFS | TC_SR_LOVRS | TC_SR_LDRAS | TC_SR_LDRBS);
-	printf("IMR: %x\n", RD4(tc, TC_IMR));
 
 	// Now that we have ISR setup, we can enable clocks and go!
 	WR4(tc, TC_CCR, TC_CCR_SWTRG | TC_CCR_CLKEN);
-	{
-	    uint32_t last;
-	    last = RD4(tc, TC_CV);
-	    printf("Last is %x and %x SR %x\n", last, RD4(tc, TC_CV), RD4(tc, TC_SR));
+#endif
+}
+
+#ifdef AT91_TSC
+static	int
+at91_tc_open(struct cdev *dev, int flags, int fmt, struct thread *td)
+{
+	return(0);
+}
+
+static	int
+at91_tc_close(struct cdev *dev, int flags, int fmt, struct thread *td)
+{
+	struct at91_tc_counter *tc = dev->si_drv1;
+
+	mtx_lock(&tc->sc->mtx);
+	tc->pps.ppsparam.mode = 0;	/* PHK ??? */
+	mtx_unlock(&tc->sc->mtx);
+	return(0);
+}
+
+static int
+at91_tc_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags,
+    struct thread *td)
+{
+	struct at91_tc_counter *tc = dev->si_drv1;
+	int err;
+
+	mtx_lock(&tc->sc->mtx);
+	err = pps_ioctl(cmd, data, &tc->pps);
+	mtx_unlock(&tc->sc->mtx);
+	return (err);
+}
+
+static	int
+at91_tc_ctl_open(struct cdev *dev, int flags, int fmt, struct thread *td)
+{
+	struct at91_tc_counter *tc = dev->si_drv1;
+	mtx_lock(&tc->sc->mtx);
+	if (flags & O_NONBLOCK)
+		tc->cnonblock = 1;
+	else
+		tc->cnonblock = 0;
+	tc->pps_read = tc->pps_seen;
+	mtx_unlock(&tc->sc->mtx);
+	return(0);
+}
+
+static	int
+at91_tc_ctl_close(struct cdev *dev, int flags, int fmt, struct thread *td)
+{
+	return(0);
+}
+
+static int
+at91_tc_ctl_poll(struct cdev *dev, int events, struct thread *td)
+{
+	struct at91_tc_counter *tc = dev->si_drv1;
+	int revents = 0;
+
+	mtx_lock(&tc->sc->mtx);
+	if (events & (POLLIN | POLLRDNORM)) {
+		if (tc->pps_seen != tc->pps_read)
+			revents |= events & (POLLIN | POLLRDNORM);
+		else
+			selrecord(td, &tc->selp);
 	}
+	mtx_unlock(&tc->sc->mtx);
+
+	return (revents);
+}
+
+static int
+at91_tc_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flags,
+    struct thread *td)
+{
+	struct at91_tc_counter *tc = dev->si_drv1;
+
+	mtx_lock(&tc->sc->mtx);
+	switch (cmd) {
+	case FIONBIO:
+		if (*(int*)arg)
+			tc->cnonblock = 1;
+		else
+			tc->cnonblock = 0;
+		break;
+
+#if 0
+	case TSCPPSIOC_FLUSH:
+		tc->pps_read = tc->pps_seen;
+		break;
+
+	case TSCPPSIOC_GET_COUNT:
+		*(int *)arg = tc->pps_seen;
+		break;
 #endif
+
+	default:
+		mtx_unlock(&tc->sc->mtx);
+		return (ENOTTY);
+	}
+	mtx_unlock(&tc->sc->mtx);
+	return (0);
+}
+
+static int
+at91_tc_ctl_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+	struct at91_tc_counter *tc = dev->si_drv1;
+	uint8_t val;
+	int err = 0;
+
+	mtx_lock(&tc->sc->mtx);
+	while (err == 0) {
+		if (tc->pps_seen != tc->pps_read) {
+			tc->pps_read++;
+			mtx_unlock(&tc->sc->mtx);
+			val = 'p';
+			return (uiomove(&val, 1, uio));
+		}
+		if (tc->cnonblock) {
+			mtx_unlock(&tc->sc->mtx);
+			return EWOULDBLOCK;
+		}
+		err = msleep(tc, &tc->sc->mtx, PZERO, "ctlread", 0);
+	}
+	mtx_unlock(&tc->sc->mtx);
+	return (err);
+}
+
+static void
+at91_tc_tq_wakeup(void *arg, int pending)
+{
+	struct at91_tc_counter *tc = (struct at91_tc_counter *)arg;
+
+	/*
+	 * pps_even modifies ppsinfo.  This is also accessed and modified
+	 * by the ioctl routines, so we need an interlock.  We use the same
+	 * interlock to proect pps_seen.  The read code interlocks against
+	 * this.  Given the low data rate for this driver (pps), contention
+	 * is not a consideration.
+	 */
+	mtx_lock(&tc->sc->mtx);
+	pps_event(&tc->pps, PPS_CAPTUREASSERT);
+
+	/*
+	 * create a PPS event so that the ctl driver can return it via the
+	 * read channel.
+	 */
+	tc->pps_seen++;
+	mtx_unlock(&tc->sc->mtx);
+	selwakeup(&tc->selp);
+	wakeup(tc);
 }
 
-#ifdef AT91_TSC
 static void
 at91_tc_counter_isr(void *argp)
 {
 	struct at91_tc_counter *tc = argp;
-	uint32_t status;
+	uint32_t status, r;
 
-printf("Howdy!\n");
 	status = RD4(tc, TC_SR);
-	if (status & TC_SR_COVFS) {
+	if (status & TC_SR_COVFS)
 		tc->sc->overflows++;
-	}
 	if (status & TC_SR_LOVRS)
 		printf("Didn't read RA or RB in time\n");
-	if (status & TC_SR_LDRAS)
-		printf("RA loaded at 0x%x\n", RD4(tc, TC_RA) & 0xffff);
+	if (status & TC_SR_LDRAS) {
+		r = RD4(tc, TC_RA) & 0xffff;
+		pps_capture(&tc->pps);
+		tc->pps.capcount = r;
+		taskqueue_enqueue_fast(taskqueue_swi, &tc->task);
+	}
 	if (status & TC_SR_LDRBS)
-		printf("RB loaded at 0x%x\n", RD4(tc, TC_RB) & 0xffff);
+		RD4(tc, TC_RB);
 }
 #endif
 


More information about the p4-projects mailing list