xref: /openbmc/linux/drivers/ptp/ptp_sysfs.c (revision 57904291176fa16a981cefca5cbe1a0b50196792)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2d94ba80eSRichard Cochran /*
3d94ba80eSRichard Cochran  * PTP 1588 clock support - sysfs interface.
4d94ba80eSRichard Cochran  *
5d94ba80eSRichard Cochran  * Copyright (C) 2010 OMICRON electronics GmbH
673f37068SYangbo Lu  * Copyright 2021 NXP
7d94ba80eSRichard Cochran  */
8d94ba80eSRichard Cochran #include <linux/capability.h>
9653104d1SRichard Cochran #include <linux/slab.h>
10d94ba80eSRichard Cochran 
11d94ba80eSRichard Cochran #include "ptp_private.h"
12d94ba80eSRichard Cochran 
clock_name_show(struct device * dev,struct device_attribute * attr,char * page)13d94ba80eSRichard Cochran static ssize_t clock_name_show(struct device *dev,
14d94ba80eSRichard Cochran 			       struct device_attribute *attr, char *page)
15d94ba80eSRichard Cochran {
16d94ba80eSRichard Cochran 	struct ptp_clock *ptp = dev_get_drvdata(dev);
17e2cf0765SYang Guang 	return sysfs_emit(page, "%s\n", ptp->info->name);
18d94ba80eSRichard Cochran }
1963215705SJulia Lawall static DEVICE_ATTR_RO(clock_name);
20d94ba80eSRichard Cochran 
max_phase_adjustment_show(struct device * dev,struct device_attribute * attr,char * page)21c3b60ab7SRahul Rameshbabu static ssize_t max_phase_adjustment_show(struct device *dev,
22c3b60ab7SRahul Rameshbabu 					 struct device_attribute *attr,
23c3b60ab7SRahul Rameshbabu 					 char *page)
24c3b60ab7SRahul Rameshbabu {
25c3b60ab7SRahul Rameshbabu 	struct ptp_clock *ptp = dev_get_drvdata(dev);
26c3b60ab7SRahul Rameshbabu 
27c3b60ab7SRahul Rameshbabu 	return snprintf(page, PAGE_SIZE - 1, "%d\n",
28c3b60ab7SRahul Rameshbabu 			ptp->info->getmaxphase(ptp->info));
29c3b60ab7SRahul Rameshbabu }
30c3b60ab7SRahul Rameshbabu static DEVICE_ATTR_RO(max_phase_adjustment);
31c3b60ab7SRahul Rameshbabu 
323499116bSGreg Kroah-Hartman #define PTP_SHOW_INT(name, var)						\
333499116bSGreg Kroah-Hartman static ssize_t var##_show(struct device *dev,				\
34d94ba80eSRichard Cochran 			   struct device_attribute *attr, char *page)	\
35d94ba80eSRichard Cochran {									\
36d94ba80eSRichard Cochran 	struct ptp_clock *ptp = dev_get_drvdata(dev);			\
373499116bSGreg Kroah-Hartman 	return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->info->var);	\
383499116bSGreg Kroah-Hartman }									\
393499116bSGreg Kroah-Hartman static DEVICE_ATTR(name, 0444, var##_show, NULL);
40d94ba80eSRichard Cochran 
413499116bSGreg Kroah-Hartman PTP_SHOW_INT(max_adjustment, max_adj);
423499116bSGreg Kroah-Hartman PTP_SHOW_INT(n_alarms, n_alarm);
433499116bSGreg Kroah-Hartman PTP_SHOW_INT(n_external_timestamps, n_ext_ts);
443499116bSGreg Kroah-Hartman PTP_SHOW_INT(n_periodic_outputs, n_per_out);
45653104d1SRichard Cochran PTP_SHOW_INT(n_programmable_pins, n_pins);
463499116bSGreg Kroah-Hartman PTP_SHOW_INT(pps_available, pps);
47d94ba80eSRichard Cochran 
extts_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)48d94ba80eSRichard Cochran static ssize_t extts_enable_store(struct device *dev,
49d94ba80eSRichard Cochran 				  struct device_attribute *attr,
50d94ba80eSRichard Cochran 				  const char *buf, size_t count)
51d94ba80eSRichard Cochran {
52d94ba80eSRichard Cochran 	struct ptp_clock *ptp = dev_get_drvdata(dev);
53d94ba80eSRichard Cochran 	struct ptp_clock_info *ops = ptp->info;
54d94ba80eSRichard Cochran 	struct ptp_clock_request req = { .type = PTP_CLK_REQ_EXTTS };
55d94ba80eSRichard Cochran 	int cnt, enable;
56d94ba80eSRichard Cochran 	int err = -EINVAL;
57d94ba80eSRichard Cochran 
58d94ba80eSRichard Cochran 	cnt = sscanf(buf, "%u %d", &req.extts.index, &enable);
59d94ba80eSRichard Cochran 	if (cnt != 2)
60d94ba80eSRichard Cochran 		goto out;
61d94ba80eSRichard Cochran 	if (req.extts.index >= ops->n_ext_ts)
62d94ba80eSRichard Cochran 		goto out;
63d94ba80eSRichard Cochran 
64d94ba80eSRichard Cochran 	err = ops->enable(ops, &req, enable ? 1 : 0);
65d94ba80eSRichard Cochran 	if (err)
66d94ba80eSRichard Cochran 		goto out;
67d94ba80eSRichard Cochran 
68d94ba80eSRichard Cochran 	return count;
69d94ba80eSRichard Cochran out:
70d94ba80eSRichard Cochran 	return err;
71d94ba80eSRichard Cochran }
72af59e717SDmitry Torokhov static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store);
73d94ba80eSRichard Cochran 
extts_fifo_show(struct device * dev,struct device_attribute * attr,char * page)74d94ba80eSRichard Cochran static ssize_t extts_fifo_show(struct device *dev,
75d94ba80eSRichard Cochran 			       struct device_attribute *attr, char *page)
76d94ba80eSRichard Cochran {
77d94ba80eSRichard Cochran 	struct ptp_clock *ptp = dev_get_drvdata(dev);
78d94ba80eSRichard Cochran 	struct timestamp_event_queue *queue = &ptp->tsevq;
79d94ba80eSRichard Cochran 	struct ptp_extts_event event;
80d94ba80eSRichard Cochran 	unsigned long flags;
81d94ba80eSRichard Cochran 	size_t qcnt;
82d94ba80eSRichard Cochran 	int cnt = 0;
83d94ba80eSRichard Cochran 
84d94ba80eSRichard Cochran 	memset(&event, 0, sizeof(event));
85d94ba80eSRichard Cochran 
86d94ba80eSRichard Cochran 	if (mutex_lock_interruptible(&ptp->tsevq_mux))
87d94ba80eSRichard Cochran 		return -ERESTARTSYS;
88d94ba80eSRichard Cochran 
89d94ba80eSRichard Cochran 	spin_lock_irqsave(&queue->lock, flags);
90d94ba80eSRichard Cochran 	qcnt = queue_cnt(queue);
91d94ba80eSRichard Cochran 	if (qcnt) {
92d94ba80eSRichard Cochran 		event = queue->buf[queue->head];
93c8608558SEric Dumazet 		/* Paired with READ_ONCE() in queue_cnt() */
94c8608558SEric Dumazet 		WRITE_ONCE(queue->head, (queue->head + 1) % PTP_MAX_TIMESTAMPS);
95d94ba80eSRichard Cochran 	}
96d94ba80eSRichard Cochran 	spin_unlock_irqrestore(&queue->lock, flags);
97d94ba80eSRichard Cochran 
98d94ba80eSRichard Cochran 	if (!qcnt)
99d94ba80eSRichard Cochran 		goto out;
100d94ba80eSRichard Cochran 
101d94ba80eSRichard Cochran 	cnt = snprintf(page, PAGE_SIZE, "%u %lld %u\n",
102d94ba80eSRichard Cochran 		       event.index, event.t.sec, event.t.nsec);
103d94ba80eSRichard Cochran out:
104d94ba80eSRichard Cochran 	mutex_unlock(&ptp->tsevq_mux);
105d94ba80eSRichard Cochran 	return cnt;
106d94ba80eSRichard Cochran }
107af59e717SDmitry Torokhov static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL);
108d94ba80eSRichard Cochran 
period_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)109d94ba80eSRichard Cochran static ssize_t period_store(struct device *dev,
110d94ba80eSRichard Cochran 			    struct device_attribute *attr,
111d94ba80eSRichard Cochran 			    const char *buf, size_t count)
112d94ba80eSRichard Cochran {
113d94ba80eSRichard Cochran 	struct ptp_clock *ptp = dev_get_drvdata(dev);
114d94ba80eSRichard Cochran 	struct ptp_clock_info *ops = ptp->info;
115d94ba80eSRichard Cochran 	struct ptp_clock_request req = { .type = PTP_CLK_REQ_PEROUT };
116d94ba80eSRichard Cochran 	int cnt, enable, err = -EINVAL;
117d94ba80eSRichard Cochran 
118d94ba80eSRichard Cochran 	cnt = sscanf(buf, "%u %lld %u %lld %u", &req.perout.index,
119d94ba80eSRichard Cochran 		     &req.perout.start.sec, &req.perout.start.nsec,
120d94ba80eSRichard Cochran 		     &req.perout.period.sec, &req.perout.period.nsec);
121d94ba80eSRichard Cochran 	if (cnt != 5)
122d94ba80eSRichard Cochran 		goto out;
123d94ba80eSRichard Cochran 	if (req.perout.index >= ops->n_per_out)
124d94ba80eSRichard Cochran 		goto out;
125d94ba80eSRichard Cochran 
126d94ba80eSRichard Cochran 	enable = req.perout.period.sec || req.perout.period.nsec;
127d94ba80eSRichard Cochran 	err = ops->enable(ops, &req, enable);
128d94ba80eSRichard Cochran 	if (err)
129d94ba80eSRichard Cochran 		goto out;
130d94ba80eSRichard Cochran 
131d94ba80eSRichard Cochran 	return count;
132d94ba80eSRichard Cochran out:
133d94ba80eSRichard Cochran 	return err;
134d94ba80eSRichard Cochran }
135af59e717SDmitry Torokhov static DEVICE_ATTR(period, 0220, NULL, period_store);
136d94ba80eSRichard Cochran 
pps_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)137d94ba80eSRichard Cochran static ssize_t pps_enable_store(struct device *dev,
138d94ba80eSRichard Cochran 				struct device_attribute *attr,
139d94ba80eSRichard Cochran 				const char *buf, size_t count)
140d94ba80eSRichard Cochran {
141d94ba80eSRichard Cochran 	struct ptp_clock *ptp = dev_get_drvdata(dev);
142d94ba80eSRichard Cochran 	struct ptp_clock_info *ops = ptp->info;
143d94ba80eSRichard Cochran 	struct ptp_clock_request req = { .type = PTP_CLK_REQ_PPS };
144d94ba80eSRichard Cochran 	int cnt, enable;
145d94ba80eSRichard Cochran 	int err = -EINVAL;
146d94ba80eSRichard Cochran 
147d94ba80eSRichard Cochran 	if (!capable(CAP_SYS_TIME))
148d94ba80eSRichard Cochran 		return -EPERM;
149d94ba80eSRichard Cochran 
150d94ba80eSRichard Cochran 	cnt = sscanf(buf, "%d", &enable);
151d94ba80eSRichard Cochran 	if (cnt != 1)
152d94ba80eSRichard Cochran 		goto out;
153d94ba80eSRichard Cochran 
154d94ba80eSRichard Cochran 	err = ops->enable(ops, &req, enable ? 1 : 0);
155d94ba80eSRichard Cochran 	if (err)
156d94ba80eSRichard Cochran 		goto out;
157d94ba80eSRichard Cochran 
158d94ba80eSRichard Cochran 	return count;
159d94ba80eSRichard Cochran out:
160d94ba80eSRichard Cochran 	return err;
161d94ba80eSRichard Cochran }
162af59e717SDmitry Torokhov static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store);
163af59e717SDmitry Torokhov 
unregister_vclock(struct device * dev,void * data)16473f37068SYangbo Lu static int unregister_vclock(struct device *dev, void *data)
16573f37068SYangbo Lu {
16673f37068SYangbo Lu 	struct ptp_clock *ptp = dev_get_drvdata(dev);
16773f37068SYangbo Lu 	struct ptp_clock_info *info = ptp->info;
16873f37068SYangbo Lu 	struct ptp_vclock *vclock;
169d329e41aSVinicius Costa Gomes 	u32 *num = data;
17073f37068SYangbo Lu 
17173f37068SYangbo Lu 	vclock = info_to_vclock(info);
17273f37068SYangbo Lu 	dev_info(dev->parent, "delete virtual clock ptp%d\n",
17373f37068SYangbo Lu 		 vclock->clock->index);
17473f37068SYangbo Lu 
17573f37068SYangbo Lu 	ptp_vclock_unregister(vclock);
17673f37068SYangbo Lu 	(*num)--;
17773f37068SYangbo Lu 
17873f37068SYangbo Lu 	/* For break. Not error. */
17973f37068SYangbo Lu 	if (*num == 0)
18073f37068SYangbo Lu 		return -EINVAL;
18173f37068SYangbo Lu 
18273f37068SYangbo Lu 	return 0;
18373f37068SYangbo Lu }
18473f37068SYangbo Lu 
n_vclocks_show(struct device * dev,struct device_attribute * attr,char * page)18573f37068SYangbo Lu static ssize_t n_vclocks_show(struct device *dev,
18673f37068SYangbo Lu 			      struct device_attribute *attr, char *page)
18773f37068SYangbo Lu {
18873f37068SYangbo Lu 	struct ptp_clock *ptp = dev_get_drvdata(dev);
18973f37068SYangbo Lu 	ssize_t size;
19073f37068SYangbo Lu 
19173f37068SYangbo Lu 	if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
19273f37068SYangbo Lu 		return -ERESTARTSYS;
19373f37068SYangbo Lu 
194f6a175cfSYangbo Lu 	size = snprintf(page, PAGE_SIZE - 1, "%u\n", ptp->n_vclocks);
19573f37068SYangbo Lu 
19673f37068SYangbo Lu 	mutex_unlock(&ptp->n_vclocks_mux);
19773f37068SYangbo Lu 
19873f37068SYangbo Lu 	return size;
19973f37068SYangbo Lu }
20073f37068SYangbo Lu 
n_vclocks_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)20173f37068SYangbo Lu static ssize_t n_vclocks_store(struct device *dev,
20273f37068SYangbo Lu 			       struct device_attribute *attr,
20373f37068SYangbo Lu 			       const char *buf, size_t count)
20473f37068SYangbo Lu {
20573f37068SYangbo Lu 	struct ptp_clock *ptp = dev_get_drvdata(dev);
20673f37068SYangbo Lu 	struct ptp_vclock *vclock;
20773f37068SYangbo Lu 	int err = -EINVAL;
20873f37068SYangbo Lu 	u32 num, i;
20973f37068SYangbo Lu 
21073f37068SYangbo Lu 	if (kstrtou32(buf, 0, &num))
21173f37068SYangbo Lu 		return err;
21273f37068SYangbo Lu 
21373f37068SYangbo Lu 	if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
21473f37068SYangbo Lu 		return -ERESTARTSYS;
21573f37068SYangbo Lu 
21673f37068SYangbo Lu 	if (num > ptp->max_vclocks) {
21773f37068SYangbo Lu 		dev_err(dev, "max value is %d\n", ptp->max_vclocks);
21873f37068SYangbo Lu 		goto out;
21973f37068SYangbo Lu 	}
22073f37068SYangbo Lu 
22173f37068SYangbo Lu 	/* Need to create more vclocks */
22273f37068SYangbo Lu 	if (num > ptp->n_vclocks) {
22373f37068SYangbo Lu 		for (i = 0; i < num - ptp->n_vclocks; i++) {
22473f37068SYangbo Lu 			vclock = ptp_vclock_register(ptp);
22573f37068SYangbo Lu 			if (!vclock)
22673f37068SYangbo Lu 				goto out;
22773f37068SYangbo Lu 
22844c494c8SYangbo Lu 			*(ptp->vclock_index + ptp->n_vclocks + i) =
22944c494c8SYangbo Lu 				vclock->clock->index;
23044c494c8SYangbo Lu 
23173f37068SYangbo Lu 			dev_info(dev, "new virtual clock ptp%d\n",
23273f37068SYangbo Lu 				 vclock->clock->index);
23373f37068SYangbo Lu 		}
23473f37068SYangbo Lu 	}
23573f37068SYangbo Lu 
23673f37068SYangbo Lu 	/* Need to delete vclocks */
23773f37068SYangbo Lu 	if (num < ptp->n_vclocks) {
23873f37068SYangbo Lu 		i = ptp->n_vclocks - num;
23973f37068SYangbo Lu 		device_for_each_child_reverse(dev, &i,
24073f37068SYangbo Lu 					      unregister_vclock);
24144c494c8SYangbo Lu 
24244c494c8SYangbo Lu 		for (i = 1; i <= ptp->n_vclocks - num; i++)
24344c494c8SYangbo Lu 			*(ptp->vclock_index + ptp->n_vclocks - i) = -1;
24473f37068SYangbo Lu 	}
24573f37068SYangbo Lu 
24642704b26SGerhard Engleder 	/* Need to inform about changed physical clock behavior */
24742704b26SGerhard Engleder 	if (!ptp->has_cycles) {
24873f37068SYangbo Lu 		if (num == 0)
24973f37068SYangbo Lu 			dev_info(dev, "only physical clock in use now\n");
25073f37068SYangbo Lu 		else
25173f37068SYangbo Lu 			dev_info(dev, "guarantee physical clock free running\n");
25242704b26SGerhard Engleder 	}
25373f37068SYangbo Lu 
25473f37068SYangbo Lu 	ptp->n_vclocks = num;
25573f37068SYangbo Lu 	mutex_unlock(&ptp->n_vclocks_mux);
25673f37068SYangbo Lu 
25773f37068SYangbo Lu 	return count;
25873f37068SYangbo Lu out:
25973f37068SYangbo Lu 	mutex_unlock(&ptp->n_vclocks_mux);
26073f37068SYangbo Lu 	return err;
26173f37068SYangbo Lu }
26273f37068SYangbo Lu static DEVICE_ATTR_RW(n_vclocks);
26373f37068SYangbo Lu 
max_vclocks_show(struct device * dev,struct device_attribute * attr,char * page)26473f37068SYangbo Lu static ssize_t max_vclocks_show(struct device *dev,
26573f37068SYangbo Lu 				struct device_attribute *attr, char *page)
26673f37068SYangbo Lu {
26773f37068SYangbo Lu 	struct ptp_clock *ptp = dev_get_drvdata(dev);
26873f37068SYangbo Lu 	ssize_t size;
26973f37068SYangbo Lu 
270f6a175cfSYangbo Lu 	size = snprintf(page, PAGE_SIZE - 1, "%u\n", ptp->max_vclocks);
27173f37068SYangbo Lu 
27273f37068SYangbo Lu 	return size;
27373f37068SYangbo Lu }
27473f37068SYangbo Lu 
max_vclocks_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)27573f37068SYangbo Lu static ssize_t max_vclocks_store(struct device *dev,
27673f37068SYangbo Lu 				 struct device_attribute *attr,
27773f37068SYangbo Lu 				 const char *buf, size_t count)
27873f37068SYangbo Lu {
27973f37068SYangbo Lu 	struct ptp_clock *ptp = dev_get_drvdata(dev);
28044c494c8SYangbo Lu 	unsigned int *vclock_index;
28144c494c8SYangbo Lu 	int err = -EINVAL;
28244c494c8SYangbo Lu 	size_t size;
28373f37068SYangbo Lu 	u32 max;
28473f37068SYangbo Lu 
28573f37068SYangbo Lu 	if (kstrtou32(buf, 0, &max) || max == 0)
28673f37068SYangbo Lu 		return -EINVAL;
28773f37068SYangbo Lu 
28873f37068SYangbo Lu 	if (max == ptp->max_vclocks)
28973f37068SYangbo Lu 		return count;
29073f37068SYangbo Lu 
29173f37068SYangbo Lu 	if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
29273f37068SYangbo Lu 		return -ERESTARTSYS;
29373f37068SYangbo Lu 
29444c494c8SYangbo Lu 	if (max < ptp->n_vclocks)
29544c494c8SYangbo Lu 		goto out;
29644c494c8SYangbo Lu 
297*666e934dSDan Carpenter 	vclock_index = kcalloc(max, sizeof(int), GFP_KERNEL);
29844c494c8SYangbo Lu 	if (!vclock_index) {
29944c494c8SYangbo Lu 		err = -ENOMEM;
30044c494c8SYangbo Lu 		goto out;
30173f37068SYangbo Lu 	}
30273f37068SYangbo Lu 
30344c494c8SYangbo Lu 	size = sizeof(int) * ptp->n_vclocks;
30444c494c8SYangbo Lu 	memcpy(vclock_index, ptp->vclock_index, size);
30544c494c8SYangbo Lu 
30644c494c8SYangbo Lu 	kfree(ptp->vclock_index);
30744c494c8SYangbo Lu 	ptp->vclock_index = vclock_index;
30873f37068SYangbo Lu 	ptp->max_vclocks = max;
30973f37068SYangbo Lu 
31073f37068SYangbo Lu 	mutex_unlock(&ptp->n_vclocks_mux);
31173f37068SYangbo Lu 
31273f37068SYangbo Lu 	return count;
31344c494c8SYangbo Lu out:
31444c494c8SYangbo Lu 	mutex_unlock(&ptp->n_vclocks_mux);
31544c494c8SYangbo Lu 	return err;
31673f37068SYangbo Lu }
31773f37068SYangbo Lu static DEVICE_ATTR_RW(max_vclocks);
31873f37068SYangbo Lu 
319af59e717SDmitry Torokhov static struct attribute *ptp_attrs[] = {
320af59e717SDmitry Torokhov 	&dev_attr_clock_name.attr,
321af59e717SDmitry Torokhov 
322af59e717SDmitry Torokhov 	&dev_attr_max_adjustment.attr,
323c3b60ab7SRahul Rameshbabu 	&dev_attr_max_phase_adjustment.attr,
324af59e717SDmitry Torokhov 	&dev_attr_n_alarms.attr,
325af59e717SDmitry Torokhov 	&dev_attr_n_external_timestamps.attr,
326af59e717SDmitry Torokhov 	&dev_attr_n_periodic_outputs.attr,
327af59e717SDmitry Torokhov 	&dev_attr_n_programmable_pins.attr,
328af59e717SDmitry Torokhov 	&dev_attr_pps_available.attr,
329af59e717SDmitry Torokhov 
330af59e717SDmitry Torokhov 	&dev_attr_extts_enable.attr,
331af59e717SDmitry Torokhov 	&dev_attr_fifo.attr,
332af59e717SDmitry Torokhov 	&dev_attr_period.attr,
333af59e717SDmitry Torokhov 	&dev_attr_pps_enable.attr,
33473f37068SYangbo Lu 	&dev_attr_n_vclocks.attr,
33573f37068SYangbo Lu 	&dev_attr_max_vclocks.attr,
336af59e717SDmitry Torokhov 	NULL
337af59e717SDmitry Torokhov };
338af59e717SDmitry Torokhov 
ptp_is_attribute_visible(struct kobject * kobj,struct attribute * attr,int n)339af59e717SDmitry Torokhov static umode_t ptp_is_attribute_visible(struct kobject *kobj,
340af59e717SDmitry Torokhov 					struct attribute *attr, int n)
341af59e717SDmitry Torokhov {
342af59e717SDmitry Torokhov 	struct device *dev = kobj_to_dev(kobj);
343af59e717SDmitry Torokhov 	struct ptp_clock *ptp = dev_get_drvdata(dev);
344af59e717SDmitry Torokhov 	struct ptp_clock_info *info = ptp->info;
345af59e717SDmitry Torokhov 	umode_t mode = attr->mode;
346af59e717SDmitry Torokhov 
347af59e717SDmitry Torokhov 	if (attr == &dev_attr_extts_enable.attr ||
348af59e717SDmitry Torokhov 	    attr == &dev_attr_fifo.attr) {
349af59e717SDmitry Torokhov 		if (!info->n_ext_ts)
350af59e717SDmitry Torokhov 			mode = 0;
351af59e717SDmitry Torokhov 	} else if (attr == &dev_attr_period.attr) {
352af59e717SDmitry Torokhov 		if (!info->n_per_out)
353af59e717SDmitry Torokhov 			mode = 0;
354af59e717SDmitry Torokhov 	} else if (attr == &dev_attr_pps_enable.attr) {
355af59e717SDmitry Torokhov 		if (!info->pps)
356af59e717SDmitry Torokhov 			mode = 0;
35773f37068SYangbo Lu 	} else if (attr == &dev_attr_n_vclocks.attr ||
35873f37068SYangbo Lu 		   attr == &dev_attr_max_vclocks.attr) {
35973f37068SYangbo Lu 		if (ptp->is_virtual_clock)
36073f37068SYangbo Lu 			mode = 0;
3612c5d234dSRahul Rameshbabu 	} else if (attr == &dev_attr_max_phase_adjustment.attr) {
3622c5d234dSRahul Rameshbabu 		if (!info->adjphase || !info->getmaxphase)
3632c5d234dSRahul Rameshbabu 			mode = 0;
364af59e717SDmitry Torokhov 	}
365af59e717SDmitry Torokhov 
366af59e717SDmitry Torokhov 	return mode;
367af59e717SDmitry Torokhov }
368af59e717SDmitry Torokhov 
369af59e717SDmitry Torokhov static const struct attribute_group ptp_group = {
370af59e717SDmitry Torokhov 	.is_visible	= ptp_is_attribute_visible,
371af59e717SDmitry Torokhov 	.attrs		= ptp_attrs,
372af59e717SDmitry Torokhov };
373af59e717SDmitry Torokhov 
374af59e717SDmitry Torokhov const struct attribute_group *ptp_groups[] = {
375af59e717SDmitry Torokhov 	&ptp_group,
376af59e717SDmitry Torokhov 	NULL
377af59e717SDmitry Torokhov };
378d94ba80eSRichard Cochran 
ptp_pin_name2index(struct ptp_clock * ptp,const char * name)379653104d1SRichard Cochran static int ptp_pin_name2index(struct ptp_clock *ptp, const char *name)
380653104d1SRichard Cochran {
381653104d1SRichard Cochran 	int i;
382653104d1SRichard Cochran 	for (i = 0; i < ptp->info->n_pins; i++) {
383653104d1SRichard Cochran 		if (!strcmp(ptp->info->pin_config[i].name, name))
384653104d1SRichard Cochran 			return i;
385653104d1SRichard Cochran 	}
386653104d1SRichard Cochran 	return -1;
387653104d1SRichard Cochran }
388653104d1SRichard Cochran 
ptp_pin_show(struct device * dev,struct device_attribute * attr,char * page)389653104d1SRichard Cochran static ssize_t ptp_pin_show(struct device *dev, struct device_attribute *attr,
390653104d1SRichard Cochran 			    char *page)
391653104d1SRichard Cochran {
392653104d1SRichard Cochran 	struct ptp_clock *ptp = dev_get_drvdata(dev);
393653104d1SRichard Cochran 	unsigned int func, chan;
394653104d1SRichard Cochran 	int index;
395653104d1SRichard Cochran 
396653104d1SRichard Cochran 	index = ptp_pin_name2index(ptp, attr->attr.name);
397653104d1SRichard Cochran 	if (index < 0)
398653104d1SRichard Cochran 		return -EINVAL;
399653104d1SRichard Cochran 
400653104d1SRichard Cochran 	if (mutex_lock_interruptible(&ptp->pincfg_mux))
401653104d1SRichard Cochran 		return -ERESTARTSYS;
402653104d1SRichard Cochran 
403653104d1SRichard Cochran 	func = ptp->info->pin_config[index].func;
404653104d1SRichard Cochran 	chan = ptp->info->pin_config[index].chan;
405653104d1SRichard Cochran 
406653104d1SRichard Cochran 	mutex_unlock(&ptp->pincfg_mux);
407653104d1SRichard Cochran 
408e2cf0765SYang Guang 	return sysfs_emit(page, "%u %u\n", func, chan);
409653104d1SRichard Cochran }
410653104d1SRichard Cochran 
ptp_pin_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)411653104d1SRichard Cochran static ssize_t ptp_pin_store(struct device *dev, struct device_attribute *attr,
412653104d1SRichard Cochran 			     const char *buf, size_t count)
413653104d1SRichard Cochran {
414653104d1SRichard Cochran 	struct ptp_clock *ptp = dev_get_drvdata(dev);
415653104d1SRichard Cochran 	unsigned int func, chan;
416653104d1SRichard Cochran 	int cnt, err, index;
417653104d1SRichard Cochran 
418653104d1SRichard Cochran 	cnt = sscanf(buf, "%u %u", &func, &chan);
419653104d1SRichard Cochran 	if (cnt != 2)
420653104d1SRichard Cochran 		return -EINVAL;
421653104d1SRichard Cochran 
422653104d1SRichard Cochran 	index = ptp_pin_name2index(ptp, attr->attr.name);
423653104d1SRichard Cochran 	if (index < 0)
424653104d1SRichard Cochran 		return -EINVAL;
425653104d1SRichard Cochran 
426653104d1SRichard Cochran 	if (mutex_lock_interruptible(&ptp->pincfg_mux))
427653104d1SRichard Cochran 		return -ERESTARTSYS;
428653104d1SRichard Cochran 	err = ptp_set_pinfunc(ptp, index, func, chan);
429653104d1SRichard Cochran 	mutex_unlock(&ptp->pincfg_mux);
430653104d1SRichard Cochran 	if (err)
431653104d1SRichard Cochran 		return err;
432653104d1SRichard Cochran 
433653104d1SRichard Cochran 	return count;
434653104d1SRichard Cochran }
435653104d1SRichard Cochran 
ptp_populate_pin_groups(struct ptp_clock * ptp)43685a66e55SDmitry Torokhov int ptp_populate_pin_groups(struct ptp_clock *ptp)
437d94ba80eSRichard Cochran {
438653104d1SRichard Cochran 	struct ptp_clock_info *info = ptp->info;
439653104d1SRichard Cochran 	int err = -ENOMEM, i, n_pins = info->n_pins;
440653104d1SRichard Cochran 
44185a66e55SDmitry Torokhov 	if (!n_pins)
44285a66e55SDmitry Torokhov 		return 0;
44385a66e55SDmitry Torokhov 
4446f7aa56bSDmitry Torokhov 	ptp->pin_dev_attr = kcalloc(n_pins, sizeof(*ptp->pin_dev_attr),
445653104d1SRichard Cochran 				    GFP_KERNEL);
446653104d1SRichard Cochran 	if (!ptp->pin_dev_attr)
447653104d1SRichard Cochran 		goto no_dev_attr;
448653104d1SRichard Cochran 
4496f7aa56bSDmitry Torokhov 	ptp->pin_attr = kcalloc(1 + n_pins, sizeof(*ptp->pin_attr), GFP_KERNEL);
450653104d1SRichard Cochran 	if (!ptp->pin_attr)
451653104d1SRichard Cochran 		goto no_pin_attr;
452653104d1SRichard Cochran 
453653104d1SRichard Cochran 	for (i = 0; i < n_pins; i++) {
454653104d1SRichard Cochran 		struct device_attribute *da = &ptp->pin_dev_attr[i];
455653104d1SRichard Cochran 		sysfs_attr_init(&da->attr);
456653104d1SRichard Cochran 		da->attr.name = info->pin_config[i].name;
457653104d1SRichard Cochran 		da->attr.mode = 0644;
458653104d1SRichard Cochran 		da->show = ptp_pin_show;
459653104d1SRichard Cochran 		da->store = ptp_pin_store;
460653104d1SRichard Cochran 		ptp->pin_attr[i] = &da->attr;
461653104d1SRichard Cochran 	}
462653104d1SRichard Cochran 
463653104d1SRichard Cochran 	ptp->pin_attr_group.name = "pins";
464653104d1SRichard Cochran 	ptp->pin_attr_group.attrs = ptp->pin_attr;
465653104d1SRichard Cochran 
46685a66e55SDmitry Torokhov 	ptp->pin_attr_groups[0] = &ptp->pin_attr_group;
46785a66e55SDmitry Torokhov 
468653104d1SRichard Cochran 	return 0;
469653104d1SRichard Cochran 
470653104d1SRichard Cochran no_pin_attr:
471653104d1SRichard Cochran 	kfree(ptp->pin_dev_attr);
472653104d1SRichard Cochran no_dev_attr:
473653104d1SRichard Cochran 	return err;
474653104d1SRichard Cochran }
475653104d1SRichard Cochran 
ptp_cleanup_pin_groups(struct ptp_clock * ptp)47685a66e55SDmitry Torokhov void ptp_cleanup_pin_groups(struct ptp_clock *ptp)
477d94ba80eSRichard Cochran {
47885a66e55SDmitry Torokhov 	kfree(ptp->pin_attr);
47985a66e55SDmitry Torokhov 	kfree(ptp->pin_dev_attr);
480d94ba80eSRichard Cochran }
481