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