18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PTP 1588 clock support - sysfs interface. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 OMICRON electronics GmbH 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/capability.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "ptp_private.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic ssize_t clock_name_show(struct device *dev, 138c2ecf20Sopenharmony_ci struct device_attribute *attr, char *page) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci struct ptp_clock *ptp = dev_get_drvdata(dev); 168c2ecf20Sopenharmony_ci return sysfs_emit(page, "%s\n", ptp->info->name); 178c2ecf20Sopenharmony_ci} 188c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(clock_name); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define PTP_SHOW_INT(name, var) \ 218c2ecf20Sopenharmony_cistatic ssize_t var##_show(struct device *dev, \ 228c2ecf20Sopenharmony_ci struct device_attribute *attr, char *page) \ 238c2ecf20Sopenharmony_ci{ \ 248c2ecf20Sopenharmony_ci struct ptp_clock *ptp = dev_get_drvdata(dev); \ 258c2ecf20Sopenharmony_ci return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->info->var); \ 268c2ecf20Sopenharmony_ci} \ 278c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, 0444, var##_show, NULL); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciPTP_SHOW_INT(max_adjustment, max_adj); 308c2ecf20Sopenharmony_ciPTP_SHOW_INT(n_alarms, n_alarm); 318c2ecf20Sopenharmony_ciPTP_SHOW_INT(n_external_timestamps, n_ext_ts); 328c2ecf20Sopenharmony_ciPTP_SHOW_INT(n_periodic_outputs, n_per_out); 338c2ecf20Sopenharmony_ciPTP_SHOW_INT(n_programmable_pins, n_pins); 348c2ecf20Sopenharmony_ciPTP_SHOW_INT(pps_available, pps); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic ssize_t extts_enable_store(struct device *dev, 378c2ecf20Sopenharmony_ci struct device_attribute *attr, 388c2ecf20Sopenharmony_ci const char *buf, size_t count) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct ptp_clock *ptp = dev_get_drvdata(dev); 418c2ecf20Sopenharmony_ci struct ptp_clock_info *ops = ptp->info; 428c2ecf20Sopenharmony_ci struct ptp_clock_request req = { .type = PTP_CLK_REQ_EXTTS }; 438c2ecf20Sopenharmony_ci int cnt, enable; 448c2ecf20Sopenharmony_ci int err = -EINVAL; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci cnt = sscanf(buf, "%u %d", &req.extts.index, &enable); 478c2ecf20Sopenharmony_ci if (cnt != 2) 488c2ecf20Sopenharmony_ci goto out; 498c2ecf20Sopenharmony_ci if (req.extts.index >= ops->n_ext_ts) 508c2ecf20Sopenharmony_ci goto out; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci err = ops->enable(ops, &req, enable ? 1 : 0); 538c2ecf20Sopenharmony_ci if (err) 548c2ecf20Sopenharmony_ci goto out; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return count; 578c2ecf20Sopenharmony_ciout: 588c2ecf20Sopenharmony_ci return err; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_cistatic DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic ssize_t extts_fifo_show(struct device *dev, 638c2ecf20Sopenharmony_ci struct device_attribute *attr, char *page) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct ptp_clock *ptp = dev_get_drvdata(dev); 668c2ecf20Sopenharmony_ci struct timestamp_event_queue *queue = &ptp->tsevq; 678c2ecf20Sopenharmony_ci struct ptp_extts_event event; 688c2ecf20Sopenharmony_ci unsigned long flags; 698c2ecf20Sopenharmony_ci size_t qcnt; 708c2ecf20Sopenharmony_ci int cnt = 0; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci memset(&event, 0, sizeof(event)); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&ptp->tsevq_mux)) 758c2ecf20Sopenharmony_ci return -ERESTARTSYS; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci spin_lock_irqsave(&queue->lock, flags); 788c2ecf20Sopenharmony_ci qcnt = queue_cnt(queue); 798c2ecf20Sopenharmony_ci if (qcnt) { 808c2ecf20Sopenharmony_ci event = queue->buf[queue->head]; 818c2ecf20Sopenharmony_ci /* Paired with READ_ONCE() in queue_cnt() */ 828c2ecf20Sopenharmony_ci WRITE_ONCE(queue->head, (queue->head + 1) % PTP_MAX_TIMESTAMPS); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&queue->lock, flags); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!qcnt) 878c2ecf20Sopenharmony_ci goto out; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci cnt = snprintf(page, PAGE_SIZE, "%u %lld %u\n", 908c2ecf20Sopenharmony_ci event.index, event.t.sec, event.t.nsec); 918c2ecf20Sopenharmony_ciout: 928c2ecf20Sopenharmony_ci mutex_unlock(&ptp->tsevq_mux); 938c2ecf20Sopenharmony_ci return cnt; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_cistatic DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic ssize_t period_store(struct device *dev, 988c2ecf20Sopenharmony_ci struct device_attribute *attr, 998c2ecf20Sopenharmony_ci const char *buf, size_t count) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct ptp_clock *ptp = dev_get_drvdata(dev); 1028c2ecf20Sopenharmony_ci struct ptp_clock_info *ops = ptp->info; 1038c2ecf20Sopenharmony_ci struct ptp_clock_request req = { .type = PTP_CLK_REQ_PEROUT }; 1048c2ecf20Sopenharmony_ci int cnt, enable, err = -EINVAL; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci cnt = sscanf(buf, "%u %lld %u %lld %u", &req.perout.index, 1078c2ecf20Sopenharmony_ci &req.perout.start.sec, &req.perout.start.nsec, 1088c2ecf20Sopenharmony_ci &req.perout.period.sec, &req.perout.period.nsec); 1098c2ecf20Sopenharmony_ci if (cnt != 5) 1108c2ecf20Sopenharmony_ci goto out; 1118c2ecf20Sopenharmony_ci if (req.perout.index >= ops->n_per_out) 1128c2ecf20Sopenharmony_ci goto out; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci enable = req.perout.period.sec || req.perout.period.nsec; 1158c2ecf20Sopenharmony_ci err = ops->enable(ops, &req, enable); 1168c2ecf20Sopenharmony_ci if (err) 1178c2ecf20Sopenharmony_ci goto out; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return count; 1208c2ecf20Sopenharmony_ciout: 1218c2ecf20Sopenharmony_ci return err; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_cistatic DEVICE_ATTR(period, 0220, NULL, period_store); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic ssize_t pps_enable_store(struct device *dev, 1268c2ecf20Sopenharmony_ci struct device_attribute *attr, 1278c2ecf20Sopenharmony_ci const char *buf, size_t count) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct ptp_clock *ptp = dev_get_drvdata(dev); 1308c2ecf20Sopenharmony_ci struct ptp_clock_info *ops = ptp->info; 1318c2ecf20Sopenharmony_ci struct ptp_clock_request req = { .type = PTP_CLK_REQ_PPS }; 1328c2ecf20Sopenharmony_ci int cnt, enable; 1338c2ecf20Sopenharmony_ci int err = -EINVAL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_TIME)) 1368c2ecf20Sopenharmony_ci return -EPERM; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci cnt = sscanf(buf, "%d", &enable); 1398c2ecf20Sopenharmony_ci if (cnt != 1) 1408c2ecf20Sopenharmony_ci goto out; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci err = ops->enable(ops, &req, enable ? 1 : 0); 1438c2ecf20Sopenharmony_ci if (err) 1448c2ecf20Sopenharmony_ci goto out; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return count; 1478c2ecf20Sopenharmony_ciout: 1488c2ecf20Sopenharmony_ci return err; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_cistatic DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic struct attribute *ptp_attrs[] = { 1538c2ecf20Sopenharmony_ci &dev_attr_clock_name.attr, 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci &dev_attr_max_adjustment.attr, 1568c2ecf20Sopenharmony_ci &dev_attr_n_alarms.attr, 1578c2ecf20Sopenharmony_ci &dev_attr_n_external_timestamps.attr, 1588c2ecf20Sopenharmony_ci &dev_attr_n_periodic_outputs.attr, 1598c2ecf20Sopenharmony_ci &dev_attr_n_programmable_pins.attr, 1608c2ecf20Sopenharmony_ci &dev_attr_pps_available.attr, 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci &dev_attr_extts_enable.attr, 1638c2ecf20Sopenharmony_ci &dev_attr_fifo.attr, 1648c2ecf20Sopenharmony_ci &dev_attr_period.attr, 1658c2ecf20Sopenharmony_ci &dev_attr_pps_enable.attr, 1668c2ecf20Sopenharmony_ci NULL 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic umode_t ptp_is_attribute_visible(struct kobject *kobj, 1708c2ecf20Sopenharmony_ci struct attribute *attr, int n) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 1738c2ecf20Sopenharmony_ci struct ptp_clock *ptp = dev_get_drvdata(dev); 1748c2ecf20Sopenharmony_ci struct ptp_clock_info *info = ptp->info; 1758c2ecf20Sopenharmony_ci umode_t mode = attr->mode; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (attr == &dev_attr_extts_enable.attr || 1788c2ecf20Sopenharmony_ci attr == &dev_attr_fifo.attr) { 1798c2ecf20Sopenharmony_ci if (!info->n_ext_ts) 1808c2ecf20Sopenharmony_ci mode = 0; 1818c2ecf20Sopenharmony_ci } else if (attr == &dev_attr_period.attr) { 1828c2ecf20Sopenharmony_ci if (!info->n_per_out) 1838c2ecf20Sopenharmony_ci mode = 0; 1848c2ecf20Sopenharmony_ci } else if (attr == &dev_attr_pps_enable.attr) { 1858c2ecf20Sopenharmony_ci if (!info->pps) 1868c2ecf20Sopenharmony_ci mode = 0; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return mode; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic const struct attribute_group ptp_group = { 1938c2ecf20Sopenharmony_ci .is_visible = ptp_is_attribute_visible, 1948c2ecf20Sopenharmony_ci .attrs = ptp_attrs, 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciconst struct attribute_group *ptp_groups[] = { 1988c2ecf20Sopenharmony_ci &ptp_group, 1998c2ecf20Sopenharmony_ci NULL 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int ptp_pin_name2index(struct ptp_clock *ptp, const char *name) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci int i; 2058c2ecf20Sopenharmony_ci for (i = 0; i < ptp->info->n_pins; i++) { 2068c2ecf20Sopenharmony_ci if (!strcmp(ptp->info->pin_config[i].name, name)) 2078c2ecf20Sopenharmony_ci return i; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci return -1; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic ssize_t ptp_pin_show(struct device *dev, struct device_attribute *attr, 2138c2ecf20Sopenharmony_ci char *page) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct ptp_clock *ptp = dev_get_drvdata(dev); 2168c2ecf20Sopenharmony_ci unsigned int func, chan; 2178c2ecf20Sopenharmony_ci int index; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci index = ptp_pin_name2index(ptp, attr->attr.name); 2208c2ecf20Sopenharmony_ci if (index < 0) 2218c2ecf20Sopenharmony_ci return -EINVAL; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&ptp->pincfg_mux)) 2248c2ecf20Sopenharmony_ci return -ERESTARTSYS; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci func = ptp->info->pin_config[index].func; 2278c2ecf20Sopenharmony_ci chan = ptp->info->pin_config[index].chan; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci mutex_unlock(&ptp->pincfg_mux); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return sysfs_emit(page, "%u %u\n", func, chan); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic ssize_t ptp_pin_store(struct device *dev, struct device_attribute *attr, 2358c2ecf20Sopenharmony_ci const char *buf, size_t count) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct ptp_clock *ptp = dev_get_drvdata(dev); 2388c2ecf20Sopenharmony_ci unsigned int func, chan; 2398c2ecf20Sopenharmony_ci int cnt, err, index; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci cnt = sscanf(buf, "%u %u", &func, &chan); 2428c2ecf20Sopenharmony_ci if (cnt != 2) 2438c2ecf20Sopenharmony_ci return -EINVAL; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci index = ptp_pin_name2index(ptp, attr->attr.name); 2468c2ecf20Sopenharmony_ci if (index < 0) 2478c2ecf20Sopenharmony_ci return -EINVAL; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&ptp->pincfg_mux)) 2508c2ecf20Sopenharmony_ci return -ERESTARTSYS; 2518c2ecf20Sopenharmony_ci err = ptp_set_pinfunc(ptp, index, func, chan); 2528c2ecf20Sopenharmony_ci mutex_unlock(&ptp->pincfg_mux); 2538c2ecf20Sopenharmony_ci if (err) 2548c2ecf20Sopenharmony_ci return err; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return count; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ciint ptp_populate_pin_groups(struct ptp_clock *ptp) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct ptp_clock_info *info = ptp->info; 2628c2ecf20Sopenharmony_ci int err = -ENOMEM, i, n_pins = info->n_pins; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (!n_pins) 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ptp->pin_dev_attr = kcalloc(n_pins, sizeof(*ptp->pin_dev_attr), 2688c2ecf20Sopenharmony_ci GFP_KERNEL); 2698c2ecf20Sopenharmony_ci if (!ptp->pin_dev_attr) 2708c2ecf20Sopenharmony_ci goto no_dev_attr; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ptp->pin_attr = kcalloc(1 + n_pins, sizeof(*ptp->pin_attr), GFP_KERNEL); 2738c2ecf20Sopenharmony_ci if (!ptp->pin_attr) 2748c2ecf20Sopenharmony_ci goto no_pin_attr; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci for (i = 0; i < n_pins; i++) { 2778c2ecf20Sopenharmony_ci struct device_attribute *da = &ptp->pin_dev_attr[i]; 2788c2ecf20Sopenharmony_ci sysfs_attr_init(&da->attr); 2798c2ecf20Sopenharmony_ci da->attr.name = info->pin_config[i].name; 2808c2ecf20Sopenharmony_ci da->attr.mode = 0644; 2818c2ecf20Sopenharmony_ci da->show = ptp_pin_show; 2828c2ecf20Sopenharmony_ci da->store = ptp_pin_store; 2838c2ecf20Sopenharmony_ci ptp->pin_attr[i] = &da->attr; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci ptp->pin_attr_group.name = "pins"; 2878c2ecf20Sopenharmony_ci ptp->pin_attr_group.attrs = ptp->pin_attr; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ptp->pin_attr_groups[0] = &ptp->pin_attr_group; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cino_pin_attr: 2948c2ecf20Sopenharmony_ci kfree(ptp->pin_dev_attr); 2958c2ecf20Sopenharmony_cino_dev_attr: 2968c2ecf20Sopenharmony_ci return err; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_civoid ptp_cleanup_pin_groups(struct ptp_clock *ptp) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci kfree(ptp->pin_attr); 3028c2ecf20Sopenharmony_ci kfree(ptp->pin_dev_attr); 3038c2ecf20Sopenharmony_ci} 304