1d94ba80eSRichard Cochran /* 2d94ba80eSRichard Cochran * PTP 1588 clock support - character device implementation. 3d94ba80eSRichard Cochran * 4d94ba80eSRichard Cochran * Copyright (C) 2010 OMICRON electronics GmbH 5d94ba80eSRichard Cochran * 6d94ba80eSRichard Cochran * This program is free software; you can redistribute it and/or modify 7d94ba80eSRichard Cochran * it under the terms of the GNU General Public License as published by 8d94ba80eSRichard Cochran * the Free Software Foundation; either version 2 of the License, or 9d94ba80eSRichard Cochran * (at your option) any later version. 10d94ba80eSRichard Cochran * 11d94ba80eSRichard Cochran * This program is distributed in the hope that it will be useful, 12d94ba80eSRichard Cochran * but WITHOUT ANY WARRANTY; without even the implied warranty of 13d94ba80eSRichard Cochran * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14d94ba80eSRichard Cochran * GNU General Public License for more details. 15d94ba80eSRichard Cochran * 16d94ba80eSRichard Cochran * You should have received a copy of the GNU General Public License 17d94ba80eSRichard Cochran * along with this program; if not, write to the Free Software 18d94ba80eSRichard Cochran * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19d94ba80eSRichard Cochran */ 20d94ba80eSRichard Cochran #include <linux/module.h> 21d94ba80eSRichard Cochran #include <linux/posix-clock.h> 22d94ba80eSRichard Cochran #include <linux/poll.h> 23d94ba80eSRichard Cochran #include <linux/sched.h> 24c7ec0badSRichard Cochran #include <linux/slab.h> 25719f1aa4SChristopher S. Hall #include <linux/timekeeping.h> 26d94ba80eSRichard Cochran 27d94ba80eSRichard Cochran #include "ptp_private.h" 28d94ba80eSRichard Cochran 296092315dSRichard Cochran static int ptp_disable_pinfunc(struct ptp_clock_info *ops, 306092315dSRichard Cochran enum ptp_pin_function func, unsigned int chan) 316092315dSRichard Cochran { 326092315dSRichard Cochran struct ptp_clock_request rq; 336092315dSRichard Cochran int err = 0; 346092315dSRichard Cochran 356092315dSRichard Cochran memset(&rq, 0, sizeof(rq)); 366092315dSRichard Cochran 376092315dSRichard Cochran switch (func) { 386092315dSRichard Cochran case PTP_PF_NONE: 396092315dSRichard Cochran break; 406092315dSRichard Cochran case PTP_PF_EXTTS: 416092315dSRichard Cochran rq.type = PTP_CLK_REQ_EXTTS; 426092315dSRichard Cochran rq.extts.index = chan; 436092315dSRichard Cochran err = ops->enable(ops, &rq, 0); 446092315dSRichard Cochran break; 456092315dSRichard Cochran case PTP_PF_PEROUT: 466092315dSRichard Cochran rq.type = PTP_CLK_REQ_PEROUT; 476092315dSRichard Cochran rq.perout.index = chan; 486092315dSRichard Cochran err = ops->enable(ops, &rq, 0); 496092315dSRichard Cochran break; 506092315dSRichard Cochran case PTP_PF_PHYSYNC: 516092315dSRichard Cochran break; 526092315dSRichard Cochran default: 536092315dSRichard Cochran return -EINVAL; 546092315dSRichard Cochran } 556092315dSRichard Cochran 566092315dSRichard Cochran return err; 576092315dSRichard Cochran } 586092315dSRichard Cochran 596092315dSRichard Cochran int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, 606092315dSRichard Cochran enum ptp_pin_function func, unsigned int chan) 616092315dSRichard Cochran { 626092315dSRichard Cochran struct ptp_clock_info *info = ptp->info; 636092315dSRichard Cochran struct ptp_pin_desc *pin1 = NULL, *pin2 = &info->pin_config[pin]; 646092315dSRichard Cochran unsigned int i; 656092315dSRichard Cochran 666092315dSRichard Cochran /* Check to see if any other pin previously had this function. */ 676092315dSRichard Cochran for (i = 0; i < info->n_pins; i++) { 686092315dSRichard Cochran if (info->pin_config[i].func == func && 696092315dSRichard Cochran info->pin_config[i].chan == chan) { 706092315dSRichard Cochran pin1 = &info->pin_config[i]; 716092315dSRichard Cochran break; 726092315dSRichard Cochran } 736092315dSRichard Cochran } 746092315dSRichard Cochran if (pin1 && i == pin) 756092315dSRichard Cochran return 0; 766092315dSRichard Cochran 776092315dSRichard Cochran /* Check the desired function and channel. */ 786092315dSRichard Cochran switch (func) { 796092315dSRichard Cochran case PTP_PF_NONE: 806092315dSRichard Cochran break; 816092315dSRichard Cochran case PTP_PF_EXTTS: 826092315dSRichard Cochran if (chan >= info->n_ext_ts) 836092315dSRichard Cochran return -EINVAL; 846092315dSRichard Cochran break; 856092315dSRichard Cochran case PTP_PF_PEROUT: 866092315dSRichard Cochran if (chan >= info->n_per_out) 876092315dSRichard Cochran return -EINVAL; 886092315dSRichard Cochran break; 896092315dSRichard Cochran case PTP_PF_PHYSYNC: 9072df7a72SStefan Sørensen if (chan != 0) 916092315dSRichard Cochran return -EINVAL; 926092315dSRichard Cochran default: 936092315dSRichard Cochran return -EINVAL; 946092315dSRichard Cochran } 956092315dSRichard Cochran 966092315dSRichard Cochran if (info->verify(info, pin, func, chan)) { 976092315dSRichard Cochran pr_err("driver cannot use function %u on pin %u\n", func, chan); 986092315dSRichard Cochran return -EOPNOTSUPP; 996092315dSRichard Cochran } 1006092315dSRichard Cochran 1016092315dSRichard Cochran /* Disable whatever function was previously assigned. */ 1026092315dSRichard Cochran if (pin1) { 1036092315dSRichard Cochran ptp_disable_pinfunc(info, func, chan); 1046092315dSRichard Cochran pin1->func = PTP_PF_NONE; 1056092315dSRichard Cochran pin1->chan = 0; 1066092315dSRichard Cochran } 1076092315dSRichard Cochran ptp_disable_pinfunc(info, pin2->func, pin2->chan); 1086092315dSRichard Cochran pin2->func = func; 1096092315dSRichard Cochran pin2->chan = chan; 1106092315dSRichard Cochran 1116092315dSRichard Cochran return 0; 1126092315dSRichard Cochran } 1136092315dSRichard Cochran 114d94ba80eSRichard Cochran int ptp_open(struct posix_clock *pc, fmode_t fmode) 115d94ba80eSRichard Cochran { 116d94ba80eSRichard Cochran return 0; 117d94ba80eSRichard Cochran } 118d94ba80eSRichard Cochran 119d94ba80eSRichard Cochran long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) 120d94ba80eSRichard Cochran { 121d94ba80eSRichard Cochran struct ptp_clock_caps caps; 122d94ba80eSRichard Cochran struct ptp_clock_request req; 123c3484c27SRichard Cochran struct ptp_sys_offset *sysoff = NULL; 124719f1aa4SChristopher S. Hall struct ptp_sys_offset_precise precise_offset; 1256092315dSRichard Cochran struct ptp_pin_desc pd; 126d94ba80eSRichard Cochran struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 127d94ba80eSRichard Cochran struct ptp_clock_info *ops = ptp->info; 128215b13ddSRichard Cochran struct ptp_clock_time *pct; 129e13cfcb0SRichard Cochran struct timespec64 ts; 130719f1aa4SChristopher S. Hall struct system_device_crosststamp xtstamp; 131d94ba80eSRichard Cochran int enable, err = 0; 1326092315dSRichard Cochran unsigned int i, pin_index; 133d94ba80eSRichard Cochran 134d94ba80eSRichard Cochran switch (cmd) { 135d94ba80eSRichard Cochran 136d94ba80eSRichard Cochran case PTP_CLOCK_GETCAPS: 137d94ba80eSRichard Cochran memset(&caps, 0, sizeof(caps)); 138d94ba80eSRichard Cochran caps.max_adj = ptp->info->max_adj; 139d94ba80eSRichard Cochran caps.n_alarm = ptp->info->n_alarm; 140d94ba80eSRichard Cochran caps.n_ext_ts = ptp->info->n_ext_ts; 141d94ba80eSRichard Cochran caps.n_per_out = ptp->info->n_per_out; 142d94ba80eSRichard Cochran caps.pps = ptp->info->pps; 1436092315dSRichard Cochran caps.n_pins = ptp->info->n_pins; 144719f1aa4SChristopher S. Hall caps.cross_timestamping = ptp->info->getcrosststamp != NULL; 145e23ef227SDan Carpenter if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) 146e23ef227SDan Carpenter err = -EFAULT; 147d94ba80eSRichard Cochran break; 148d94ba80eSRichard Cochran 149d94ba80eSRichard Cochran case PTP_EXTTS_REQUEST: 150d94ba80eSRichard Cochran if (copy_from_user(&req.extts, (void __user *)arg, 151d94ba80eSRichard Cochran sizeof(req.extts))) { 152d94ba80eSRichard Cochran err = -EFAULT; 153d94ba80eSRichard Cochran break; 154d94ba80eSRichard Cochran } 155d94ba80eSRichard Cochran if (req.extts.index >= ops->n_ext_ts) { 156d94ba80eSRichard Cochran err = -EINVAL; 157d94ba80eSRichard Cochran break; 158d94ba80eSRichard Cochran } 159d94ba80eSRichard Cochran req.type = PTP_CLK_REQ_EXTTS; 160d94ba80eSRichard Cochran enable = req.extts.flags & PTP_ENABLE_FEATURE ? 1 : 0; 161d94ba80eSRichard Cochran err = ops->enable(ops, &req, enable); 162d94ba80eSRichard Cochran break; 163d94ba80eSRichard Cochran 164d94ba80eSRichard Cochran case PTP_PEROUT_REQUEST: 165d94ba80eSRichard Cochran if (copy_from_user(&req.perout, (void __user *)arg, 166d94ba80eSRichard Cochran sizeof(req.perout))) { 167d94ba80eSRichard Cochran err = -EFAULT; 168d94ba80eSRichard Cochran break; 169d94ba80eSRichard Cochran } 170d94ba80eSRichard Cochran if (req.perout.index >= ops->n_per_out) { 171d94ba80eSRichard Cochran err = -EINVAL; 172d94ba80eSRichard Cochran break; 173d94ba80eSRichard Cochran } 174d94ba80eSRichard Cochran req.type = PTP_CLK_REQ_PEROUT; 175d94ba80eSRichard Cochran enable = req.perout.period.sec || req.perout.period.nsec; 176d94ba80eSRichard Cochran err = ops->enable(ops, &req, enable); 177d94ba80eSRichard Cochran break; 178d94ba80eSRichard Cochran 179d94ba80eSRichard Cochran case PTP_ENABLE_PPS: 180d94ba80eSRichard Cochran if (!capable(CAP_SYS_TIME)) 181d94ba80eSRichard Cochran return -EPERM; 182d94ba80eSRichard Cochran req.type = PTP_CLK_REQ_PPS; 183d94ba80eSRichard Cochran enable = arg ? 1 : 0; 184d94ba80eSRichard Cochran err = ops->enable(ops, &req, enable); 185d94ba80eSRichard Cochran break; 186d94ba80eSRichard Cochran 187719f1aa4SChristopher S. Hall case PTP_SYS_OFFSET_PRECISE: 188719f1aa4SChristopher S. Hall if (!ptp->info->getcrosststamp) { 189719f1aa4SChristopher S. Hall err = -EOPNOTSUPP; 190719f1aa4SChristopher S. Hall break; 191719f1aa4SChristopher S. Hall } 192719f1aa4SChristopher S. Hall err = ptp->info->getcrosststamp(ptp->info, &xtstamp); 193719f1aa4SChristopher S. Hall if (err) 194719f1aa4SChristopher S. Hall break; 195719f1aa4SChristopher S. Hall 19602a9079cSVlad Tsyrklevich memset(&precise_offset, 0, sizeof(precise_offset)); 197719f1aa4SChristopher S. Hall ts = ktime_to_timespec64(xtstamp.device); 198719f1aa4SChristopher S. Hall precise_offset.device.sec = ts.tv_sec; 199719f1aa4SChristopher S. Hall precise_offset.device.nsec = ts.tv_nsec; 200719f1aa4SChristopher S. Hall ts = ktime_to_timespec64(xtstamp.sys_realtime); 201719f1aa4SChristopher S. Hall precise_offset.sys_realtime.sec = ts.tv_sec; 202719f1aa4SChristopher S. Hall precise_offset.sys_realtime.nsec = ts.tv_nsec; 203719f1aa4SChristopher S. Hall ts = ktime_to_timespec64(xtstamp.sys_monoraw); 204719f1aa4SChristopher S. Hall precise_offset.sys_monoraw.sec = ts.tv_sec; 205719f1aa4SChristopher S. Hall precise_offset.sys_monoraw.nsec = ts.tv_nsec; 206719f1aa4SChristopher S. Hall if (copy_to_user((void __user *)arg, &precise_offset, 207719f1aa4SChristopher S. Hall sizeof(precise_offset))) 208719f1aa4SChristopher S. Hall err = -EFAULT; 209719f1aa4SChristopher S. Hall break; 210719f1aa4SChristopher S. Hall 211215b13ddSRichard Cochran case PTP_SYS_OFFSET: 2122ece068eSMuhammad Falak R Wani sysoff = memdup_user((void __user *)arg, sizeof(*sysoff)); 2132ece068eSMuhammad Falak R Wani if (IS_ERR(sysoff)) { 2142ece068eSMuhammad Falak R Wani err = PTR_ERR(sysoff); 2156756325aSDan Carpenter sysoff = NULL; 216215b13ddSRichard Cochran break; 217215b13ddSRichard Cochran } 218c3484c27SRichard Cochran if (sysoff->n_samples > PTP_MAX_SAMPLES) { 219215b13ddSRichard Cochran err = -EINVAL; 220215b13ddSRichard Cochran break; 221215b13ddSRichard Cochran } 222c3484c27SRichard Cochran pct = &sysoff->ts[0]; 223c3484c27SRichard Cochran for (i = 0; i < sysoff->n_samples; i++) { 224f696a21cSArnd Bergmann ktime_get_real_ts64(&ts); 225215b13ddSRichard Cochran pct->sec = ts.tv_sec; 226215b13ddSRichard Cochran pct->nsec = ts.tv_nsec; 227215b13ddSRichard Cochran pct++; 228e13cfcb0SRichard Cochran ptp->info->gettime64(ptp->info, &ts); 229215b13ddSRichard Cochran pct->sec = ts.tv_sec; 230215b13ddSRichard Cochran pct->nsec = ts.tv_nsec; 231215b13ddSRichard Cochran pct++; 232215b13ddSRichard Cochran } 233f696a21cSArnd Bergmann ktime_get_real_ts64(&ts); 234215b13ddSRichard Cochran pct->sec = ts.tv_sec; 235215b13ddSRichard Cochran pct->nsec = ts.tv_nsec; 236c3484c27SRichard Cochran if (copy_to_user((void __user *)arg, sysoff, sizeof(*sysoff))) 237215b13ddSRichard Cochran err = -EFAULT; 238215b13ddSRichard Cochran break; 239215b13ddSRichard Cochran 2406092315dSRichard Cochran case PTP_PIN_GETFUNC: 2416092315dSRichard Cochran if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) { 2426092315dSRichard Cochran err = -EFAULT; 2436092315dSRichard Cochran break; 2446092315dSRichard Cochran } 2456092315dSRichard Cochran pin_index = pd.index; 2466092315dSRichard Cochran if (pin_index >= ops->n_pins) { 2476092315dSRichard Cochran err = -EINVAL; 2486092315dSRichard Cochran break; 2496092315dSRichard Cochran } 2506092315dSRichard Cochran if (mutex_lock_interruptible(&ptp->pincfg_mux)) 2516092315dSRichard Cochran return -ERESTARTSYS; 2526092315dSRichard Cochran pd = ops->pin_config[pin_index]; 2536092315dSRichard Cochran mutex_unlock(&ptp->pincfg_mux); 2546092315dSRichard Cochran if (!err && copy_to_user((void __user *)arg, &pd, sizeof(pd))) 2556092315dSRichard Cochran err = -EFAULT; 2566092315dSRichard Cochran break; 2576092315dSRichard Cochran 2586092315dSRichard Cochran case PTP_PIN_SETFUNC: 2596092315dSRichard Cochran if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) { 2606092315dSRichard Cochran err = -EFAULT; 2616092315dSRichard Cochran break; 2626092315dSRichard Cochran } 2636092315dSRichard Cochran pin_index = pd.index; 2646092315dSRichard Cochran if (pin_index >= ops->n_pins) { 2656092315dSRichard Cochran err = -EINVAL; 2666092315dSRichard Cochran break; 2676092315dSRichard Cochran } 2686092315dSRichard Cochran if (mutex_lock_interruptible(&ptp->pincfg_mux)) 2696092315dSRichard Cochran return -ERESTARTSYS; 2706092315dSRichard Cochran err = ptp_set_pinfunc(ptp, pin_index, pd.func, pd.chan); 2716092315dSRichard Cochran mutex_unlock(&ptp->pincfg_mux); 2726092315dSRichard Cochran break; 2736092315dSRichard Cochran 274d94ba80eSRichard Cochran default: 275d94ba80eSRichard Cochran err = -ENOTTY; 276d94ba80eSRichard Cochran break; 277d94ba80eSRichard Cochran } 278c3484c27SRichard Cochran 279c3484c27SRichard Cochran kfree(sysoff); 280d94ba80eSRichard Cochran return err; 281d94ba80eSRichard Cochran } 282d94ba80eSRichard Cochran 283afc9a42bSAl Viro __poll_t ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait) 284d94ba80eSRichard Cochran { 285d94ba80eSRichard Cochran struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 286d94ba80eSRichard Cochran 287d94ba80eSRichard Cochran poll_wait(fp, &ptp->tsev_wq, wait); 288d94ba80eSRichard Cochran 289a9a08845SLinus Torvalds return queue_cnt(&ptp->tsevq) ? EPOLLIN : 0; 290d94ba80eSRichard Cochran } 291d94ba80eSRichard Cochran 292c7ec0badSRichard Cochran #define EXTTS_BUFSIZE (PTP_BUF_TIMESTAMPS * sizeof(struct ptp_extts_event)) 293c7ec0badSRichard Cochran 294d94ba80eSRichard Cochran ssize_t ptp_read(struct posix_clock *pc, 295d94ba80eSRichard Cochran uint rdflags, char __user *buf, size_t cnt) 296d94ba80eSRichard Cochran { 297d94ba80eSRichard Cochran struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 298d94ba80eSRichard Cochran struct timestamp_event_queue *queue = &ptp->tsevq; 299c7ec0badSRichard Cochran struct ptp_extts_event *event; 300d94ba80eSRichard Cochran unsigned long flags; 301d94ba80eSRichard Cochran size_t qcnt, i; 302c7ec0badSRichard Cochran int result; 303d94ba80eSRichard Cochran 304d94ba80eSRichard Cochran if (cnt % sizeof(struct ptp_extts_event) != 0) 305d94ba80eSRichard Cochran return -EINVAL; 306d94ba80eSRichard Cochran 307c7ec0badSRichard Cochran if (cnt > EXTTS_BUFSIZE) 308c7ec0badSRichard Cochran cnt = EXTTS_BUFSIZE; 309d94ba80eSRichard Cochran 310d94ba80eSRichard Cochran cnt = cnt / sizeof(struct ptp_extts_event); 311d94ba80eSRichard Cochran 312d94ba80eSRichard Cochran if (mutex_lock_interruptible(&ptp->tsevq_mux)) 313d94ba80eSRichard Cochran return -ERESTARTSYS; 314d94ba80eSRichard Cochran 315d94ba80eSRichard Cochran if (wait_event_interruptible(ptp->tsev_wq, 316d94ba80eSRichard Cochran ptp->defunct || queue_cnt(queue))) { 317d94ba80eSRichard Cochran mutex_unlock(&ptp->tsevq_mux); 318d94ba80eSRichard Cochran return -ERESTARTSYS; 319d94ba80eSRichard Cochran } 320d94ba80eSRichard Cochran 321fb5a18cfSDan Carpenter if (ptp->defunct) { 322fb5a18cfSDan Carpenter mutex_unlock(&ptp->tsevq_mux); 323d94ba80eSRichard Cochran return -ENODEV; 324fb5a18cfSDan Carpenter } 325d94ba80eSRichard Cochran 326c7ec0badSRichard Cochran event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL); 327c7ec0badSRichard Cochran if (!event) { 328c7ec0badSRichard Cochran mutex_unlock(&ptp->tsevq_mux); 329c7ec0badSRichard Cochran return -ENOMEM; 330c7ec0badSRichard Cochran } 331c7ec0badSRichard Cochran 332d94ba80eSRichard Cochran spin_lock_irqsave(&queue->lock, flags); 333d94ba80eSRichard Cochran 334d94ba80eSRichard Cochran qcnt = queue_cnt(queue); 335d94ba80eSRichard Cochran 336d94ba80eSRichard Cochran if (cnt > qcnt) 337d94ba80eSRichard Cochran cnt = qcnt; 338d94ba80eSRichard Cochran 339d94ba80eSRichard Cochran for (i = 0; i < cnt; i++) { 340d94ba80eSRichard Cochran event[i] = queue->buf[queue->head]; 341d94ba80eSRichard Cochran queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS; 342d94ba80eSRichard Cochran } 343d94ba80eSRichard Cochran 344d94ba80eSRichard Cochran spin_unlock_irqrestore(&queue->lock, flags); 345d94ba80eSRichard Cochran 346d94ba80eSRichard Cochran cnt = cnt * sizeof(struct ptp_extts_event); 347d94ba80eSRichard Cochran 348d94ba80eSRichard Cochran mutex_unlock(&ptp->tsevq_mux); 349d94ba80eSRichard Cochran 350c7ec0badSRichard Cochran result = cnt; 351fb5a18cfSDan Carpenter if (copy_to_user(buf, event, cnt)) 352c7ec0badSRichard Cochran result = -EFAULT; 353d94ba80eSRichard Cochran 354c7ec0badSRichard Cochran kfree(event); 355c7ec0badSRichard Cochran return result; 356d94ba80eSRichard Cochran } 357