Date: Fri, 14 Jul 2006 07:16:42 GMT From: Warner Losh <imp@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 101529 for review Message-ID: <200607140716.k6E7GgAE043558@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=101529 Change 101529 by imp@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
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200607140716.k6E7GgAE043558>