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