summaryrefslogtreecommitdiffstats
path: root/sys/dev/ppbus/pps.c
blob: 5279f113e8b9d3454fd2fb1c558bf2fb2f7438f6 (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
/*
 * ----------------------------------------------------------------------------
 * "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.19 1999/05/31 11:25:00 phk 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 <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/timepps.h>
#include <sys/malloc.h>

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

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

struct pps_data {
	int	pps_open;
	struct	ppb_device pps_dev;	
	struct	pps_state pps;
};

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(struct ppb_device *ppd);

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 = {
	/* open */	ppsopen,
	/* close */	ppsclose,
	/* read */	noread,
	/* write */	nowrite,
	/* ioctl */	ppsioctl,
	/* stop */	nostop,
	/* reset */	noreset,
	/* devtotty */	nodevtotty,
	/* poll */	nopoll,
	/* mmap */	nommap,
	/* strategy */	nostrategy,
	/* name */	PPS_NAME,
	/* parms */	noparms,
	/* maj */	CDEV_MAJOR,
	/* dump */	nodump,
	/* psize */	nopsize,
	/* flags */	0,
	/* maxio */	0,
	/* bmaj */	-1
};


static struct ppb_device *
ppsprobe(struct ppb_data *ppb)
{
	struct pps_data *sc;
	static int once;
	dev_t dev;

	if (!once++)
		cdevsw_add(&pps_cdevsw);

	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));

	dev = make_dev(&pps_cdevsw, npps,
	    UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%d", npps);

	dev->si_drv1 = sc;

	sc->pps_dev.id_unit = npps++;
	sc->pps_dev.ppb = ppb;
	sc->pps_dev.name = ppsdriver.name;
	sc->pps_dev.bintr = ppsintr;
	sc->pps_dev.drv1 = sc;

	sc->pps.ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
	pps_init(&sc->pps);
	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);

	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 = dev->si_drv1;

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

		ppb_wctr(&sc->pps_dev, 0);
		ppb_wctr(&sc->pps_dev, IRQENABLE);
		sc->pps_open = 1;
	}

	return(0);
}

static	int
ppsclose(dev_t dev, int flags, int fmt, struct proc *p)
{
	struct pps_data *sc = dev->si_drv1;

	sc->pps.ppsparam.mode = 0;	/* PHK ??? */

	ppb_wdtr(&sc->pps_dev, 0);
	ppb_wctr(&sc->pps_dev, 0);

	ppb_release_bus(&sc->pps_dev);
	sc->pps_open = 0;
	return(0);
}

static void
ppsintr(struct ppb_device *ppd)
{
	struct pps_data *sc = ppd->drv1;
	struct timecounter *tc;
	unsigned count;

	tc = timecounter;
	count = timecounter->tc_get_timecount(tc);
	if (!(ppb_rstr(&sc->pps_dev) & nACK))
		return;
	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT) 
		ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED);
	pps_event(&sc->pps, tc, count, PPS_CAPTUREASSERT);
	if (sc->pps.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 = dev->si_drv1;

	return (pps_ioctl(cmd, data, &sc->pps));
}

OpenPOWER on IntegriCloud