pps.c revision 143398
1139749Simp/*-
233323Sphk * ----------------------------------------------------------------------------
333323Sphk * "THE BEER-WARE LICENSE" (Revision 42):
433323Sphk * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
533323Sphk * can do whatever you want with this stuff. If we meet some day, and you think
633323Sphk * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
733323Sphk * ----------------------------------------------------------------------------
833323Sphk *
933323Sphk *
1036938Sphk * This driver implements a draft-mogul-pps-api-02.txt PPS source.
1136938Sphk *
1236938Sphk * The input pin is pin#10
1336938Sphk * The echo output pin is pin#14
1436938Sphk *
1533323Sphk */
1633323Sphk
17119418Sobrien#include <sys/cdefs.h>
18119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/ppbus/pps.c 143398 2005-03-11 07:03:46Z imp $");
19119418Sobrien
2033323Sphk#include <sys/param.h>
2133323Sphk#include <sys/kernel.h>
2233323Sphk#include <sys/systm.h>
2355939Snsouch#include <sys/module.h>
2455939Snsouch#include <sys/bus.h>
2533323Sphk#include <sys/conf.h>
2636739Sphk#include <sys/timepps.h>
2755939Snsouch#include <machine/bus.h>
2855939Snsouch#include <machine/resource.h>
2955939Snsouch#include <sys/rman.h>
3033323Sphk
3133323Sphk#include <dev/ppbus/ppbconf.h>
3255939Snsouch#include "ppbus_if.h"
3355939Snsouch#include <dev/ppbus/ppbio.h>
3433323Sphk
3549550Sphk#define PPS_NAME	"pps"		/* our official name */
3633323Sphk
3797228Speter#define PRVERBOSE(fmt, arg...)	if (bootverbose) printf(fmt, ##arg);
3888220Simp
3949550Sphkstruct pps_data {
40143390Simp	struct	ppb_device pps_dev;
4183818Sphk	struct	pps_state pps[9];
42130585Sphk	struct cdev *devs[9];
4383818Sphk	device_t ppsdev;
4483818Sphk	device_t ppbus;
4583818Sphk	int	busy;
4683818Sphk	struct callout_handle timeout;
4783818Sphk	int	lastdata;
4855939Snsouch
49143398Simp	struct mtx	mtx;
5055939Snsouch	struct resource *intr_resource;	/* interrupt resource */
5155939Snsouch	void *intr_cookie;		/* interrupt registration cookie */
5249550Sphk};
5333323Sphk
5455939Snsouchstatic void	ppsintr(void *arg);
5583818Sphkstatic void 	ppshcpoll(void *arg);
5633323Sphk
5755939Snsouch#define DEVTOSOFTC(dev) \
5855939Snsouch	((struct pps_data *)device_get_softc(dev))
5933323Sphk
6055939Snsouchstatic devclass_t pps_devclass;
6133323Sphk
6233396Sphkstatic	d_open_t	ppsopen;
6333396Sphkstatic	d_close_t	ppsclose;
6436739Sphkstatic	d_ioctl_t	ppsioctl;
6533323Sphk
6647625Sphkstatic struct cdevsw pps_cdevsw = {
67126080Sphk	.d_version =	D_VERSION,
68111815Sphk	.d_open =	ppsopen,
69111815Sphk	.d_close =	ppsclose,
70111815Sphk	.d_ioctl =	ppsioctl,
71111815Sphk	.d_name =	PPS_NAME,
7247625Sphk};
7333323Sphk
7456455Speterstatic void
7556455Speterppsidentify(driver_t *driver, device_t parent)
7656455Speter{
7756455Speter
78127189Sguido	device_t dev;
79127189Sguido
80127189Sguido	dev = device_find_child(parent, PPS_NAME, 0);
81127189Sguido	if (!dev)
82127189Sguido		BUS_ADD_CHILD(parent, 0, PPS_NAME, -1);
8356455Speter}
8456455Speter
8555939Snsouchstatic int
8683818Sphkppstry(device_t ppbus, int send, int expect)
8783818Sphk{
8883818Sphk	int i;
8983818Sphk
9083818Sphk	ppb_wdtr(ppbus, send);
9183818Sphk	i = ppb_rdtr(ppbus);
9288220Simp	PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i);
9383818Sphk	return (i != expect);
9483818Sphk}
9583818Sphk
9683818Sphkstatic int
9755939Snsouchppsprobe(device_t ppsdev)
9833323Sphk{
9988220Simp	device_set_desc(ppsdev, "Pulse per second Timing Interface");
10088220Simp
10188220Simp	return (0);
10288220Simp}
10388220Simp
10488220Simpstatic int
10588220Simpppsattach(device_t dev)
10688220Simp{
10788220Simp	struct pps_data *sc = DEVTOSOFTC(dev);
10888220Simp	device_t ppbus = device_get_parent(dev);
109130585Sphk	struct cdev *d;
110106563Sjhb	intptr_t irq;
111106563Sjhb	int i, unit, zero = 0;
11233323Sphk
113143398Simp	mtx_init(&sc->mtx, device_get_nameunit(dev), "pps", MTX_SPIN);
11488220Simp	/* retrieve the ppbus irq */
11588220Simp	BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
11688220Simp
11788220Simp	if (irq > 0) {
11888220Simp		/* declare our interrupt handler */
11988220Simp		sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
12088220Simp		    &zero, irq, irq, 1, RF_SHAREABLE);
12188220Simp	}
12288220Simp	/* interrupts seem mandatory */
12388220Simp	if (sc->intr_resource == NULL)
12488220Simp		return (ENXIO);
12588220Simp
12688220Simp	sc->ppsdev = dev;
12788220Simp	sc->ppbus = ppbus;
12883818Sphk	unit = device_get_unit(ppbus);
12988220Simp	d = make_dev(&pps_cdevsw, unit,
130108322Srwatson	    UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit);
13188220Simp	sc->devs[0] = d;
13283818Sphk	sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
13388220Simp	d->si_drv1 = sc;
13488220Simp	d->si_drv2 = (void*)0;
13583818Sphk	pps_init(&sc->pps[0]);
13633323Sphk
13788220Simp	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT))
13883818Sphk		return (0);
13933323Sphk
14083818Sphk	do {
14183818Sphk		i = ppb_set_mode(sc->ppbus, PPB_EPP);
14288220Simp		PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
14383818Sphk		if (i == -1)
14483818Sphk			break;
14583818Sphk		i = 0;
14683818Sphk		ppb_wctr(ppbus, i);
14783818Sphk		if (ppstry(ppbus, 0x00, 0x00))
14883818Sphk			break;
14983818Sphk		if (ppstry(ppbus, 0x55, 0x55))
15083818Sphk			break;
15183818Sphk		if (ppstry(ppbus, 0xaa, 0xaa))
15283818Sphk			break;
15383818Sphk		if (ppstry(ppbus, 0xff, 0xff))
15483818Sphk			break;
15583818Sphk
15683818Sphk		i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN;
15783818Sphk		ppb_wctr(ppbus, i);
15888220Simp		PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
15983818Sphk		if (ppstry(ppbus, 0x00, 0x00))
16083818Sphk			break;
16183818Sphk		if (ppstry(ppbus, 0x55, 0x00))
16283818Sphk			break;
16383818Sphk		if (ppstry(ppbus, 0xaa, 0x00))
16483818Sphk			break;
16583818Sphk		if (ppstry(ppbus, 0xff, 0x00))
16683818Sphk			break;
16783818Sphk
16883818Sphk		i = IRQENABLE | PCD | nINIT | SELECTIN;
16983818Sphk		ppb_wctr(ppbus, i);
17088220Simp		PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
17183818Sphk		ppstry(ppbus, 0x00, 0xff);
17283818Sphk		ppstry(ppbus, 0x55, 0xff);
17383818Sphk		ppstry(ppbus, 0xaa, 0xff);
17483818Sphk		ppstry(ppbus, 0xff, 0xff);
17583818Sphk
17683818Sphk		for (i = 1; i < 9; i++) {
17788220Simp			d = make_dev(&pps_cdevsw, unit + 0x10000 * i,
178108322Srwatson			  UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1);
17988220Simp			sc->devs[i] = d;
18083818Sphk			sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR;
18188220Simp			d->si_drv1 = sc;
182106563Sjhb			d->si_drv2 = (void *)(intptr_t)i;
18383818Sphk			pps_init(&sc->pps[i]);
18483818Sphk		}
18583818Sphk	} while (0);
18683818Sphk	i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE);
18788220Simp	ppb_release_bus(ppbus, dev);
18833323Sphk
18955939Snsouch	return (0);
19033323Sphk}
19133323Sphk
19233323Sphkstatic	int
193130585Sphkppsopen(struct cdev *dev, int flags, int fmt, struct thread *td)
19433323Sphk{
19583818Sphk	struct pps_data *sc = dev->si_drv1;
196106563Sjhb	int subdev = (intptr_t)dev->si_drv2;
19783818Sphk	int error, i;
19833323Sphk
19983818Sphk	if (!sc->busy) {
20083818Sphk		device_t ppsdev = sc->ppsdev;
20183818Sphk		device_t ppbus = sc->ppbus;
20283818Sphk
20355939Snsouch		if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR))
20446053Sphk			return (EINTR);
20533323Sphk
20655939Snsouch		/* attach the interrupt handler */
207143398Simp		if ((error = bus_setup_intr(ppsdev, sc->intr_resource,
208143398Simp		    (INTR_TYPE_TTY | INTR_MPSAFE | INTR_FAST), ppsintr,
209143398Simp		    ppsdev, &sc->intr_cookie))) {
21055939Snsouch			ppb_release_bus(ppbus, ppsdev);
21155939Snsouch			return (error);
21255939Snsouch		}
21355939Snsouch
21483818Sphk		i = ppb_set_mode(sc->ppbus, PPB_PS2);
21588220Simp		PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
21683818Sphk
21783818Sphk		i = IRQENABLE | PCD | nINIT | SELECTIN;
21883818Sphk		ppb_wctr(ppbus, i);
219143390Simp	}
22083818Sphk	if (subdev > 0 && !(sc->busy & ~1)) {
22183818Sphk		sc->timeout = timeout(ppshcpoll, sc, 1);
22283818Sphk		sc->lastdata = ppb_rdtr(sc->ppbus);
22346053Sphk	}
22483818Sphk	sc->busy |= (1 << subdev);
22533323Sphk	return(0);
22633323Sphk}
22733323Sphk
22833323Sphkstatic	int
229130585Sphkppsclose(struct cdev *dev, int flags, int fmt, struct thread *td)
23033323Sphk{
23183818Sphk	struct pps_data *sc = dev->si_drv1;
232106563Sjhb	int subdev = (intptr_t)dev->si_drv2;
23333323Sphk
23483818Sphk	sc->pps[subdev].ppsparam.mode = 0;	/* PHK ??? */
23583818Sphk	sc->busy &= ~(1 << subdev);
236143390Simp	if (subdev > 0 && !(sc->busy & ~1))
23783818Sphk		untimeout(ppshcpoll, sc, sc->timeout);
23883818Sphk	if (!sc->busy) {
23983818Sphk		device_t ppsdev = sc->ppsdev;
24083818Sphk		device_t ppbus = sc->ppbus;
24143433Snsouch
24283818Sphk		ppb_wdtr(ppbus, 0);
24383818Sphk		ppb_wctr(ppbus, 0);
24443433Snsouch
24583818Sphk		/* Note: the interrupt handler is automatically detached */
24683818Sphk		ppb_set_mode(ppbus, PPB_COMPATIBLE);
24783818Sphk		ppb_release_bus(ppbus, ppsdev);
24883818Sphk	}
24933323Sphk	return(0);
25033323Sphk}
25133323Sphk
25233323Sphkstatic void
25383818Sphkppshcpoll(void *arg)
25483818Sphk{
25583818Sphk	struct pps_data *sc = arg;
25683818Sphk	int i, j, k, l;
25783818Sphk
25883818Sphk	if (!(sc->busy & ~1))
25983818Sphk		return;
26083818Sphk	sc->timeout = timeout(ppshcpoll, sc, 1);
26183818Sphk	i = ppb_rdtr(sc->ppbus);
26283818Sphk	if (i == sc->lastdata)
26383818Sphk		return;
26483818Sphk	l = sc->lastdata ^ i;
26583818Sphk	k = 1;
26683818Sphk	for (j = 1; j < 9; j ++) {
26795523Sphk		if (l & k) {
26895523Sphk			pps_capture(&sc->pps[j]);
26995523Sphk			pps_event(&sc->pps[j],
27095523Sphk			    i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
27195523Sphk		}
27283818Sphk		k += k;
27383818Sphk	}
27483818Sphk	sc->lastdata = i;
27583818Sphk}
27683818Sphk
27783818Sphkstatic void
27855939Snsouchppsintr(void *arg)
27933323Sphk{
28055939Snsouch	device_t ppsdev = (device_t)arg;
28155939Snsouch	struct pps_data *sc = DEVTOSOFTC(ppsdev);
28283818Sphk	device_t ppbus = sc->ppbus;
28333323Sphk
284143398Simp	mtx_lock(&sc->mtx);
28595523Sphk	pps_capture(&sc->pps[0]);
286143398Simp	if (!(ppb_rstr(ppbus) & nACK)) {
287143398Simp		mtx_unlock(&sc->mtx);
28833323Sphk		return;
289143398Simp	}
29083818Sphk	if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT)
29155939Snsouch		ppb_wctr(ppbus, IRQENABLE | AUTOFEED);
29295523Sphk	pps_event(&sc->pps[0], PPS_CAPTUREASSERT);
29383818Sphk	if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT)
29455939Snsouch		ppb_wctr(ppbus, IRQENABLE);
295143398Simp	mtx_unlock(&sc->mtx);
29633323Sphk}
29733323Sphk
29836739Sphkstatic int
299130585Sphkppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
30033323Sphk{
30183818Sphk	struct pps_data *sc = dev->si_drv1;
302106563Sjhb	int subdev = (intptr_t)dev->si_drv2;
303143398Simp	int err;
30433323Sphk
305143398Simp	mtx_lock(&sc->mtx);
306143398Simp	err = pps_ioctl(cmd, data, &sc->pps[subdev]);
307143398Simp	mtx_unlock(&sc->mtx);
308143398Simp	return (err);
30933323Sphk}
31033323Sphk
31156455Speterstatic device_method_t pps_methods[] = {
31256455Speter	/* device interface */
31356455Speter	DEVMETHOD(device_identify,	ppsidentify),
31456455Speter	DEVMETHOD(device_probe,		ppsprobe),
31556455Speter	DEVMETHOD(device_attach,	ppsattach),
31656455Speter
31756455Speter	{ 0, 0 }
31856455Speter};
31956455Speter
32056455Speterstatic driver_t pps_driver = {
32156455Speter	PPS_NAME,
32256455Speter	pps_methods,
32356455Speter	sizeof(struct pps_data),
32456455Speter};
32555939SnsouchDRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0);
326