18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PTP 1588 clock support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010 OMICRON electronics GmbH
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/idr.h>
88c2ecf20Sopenharmony_ci#include <linux/device.h>
98c2ecf20Sopenharmony_ci#include <linux/err.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/posix-clock.h>
148c2ecf20Sopenharmony_ci#include <linux/pps_kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/syscalls.h>
178c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
188c2ecf20Sopenharmony_ci#include <uapi/linux/sched/types.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "ptp_private.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define PTP_MAX_ALARMS 4
238c2ecf20Sopenharmony_ci#define PTP_PPS_DEFAULTS (PPS_CAPTUREASSERT | PPS_OFFSETASSERT)
248c2ecf20Sopenharmony_ci#define PTP_PPS_EVENT PPS_CAPTUREASSERT
258c2ecf20Sopenharmony_ci#define PTP_PPS_MODE (PTP_PPS_DEFAULTS | PPS_CANWAIT | PPS_TSFMT_TSPEC)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* private globals */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic dev_t ptp_devt;
308c2ecf20Sopenharmony_cistatic struct class *ptp_class;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic DEFINE_IDA(ptp_clocks_map);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* time stamp event queue operations */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic inline int queue_free(struct timestamp_event_queue *q)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void enqueue_external_timestamp(struct timestamp_event_queue *queue,
428c2ecf20Sopenharmony_ci				       struct ptp_clock_event *src)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct ptp_extts_event *dst;
458c2ecf20Sopenharmony_ci	unsigned long flags;
468c2ecf20Sopenharmony_ci	s64 seconds;
478c2ecf20Sopenharmony_ci	u32 remainder;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	seconds = div_u64_rem(src->timestamp, 1000000000, &remainder);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	spin_lock_irqsave(&queue->lock, flags);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	dst = &queue->buf[queue->tail];
548c2ecf20Sopenharmony_ci	dst->index = src->index;
558c2ecf20Sopenharmony_ci	dst->t.sec = seconds;
568c2ecf20Sopenharmony_ci	dst->t.nsec = remainder;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* Both WRITE_ONCE() are paired with READ_ONCE() in queue_cnt() */
598c2ecf20Sopenharmony_ci	if (!queue_free(queue))
608c2ecf20Sopenharmony_ci		WRITE_ONCE(queue->head, (queue->head + 1) % PTP_MAX_TIMESTAMPS);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	WRITE_ONCE(queue->tail, (queue->tail + 1) % PTP_MAX_TIMESTAMPS);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&queue->lock, flags);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cilong scaled_ppm_to_ppb(long ppm)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	/*
708c2ecf20Sopenharmony_ci	 * The 'freq' field in the 'struct timex' is in parts per
718c2ecf20Sopenharmony_ci	 * million, but with a 16 bit binary fractional field.
728c2ecf20Sopenharmony_ci	 *
738c2ecf20Sopenharmony_ci	 * We want to calculate
748c2ecf20Sopenharmony_ci	 *
758c2ecf20Sopenharmony_ci	 *    ppb = scaled_ppm * 1000 / 2^16
768c2ecf20Sopenharmony_ci	 *
778c2ecf20Sopenharmony_ci	 * which simplifies to
788c2ecf20Sopenharmony_ci	 *
798c2ecf20Sopenharmony_ci	 *    ppb = scaled_ppm * 125 / 2^13
808c2ecf20Sopenharmony_ci	 */
818c2ecf20Sopenharmony_ci	s64 ppb = 1 + ppm;
828c2ecf20Sopenharmony_ci	ppb *= 125;
838c2ecf20Sopenharmony_ci	ppb >>= 13;
848c2ecf20Sopenharmony_ci	return (long) ppb;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(scaled_ppm_to_ppb);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* posix clock implementation */
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int ptp_clock_getres(struct posix_clock *pc, struct timespec64 *tp)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	tp->tv_sec = 0;
938c2ecf20Sopenharmony_ci	tp->tv_nsec = 1;
948c2ecf20Sopenharmony_ci	return 0;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int ptp_clock_settime(struct posix_clock *pc, const struct timespec64 *tp)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return  ptp->info->settime64(ptp->info, tp);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int ptp_clock_gettime(struct posix_clock *pc, struct timespec64 *tp)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
1078c2ecf20Sopenharmony_ci	int err;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (ptp->info->gettimex64)
1108c2ecf20Sopenharmony_ci		err = ptp->info->gettimex64(ptp->info, tp, NULL);
1118c2ecf20Sopenharmony_ci	else
1128c2ecf20Sopenharmony_ci		err = ptp->info->gettime64(ptp->info, tp);
1138c2ecf20Sopenharmony_ci	return err;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
1198c2ecf20Sopenharmony_ci	struct ptp_clock_info *ops;
1208c2ecf20Sopenharmony_ci	int err = -EOPNOTSUPP;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	ops = ptp->info;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (tx->modes & ADJ_SETOFFSET) {
1258c2ecf20Sopenharmony_ci		struct timespec64 ts;
1268c2ecf20Sopenharmony_ci		ktime_t kt;
1278c2ecf20Sopenharmony_ci		s64 delta;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		ts.tv_sec  = tx->time.tv_sec;
1308c2ecf20Sopenharmony_ci		ts.tv_nsec = tx->time.tv_usec;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		if (!(tx->modes & ADJ_NANO))
1338c2ecf20Sopenharmony_ci			ts.tv_nsec *= 1000;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		if ((unsigned long) ts.tv_nsec >= NSEC_PER_SEC)
1368c2ecf20Sopenharmony_ci			return -EINVAL;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		kt = timespec64_to_ktime(ts);
1398c2ecf20Sopenharmony_ci		delta = ktime_to_ns(kt);
1408c2ecf20Sopenharmony_ci		err = ops->adjtime(ops, delta);
1418c2ecf20Sopenharmony_ci	} else if (tx->modes & ADJ_FREQUENCY) {
1428c2ecf20Sopenharmony_ci		long ppb = scaled_ppm_to_ppb(tx->freq);
1438c2ecf20Sopenharmony_ci		if (ppb > ops->max_adj || ppb < -ops->max_adj)
1448c2ecf20Sopenharmony_ci			return -ERANGE;
1458c2ecf20Sopenharmony_ci		if (ops->adjfine)
1468c2ecf20Sopenharmony_ci			err = ops->adjfine(ops, tx->freq);
1478c2ecf20Sopenharmony_ci		else
1488c2ecf20Sopenharmony_ci			err = ops->adjfreq(ops, ppb);
1498c2ecf20Sopenharmony_ci		ptp->dialed_frequency = tx->freq;
1508c2ecf20Sopenharmony_ci	} else if (tx->modes & ADJ_OFFSET) {
1518c2ecf20Sopenharmony_ci		if (ops->adjphase) {
1528c2ecf20Sopenharmony_ci			s32 offset = tx->offset;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci			if (!(tx->modes & ADJ_NANO))
1558c2ecf20Sopenharmony_ci				offset *= NSEC_PER_USEC;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci			err = ops->adjphase(ops, offset);
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci	} else if (tx->modes == 0) {
1608c2ecf20Sopenharmony_ci		tx->freq = ptp->dialed_frequency;
1618c2ecf20Sopenharmony_ci		err = 0;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return err;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic struct posix_clock_operations ptp_clock_ops = {
1688c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1698c2ecf20Sopenharmony_ci	.clock_adjtime	= ptp_clock_adjtime,
1708c2ecf20Sopenharmony_ci	.clock_gettime	= ptp_clock_gettime,
1718c2ecf20Sopenharmony_ci	.clock_getres	= ptp_clock_getres,
1728c2ecf20Sopenharmony_ci	.clock_settime	= ptp_clock_settime,
1738c2ecf20Sopenharmony_ci	.ioctl		= ptp_ioctl,
1748c2ecf20Sopenharmony_ci	.open		= ptp_open,
1758c2ecf20Sopenharmony_ci	.poll		= ptp_poll,
1768c2ecf20Sopenharmony_ci	.read		= ptp_read,
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic void ptp_clock_release(struct device *dev)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct ptp_clock *ptp = container_of(dev, struct ptp_clock, dev);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	ptp_cleanup_pin_groups(ptp);
1848c2ecf20Sopenharmony_ci	mutex_destroy(&ptp->tsevq_mux);
1858c2ecf20Sopenharmony_ci	mutex_destroy(&ptp->pincfg_mux);
1868c2ecf20Sopenharmony_ci	ida_simple_remove(&ptp_clocks_map, ptp->index);
1878c2ecf20Sopenharmony_ci	kfree(ptp);
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic void ptp_aux_kworker(struct kthread_work *work)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct ptp_clock *ptp = container_of(work, struct ptp_clock,
1938c2ecf20Sopenharmony_ci					     aux_work.work);
1948c2ecf20Sopenharmony_ci	struct ptp_clock_info *info = ptp->info;
1958c2ecf20Sopenharmony_ci	long delay;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	delay = info->do_aux_work(info);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (delay >= 0)
2008c2ecf20Sopenharmony_ci		kthread_queue_delayed_work(ptp->kworker, &ptp->aux_work, delay);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/* public interface */
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistruct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
2068c2ecf20Sopenharmony_ci				     struct device *parent)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct ptp_clock *ptp;
2098c2ecf20Sopenharmony_ci	int err = 0, index, major = MAJOR(ptp_devt);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (info->n_alarm > PTP_MAX_ALARMS)
2128c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* Initialize a clock structure. */
2158c2ecf20Sopenharmony_ci	err = -ENOMEM;
2168c2ecf20Sopenharmony_ci	ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL);
2178c2ecf20Sopenharmony_ci	if (ptp == NULL)
2188c2ecf20Sopenharmony_ci		goto no_memory;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	index = ida_simple_get(&ptp_clocks_map, 0, MINORMASK + 1, GFP_KERNEL);
2218c2ecf20Sopenharmony_ci	if (index < 0) {
2228c2ecf20Sopenharmony_ci		err = index;
2238c2ecf20Sopenharmony_ci		goto no_slot;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	ptp->clock.ops = ptp_clock_ops;
2278c2ecf20Sopenharmony_ci	ptp->info = info;
2288c2ecf20Sopenharmony_ci	ptp->devid = MKDEV(major, index);
2298c2ecf20Sopenharmony_ci	ptp->index = index;
2308c2ecf20Sopenharmony_ci	spin_lock_init(&ptp->tsevq.lock);
2318c2ecf20Sopenharmony_ci	mutex_init(&ptp->tsevq_mux);
2328c2ecf20Sopenharmony_ci	mutex_init(&ptp->pincfg_mux);
2338c2ecf20Sopenharmony_ci	init_waitqueue_head(&ptp->tsev_wq);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (ptp->info->do_aux_work) {
2368c2ecf20Sopenharmony_ci		kthread_init_delayed_work(&ptp->aux_work, ptp_aux_kworker);
2378c2ecf20Sopenharmony_ci		ptp->kworker = kthread_create_worker(0, "ptp%d", ptp->index);
2388c2ecf20Sopenharmony_ci		if (IS_ERR(ptp->kworker)) {
2398c2ecf20Sopenharmony_ci			err = PTR_ERR(ptp->kworker);
2408c2ecf20Sopenharmony_ci			pr_err("failed to create ptp aux_worker %d\n", err);
2418c2ecf20Sopenharmony_ci			goto kworker_err;
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	err = ptp_populate_pin_groups(ptp);
2468c2ecf20Sopenharmony_ci	if (err)
2478c2ecf20Sopenharmony_ci		goto no_pin_groups;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* Register a new PPS source. */
2508c2ecf20Sopenharmony_ci	if (info->pps) {
2518c2ecf20Sopenharmony_ci		struct pps_source_info pps;
2528c2ecf20Sopenharmony_ci		memset(&pps, 0, sizeof(pps));
2538c2ecf20Sopenharmony_ci		snprintf(pps.name, PPS_MAX_NAME_LEN, "ptp%d", index);
2548c2ecf20Sopenharmony_ci		pps.mode = PTP_PPS_MODE;
2558c2ecf20Sopenharmony_ci		pps.owner = info->owner;
2568c2ecf20Sopenharmony_ci		ptp->pps_source = pps_register_source(&pps, PTP_PPS_DEFAULTS);
2578c2ecf20Sopenharmony_ci		if (IS_ERR(ptp->pps_source)) {
2588c2ecf20Sopenharmony_ci			err = PTR_ERR(ptp->pps_source);
2598c2ecf20Sopenharmony_ci			pr_err("failed to register pps source\n");
2608c2ecf20Sopenharmony_ci			goto no_pps;
2618c2ecf20Sopenharmony_ci		}
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* Initialize a new device of our class in our clock structure. */
2658c2ecf20Sopenharmony_ci	device_initialize(&ptp->dev);
2668c2ecf20Sopenharmony_ci	ptp->dev.devt = ptp->devid;
2678c2ecf20Sopenharmony_ci	ptp->dev.class = ptp_class;
2688c2ecf20Sopenharmony_ci	ptp->dev.parent = parent;
2698c2ecf20Sopenharmony_ci	ptp->dev.groups = ptp->pin_attr_groups;
2708c2ecf20Sopenharmony_ci	ptp->dev.release = ptp_clock_release;
2718c2ecf20Sopenharmony_ci	dev_set_drvdata(&ptp->dev, ptp);
2728c2ecf20Sopenharmony_ci	dev_set_name(&ptp->dev, "ptp%d", ptp->index);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* Create a posix clock and link it to the device. */
2758c2ecf20Sopenharmony_ci	err = posix_clock_register(&ptp->clock, &ptp->dev);
2768c2ecf20Sopenharmony_ci	if (err) {
2778c2ecf20Sopenharmony_ci		pr_err("failed to create posix clock\n");
2788c2ecf20Sopenharmony_ci		goto no_clock;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return ptp;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cino_clock:
2848c2ecf20Sopenharmony_ci	if (ptp->pps_source)
2858c2ecf20Sopenharmony_ci		pps_unregister_source(ptp->pps_source);
2868c2ecf20Sopenharmony_cino_pps:
2878c2ecf20Sopenharmony_ci	ptp_cleanup_pin_groups(ptp);
2888c2ecf20Sopenharmony_cino_pin_groups:
2898c2ecf20Sopenharmony_ci	if (ptp->kworker)
2908c2ecf20Sopenharmony_ci		kthread_destroy_worker(ptp->kworker);
2918c2ecf20Sopenharmony_cikworker_err:
2928c2ecf20Sopenharmony_ci	mutex_destroy(&ptp->tsevq_mux);
2938c2ecf20Sopenharmony_ci	mutex_destroy(&ptp->pincfg_mux);
2948c2ecf20Sopenharmony_ci	ida_simple_remove(&ptp_clocks_map, index);
2958c2ecf20Sopenharmony_cino_slot:
2968c2ecf20Sopenharmony_ci	kfree(ptp);
2978c2ecf20Sopenharmony_cino_memory:
2988c2ecf20Sopenharmony_ci	return ERR_PTR(err);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ptp_clock_register);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ciint ptp_clock_unregister(struct ptp_clock *ptp)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	ptp->defunct = 1;
3058c2ecf20Sopenharmony_ci	wake_up_interruptible(&ptp->tsev_wq);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (ptp->kworker) {
3088c2ecf20Sopenharmony_ci		kthread_cancel_delayed_work_sync(&ptp->aux_work);
3098c2ecf20Sopenharmony_ci		kthread_destroy_worker(ptp->kworker);
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/* Release the clock's resources. */
3138c2ecf20Sopenharmony_ci	if (ptp->pps_source)
3148c2ecf20Sopenharmony_ci		pps_unregister_source(ptp->pps_source);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	posix_clock_unregister(&ptp->clock);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ptp_clock_unregister);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_civoid ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct pps_event_time evt;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	switch (event->type) {
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	case PTP_CLOCK_ALARM:
3298c2ecf20Sopenharmony_ci		break;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	case PTP_CLOCK_EXTTS:
3328c2ecf20Sopenharmony_ci		enqueue_external_timestamp(&ptp->tsevq, event);
3338c2ecf20Sopenharmony_ci		wake_up_interruptible(&ptp->tsev_wq);
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	case PTP_CLOCK_PPS:
3378c2ecf20Sopenharmony_ci		pps_get_ts(&evt);
3388c2ecf20Sopenharmony_ci		pps_event(ptp->pps_source, &evt, PTP_PPS_EVENT, NULL);
3398c2ecf20Sopenharmony_ci		break;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	case PTP_CLOCK_PPSUSR:
3428c2ecf20Sopenharmony_ci		pps_event(ptp->pps_source, &event->pps_times,
3438c2ecf20Sopenharmony_ci			  PTP_PPS_EVENT, NULL);
3448c2ecf20Sopenharmony_ci		break;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ptp_clock_event);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ciint ptp_clock_index(struct ptp_clock *ptp)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	return ptp->index;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ptp_clock_index);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ciint ptp_find_pin(struct ptp_clock *ptp,
3568c2ecf20Sopenharmony_ci		 enum ptp_pin_function func, unsigned int chan)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct ptp_pin_desc *pin = NULL;
3598c2ecf20Sopenharmony_ci	int i;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	for (i = 0; i < ptp->info->n_pins; i++) {
3628c2ecf20Sopenharmony_ci		if (ptp->info->pin_config[i].func == func &&
3638c2ecf20Sopenharmony_ci		    ptp->info->pin_config[i].chan == chan) {
3648c2ecf20Sopenharmony_ci			pin = &ptp->info->pin_config[i];
3658c2ecf20Sopenharmony_ci			break;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return pin ? i : -1;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ptp_find_pin);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ciint ptp_find_pin_unlocked(struct ptp_clock *ptp,
3748c2ecf20Sopenharmony_ci			  enum ptp_pin_function func, unsigned int chan)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	int result;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	mutex_lock(&ptp->pincfg_mux);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	result = ptp_find_pin(ptp, func, chan);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	mutex_unlock(&ptp->pincfg_mux);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return result;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ptp_find_pin_unlocked);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ciint ptp_schedule_worker(struct ptp_clock *ptp, unsigned long delay)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	return kthread_mod_delayed_work(ptp->kworker, &ptp->aux_work, delay);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ptp_schedule_worker);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_civoid ptp_cancel_worker_sync(struct ptp_clock *ptp)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	kthread_cancel_delayed_work_sync(&ptp->aux_work);
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ptp_cancel_worker_sync);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/* module operations */
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic void __exit ptp_exit(void)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	class_destroy(ptp_class);
4058c2ecf20Sopenharmony_ci	unregister_chrdev_region(ptp_devt, MINORMASK + 1);
4068c2ecf20Sopenharmony_ci	ida_destroy(&ptp_clocks_map);
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic int __init ptp_init(void)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	int err;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	ptp_class = class_create(THIS_MODULE, "ptp");
4148c2ecf20Sopenharmony_ci	if (IS_ERR(ptp_class)) {
4158c2ecf20Sopenharmony_ci		pr_err("ptp: failed to allocate class\n");
4168c2ecf20Sopenharmony_ci		return PTR_ERR(ptp_class);
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	err = alloc_chrdev_region(&ptp_devt, 0, MINORMASK + 1, "ptp");
4208c2ecf20Sopenharmony_ci	if (err < 0) {
4218c2ecf20Sopenharmony_ci		pr_err("ptp: failed to allocate device region\n");
4228c2ecf20Sopenharmony_ci		goto no_region;
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	ptp_class->dev_groups = ptp_groups;
4268c2ecf20Sopenharmony_ci	pr_info("PTP clock support registered\n");
4278c2ecf20Sopenharmony_ci	return 0;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cino_region:
4308c2ecf20Sopenharmony_ci	class_destroy(ptp_class);
4318c2ecf20Sopenharmony_ci	return err;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cisubsys_initcall(ptp_init);
4358c2ecf20Sopenharmony_cimodule_exit(ptp_exit);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
4388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PTP clocks support");
4398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
440