162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PTP 1588 clock support - character device implementation. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 OMICRON electronics GmbH 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/posix-clock.h> 962306a36Sopenharmony_ci#include <linux/poll.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/timekeeping.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/nospec.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "ptp_private.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic int ptp_disable_pinfunc(struct ptp_clock_info *ops, 1962306a36Sopenharmony_ci enum ptp_pin_function func, unsigned int chan) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct ptp_clock_request rq; 2262306a36Sopenharmony_ci int err = 0; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci memset(&rq, 0, sizeof(rq)); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci switch (func) { 2762306a36Sopenharmony_ci case PTP_PF_NONE: 2862306a36Sopenharmony_ci break; 2962306a36Sopenharmony_ci case PTP_PF_EXTTS: 3062306a36Sopenharmony_ci rq.type = PTP_CLK_REQ_EXTTS; 3162306a36Sopenharmony_ci rq.extts.index = chan; 3262306a36Sopenharmony_ci err = ops->enable(ops, &rq, 0); 3362306a36Sopenharmony_ci break; 3462306a36Sopenharmony_ci case PTP_PF_PEROUT: 3562306a36Sopenharmony_ci rq.type = PTP_CLK_REQ_PEROUT; 3662306a36Sopenharmony_ci rq.perout.index = chan; 3762306a36Sopenharmony_ci err = ops->enable(ops, &rq, 0); 3862306a36Sopenharmony_ci break; 3962306a36Sopenharmony_ci case PTP_PF_PHYSYNC: 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci default: 4262306a36Sopenharmony_ci return -EINVAL; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return err; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciint ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, 4962306a36Sopenharmony_ci enum ptp_pin_function func, unsigned int chan) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct ptp_clock_info *info = ptp->info; 5262306a36Sopenharmony_ci struct ptp_pin_desc *pin1 = NULL, *pin2 = &info->pin_config[pin]; 5362306a36Sopenharmony_ci unsigned int i; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* Check to see if any other pin previously had this function. */ 5662306a36Sopenharmony_ci for (i = 0; i < info->n_pins; i++) { 5762306a36Sopenharmony_ci if (info->pin_config[i].func == func && 5862306a36Sopenharmony_ci info->pin_config[i].chan == chan) { 5962306a36Sopenharmony_ci pin1 = &info->pin_config[i]; 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci if (pin1 && i == pin) 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Check the desired function and channel. */ 6762306a36Sopenharmony_ci switch (func) { 6862306a36Sopenharmony_ci case PTP_PF_NONE: 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci case PTP_PF_EXTTS: 7162306a36Sopenharmony_ci if (chan >= info->n_ext_ts) 7262306a36Sopenharmony_ci return -EINVAL; 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci case PTP_PF_PEROUT: 7562306a36Sopenharmony_ci if (chan >= info->n_per_out) 7662306a36Sopenharmony_ci return -EINVAL; 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci case PTP_PF_PHYSYNC: 7962306a36Sopenharmony_ci if (chan != 0) 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci default: 8362306a36Sopenharmony_ci return -EINVAL; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (info->verify(info, pin, func, chan)) { 8762306a36Sopenharmony_ci pr_err("driver cannot use function %u on pin %u\n", func, chan); 8862306a36Sopenharmony_ci return -EOPNOTSUPP; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Disable whatever function was previously assigned. */ 9262306a36Sopenharmony_ci if (pin1) { 9362306a36Sopenharmony_ci ptp_disable_pinfunc(info, func, chan); 9462306a36Sopenharmony_ci pin1->func = PTP_PF_NONE; 9562306a36Sopenharmony_ci pin1->chan = 0; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci ptp_disable_pinfunc(info, pin2->func, pin2->chan); 9862306a36Sopenharmony_ci pin2->func = func; 9962306a36Sopenharmony_ci pin2->chan = chan; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciint ptp_open(struct posix_clock *pc, fmode_t fmode) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cilong ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 11262306a36Sopenharmony_ci struct ptp_sys_offset_extended *extoff = NULL; 11362306a36Sopenharmony_ci struct ptp_sys_offset_precise precise_offset; 11462306a36Sopenharmony_ci struct system_device_crosststamp xtstamp; 11562306a36Sopenharmony_ci struct ptp_clock_info *ops = ptp->info; 11662306a36Sopenharmony_ci struct ptp_sys_offset *sysoff = NULL; 11762306a36Sopenharmony_ci struct ptp_system_timestamp sts; 11862306a36Sopenharmony_ci struct ptp_clock_request req; 11962306a36Sopenharmony_ci struct ptp_clock_caps caps; 12062306a36Sopenharmony_ci struct ptp_clock_time *pct; 12162306a36Sopenharmony_ci unsigned int i, pin_index; 12262306a36Sopenharmony_ci struct ptp_pin_desc pd; 12362306a36Sopenharmony_ci struct timespec64 ts; 12462306a36Sopenharmony_ci int enable, err = 0; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci switch (cmd) { 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci case PTP_CLOCK_GETCAPS: 12962306a36Sopenharmony_ci case PTP_CLOCK_GETCAPS2: 13062306a36Sopenharmony_ci memset(&caps, 0, sizeof(caps)); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci caps.max_adj = ptp->info->max_adj; 13362306a36Sopenharmony_ci caps.n_alarm = ptp->info->n_alarm; 13462306a36Sopenharmony_ci caps.n_ext_ts = ptp->info->n_ext_ts; 13562306a36Sopenharmony_ci caps.n_per_out = ptp->info->n_per_out; 13662306a36Sopenharmony_ci caps.pps = ptp->info->pps; 13762306a36Sopenharmony_ci caps.n_pins = ptp->info->n_pins; 13862306a36Sopenharmony_ci caps.cross_timestamping = ptp->info->getcrosststamp != NULL; 13962306a36Sopenharmony_ci caps.adjust_phase = ptp->info->adjphase != NULL && 14062306a36Sopenharmony_ci ptp->info->getmaxphase != NULL; 14162306a36Sopenharmony_ci if (caps.adjust_phase) 14262306a36Sopenharmony_ci caps.max_phase_adj = ptp->info->getmaxphase(ptp->info); 14362306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) 14462306a36Sopenharmony_ci err = -EFAULT; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci case PTP_EXTTS_REQUEST: 14862306a36Sopenharmony_ci case PTP_EXTTS_REQUEST2: 14962306a36Sopenharmony_ci memset(&req, 0, sizeof(req)); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (copy_from_user(&req.extts, (void __user *)arg, 15262306a36Sopenharmony_ci sizeof(req.extts))) { 15362306a36Sopenharmony_ci err = -EFAULT; 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci if (cmd == PTP_EXTTS_REQUEST2) { 15762306a36Sopenharmony_ci /* Tell the drivers to check the flags carefully. */ 15862306a36Sopenharmony_ci req.extts.flags |= PTP_STRICT_FLAGS; 15962306a36Sopenharmony_ci /* Make sure no reserved bit is set. */ 16062306a36Sopenharmony_ci if ((req.extts.flags & ~PTP_EXTTS_VALID_FLAGS) || 16162306a36Sopenharmony_ci req.extts.rsv[0] || req.extts.rsv[1]) { 16262306a36Sopenharmony_ci err = -EINVAL; 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci /* Ensure one of the rising/falling edge bits is set. */ 16662306a36Sopenharmony_ci if ((req.extts.flags & PTP_ENABLE_FEATURE) && 16762306a36Sopenharmony_ci (req.extts.flags & PTP_EXTTS_EDGES) == 0) { 16862306a36Sopenharmony_ci err = -EINVAL; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } else if (cmd == PTP_EXTTS_REQUEST) { 17262306a36Sopenharmony_ci req.extts.flags &= PTP_EXTTS_V1_VALID_FLAGS; 17362306a36Sopenharmony_ci req.extts.rsv[0] = 0; 17462306a36Sopenharmony_ci req.extts.rsv[1] = 0; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci if (req.extts.index >= ops->n_ext_ts) { 17762306a36Sopenharmony_ci err = -EINVAL; 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci req.type = PTP_CLK_REQ_EXTTS; 18162306a36Sopenharmony_ci enable = req.extts.flags & PTP_ENABLE_FEATURE ? 1 : 0; 18262306a36Sopenharmony_ci if (mutex_lock_interruptible(&ptp->pincfg_mux)) 18362306a36Sopenharmony_ci return -ERESTARTSYS; 18462306a36Sopenharmony_ci err = ops->enable(ops, &req, enable); 18562306a36Sopenharmony_ci mutex_unlock(&ptp->pincfg_mux); 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci case PTP_PEROUT_REQUEST: 18962306a36Sopenharmony_ci case PTP_PEROUT_REQUEST2: 19062306a36Sopenharmony_ci memset(&req, 0, sizeof(req)); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (copy_from_user(&req.perout, (void __user *)arg, 19362306a36Sopenharmony_ci sizeof(req.perout))) { 19462306a36Sopenharmony_ci err = -EFAULT; 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci if (cmd == PTP_PEROUT_REQUEST2) { 19862306a36Sopenharmony_ci struct ptp_perout_request *perout = &req.perout; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (perout->flags & ~PTP_PEROUT_VALID_FLAGS) { 20162306a36Sopenharmony_ci err = -EINVAL; 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * The "on" field has undefined meaning if 20662306a36Sopenharmony_ci * PTP_PEROUT_DUTY_CYCLE isn't set, we must still treat 20762306a36Sopenharmony_ci * it as reserved, which must be set to zero. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci if (!(perout->flags & PTP_PEROUT_DUTY_CYCLE) && 21062306a36Sopenharmony_ci (perout->rsv[0] || perout->rsv[1] || 21162306a36Sopenharmony_ci perout->rsv[2] || perout->rsv[3])) { 21262306a36Sopenharmony_ci err = -EINVAL; 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci if (perout->flags & PTP_PEROUT_DUTY_CYCLE) { 21662306a36Sopenharmony_ci /* The duty cycle must be subunitary. */ 21762306a36Sopenharmony_ci if (perout->on.sec > perout->period.sec || 21862306a36Sopenharmony_ci (perout->on.sec == perout->period.sec && 21962306a36Sopenharmony_ci perout->on.nsec > perout->period.nsec)) { 22062306a36Sopenharmony_ci err = -ERANGE; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci if (perout->flags & PTP_PEROUT_PHASE) { 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * The phase should be specified modulo the 22762306a36Sopenharmony_ci * period, therefore anything equal or larger 22862306a36Sopenharmony_ci * than 1 period is invalid. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci if (perout->phase.sec > perout->period.sec || 23162306a36Sopenharmony_ci (perout->phase.sec == perout->period.sec && 23262306a36Sopenharmony_ci perout->phase.nsec >= perout->period.nsec)) { 23362306a36Sopenharmony_ci err = -ERANGE; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci } else if (cmd == PTP_PEROUT_REQUEST) { 23862306a36Sopenharmony_ci req.perout.flags &= PTP_PEROUT_V1_VALID_FLAGS; 23962306a36Sopenharmony_ci req.perout.rsv[0] = 0; 24062306a36Sopenharmony_ci req.perout.rsv[1] = 0; 24162306a36Sopenharmony_ci req.perout.rsv[2] = 0; 24262306a36Sopenharmony_ci req.perout.rsv[3] = 0; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci if (req.perout.index >= ops->n_per_out) { 24562306a36Sopenharmony_ci err = -EINVAL; 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci req.type = PTP_CLK_REQ_PEROUT; 24962306a36Sopenharmony_ci enable = req.perout.period.sec || req.perout.period.nsec; 25062306a36Sopenharmony_ci if (mutex_lock_interruptible(&ptp->pincfg_mux)) 25162306a36Sopenharmony_ci return -ERESTARTSYS; 25262306a36Sopenharmony_ci err = ops->enable(ops, &req, enable); 25362306a36Sopenharmony_ci mutex_unlock(&ptp->pincfg_mux); 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci case PTP_ENABLE_PPS: 25762306a36Sopenharmony_ci case PTP_ENABLE_PPS2: 25862306a36Sopenharmony_ci memset(&req, 0, sizeof(req)); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (!capable(CAP_SYS_TIME)) 26162306a36Sopenharmony_ci return -EPERM; 26262306a36Sopenharmony_ci req.type = PTP_CLK_REQ_PPS; 26362306a36Sopenharmony_ci enable = arg ? 1 : 0; 26462306a36Sopenharmony_ci if (mutex_lock_interruptible(&ptp->pincfg_mux)) 26562306a36Sopenharmony_ci return -ERESTARTSYS; 26662306a36Sopenharmony_ci err = ops->enable(ops, &req, enable); 26762306a36Sopenharmony_ci mutex_unlock(&ptp->pincfg_mux); 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci case PTP_SYS_OFFSET_PRECISE: 27162306a36Sopenharmony_ci case PTP_SYS_OFFSET_PRECISE2: 27262306a36Sopenharmony_ci if (!ptp->info->getcrosststamp) { 27362306a36Sopenharmony_ci err = -EOPNOTSUPP; 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci err = ptp->info->getcrosststamp(ptp->info, &xtstamp); 27762306a36Sopenharmony_ci if (err) 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci memset(&precise_offset, 0, sizeof(precise_offset)); 28162306a36Sopenharmony_ci ts = ktime_to_timespec64(xtstamp.device); 28262306a36Sopenharmony_ci precise_offset.device.sec = ts.tv_sec; 28362306a36Sopenharmony_ci precise_offset.device.nsec = ts.tv_nsec; 28462306a36Sopenharmony_ci ts = ktime_to_timespec64(xtstamp.sys_realtime); 28562306a36Sopenharmony_ci precise_offset.sys_realtime.sec = ts.tv_sec; 28662306a36Sopenharmony_ci precise_offset.sys_realtime.nsec = ts.tv_nsec; 28762306a36Sopenharmony_ci ts = ktime_to_timespec64(xtstamp.sys_monoraw); 28862306a36Sopenharmony_ci precise_offset.sys_monoraw.sec = ts.tv_sec; 28962306a36Sopenharmony_ci precise_offset.sys_monoraw.nsec = ts.tv_nsec; 29062306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &precise_offset, 29162306a36Sopenharmony_ci sizeof(precise_offset))) 29262306a36Sopenharmony_ci err = -EFAULT; 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci case PTP_SYS_OFFSET_EXTENDED: 29662306a36Sopenharmony_ci case PTP_SYS_OFFSET_EXTENDED2: 29762306a36Sopenharmony_ci if (!ptp->info->gettimex64) { 29862306a36Sopenharmony_ci err = -EOPNOTSUPP; 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci extoff = memdup_user((void __user *)arg, sizeof(*extoff)); 30262306a36Sopenharmony_ci if (IS_ERR(extoff)) { 30362306a36Sopenharmony_ci err = PTR_ERR(extoff); 30462306a36Sopenharmony_ci extoff = NULL; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci if (extoff->n_samples > PTP_MAX_SAMPLES 30862306a36Sopenharmony_ci || extoff->rsv[0] || extoff->rsv[1] || extoff->rsv[2]) { 30962306a36Sopenharmony_ci err = -EINVAL; 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci for (i = 0; i < extoff->n_samples; i++) { 31362306a36Sopenharmony_ci err = ptp->info->gettimex64(ptp->info, &ts, &sts); 31462306a36Sopenharmony_ci if (err) 31562306a36Sopenharmony_ci goto out; 31662306a36Sopenharmony_ci extoff->ts[i][0].sec = sts.pre_ts.tv_sec; 31762306a36Sopenharmony_ci extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec; 31862306a36Sopenharmony_ci extoff->ts[i][1].sec = ts.tv_sec; 31962306a36Sopenharmony_ci extoff->ts[i][1].nsec = ts.tv_nsec; 32062306a36Sopenharmony_ci extoff->ts[i][2].sec = sts.post_ts.tv_sec; 32162306a36Sopenharmony_ci extoff->ts[i][2].nsec = sts.post_ts.tv_nsec; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, extoff, sizeof(*extoff))) 32462306a36Sopenharmony_ci err = -EFAULT; 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci case PTP_SYS_OFFSET: 32862306a36Sopenharmony_ci case PTP_SYS_OFFSET2: 32962306a36Sopenharmony_ci sysoff = memdup_user((void __user *)arg, sizeof(*sysoff)); 33062306a36Sopenharmony_ci if (IS_ERR(sysoff)) { 33162306a36Sopenharmony_ci err = PTR_ERR(sysoff); 33262306a36Sopenharmony_ci sysoff = NULL; 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci if (sysoff->n_samples > PTP_MAX_SAMPLES) { 33662306a36Sopenharmony_ci err = -EINVAL; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci pct = &sysoff->ts[0]; 34062306a36Sopenharmony_ci for (i = 0; i < sysoff->n_samples; i++) { 34162306a36Sopenharmony_ci ktime_get_real_ts64(&ts); 34262306a36Sopenharmony_ci pct->sec = ts.tv_sec; 34362306a36Sopenharmony_ci pct->nsec = ts.tv_nsec; 34462306a36Sopenharmony_ci pct++; 34562306a36Sopenharmony_ci if (ops->gettimex64) 34662306a36Sopenharmony_ci err = ops->gettimex64(ops, &ts, NULL); 34762306a36Sopenharmony_ci else 34862306a36Sopenharmony_ci err = ops->gettime64(ops, &ts); 34962306a36Sopenharmony_ci if (err) 35062306a36Sopenharmony_ci goto out; 35162306a36Sopenharmony_ci pct->sec = ts.tv_sec; 35262306a36Sopenharmony_ci pct->nsec = ts.tv_nsec; 35362306a36Sopenharmony_ci pct++; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci ktime_get_real_ts64(&ts); 35662306a36Sopenharmony_ci pct->sec = ts.tv_sec; 35762306a36Sopenharmony_ci pct->nsec = ts.tv_nsec; 35862306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, sysoff, sizeof(*sysoff))) 35962306a36Sopenharmony_ci err = -EFAULT; 36062306a36Sopenharmony_ci break; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci case PTP_PIN_GETFUNC: 36362306a36Sopenharmony_ci case PTP_PIN_GETFUNC2: 36462306a36Sopenharmony_ci if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) { 36562306a36Sopenharmony_ci err = -EFAULT; 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci if ((pd.rsv[0] || pd.rsv[1] || pd.rsv[2] 36962306a36Sopenharmony_ci || pd.rsv[3] || pd.rsv[4]) 37062306a36Sopenharmony_ci && cmd == PTP_PIN_GETFUNC2) { 37162306a36Sopenharmony_ci err = -EINVAL; 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci } else if (cmd == PTP_PIN_GETFUNC) { 37462306a36Sopenharmony_ci pd.rsv[0] = 0; 37562306a36Sopenharmony_ci pd.rsv[1] = 0; 37662306a36Sopenharmony_ci pd.rsv[2] = 0; 37762306a36Sopenharmony_ci pd.rsv[3] = 0; 37862306a36Sopenharmony_ci pd.rsv[4] = 0; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci pin_index = pd.index; 38162306a36Sopenharmony_ci if (pin_index >= ops->n_pins) { 38262306a36Sopenharmony_ci err = -EINVAL; 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci pin_index = array_index_nospec(pin_index, ops->n_pins); 38662306a36Sopenharmony_ci if (mutex_lock_interruptible(&ptp->pincfg_mux)) 38762306a36Sopenharmony_ci return -ERESTARTSYS; 38862306a36Sopenharmony_ci pd = ops->pin_config[pin_index]; 38962306a36Sopenharmony_ci mutex_unlock(&ptp->pincfg_mux); 39062306a36Sopenharmony_ci if (!err && copy_to_user((void __user *)arg, &pd, sizeof(pd))) 39162306a36Sopenharmony_ci err = -EFAULT; 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci case PTP_PIN_SETFUNC: 39562306a36Sopenharmony_ci case PTP_PIN_SETFUNC2: 39662306a36Sopenharmony_ci if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) { 39762306a36Sopenharmony_ci err = -EFAULT; 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci if ((pd.rsv[0] || pd.rsv[1] || pd.rsv[2] 40162306a36Sopenharmony_ci || pd.rsv[3] || pd.rsv[4]) 40262306a36Sopenharmony_ci && cmd == PTP_PIN_SETFUNC2) { 40362306a36Sopenharmony_ci err = -EINVAL; 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci } else if (cmd == PTP_PIN_SETFUNC) { 40662306a36Sopenharmony_ci pd.rsv[0] = 0; 40762306a36Sopenharmony_ci pd.rsv[1] = 0; 40862306a36Sopenharmony_ci pd.rsv[2] = 0; 40962306a36Sopenharmony_ci pd.rsv[3] = 0; 41062306a36Sopenharmony_ci pd.rsv[4] = 0; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci pin_index = pd.index; 41362306a36Sopenharmony_ci if (pin_index >= ops->n_pins) { 41462306a36Sopenharmony_ci err = -EINVAL; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci pin_index = array_index_nospec(pin_index, ops->n_pins); 41862306a36Sopenharmony_ci if (mutex_lock_interruptible(&ptp->pincfg_mux)) 41962306a36Sopenharmony_ci return -ERESTARTSYS; 42062306a36Sopenharmony_ci err = ptp_set_pinfunc(ptp, pin_index, pd.func, pd.chan); 42162306a36Sopenharmony_ci mutex_unlock(&ptp->pincfg_mux); 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci default: 42562306a36Sopenharmony_ci err = -ENOTTY; 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ciout: 43062306a36Sopenharmony_ci kfree(extoff); 43162306a36Sopenharmony_ci kfree(sysoff); 43262306a36Sopenharmony_ci return err; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci__poll_t ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci poll_wait(fp, &ptp->tsev_wq, wait); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return queue_cnt(&ptp->tsevq) ? EPOLLIN : 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci#define EXTTS_BUFSIZE (PTP_BUF_TIMESTAMPS * sizeof(struct ptp_extts_event)) 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cissize_t ptp_read(struct posix_clock *pc, 44762306a36Sopenharmony_ci uint rdflags, char __user *buf, size_t cnt) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 45062306a36Sopenharmony_ci struct timestamp_event_queue *queue = &ptp->tsevq; 45162306a36Sopenharmony_ci struct ptp_extts_event *event; 45262306a36Sopenharmony_ci unsigned long flags; 45362306a36Sopenharmony_ci size_t qcnt, i; 45462306a36Sopenharmony_ci int result; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (cnt % sizeof(struct ptp_extts_event) != 0) 45762306a36Sopenharmony_ci return -EINVAL; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (cnt > EXTTS_BUFSIZE) 46062306a36Sopenharmony_ci cnt = EXTTS_BUFSIZE; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci cnt = cnt / sizeof(struct ptp_extts_event); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (mutex_lock_interruptible(&ptp->tsevq_mux)) 46562306a36Sopenharmony_ci return -ERESTARTSYS; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (wait_event_interruptible(ptp->tsev_wq, 46862306a36Sopenharmony_ci ptp->defunct || queue_cnt(queue))) { 46962306a36Sopenharmony_ci mutex_unlock(&ptp->tsevq_mux); 47062306a36Sopenharmony_ci return -ERESTARTSYS; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (ptp->defunct) { 47462306a36Sopenharmony_ci mutex_unlock(&ptp->tsevq_mux); 47562306a36Sopenharmony_ci return -ENODEV; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL); 47962306a36Sopenharmony_ci if (!event) { 48062306a36Sopenharmony_ci mutex_unlock(&ptp->tsevq_mux); 48162306a36Sopenharmony_ci return -ENOMEM; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci spin_lock_irqsave(&queue->lock, flags); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci qcnt = queue_cnt(queue); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (cnt > qcnt) 48962306a36Sopenharmony_ci cnt = qcnt; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci for (i = 0; i < cnt; i++) { 49262306a36Sopenharmony_ci event[i] = queue->buf[queue->head]; 49362306a36Sopenharmony_ci /* Paired with READ_ONCE() in queue_cnt() */ 49462306a36Sopenharmony_ci WRITE_ONCE(queue->head, (queue->head + 1) % PTP_MAX_TIMESTAMPS); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->lock, flags); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci cnt = cnt * sizeof(struct ptp_extts_event); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci mutex_unlock(&ptp->tsevq_mux); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci result = cnt; 50462306a36Sopenharmony_ci if (copy_to_user(buf, event, cnt)) 50562306a36Sopenharmony_ci result = -EFAULT; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci kfree(event); 50862306a36Sopenharmony_ci return result; 50962306a36Sopenharmony_ci} 510