2019-05-20 10:19:02 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-04-22 14:03:08 +04:00
/*
* PTP 1588 clock support - sysfs interface .
*
* Copyright ( C ) 2010 OMICRON electronics GmbH
2021-06-30 11:11:53 +03:00
* Copyright 2021 NXP
2011-04-22 14:03:08 +04:00
*/
# include <linux/capability.h>
2014-03-21 01:21:54 +04:00
# include <linux/slab.h>
2011-04-22 14:03:08 +04:00
# include "ptp_private.h"
static ssize_t clock_name_show ( struct device * dev ,
struct device_attribute * attr , char * page )
{
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
2022-01-27 03:02:36 +03:00
return sysfs_emit ( page , " %s \n " , ptp - > info - > name ) ;
2011-04-22 14:03:08 +04:00
}
ptp: use permission-specific DEVICE_ATTR variants
Use DEVICE_ATTR_RO for read only attributes. This simplifies the
source code, improves readbility, and reduces the chance of
inconsistencies.
The semantic patch that makes this change is as follows:
(http://coccinelle.lip6.fr/)
// <smpl>
@ro@
declarer name DEVICE_ATTR;
identifier x,x_show;
@@
DEVICE_ATTR(x, \(0444\|S_IRUGO\), x_show, NULL);
@script:ocaml@
x << ro.x;
x_show << ro.x_show;
@@
if not (x^"_show" = x_show) then Coccilib.include_match false
@@
declarer name DEVICE_ATTR_RO;
identifier ro.x,ro.x_show;
@@
- DEVICE_ATTR(x, \(0444\|S_IRUGO\), x_show, NULL);
+ DEVICE_ATTR_RO(x);
// </smpl>
Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-10-29 22:37:06 +03:00
static DEVICE_ATTR_RO ( clock_name ) ;
2011-04-22 14:03:08 +04:00
2013-07-25 02:05:20 +04:00
# define PTP_SHOW_INT(name, var) \
static ssize_t var # # _show ( struct device * dev , \
2011-04-22 14:03:08 +04:00
struct device_attribute * attr , char * page ) \
{ \
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ; \
2013-07-25 02:05:20 +04:00
return snprintf ( page , PAGE_SIZE - 1 , " %d \n " , ptp - > info - > var ) ; \
} \
static DEVICE_ATTR ( name , 0444 , var # # _show , NULL ) ;
2011-04-22 14:03:08 +04:00
2013-07-25 02:05:20 +04:00
PTP_SHOW_INT ( max_adjustment , max_adj ) ;
PTP_SHOW_INT ( n_alarms , n_alarm ) ;
PTP_SHOW_INT ( n_external_timestamps , n_ext_ts ) ;
PTP_SHOW_INT ( n_periodic_outputs , n_per_out ) ;
2014-03-21 01:21:54 +04:00
PTP_SHOW_INT ( n_programmable_pins , n_pins ) ;
2013-07-25 02:05:20 +04:00
PTP_SHOW_INT ( pps_available , pps ) ;
2011-04-22 14:03:08 +04:00
static ssize_t extts_enable_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
struct ptp_clock_info * ops = ptp - > info ;
struct ptp_clock_request req = { . type = PTP_CLK_REQ_EXTTS } ;
int cnt , enable ;
int err = - EINVAL ;
cnt = sscanf ( buf , " %u %d " , & req . extts . index , & enable ) ;
if ( cnt ! = 2 )
goto out ;
if ( req . extts . index > = ops - > n_ext_ts )
goto out ;
err = ops - > enable ( ops , & req , enable ? 1 : 0 ) ;
if ( err )
goto out ;
return count ;
out :
return err ;
}
2017-02-14 21:23:33 +03:00
static DEVICE_ATTR ( extts_enable , 0220 , NULL , extts_enable_store ) ;
2011-04-22 14:03:08 +04:00
static ssize_t extts_fifo_show ( struct device * dev ,
struct device_attribute * attr , char * page )
{
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
struct timestamp_event_queue * queue = & ptp - > tsevq ;
struct ptp_extts_event event ;
unsigned long flags ;
size_t qcnt ;
int cnt = 0 ;
memset ( & event , 0 , sizeof ( event ) ) ;
if ( mutex_lock_interruptible ( & ptp - > tsevq_mux ) )
return - ERESTARTSYS ;
spin_lock_irqsave ( & queue - > lock , flags ) ;
qcnt = queue_cnt ( queue ) ;
if ( qcnt ) {
event = queue - > buf [ queue - > head ] ;
queue - > head = ( queue - > head + 1 ) % PTP_MAX_TIMESTAMPS ;
}
spin_unlock_irqrestore ( & queue - > lock , flags ) ;
if ( ! qcnt )
goto out ;
cnt = snprintf ( page , PAGE_SIZE , " %u %lld %u \n " ,
event . index , event . t . sec , event . t . nsec ) ;
out :
mutex_unlock ( & ptp - > tsevq_mux ) ;
return cnt ;
}
2017-02-14 21:23:33 +03:00
static DEVICE_ATTR ( fifo , 0444 , extts_fifo_show , NULL ) ;
2011-04-22 14:03:08 +04:00
static ssize_t period_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
struct ptp_clock_info * ops = ptp - > info ;
struct ptp_clock_request req = { . type = PTP_CLK_REQ_PEROUT } ;
int cnt , enable , err = - EINVAL ;
cnt = sscanf ( buf , " %u %lld %u %lld %u " , & req . perout . index ,
& req . perout . start . sec , & req . perout . start . nsec ,
& req . perout . period . sec , & req . perout . period . nsec ) ;
if ( cnt ! = 5 )
goto out ;
if ( req . perout . index > = ops - > n_per_out )
goto out ;
enable = req . perout . period . sec | | req . perout . period . nsec ;
err = ops - > enable ( ops , & req , enable ) ;
if ( err )
goto out ;
return count ;
out :
return err ;
}
2017-02-14 21:23:33 +03:00
static DEVICE_ATTR ( period , 0220 , NULL , period_store ) ;
2011-04-22 14:03:08 +04:00
static ssize_t pps_enable_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
struct ptp_clock_info * ops = ptp - > info ;
struct ptp_clock_request req = { . type = PTP_CLK_REQ_PPS } ;
int cnt , enable ;
int err = - EINVAL ;
if ( ! capable ( CAP_SYS_TIME ) )
return - EPERM ;
cnt = sscanf ( buf , " %d " , & enable ) ;
if ( cnt ! = 1 )
goto out ;
err = ops - > enable ( ops , & req , enable ? 1 : 0 ) ;
if ( err )
goto out ;
return count ;
out :
return err ;
}
2017-02-14 21:23:33 +03:00
static DEVICE_ATTR ( pps_enable , 0220 , NULL , pps_enable_store ) ;
2021-06-30 11:11:53 +03:00
static int unregister_vclock ( struct device * dev , void * data )
{
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
struct ptp_clock_info * info = ptp - > info ;
struct ptp_vclock * vclock ;
2021-08-07 04:15:46 +03:00
u32 * num = data ;
2021-06-30 11:11:53 +03:00
vclock = info_to_vclock ( info ) ;
dev_info ( dev - > parent , " delete virtual clock ptp%d \n " ,
vclock - > clock - > index ) ;
ptp_vclock_unregister ( vclock ) ;
( * num ) - - ;
/* For break. Not error. */
if ( * num = = 0 )
return - EINVAL ;
return 0 ;
}
static ssize_t n_vclocks_show ( struct device * dev ,
struct device_attribute * attr , char * page )
{
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
ssize_t size ;
if ( mutex_lock_interruptible ( & ptp - > n_vclocks_mux ) )
return - ERESTARTSYS ;
2021-07-05 12:46:17 +03:00
size = snprintf ( page , PAGE_SIZE - 1 , " %u \n " , ptp - > n_vclocks ) ;
2021-06-30 11:11:53 +03:00
mutex_unlock ( & ptp - > n_vclocks_mux ) ;
return size ;
}
static ssize_t n_vclocks_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
struct ptp_vclock * vclock ;
int err = - EINVAL ;
u32 num , i ;
if ( kstrtou32 ( buf , 0 , & num ) )
return err ;
if ( mutex_lock_interruptible ( & ptp - > n_vclocks_mux ) )
return - ERESTARTSYS ;
if ( num > ptp - > max_vclocks ) {
dev_err ( dev , " max value is %d \n " , ptp - > max_vclocks ) ;
goto out ;
}
/* Need to create more vclocks */
if ( num > ptp - > n_vclocks ) {
for ( i = 0 ; i < num - ptp - > n_vclocks ; i + + ) {
vclock = ptp_vclock_register ( ptp ) ;
if ( ! vclock )
goto out ;
2021-06-30 11:11:54 +03:00
* ( ptp - > vclock_index + ptp - > n_vclocks + i ) =
vclock - > clock - > index ;
2021-06-30 11:11:53 +03:00
dev_info ( dev , " new virtual clock ptp%d \n " ,
vclock - > clock - > index ) ;
}
}
/* Need to delete vclocks */
if ( num < ptp - > n_vclocks ) {
i = ptp - > n_vclocks - num ;
device_for_each_child_reverse ( dev , & i ,
unregister_vclock ) ;
2021-06-30 11:11:54 +03:00
for ( i = 1 ; i < = ptp - > n_vclocks - num ; i + + )
* ( ptp - > vclock_index + ptp - > n_vclocks - i ) = - 1 ;
2021-06-30 11:11:53 +03:00
}
2022-05-06 23:01:37 +03:00
/* Need to inform about changed physical clock behavior */
if ( ! ptp - > has_cycles ) {
if ( num = = 0 )
dev_info ( dev , " only physical clock in use now \n " ) ;
else
dev_info ( dev , " guarantee physical clock free running \n " ) ;
}
2021-06-30 11:11:53 +03:00
ptp - > n_vclocks = num ;
mutex_unlock ( & ptp - > n_vclocks_mux ) ;
return count ;
out :
mutex_unlock ( & ptp - > n_vclocks_mux ) ;
return err ;
}
static DEVICE_ATTR_RW ( n_vclocks ) ;
static ssize_t max_vclocks_show ( struct device * dev ,
struct device_attribute * attr , char * page )
{
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
ssize_t size ;
2021-07-05 12:46:17 +03:00
size = snprintf ( page , PAGE_SIZE - 1 , " %u \n " , ptp - > max_vclocks ) ;
2021-06-30 11:11:53 +03:00
return size ;
}
static ssize_t max_vclocks_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
2021-06-30 11:11:54 +03:00
unsigned int * vclock_index ;
int err = - EINVAL ;
size_t size ;
2021-06-30 11:11:53 +03:00
u32 max ;
if ( kstrtou32 ( buf , 0 , & max ) | | max = = 0 )
return - EINVAL ;
if ( max = = ptp - > max_vclocks )
return count ;
if ( mutex_lock_interruptible ( & ptp - > n_vclocks_mux ) )
return - ERESTARTSYS ;
2021-06-30 11:11:54 +03:00
if ( max < ptp - > n_vclocks )
goto out ;
size = sizeof ( int ) * max ;
vclock_index = kzalloc ( size , GFP_KERNEL ) ;
if ( ! vclock_index ) {
err = - ENOMEM ;
goto out ;
2021-06-30 11:11:53 +03:00
}
2021-06-30 11:11:54 +03:00
size = sizeof ( int ) * ptp - > n_vclocks ;
memcpy ( vclock_index , ptp - > vclock_index , size ) ;
kfree ( ptp - > vclock_index ) ;
ptp - > vclock_index = vclock_index ;
2021-06-30 11:11:53 +03:00
ptp - > max_vclocks = max ;
mutex_unlock ( & ptp - > n_vclocks_mux ) ;
return count ;
2021-06-30 11:11:54 +03:00
out :
mutex_unlock ( & ptp - > n_vclocks_mux ) ;
return err ;
2021-06-30 11:11:53 +03:00
}
static DEVICE_ATTR_RW ( max_vclocks ) ;
2017-02-14 21:23:33 +03:00
static struct attribute * ptp_attrs [ ] = {
& dev_attr_clock_name . attr ,
& dev_attr_max_adjustment . attr ,
& dev_attr_n_alarms . attr ,
& dev_attr_n_external_timestamps . attr ,
& dev_attr_n_periodic_outputs . attr ,
& dev_attr_n_programmable_pins . attr ,
& dev_attr_pps_available . attr ,
& dev_attr_extts_enable . attr ,
& dev_attr_fifo . attr ,
& dev_attr_period . attr ,
& dev_attr_pps_enable . attr ,
2021-06-30 11:11:53 +03:00
& dev_attr_n_vclocks . attr ,
& dev_attr_max_vclocks . attr ,
2017-02-14 21:23:33 +03:00
NULL
} ;
static umode_t ptp_is_attribute_visible ( struct kobject * kobj ,
struct attribute * attr , int n )
{
struct device * dev = kobj_to_dev ( kobj ) ;
struct ptp_clock * ptp = dev_get_drvdata ( dev ) ;
struct ptp_clock_info * info = ptp - > info ;
umode_t mode = attr - > mode ;
if ( attr = = & dev_attr_extts_enable . attr | |
attr = = & dev_attr_fifo . attr ) {
if ( ! info - > n_ext_ts )
mode = 0 ;
} else if ( attr = = & dev_attr_period . attr ) {
if ( ! info - > n_per_out )
mode = 0 ;
} else if ( attr = = & dev_attr_pps_enable . attr ) {
if ( ! info - > pps )
mode = 0 ;
2021-06-30 11:11:53 +03:00
} else if ( attr = = & dev_attr_n_vclocks . attr | |
attr = = & dev_attr_max_vclocks . attr ) {
if ( ptp - > is_virtual_clock )
mode = 0 ;
2017-02-14 21:23:33 +03:00
}
return mode ;
}
static const struct attribute_group ptp_group = {
. is_visible = ptp_is_attribute_visible ,
. attrs = ptp_attrs ,
} ;
const struct attribute_group * ptp_groups [ ] = {
& ptp_group ,
NULL
} ;
2011-04-22 14:03:08 +04:00
2014-03-21 01:21:54 +04:00
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 ) ;
2022-01-27 03:02:36 +03:00
return sysfs_emit ( page , " %u %u \n " , func , chan ) ;
2014-03-21 01:21:54 +04:00
}
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 ;
}
2017-02-14 21:23:34 +03:00
int ptp_populate_pin_groups ( struct ptp_clock * ptp )
2011-04-22 14:03:08 +04:00
{
2014-03-21 01:21:54 +04:00
struct ptp_clock_info * info = ptp - > info ;
int err = - ENOMEM , i , n_pins = info - > n_pins ;
2017-02-14 21:23:34 +03:00
if ( ! n_pins )
return 0 ;
2017-02-14 21:23:32 +03:00
ptp - > pin_dev_attr = kcalloc ( n_pins , sizeof ( * ptp - > pin_dev_attr ) ,
2014-03-21 01:21:54 +04:00
GFP_KERNEL ) ;
if ( ! ptp - > pin_dev_attr )
goto no_dev_attr ;
2017-02-14 21:23:32 +03:00
ptp - > pin_attr = kcalloc ( 1 + n_pins , sizeof ( * ptp - > pin_attr ) , GFP_KERNEL ) ;
2014-03-21 01:21:54 +04:00
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 ;
2017-02-14 21:23:34 +03:00
ptp - > pin_attr_groups [ 0 ] = & ptp - > pin_attr_group ;
2014-03-21 01:21:54 +04:00
return 0 ;
no_pin_attr :
kfree ( ptp - > pin_dev_attr ) ;
no_dev_attr :
return err ;
}
2017-02-14 21:23:34 +03:00
void ptp_cleanup_pin_groups ( struct ptp_clock * ptp )
2011-04-22 14:03:08 +04:00
{
2017-02-14 21:23:34 +03:00
kfree ( ptp - > pin_attr ) ;
kfree ( ptp - > pin_dev_attr ) ;
2011-04-22 14:03:08 +04:00
}