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 * $DragonFly: src/sys/dev/misc/pps/pps.c,v 1.4 2003/07/21 07:57:41 dillon Exp $ 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 19 #include <sys/param.h> 20 #include <sys/kernel.h> 21 #include <sys/systm.h> 22 #include <sys/module.h> 23 #include <sys/bus.h> 24 #include <sys/conf.h> 25 #include <sys/timepps.h> 26 #include <machine/bus.h> 27 #include <machine/resource.h> 28 #include <sys/rman.h> 29 30 #include <dev/ppbus/ppbconf.h> 31 #include "ppbus_if.h" 32 #include <dev/ppbus/ppbio.h> 33 #include "pps.h" 34 35 #define PPS_NAME "pps" /* our official name */ 36 37 struct pps_data { 38 int pps_open; 39 struct ppb_device pps_dev; 40 struct pps_state pps; 41 42 struct resource *intr_resource; /* interrupt resource */ 43 void *intr_cookie; /* interrupt registration cookie */ 44 }; 45 46 static void ppsintr(void *arg); 47 48 #define DEVTOSOFTC(dev) \ 49 ((struct pps_data *)device_get_softc(dev)) 50 #define UNITOSOFTC(unit) \ 51 ((struct pps_data *)devclass_get_softc(pps_devclass, (unit))) 52 #define UNITODEVICE(unit) \ 53 (devclass_get_device(pps_devclass, (unit))) 54 55 static devclass_t pps_devclass; 56 57 static d_open_t ppsopen; 58 static d_close_t ppsclose; 59 static d_ioctl_t ppsioctl; 60 61 #define CDEV_MAJOR 89 62 static struct cdevsw pps_cdevsw = { 63 /* name */ PPS_NAME, 64 /* maj */ CDEV_MAJOR, 65 /* flags */ 0, 66 /* port */ NULL, 67 /* autoq */ 0, 68 69 /* open */ ppsopen, 70 /* close */ ppsclose, 71 /* read */ noread, 72 /* write */ nowrite, 73 /* ioctl */ ppsioctl, 74 /* poll */ nopoll, 75 /* mmap */ nommap, 76 /* strategy */ nostrategy, 77 /* dump */ nodump, 78 /* psize */ nopsize 79 }; 80 81 static void 82 ppsidentify(driver_t *driver, device_t parent) 83 { 84 85 BUS_ADD_CHILD(parent, 0, PPS_NAME, 0); 86 } 87 88 static int 89 ppsprobe(device_t ppsdev) 90 { 91 struct pps_data *sc; 92 dev_t dev; 93 int unit; 94 95 sc = DEVTOSOFTC(ppsdev); 96 bzero(sc, sizeof(struct pps_data)); 97 98 unit = device_get_unit(ppsdev); 99 dev = make_dev(&pps_cdevsw, unit, 100 UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%d", unit); 101 102 device_set_desc(ppsdev, "Pulse per second Timing Interface"); 103 104 sc->pps.ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 105 pps_init(&sc->pps); 106 return (0); 107 } 108 109 static int 110 ppsattach(device_t dev) 111 { 112 struct pps_data *sc = DEVTOSOFTC(dev); 113 device_t ppbus = device_get_parent(dev); 114 int irq, zero = 0; 115 116 /* retrieve the ppbus irq */ 117 BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq); 118 119 if (irq > 0) { 120 /* declare our interrupt handler */ 121 sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ, 122 &zero, irq, irq, 1, RF_SHAREABLE); 123 } 124 /* interrupts seem mandatory */ 125 if (sc->intr_resource == 0) 126 return (ENXIO); 127 128 return (0); 129 } 130 131 static int 132 ppsopen(dev_t dev, int flags, int fmt, struct thread *td) 133 { 134 u_int unit = minor(dev); 135 struct pps_data *sc = UNITOSOFTC(unit); 136 device_t ppsdev = UNITODEVICE(unit); 137 device_t ppbus = device_get_parent(ppsdev); 138 int error; 139 140 if (!sc->pps_open) { 141 if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) 142 return (EINTR); 143 144 /* attach the interrupt handler */ 145 if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource, 146 INTR_TYPE_TTY, ppsintr, ppsdev, 147 &sc->intr_cookie))) { 148 ppb_release_bus(ppbus, ppsdev); 149 return (error); 150 } 151 152 ppb_wctr(ppbus, 0); 153 ppb_wctr(ppbus, IRQENABLE); 154 sc->pps_open = 1; 155 } 156 157 return(0); 158 } 159 160 static int 161 ppsclose(dev_t dev, int flags, int fmt, struct thread *td) 162 { 163 u_int unit = minor(dev); 164 struct pps_data *sc = UNITOSOFTC(unit); 165 device_t ppsdev = UNITODEVICE(unit); 166 device_t ppbus = device_get_parent(ppsdev); 167 168 sc->pps.ppsparam.mode = 0; /* PHK ??? */ 169 170 ppb_wdtr(ppbus, 0); 171 ppb_wctr(ppbus, 0); 172 173 /* Note: the interrupt handler is automatically detached */ 174 ppb_release_bus(ppbus, ppsdev); 175 sc->pps_open = 0; 176 return(0); 177 } 178 179 static void 180 ppsintr(void *arg) 181 { 182 device_t ppsdev = (device_t)arg; 183 device_t ppbus = device_get_parent(ppsdev); 184 struct pps_data *sc = DEVTOSOFTC(ppsdev); 185 struct timecounter *tc; 186 unsigned count; 187 188 tc = timecounter; 189 count = timecounter->tc_get_timecount(tc); 190 if (!(ppb_rstr(ppbus) & nACK)) 191 return; 192 if (sc->pps.ppsparam.mode & PPS_ECHOASSERT) 193 ppb_wctr(ppbus, IRQENABLE | AUTOFEED); 194 pps_event(&sc->pps, tc, count, PPS_CAPTUREASSERT); 195 if (sc->pps.ppsparam.mode & PPS_ECHOASSERT) 196 ppb_wctr(ppbus, IRQENABLE); 197 } 198 199 static int 200 ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td) 201 { 202 u_int unit = minor(dev); 203 struct pps_data *sc = UNITOSOFTC(unit); 204 205 return (pps_ioctl(cmd, data, &sc->pps)); 206 } 207 208 static device_method_t pps_methods[] = { 209 /* device interface */ 210 DEVMETHOD(device_identify, ppsidentify), 211 DEVMETHOD(device_probe, ppsprobe), 212 DEVMETHOD(device_attach, ppsattach), 213 214 { 0, 0 } 215 }; 216 217 static driver_t pps_driver = { 218 PPS_NAME, 219 pps_methods, 220 sizeof(struct pps_data), 221 }; 222 DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 223