Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ptp: expose the programmable pins via sysfs

This patch adds the sysfs hooks needed in order to get and set the
programmable pin settings.

Signed-off-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Richard Cochran and committed by
David S. Miller
653104d1 888a3687

+138
+20
Documentation/ABI/testing/sysfs-ptp
··· 54 54 This file contains the number of programmable periodic 55 55 output channels offered by the PTP hardware clock. 56 56 57 + What: /sys/class/ptp/ptpN/n_pins 58 + Date: March 2014 59 + Contact: Richard Cochran <richardcochran@gmail.com> 60 + Description: 61 + This file contains the number of programmable pins 62 + offered by the PTP hardware clock. 63 + 64 + What: /sys/class/ptp/ptpN/pins 65 + Date: March 2014 66 + Contact: Richard Cochran <richardcochran@gmail.com> 67 + Description: 68 + This directory contains one file for each programmable 69 + pin offered by the PTP hardware clock. The file name 70 + is the hardware dependent pin name. Reading from this 71 + file produces two numbers, the assigned function (see 72 + the PTP_PF_ enumeration values in linux/ptp_clock.h) 73 + and the channel number. The function and channel 74 + assignment may be changed by two writing numbers into 75 + the file. 76 + 57 77 What: /sys/class/ptp/ptpN/pps_avaiable 58 78 Date: September 2010 59 79 Contact: Richard Cochran <richardcochran@gmail.com>
+3
drivers/ptp/ptp_private.h
··· 51 51 struct mutex pincfg_mux; /* protect concurrent info->pin_config access */ 52 52 wait_queue_head_t tsev_wq; 53 53 int defunct; /* tells readers to go away when clock is being removed */ 54 + struct device_attribute *pin_dev_attr; 55 + struct attribute **pin_attr; 56 + struct attribute_group pin_attr_group; 54 57 }; 55 58 56 59 /*
+115
drivers/ptp/ptp_sysfs.c
··· 18 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 19 */ 20 20 #include <linux/capability.h> 21 + #include <linux/slab.h> 21 22 22 23 #include "ptp_private.h" 23 24 ··· 43 42 PTP_SHOW_INT(n_alarms, n_alarm); 44 43 PTP_SHOW_INT(n_external_timestamps, n_ext_ts); 45 44 PTP_SHOW_INT(n_periodic_outputs, n_per_out); 45 + PTP_SHOW_INT(n_programmable_pins, n_pins); 46 46 PTP_SHOW_INT(pps_available, pps); 47 47 48 48 static struct attribute *ptp_attrs[] = { ··· 52 50 &dev_attr_n_alarms.attr, 53 51 &dev_attr_n_external_timestamps.attr, 54 52 &dev_attr_n_periodic_outputs.attr, 53 + &dev_attr_n_programmable_pins.attr, 55 54 &dev_attr_pps_available.attr, 56 55 NULL, 57 56 }; ··· 178 175 return err; 179 176 } 180 177 178 + static int ptp_pin_name2index(struct ptp_clock *ptp, const char *name) 179 + { 180 + int i; 181 + for (i = 0; i < ptp->info->n_pins; i++) { 182 + if (!strcmp(ptp->info->pin_config[i].name, name)) 183 + return i; 184 + } 185 + return -1; 186 + } 187 + 188 + static ssize_t ptp_pin_show(struct device *dev, struct device_attribute *attr, 189 + char *page) 190 + { 191 + struct ptp_clock *ptp = dev_get_drvdata(dev); 192 + unsigned int func, chan; 193 + int index; 194 + 195 + index = ptp_pin_name2index(ptp, attr->attr.name); 196 + if (index < 0) 197 + return -EINVAL; 198 + 199 + if (mutex_lock_interruptible(&ptp->pincfg_mux)) 200 + return -ERESTARTSYS; 201 + 202 + func = ptp->info->pin_config[index].func; 203 + chan = ptp->info->pin_config[index].chan; 204 + 205 + mutex_unlock(&ptp->pincfg_mux); 206 + 207 + return snprintf(page, PAGE_SIZE, "%u %u\n", func, chan); 208 + } 209 + 210 + static ssize_t ptp_pin_store(struct device *dev, struct device_attribute *attr, 211 + const char *buf, size_t count) 212 + { 213 + struct ptp_clock *ptp = dev_get_drvdata(dev); 214 + unsigned int func, chan; 215 + int cnt, err, index; 216 + 217 + cnt = sscanf(buf, "%u %u", &func, &chan); 218 + if (cnt != 2) 219 + return -EINVAL; 220 + 221 + index = ptp_pin_name2index(ptp, attr->attr.name); 222 + if (index < 0) 223 + return -EINVAL; 224 + 225 + if (mutex_lock_interruptible(&ptp->pincfg_mux)) 226 + return -ERESTARTSYS; 227 + err = ptp_set_pinfunc(ptp, index, func, chan); 228 + mutex_unlock(&ptp->pincfg_mux); 229 + if (err) 230 + return err; 231 + 232 + return count; 233 + } 234 + 181 235 static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store); 182 236 static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL); 183 237 static DEVICE_ATTR(period, 0220, NULL, period_store); ··· 255 195 if (info->pps) 256 196 device_remove_file(dev, &dev_attr_pps_enable); 257 197 198 + if (info->n_pins) { 199 + sysfs_remove_group(&dev->kobj, &ptp->pin_attr_group); 200 + kfree(ptp->pin_attr); 201 + kfree(ptp->pin_dev_attr); 202 + } 258 203 return 0; 204 + } 205 + 206 + static int ptp_populate_pins(struct ptp_clock *ptp) 207 + { 208 + struct device *dev = ptp->dev; 209 + struct ptp_clock_info *info = ptp->info; 210 + int err = -ENOMEM, i, n_pins = info->n_pins; 211 + 212 + ptp->pin_dev_attr = kzalloc(n_pins * sizeof(*ptp->pin_dev_attr), 213 + GFP_KERNEL); 214 + if (!ptp->pin_dev_attr) 215 + goto no_dev_attr; 216 + 217 + ptp->pin_attr = kzalloc((1 + n_pins) * sizeof(struct attribute *), 218 + GFP_KERNEL); 219 + if (!ptp->pin_attr) 220 + goto no_pin_attr; 221 + 222 + for (i = 0; i < n_pins; i++) { 223 + struct device_attribute *da = &ptp->pin_dev_attr[i]; 224 + sysfs_attr_init(&da->attr); 225 + da->attr.name = info->pin_config[i].name; 226 + da->attr.mode = 0644; 227 + da->show = ptp_pin_show; 228 + da->store = ptp_pin_store; 229 + ptp->pin_attr[i] = &da->attr; 230 + } 231 + 232 + ptp->pin_attr_group.name = "pins"; 233 + ptp->pin_attr_group.attrs = ptp->pin_attr; 234 + 235 + err = sysfs_create_group(&dev->kobj, &ptp->pin_attr_group); 236 + if (err) 237 + goto no_group; 238 + return 0; 239 + 240 + no_group: 241 + kfree(ptp->pin_attr); 242 + no_pin_attr: 243 + kfree(ptp->pin_dev_attr); 244 + no_dev_attr: 245 + return err; 259 246 } 260 247 261 248 int ptp_populate_sysfs(struct ptp_clock *ptp) ··· 329 222 if (err) 330 223 goto out4; 331 224 } 225 + if (info->n_pins) { 226 + err = ptp_populate_pins(ptp); 227 + if (err) 228 + goto out5; 229 + } 332 230 return 0; 231 + out5: 232 + if (info->pps) 233 + device_remove_file(dev, &dev_attr_pps_enable); 333 234 out4: 334 235 if (info->n_per_out) 335 236 device_remove_file(dev, &dev_attr_period);