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