summaryrefslogtreecommitdiffstats
path: root/sys/dev/ppbus/pps.c
blob: e39eb14b6369452a5dd97742ee8c4fa283894c5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
 * ----------------------------------------------------------------------------
 *
 * $Id: pps.c,v 1.9 1998/06/21 18:02:32 bde Exp $
 *
 * This driver implements a draft-mogul-pps-api-02.txt PPS source.
 *
 * The input pin is pin#10 
 * The echo output pin is pin#14
 *
 */

#include "opt_devfs.h"

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/timepps.h>
#ifdef DEVFS
#include <sys/devfsext.h>
#endif
#include <sys/malloc.h>

#include <dev/ppbus/ppbconf.h>
#include "pps.h"

#define PPS_NAME	"lppps"		/* our official name */

static struct pps_data {
	int	pps_unit;
	struct	ppb_device pps_dev;	
	pps_params_t	ppsparam;
	pps_info_t	ppsinfo;
} *softc[NPPS];

static int ppscap =
	PPS_CAPTUREASSERT |
	PPS_HARDPPSONASSERT | 
	PPS_OFFSETASSERT | 
	PPS_ECHOASSERT |
	PPS_TSFMT_TSPEC;

static int npps;

/*
 * Make ourselves visible as a ppbus driver
 */

static struct ppb_device	*ppsprobe(struct ppb_data *ppb);
static int			ppsattach(struct ppb_device *dev);
static void			ppsintr(int unit);
static void			pps_drvinit(void *unused);

static struct ppb_driver ppsdriver = {
    ppsprobe, ppsattach, PPS_NAME
};

DATA_SET(ppbdriver_set, ppsdriver);

static	d_open_t	ppsopen;
static	d_close_t	ppsclose;
static	d_ioctl_t	ppsioctl;

#define CDEV_MAJOR 89
static struct cdevsw pps_cdevsw = 
	{ ppsopen,	ppsclose,	noread,		nowrite,
	  ppsioctl,	nullstop,	nullreset,	nodevtotty,
	  seltrue,	nommap,		nostrat,	PPS_NAME,
	  NULL,		-1 };

static struct ppb_device *
ppsprobe(struct ppb_data *ppb)
{
	struct pps_data *sc;

	sc = (struct pps_data *) malloc(sizeof(struct pps_data),
							M_TEMP, M_NOWAIT);
	if (!sc) {
		printf(PPS_NAME ": cannot malloc!\n");
		return (0);
	}
	bzero(sc, sizeof(struct pps_data));

	softc[npps] = sc;

	sc->pps_unit = npps++;

	sc->pps_dev.id_unit = sc->pps_unit;
	sc->pps_dev.ppb = ppb;
	sc->pps_dev.name = ppsdriver.name;
	sc->pps_dev.intr = ppsintr;

	return (&sc->pps_dev);
}

static int
ppsattach(struct ppb_device *dev)
{
	/*
	 * Report ourselves
	 */
	printf(PPS_NAME "%d: <Pulse per second Timing Interface> on ppbus %d\n",
	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);

#ifdef DEVFS
	devfs_add_devswf(&pps_cdevsw,
		dev->id_unit, DV_CHR,
		UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", dev->id_unit);
#endif

	return (1);
}

static	int
ppsopen(dev_t dev, int flags, int fmt, struct proc *p)
{
	struct pps_data *sc;
	u_int unit = minor(dev);

	if ((unit >= npps))
		return (ENXIO);

	sc = softc[unit];

	if (ppb_request_bus(&sc->pps_dev, PPB_WAIT|PPB_INTR))
		return (EINTR);

	ppb_wctr(&sc->pps_dev, IRQENABLE);

	return(0);
}

static	int
ppsclose(dev_t dev, int flags, int fmt, struct proc *p)
{
	struct pps_data *sc = softc[minor(dev)];

	sc->ppsparam.mode = 0;
	ppb_release_bus(&sc->pps_dev);
	return(0);
}

static void
ppsintr(int unit)
{
	struct pps_data *sc = softc[unit];
	struct timespec tc;
	struct timeval tv;

	nanotime(&tc);
	if (!(ppb_rstr(&sc->pps_dev) & nACK))
		return;
	if (sc->ppsparam.mode & PPS_ECHOASSERT) 
		ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED);
	if (sc->ppsparam.mode & PPS_OFFSETASSERT) {
		timespecadd(&tc, &sc->ppsparam.assert_offset);
		if (tc.tv_nsec < 0) {
			tc.tv_sec--;
			tc.tv_nsec += 1000000000;
		}
	}
	sc->ppsinfo.assert_timestamp = tc;
	sc->ppsinfo.assert_sequence++;
	if (sc->ppsparam.mode & PPS_HARDPPSONASSERT) {
		tv.tv_sec = tc.tv_sec;
		tv.tv_usec = tc.tv_nsec / 1000;
		hardpps(&tv, tv.tv_usec);
	}
	if (sc->ppsparam.mode & PPS_ECHOASSERT) 
		ppb_wctr(&sc->pps_dev, IRQENABLE);
}

static int
ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	struct pps_data *sc = softc[minor(dev)];

	return (std_pps_ioctl(cmd, data, &sc->ppsparam, &sc->ppsinfo, ppscap));
}

static pps_devsw_installed = 0;

static void
pps_drvinit(void *unused)
{
	dev_t dev;

	if( ! pps_devsw_installed ) {
		dev = makedev(CDEV_MAJOR, 0);
		cdevsw_add(&dev, &pps_cdevsw, NULL);
		pps_devsw_installed = 1;
    	}
}

SYSINIT(ppsdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pps_drvinit,NULL)
OpenPOWER on IntegriCloud