pps.c revision 56455
133323Sphk/*
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 *
950477Speter * $FreeBSD: head/sys/dev/ppbus/pps.c 56455 2000-01-23 14:41:04Z peter $
1033323Sphk *
1136938Sphk * This driver implements a draft-mogul-pps-api-02.txt PPS source.
1236938Sphk *
1336938Sphk * The input pin is pin#10
1436938Sphk * The echo output pin is pin#14
1536938Sphk *
1633323Sphk */
1733323Sphk
1833323Sphk#include <sys/param.h>
1933323Sphk#include <sys/kernel.h>
2033323Sphk#include <sys/systm.h>
2155939Snsouch#include <sys/module.h>
2255939Snsouch#include <sys/bus.h>
2333323Sphk#include <sys/conf.h>
2436739Sphk#include <sys/timepps.h>
2533323Sphk#include <sys/malloc.h>
2655939Snsouch#include <machine/bus.h>
2755939Snsouch#include <machine/resource.h>
2855939Snsouch#include <sys/rman.h>
2933323Sphk
3033323Sphk#include <dev/ppbus/ppbconf.h>
3155939Snsouch#include "ppbus_if.h"
3255939Snsouch#include <dev/ppbus/ppbio.h>
3333326Sphk#include "pps.h"
3433323Sphk
3549550Sphk#define PPS_NAME	"pps"		/* our official name */
3633323Sphk
3749550Sphkstruct pps_data {
3846053Sphk	int	pps_open;
3933396Sphk	struct	ppb_device pps_dev;
4044666Sphk	struct	pps_state pps;
4155939Snsouch
4255939Snsouch	struct resource *intr_resource;	/* interrupt resource */
4355939Snsouch	void *intr_cookie;		/* interrupt registration cookie */
4449550Sphk};
4533323Sphk
4655939Snsouchstatic void	ppsintr(void *arg);
4733323Sphk
4855939Snsouch#define DEVTOSOFTC(dev) \
4955939Snsouch	((struct pps_data *)device_get_softc(dev))
5055939Snsouch#define UNITOSOFTC(unit) \
5155939Snsouch	((struct pps_data *)devclass_get_softc(pps_devclass, (unit)))
5255939Snsouch#define UNITODEVICE(unit) \
5355939Snsouch	(devclass_get_device(pps_devclass, (unit)))
5433323Sphk
5555939Snsouchstatic devclass_t pps_devclass;
5633323Sphk
5733396Sphkstatic	d_open_t	ppsopen;
5833396Sphkstatic	d_close_t	ppsclose;
5936739Sphkstatic	d_ioctl_t	ppsioctl;
6033323Sphk
6133323Sphk#define CDEV_MAJOR 89
6247625Sphkstatic struct cdevsw pps_cdevsw = {
6347625Sphk	/* open */	ppsopen,
6447625Sphk	/* close */	ppsclose,
6547625Sphk	/* read */	noread,
6647625Sphk	/* write */	nowrite,
6747625Sphk	/* ioctl */	ppsioctl,
6847625Sphk	/* poll */	nopoll,
6947625Sphk	/* mmap */	nommap,
7047625Sphk	/* strategy */	nostrategy,
7147625Sphk	/* name */	PPS_NAME,
7247625Sphk	/* maj */	CDEV_MAJOR,
7347625Sphk	/* dump */	nodump,
7447625Sphk	/* psize */	nopsize,
7547625Sphk	/* flags */	0,
7647625Sphk	/* bmaj */	-1
7747625Sphk};
7833323Sphk
7956455Speterstatic void
8056455Speterppsidentify(driver_t *driver, device_t parent)
8156455Speter{
8256455Speter
8356455Speter	BUS_ADD_CHILD(parent, 0, PPS_NAME, 0);
8456455Speter}
8556455Speter
8655939Snsouchstatic int
8755939Snsouchppsprobe(device_t ppsdev)
8833323Sphk{
8933396Sphk	struct pps_data *sc;
9049550Sphk	dev_t dev;
9155939Snsouch	int unit;
9233323Sphk
9355939Snsouch	sc = DEVTOSOFTC(ppsdev);
9433396Sphk	bzero(sc, sizeof(struct pps_data));
9533323Sphk
9655939Snsouch	unit = device_get_unit(ppsdev);
9755939Snsouch	dev = make_dev(&pps_cdevsw, unit,
9855939Snsouch	    UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%d", unit);
9933323Sphk
10055939Snsouch	device_set_desc(ppsdev, "Pulse per second Timing Interface");
10133323Sphk
10244666Sphk	sc->pps.ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
10344666Sphk	pps_init(&sc->pps);
10455939Snsouch	return (0);
10533323Sphk}
10633323Sphk
10733323Sphkstatic int
10855939Snsouchppsattach(device_t dev)
10933323Sphk{
11055939Snsouch	struct pps_data *sc = DEVTOSOFTC(dev);
11155939Snsouch	device_t ppbus = device_get_parent(dev);
11255939Snsouch	int irq, zero = 0;
11344666Sphk
11455939Snsouch	/* retrieve the ppbus irq */
11555939Snsouch	BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
11633323Sphk
11755939Snsouch	if (irq > 0) {
11855939Snsouch		/* declare our interrupt handler */
11955939Snsouch		sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
12055939Snsouch				       &zero, irq, irq, 1, RF_SHAREABLE);
12155939Snsouch	}
12255939Snsouch	/* interrupts seem mandatory */
12355939Snsouch	if (sc->intr_resource == 0)
12455939Snsouch		return (ENXIO);
12555939Snsouch
12655939Snsouch	return (0);
12733323Sphk}
12833323Sphk
12933323Sphkstatic	int
13033396Sphkppsopen(dev_t dev, int flags, int fmt, struct proc *p)
13133323Sphk{
13233323Sphk	u_int unit = minor(dev);
13355939Snsouch	struct pps_data *sc = UNITOSOFTC(unit);
13455939Snsouch	device_t ppsdev = UNITODEVICE(unit);
13555939Snsouch	device_t ppbus = device_get_parent(ppsdev);
13655939Snsouch	int error;
13733323Sphk
13846053Sphk	if (!sc->pps_open) {
13955939Snsouch		if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR))
14046053Sphk			return (EINTR);
14133323Sphk
14255939Snsouch		/* attach the interrupt handler */
14355939Snsouch		if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource,
14455939Snsouch			       INTR_TYPE_TTY, ppsintr, ppsdev,
14555939Snsouch			       &sc->intr_cookie))) {
14655939Snsouch			ppb_release_bus(ppbus, ppsdev);
14755939Snsouch			return (error);
14855939Snsouch		}
14955939Snsouch
15055939Snsouch		ppb_wctr(ppbus, 0);
15155939Snsouch		ppb_wctr(ppbus, IRQENABLE);
15246053Sphk		sc->pps_open = 1;
15346053Sphk	}
15433323Sphk
15533323Sphk	return(0);
15633323Sphk}
15733323Sphk
15833323Sphkstatic	int
15933396Sphkppsclose(dev_t dev, int flags, int fmt, struct proc *p)
16033323Sphk{
16155939Snsouch	u_int unit = minor(dev);
16255939Snsouch	struct pps_data *sc = UNITOSOFTC(unit);
16355939Snsouch	device_t ppsdev = UNITODEVICE(unit);
16455939Snsouch	device_t ppbus = device_get_parent(ppsdev);
16533323Sphk
16644666Sphk	sc->pps.ppsparam.mode = 0;	/* PHK ??? */
16743433Snsouch
16855939Snsouch	ppb_wdtr(ppbus, 0);
16955939Snsouch	ppb_wctr(ppbus, 0);
17043433Snsouch
17155939Snsouch	/* Note: the interrupt handler is automatically detached */
17255939Snsouch	ppb_release_bus(ppbus, ppsdev);
17346053Sphk	sc->pps_open = 0;
17433323Sphk	return(0);
17533323Sphk}
17633323Sphk
17733323Sphkstatic void
17855939Snsouchppsintr(void *arg)
17933323Sphk{
18055939Snsouch	device_t ppsdev = (device_t)arg;
18155939Snsouch	device_t ppbus = device_get_parent(ppsdev);
18255939Snsouch	struct pps_data *sc = DEVTOSOFTC(ppsdev);
18344666Sphk	struct timecounter *tc;
18444666Sphk	unsigned count;
18533323Sphk
18644666Sphk	tc = timecounter;
18744666Sphk	count = timecounter->tc_get_timecount(tc);
18855939Snsouch	if (!(ppb_rstr(ppbus) & nACK))
18933323Sphk		return;
19044666Sphk	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
19155939Snsouch		ppb_wctr(ppbus, IRQENABLE | AUTOFEED);
19244666Sphk	pps_event(&sc->pps, tc, count, PPS_CAPTUREASSERT);
19344666Sphk	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
19455939Snsouch		ppb_wctr(ppbus, IRQENABLE);
19533323Sphk}
19633323Sphk
19736739Sphkstatic int
19836748Sbdeppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
19933323Sphk{
20055939Snsouch	u_int unit = minor(dev);
20155939Snsouch	struct pps_data *sc = UNITOSOFTC(unit);
20233323Sphk
20344666Sphk	return (pps_ioctl(cmd, data, &sc->pps));
20433323Sphk}
20533323Sphk
20656455Speterstatic device_method_t pps_methods[] = {
20756455Speter	/* device interface */
20856455Speter	DEVMETHOD(device_identify,	ppsidentify),
20956455Speter	DEVMETHOD(device_probe,		ppsprobe),
21056455Speter	DEVMETHOD(device_attach,	ppsattach),
21156455Speter
21256455Speter	{ 0, 0 }
21356455Speter};
21456455Speter
21556455Speterstatic driver_t pps_driver = {
21656455Speter	PPS_NAME,
21756455Speter	pps_methods,
21856455Speter	sizeof(struct pps_data),
21956455Speter};
22055939SnsouchDRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0);
221