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