Commit 653104d1 authored by Richard Cochran's avatar Richard Cochran Committed by David S. Miller

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: default avatarRichard Cochran <richardcochran@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 888a3687
...@@ -54,6 +54,26 @@ Description: ...@@ -54,6 +54,26 @@ Description:
This file contains the number of programmable periodic This file contains the number of programmable periodic
output channels offered by the PTP hardware clock. output channels offered by the PTP hardware clock.
What: /sys/class/ptp/ptpN/n_pins
Date: March 2014
Contact: Richard Cochran <richardcochran@gmail.com>
Description:
This file contains the number of programmable pins
offered by the PTP hardware clock.
What: /sys/class/ptp/ptpN/pins
Date: March 2014
Contact: Richard Cochran <richardcochran@gmail.com>
Description:
This directory contains one file for each programmable
pin offered by the PTP hardware clock. The file name
is the hardware dependent pin name. Reading from this
file produces two numbers, the assigned function (see
the PTP_PF_ enumeration values in linux/ptp_clock.h)
and the channel number. The function and channel
assignment may be changed by two writing numbers into
the file.
What: /sys/class/ptp/ptpN/pps_avaiable What: /sys/class/ptp/ptpN/pps_avaiable
Date: September 2010 Date: September 2010
Contact: Richard Cochran <richardcochran@gmail.com> Contact: Richard Cochran <richardcochran@gmail.com>
......
...@@ -51,6 +51,9 @@ struct ptp_clock { ...@@ -51,6 +51,9 @@ struct ptp_clock {
struct mutex pincfg_mux; /* protect concurrent info->pin_config access */ struct mutex pincfg_mux; /* protect concurrent info->pin_config access */
wait_queue_head_t tsev_wq; wait_queue_head_t tsev_wq;
int defunct; /* tells readers to go away when clock is being removed */ int defunct; /* tells readers to go away when clock is being removed */
struct device_attribute *pin_dev_attr;
struct attribute **pin_attr;
struct attribute_group pin_attr_group;
}; };
/* /*
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/slab.h>
#include "ptp_private.h" #include "ptp_private.h"
...@@ -42,6 +43,7 @@ PTP_SHOW_INT(max_adjustment, max_adj); ...@@ -42,6 +43,7 @@ PTP_SHOW_INT(max_adjustment, max_adj);
PTP_SHOW_INT(n_alarms, n_alarm); PTP_SHOW_INT(n_alarms, n_alarm);
PTP_SHOW_INT(n_external_timestamps, n_ext_ts); PTP_SHOW_INT(n_external_timestamps, n_ext_ts);
PTP_SHOW_INT(n_periodic_outputs, n_per_out); PTP_SHOW_INT(n_periodic_outputs, n_per_out);
PTP_SHOW_INT(n_programmable_pins, n_pins);
PTP_SHOW_INT(pps_available, pps); PTP_SHOW_INT(pps_available, pps);
static struct attribute *ptp_attrs[] = { static struct attribute *ptp_attrs[] = {
...@@ -50,6 +52,7 @@ static struct attribute *ptp_attrs[] = { ...@@ -50,6 +52,7 @@ static struct attribute *ptp_attrs[] = {
&dev_attr_n_alarms.attr, &dev_attr_n_alarms.attr,
&dev_attr_n_external_timestamps.attr, &dev_attr_n_external_timestamps.attr,
&dev_attr_n_periodic_outputs.attr, &dev_attr_n_periodic_outputs.attr,
&dev_attr_n_programmable_pins.attr,
&dev_attr_pps_available.attr, &dev_attr_pps_available.attr,
NULL, NULL,
}; };
...@@ -175,6 +178,63 @@ static ssize_t pps_enable_store(struct device *dev, ...@@ -175,6 +178,63 @@ static ssize_t pps_enable_store(struct device *dev,
return err; return err;
} }
static int ptp_pin_name2index(struct ptp_clock *ptp, const char *name)
{
int i;
for (i = 0; i < ptp->info->n_pins; i++) {
if (!strcmp(ptp->info->pin_config[i].name, name))
return i;
}
return -1;
}
static ssize_t ptp_pin_show(struct device *dev, struct device_attribute *attr,
char *page)
{
struct ptp_clock *ptp = dev_get_drvdata(dev);
unsigned int func, chan;
int index;
index = ptp_pin_name2index(ptp, attr->attr.name);
if (index < 0)
return -EINVAL;
if (mutex_lock_interruptible(&ptp->pincfg_mux))
return -ERESTARTSYS;
func = ptp->info->pin_config[index].func;
chan = ptp->info->pin_config[index].chan;
mutex_unlock(&ptp->pincfg_mux);
return snprintf(page, PAGE_SIZE, "%u %u\n", func, chan);
}
static ssize_t ptp_pin_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ptp_clock *ptp = dev_get_drvdata(dev);
unsigned int func, chan;
int cnt, err, index;
cnt = sscanf(buf, "%u %u", &func, &chan);
if (cnt != 2)
return -EINVAL;
index = ptp_pin_name2index(ptp, attr->attr.name);
if (index < 0)
return -EINVAL;
if (mutex_lock_interruptible(&ptp->pincfg_mux))
return -ERESTARTSYS;
err = ptp_set_pinfunc(ptp, index, func, chan);
mutex_unlock(&ptp->pincfg_mux);
if (err)
return err;
return count;
}
static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store); static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store);
static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL); static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL);
static DEVICE_ATTR(period, 0220, NULL, period_store); static DEVICE_ATTR(period, 0220, NULL, period_store);
...@@ -195,7 +255,54 @@ int ptp_cleanup_sysfs(struct ptp_clock *ptp) ...@@ -195,7 +255,54 @@ int ptp_cleanup_sysfs(struct ptp_clock *ptp)
if (info->pps) if (info->pps)
device_remove_file(dev, &dev_attr_pps_enable); device_remove_file(dev, &dev_attr_pps_enable);
if (info->n_pins) {
sysfs_remove_group(&dev->kobj, &ptp->pin_attr_group);
kfree(ptp->pin_attr);
kfree(ptp->pin_dev_attr);
}
return 0;
}
static int ptp_populate_pins(struct ptp_clock *ptp)
{
struct device *dev = ptp->dev;
struct ptp_clock_info *info = ptp->info;
int err = -ENOMEM, i, n_pins = info->n_pins;
ptp->pin_dev_attr = kzalloc(n_pins * sizeof(*ptp->pin_dev_attr),
GFP_KERNEL);
if (!ptp->pin_dev_attr)
goto no_dev_attr;
ptp->pin_attr = kzalloc((1 + n_pins) * sizeof(struct attribute *),
GFP_KERNEL);
if (!ptp->pin_attr)
goto no_pin_attr;
for (i = 0; i < n_pins; i++) {
struct device_attribute *da = &ptp->pin_dev_attr[i];
sysfs_attr_init(&da->attr);
da->attr.name = info->pin_config[i].name;
da->attr.mode = 0644;
da->show = ptp_pin_show;
da->store = ptp_pin_store;
ptp->pin_attr[i] = &da->attr;
}
ptp->pin_attr_group.name = "pins";
ptp->pin_attr_group.attrs = ptp->pin_attr;
err = sysfs_create_group(&dev->kobj, &ptp->pin_attr_group);
if (err)
goto no_group;
return 0; return 0;
no_group:
kfree(ptp->pin_attr);
no_pin_attr:
kfree(ptp->pin_dev_attr);
no_dev_attr:
return err;
} }
int ptp_populate_sysfs(struct ptp_clock *ptp) int ptp_populate_sysfs(struct ptp_clock *ptp)
...@@ -222,7 +329,15 @@ int ptp_populate_sysfs(struct ptp_clock *ptp) ...@@ -222,7 +329,15 @@ int ptp_populate_sysfs(struct ptp_clock *ptp)
if (err) if (err)
goto out4; goto out4;
} }
if (info->n_pins) {
err = ptp_populate_pins(ptp);
if (err)
goto out5;
}
return 0; return 0;
out5:
if (info->pps)
device_remove_file(dev, &dev_attr_pps_enable);
out4: out4:
if (info->n_per_out) if (info->n_per_out)
device_remove_file(dev, &dev_attr_period); device_remove_file(dev, &dev_attr_period);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment