| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * ---------------------------------------------------------------------------- | |
| 3 | * "THE BEER-WARE LICENSE" (Revision 42): | |
| 4 | * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you | |
| 5 | * can do whatever you want with this stuff. If we meet some day, and you think | |
| 6 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp | |
| 7 | * ---------------------------------------------------------------------------- | |
| 8 | * | |
| 9 | * $FreeBSD: src/sys/dev/ppbus/pps.c,v 1.24.2.1 2000/05/24 00:20:57 n_hibma Exp $ | |
| 1f7ab7c9 | 10 | * $DragonFly: src/sys/dev/misc/pps/pps.c,v 1.17 2006/10/25 20:55:55 dillon Exp $ |
| 984263bc MD |
11 | * |
| 12 | * This driver implements a draft-mogul-pps-api-02.txt PPS source. | |
| 13 | * | |
| 14 | * The input pin is pin#10 | |
| 15 | * The echo output pin is pin#14 | |
| 16 | * | |
| 17 | */ | |
| 18 | ||
| 1f2de5d4 MD |
19 | #include "use_pps.h" |
| 20 | ||
| 984263bc MD |
21 | #include <sys/param.h> |
| 22 | #include <sys/kernel.h> | |
| 23 | #include <sys/systm.h> | |
| fef8985e MD |
24 | #include <sys/conf.h> |
| 25 | #include <sys/device.h> | |
| 984263bc MD |
26 | #include <sys/module.h> |
| 27 | #include <sys/bus.h> | |
| 984263bc | 28 | #include <sys/timepps.h> |
| 984263bc MD |
29 | #include <sys/rman.h> |
| 30 | ||
| 1f2de5d4 | 31 | #include <bus/ppbus/ppbconf.h> |
| 1f2de5d4 | 32 | #include <bus/ppbus/ppbio.h> |
| 984263bc | 33 | |
| 1f7ab7c9 MD |
34 | #include "ppbus_if.h" |
| 35 | ||
| 984263bc MD |
36 | #define PPS_NAME "pps" /* our official name */ |
| 37 | ||
| 38 | struct pps_data { | |
| 39 | int pps_open; | |
| 40 | struct ppb_device pps_dev; | |
| 41 | struct pps_state pps; | |
| 42 | ||
| 43 | struct resource *intr_resource; /* interrupt resource */ | |
| 44 | void *intr_cookie; /* interrupt registration cookie */ | |
| 45 | }; | |
| 46 | ||
| 47 | static void ppsintr(void *arg); | |
| 48 | ||
| 49 | #define DEVTOSOFTC(dev) \ | |
| 50 | ((struct pps_data *)device_get_softc(dev)) | |
| 51 | #define UNITOSOFTC(unit) \ | |
| 52 | ((struct pps_data *)devclass_get_softc(pps_devclass, (unit))) | |
| 53 | #define UNITODEVICE(unit) \ | |
| 54 | (devclass_get_device(pps_devclass, (unit))) | |
| 55 | ||
| 56 | static devclass_t pps_devclass; | |
| 57 | ||
| 58 | static d_open_t ppsopen; | |
| 59 | static d_close_t ppsclose; | |
| 60 | static d_ioctl_t ppsioctl; | |
| 61 | ||
| 62 | #define CDEV_MAJOR 89 | |
| fef8985e MD |
63 | static struct dev_ops pps_ops = { |
| 64 | { PPS_NAME, CDEV_MAJOR, 0 }, | |
| 65 | .d_open = ppsopen, | |
| 66 | .d_close = ppsclose, | |
| 67 | .d_ioctl = ppsioctl, | |
| 984263bc MD |
68 | }; |
| 69 | ||
| 984263bc MD |
70 | static int |
| 71 | ppsprobe(device_t ppsdev) | |
| 72 | { | |
| 73 | struct pps_data *sc; | |
| 984263bc MD |
74 | |
| 75 | sc = DEVTOSOFTC(ppsdev); | |
| 76 | bzero(sc, sizeof(struct pps_data)); | |
| 77 | ||
| 984263bc MD |
78 | device_set_desc(ppsdev, "Pulse per second Timing Interface"); |
| 79 | ||
| 80 | sc->pps.ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; | |
| 81 | pps_init(&sc->pps); | |
| 82 | return (0); | |
| 83 | } | |
| 84 | ||
| 85 | static int | |
| d471ee7d | 86 | ppsattach(device_t ppsdev) |
| 984263bc | 87 | { |
| d471ee7d MD |
88 | struct pps_data *sc = DEVTOSOFTC(ppsdev); |
| 89 | device_t ppbus = device_get_parent(ppsdev); | |
| 90 | int irq; | |
| e4c9c0c8 | 91 | int unit; |
| d471ee7d | 92 | int zero = 0; |
| 984263bc MD |
93 | |
| 94 | /* retrieve the ppbus irq */ | |
| d471ee7d | 95 | BUS_READ_IVAR(ppbus, ppsdev, PPBUS_IVAR_IRQ, &irq); |
| 984263bc MD |
96 | |
| 97 | if (irq > 0) { | |
| 98 | /* declare our interrupt handler */ | |
| d471ee7d | 99 | sc->intr_resource = bus_alloc_resource(ppsdev, SYS_RES_IRQ, |
| 984263bc MD |
100 | &zero, irq, irq, 1, RF_SHAREABLE); |
| 101 | } | |
| 102 | /* interrupts seem mandatory */ | |
| 103 | if (sc->intr_resource == 0) | |
| 104 | return (ENXIO); | |
| 105 | ||
| e4c9c0c8 | 106 | unit = device_get_unit(ppsdev); |
| 3e82b46c MD |
107 | make_dev(&pps_ops, unit, UID_ROOT, GID_WHEEL, |
| 108 | 0644, PPS_NAME "%d", unit); | |
| 984263bc MD |
109 | return (0); |
| 110 | } | |
| 111 | ||
| 112 | static int | |
| fef8985e | 113 | ppsopen(struct dev_open_args *ap) |
| 984263bc | 114 | { |
| b13267a5 | 115 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
116 | u_int unit = minor(dev); |
| 117 | struct pps_data *sc = UNITOSOFTC(unit); | |
| 118 | device_t ppsdev = UNITODEVICE(unit); | |
| 119 | device_t ppbus = device_get_parent(ppsdev); | |
| 120 | int error; | |
| 121 | ||
| 122 | if (!sc->pps_open) { | |
| 123 | if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) | |
| 124 | return (EINTR); | |
| 125 | ||
| 126 | /* attach the interrupt handler */ | |
| 127 | if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource, | |
| ee61f228 | 128 | 0, ppsintr, ppsdev, |
| e9cb6d99 | 129 | &sc->intr_cookie, NULL))) { |
| 984263bc MD |
130 | ppb_release_bus(ppbus, ppsdev); |
| 131 | return (error); | |
| 132 | } | |
| 133 | ||
| 134 | ppb_wctr(ppbus, 0); | |
| 135 | ppb_wctr(ppbus, IRQENABLE); | |
| 136 | sc->pps_open = 1; | |
| 137 | } | |
| 138 | ||
| 139 | return(0); | |
| 140 | } | |
| 141 | ||
| 142 | static int | |
| fef8985e | 143 | ppsclose(struct dev_close_args *ap) |
| 984263bc | 144 | { |
| b13267a5 | 145 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
146 | u_int unit = minor(dev); |
| 147 | struct pps_data *sc = UNITOSOFTC(unit); | |
| 148 | device_t ppsdev = UNITODEVICE(unit); | |
| 149 | device_t ppbus = device_get_parent(ppsdev); | |
| 150 | ||
| 151 | sc->pps.ppsparam.mode = 0; /* PHK ??? */ | |
| 152 | ||
| 153 | ppb_wdtr(ppbus, 0); | |
| 154 | ppb_wctr(ppbus, 0); | |
| 155 | ||
| 156 | /* Note: the interrupt handler is automatically detached */ | |
| 157 | ppb_release_bus(ppbus, ppsdev); | |
| 158 | sc->pps_open = 0; | |
| 159 | return(0); | |
| 160 | } | |
| 161 | ||
| 162 | static void | |
| 163 | ppsintr(void *arg) | |
| 164 | { | |
| 165 | device_t ppsdev = (device_t)arg; | |
| 166 | device_t ppbus = device_get_parent(ppsdev); | |
| 167 | struct pps_data *sc = DEVTOSOFTC(ppsdev); | |
| 88c4d2f6 | 168 | sysclock_t count; |
| 984263bc | 169 | |
| 044ee7c4 | 170 | count = sys_cputimer->count(); |
| 984263bc MD |
171 | if (!(ppb_rstr(ppbus) & nACK)) |
| 172 | return; | |
| 173 | if (sc->pps.ppsparam.mode & PPS_ECHOASSERT) | |
| 174 | ppb_wctr(ppbus, IRQENABLE | AUTOFEED); | |
| 88c4d2f6 | 175 | pps_event(&sc->pps, count, PPS_CAPTUREASSERT); |
| 984263bc MD |
176 | if (sc->pps.ppsparam.mode & PPS_ECHOASSERT) |
| 177 | ppb_wctr(ppbus, IRQENABLE); | |
| 178 | } | |
| 179 | ||
| 180 | static int | |
| fef8985e | 181 | ppsioctl(struct dev_ioctl_args *ap) |
| 984263bc | 182 | { |
| b13267a5 | 183 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
184 | u_int unit = minor(dev); |
| 185 | struct pps_data *sc = UNITOSOFTC(unit); | |
| 186 | ||
| fef8985e | 187 | return (pps_ioctl(ap->a_cmd, ap->a_data, &sc->pps)); |
| 984263bc MD |
188 | } |
| 189 | ||
| 39b5d600 MD |
190 | /* |
| 191 | * Becuase pps is a static device under any attached ppbus, and not scanned | |
| 192 | * by the ppbus, we need an identify function to create the device. | |
| 193 | */ | |
| 984263bc MD |
194 | static device_method_t pps_methods[] = { |
| 195 | /* device interface */ | |
| 39b5d600 | 196 | DEVMETHOD(device_identify, bus_generic_identify), |
| 984263bc MD |
197 | DEVMETHOD(device_probe, ppsprobe), |
| 198 | DEVMETHOD(device_attach, ppsattach), | |
| 199 | ||
| 200 | { 0, 0 } | |
| 201 | }; | |
| 202 | ||
| 203 | static driver_t pps_driver = { | |
| 204 | PPS_NAME, | |
| 205 | pps_methods, | |
| 206 | sizeof(struct pps_data), | |
| 207 | }; | |
| 208 | DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); |