1 /* 2 * PTP 1588 clock support - sysfs interface. 3 * 4 * Copyright (C) 2010 OMICRON electronics GmbH 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 #include <linux/capability.h> 21 22 #include "ptp_private.h" 23 24 static ssize_t clock_name_show(struct device *dev, 25 struct device_attribute *attr, char *page) 26 { 27 struct ptp_clock *ptp = dev_get_drvdata(dev); 28 return snprintf(page, PAGE_SIZE-1, "%s\n", ptp->info->name); 29 } 30 static DEVICE_ATTR(clock_name, 0444, clock_name_show, NULL); 31 32 #define PTP_SHOW_INT(name, var) \ 33 static ssize_t var##_show(struct device *dev, \ 34 struct device_attribute *attr, char *page) \ 35 { \ 36 struct ptp_clock *ptp = dev_get_drvdata(dev); \ 37 return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->info->var); \ 38 } \ 39 static DEVICE_ATTR(name, 0444, var##_show, NULL); 40 41 PTP_SHOW_INT(max_adjustment, max_adj); 42 PTP_SHOW_INT(n_alarms, n_alarm); 43 PTP_SHOW_INT(n_external_timestamps, n_ext_ts); 44 PTP_SHOW_INT(n_periodic_outputs, n_per_out); 45 PTP_SHOW_INT(pps_available, pps); 46 47 static struct attribute *ptp_attrs[] = { 48 &dev_attr_clock_name.attr, 49 &dev_attr_max_adjustment.attr, 50 &dev_attr_n_alarms.attr, 51 &dev_attr_n_external_timestamps.attr, 52 &dev_attr_n_periodic_outputs.attr, 53 &dev_attr_pps_available.attr, 54 NULL, 55 }; 56 57 static const struct attribute_group ptp_group = { 58 .attrs = ptp_attrs, 59 }; 60 61 const struct attribute_group *ptp_groups[] = { 62 &ptp_group, 63 NULL, 64 }; 65 66 67 static ssize_t extts_enable_store(struct device *dev, 68 struct device_attribute *attr, 69 const char *buf, size_t count) 70 { 71 struct ptp_clock *ptp = dev_get_drvdata(dev); 72 struct ptp_clock_info *ops = ptp->info; 73 struct ptp_clock_request req = { .type = PTP_CLK_REQ_EXTTS }; 74 int cnt, enable; 75 int err = -EINVAL; 76 77 cnt = sscanf(buf, "%u %d", &req.extts.index, &enable); 78 if (cnt != 2) 79 goto out; 80 if (req.extts.index >= ops->n_ext_ts) 81 goto out; 82 83 err = ops->enable(ops, &req, enable ? 1 : 0); 84 if (err) 85 goto out; 86 87 return count; 88 out: 89 return err; 90 } 91 92 static ssize_t extts_fifo_show(struct device *dev, 93 struct device_attribute *attr, char *page) 94 { 95 struct ptp_clock *ptp = dev_get_drvdata(dev); 96 struct timestamp_event_queue *queue = &ptp->tsevq; 97 struct ptp_extts_event event; 98 unsigned long flags; 99 size_t qcnt; 100 int cnt = 0; 101 102 memset(&event, 0, sizeof(event)); 103 104 if (mutex_lock_interruptible(&ptp->tsevq_mux)) 105 return -ERESTARTSYS; 106 107 spin_lock_irqsave(&queue->lock, flags); 108 qcnt = queue_cnt(queue); 109 if (qcnt) { 110 event = queue->buf[queue->head]; 111 queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS; 112 } 113 spin_unlock_irqrestore(&queue->lock, flags); 114 115 if (!qcnt) 116 goto out; 117 118 cnt = snprintf(page, PAGE_SIZE, "%u %lld %u\n", 119 event.index, event.t.sec, event.t.nsec); 120 out: 121 mutex_unlock(&ptp->tsevq_mux); 122 return cnt; 123 } 124 125 static ssize_t period_store(struct device *dev, 126 struct device_attribute *attr, 127 const char *buf, size_t count) 128 { 129 struct ptp_clock *ptp = dev_get_drvdata(dev); 130 struct ptp_clock_info *ops = ptp->info; 131 struct ptp_clock_request req = { .type = PTP_CLK_REQ_PEROUT }; 132 int cnt, enable, err = -EINVAL; 133 134 cnt = sscanf(buf, "%u %lld %u %lld %u", &req.perout.index, 135 &req.perout.start.sec, &req.perout.start.nsec, 136 &req.perout.period.sec, &req.perout.period.nsec); 137 if (cnt != 5) 138 goto out; 139 if (req.perout.index >= ops->n_per_out) 140 goto out; 141 142 enable = req.perout.period.sec || req.perout.period.nsec; 143 err = ops->enable(ops, &req, enable); 144 if (err) 145 goto out; 146 147 return count; 148 out: 149 return err; 150 } 151 152 static ssize_t pps_enable_store(struct device *dev, 153 struct device_attribute *attr, 154 const char *buf, size_t count) 155 { 156 struct ptp_clock *ptp = dev_get_drvdata(dev); 157 struct ptp_clock_info *ops = ptp->info; 158 struct ptp_clock_request req = { .type = PTP_CLK_REQ_PPS }; 159 int cnt, enable; 160 int err = -EINVAL; 161 162 if (!capable(CAP_SYS_TIME)) 163 return -EPERM; 164 165 cnt = sscanf(buf, "%d", &enable); 166 if (cnt != 1) 167 goto out; 168 169 err = ops->enable(ops, &req, enable ? 1 : 0); 170 if (err) 171 goto out; 172 173 return count; 174 out: 175 return err; 176 } 177 178 static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store); 179 static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL); 180 static DEVICE_ATTR(period, 0220, NULL, period_store); 181 static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store); 182 183 int ptp_cleanup_sysfs(struct ptp_clock *ptp) 184 { 185 struct device *dev = ptp->dev; 186 struct ptp_clock_info *info = ptp->info; 187 188 if (info->n_ext_ts) { 189 device_remove_file(dev, &dev_attr_extts_enable); 190 device_remove_file(dev, &dev_attr_fifo); 191 } 192 if (info->n_per_out) 193 device_remove_file(dev, &dev_attr_period); 194 195 if (info->pps) 196 device_remove_file(dev, &dev_attr_pps_enable); 197 198 return 0; 199 } 200 201 int ptp_populate_sysfs(struct ptp_clock *ptp) 202 { 203 struct device *dev = ptp->dev; 204 struct ptp_clock_info *info = ptp->info; 205 int err; 206 207 if (info->n_ext_ts) { 208 err = device_create_file(dev, &dev_attr_extts_enable); 209 if (err) 210 goto out1; 211 err = device_create_file(dev, &dev_attr_fifo); 212 if (err) 213 goto out2; 214 } 215 if (info->n_per_out) { 216 err = device_create_file(dev, &dev_attr_period); 217 if (err) 218 goto out3; 219 } 220 if (info->pps) { 221 err = device_create_file(dev, &dev_attr_pps_enable); 222 if (err) 223 goto out4; 224 } 225 return 0; 226 out4: 227 if (info->n_per_out) 228 device_remove_file(dev, &dev_attr_period); 229 out3: 230 if (info->n_ext_ts) 231 device_remove_file(dev, &dev_attr_fifo); 232 out2: 233 if (info->n_ext_ts) 234 device_remove_file(dev, &dev_attr_extts_enable); 235 out1: 236 return err; 237 } 238