2011-04-22 12:04:55 +02:00
/*
* Driver for the National Semiconductor DP83640 PHYTER
*
* Copyright ( C ) 2010 OMICRON electronics GmbH
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2012-06-09 07:49:07 +00:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2011-04-22 12:04:55 +02:00
# include <linux/ethtool.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/mii.h>
# include <linux/module.h>
# include <linux/net_tstamp.h>
# include <linux/netdevice.h>
# include <linux/phy.h>
# include <linux/ptp_classify.h>
# include <linux/ptp_clock_kernel.h>
# include "dp83640_reg.h"
# define DP83640_PHY_ID 0x20005ce1
# define PAGESEL 0x13
# define LAYER4 0x02
# define LAYER2 0x01
2011-08-06 21:03:04 +00:00
# define MAX_RXTS 64
2011-09-20 01:43:14 +00:00
# define N_EXT_TS 6
2011-04-22 12:04:55 +02:00
# define PSF_PTPVER 2
# define PSF_EVNT 0x4000
# define PSF_RX 0x2000
# define PSF_TX 0x1000
# define EXT_EVENT 1
2011-09-20 01:43:14 +00:00
# define CAL_EVENT 7
# define CAL_TRIGGER 7
# define PER_TRIGGER 6
2011-04-22 12:04:55 +02:00
/* phyter seems to miss the mark by 16 ns */
# define ADJTIME_FIX 16
# if defined(__BIG_ENDIAN)
# define ENDIAN_FLAG 0
# elif defined(__LITTLE_ENDIAN)
# define ENDIAN_FLAG PSF_ENDIAN
# endif
# define SKB_PTP_TYPE(__skb) (*(unsigned int *)((__skb)->cb))
struct phy_rxts {
u16 ns_lo ; /* ns[15:0] */
u16 ns_hi ; /* overflow[1:0], ns[29:16] */
u16 sec_lo ; /* sec[15:0] */
u16 sec_hi ; /* sec[31:16] */
u16 seqid ; /* sequenceId[15:0] */
u16 msgtype ; /* messageType[3:0], hash[11:0] */
} ;
struct phy_txts {
u16 ns_lo ; /* ns[15:0] */
u16 ns_hi ; /* overflow[1:0], ns[29:16] */
u16 sec_lo ; /* sec[15:0] */
u16 sec_hi ; /* sec[31:16] */
} ;
struct rxts {
struct list_head list ;
unsigned long tmo ;
u64 ns ;
u16 seqid ;
u8 msgtype ;
u16 hash ;
} ;
struct dp83640_clock ;
struct dp83640_private {
struct list_head list ;
struct dp83640_clock * clock ;
struct phy_device * phydev ;
struct work_struct ts_work ;
int hwts_tx_en ;
int hwts_rx_en ;
int layer ;
int version ;
/* remember state of cfg0 during calibration */
int cfg0 ;
/* remember the last event time stamp */
struct phy_txts edata ;
/* list of rx timestamps */
struct list_head rxts ;
struct list_head rxpool ;
struct rxts rx_pool_data [ MAX_RXTS ] ;
/* protects above three fields from concurrent access */
spinlock_t rx_lock ;
/* queues of incoming and outgoing packets */
struct sk_buff_head rx_queue ;
struct sk_buff_head tx_queue ;
} ;
struct dp83640_clock {
/* keeps the instance in the 'phyter_clocks' list */
struct list_head list ;
/* we create one clock instance per MII bus */
struct mii_bus * bus ;
/* protects extended registers from concurrent access */
struct mutex extreg_lock ;
/* remembers which page was last selected */
int page ;
/* our advertised capabilities */
struct ptp_clock_info caps ;
/* protects the three fields below from concurrent access */
struct mutex clock_lock ;
/* the one phyter from which we shall read */
struct dp83640_private * chosen ;
/* list of the other attached phyters, not chosen */
struct list_head phylist ;
/* reference to our PTP hardware clock */
struct ptp_clock * ptp_clock ;
} ;
/* globals */
2011-09-20 01:43:14 +00:00
enum {
CALIBRATE_GPIO ,
PEROUT_GPIO ,
EXTTS0_GPIO ,
EXTTS1_GPIO ,
EXTTS2_GPIO ,
EXTTS3_GPIO ,
EXTTS4_GPIO ,
EXTTS5_GPIO ,
GPIO_TABLE_SIZE
} ;
2011-04-22 12:04:55 +02:00
static int chosen_phy = - 1 ;
2011-09-20 01:43:14 +00:00
static ushort gpio_tab [ GPIO_TABLE_SIZE ] = {
1 , 2 , 3 , 4 , 8 , 9 , 10 , 11
} ;
2011-04-22 12:04:55 +02:00
module_param ( chosen_phy , int , 0444 ) ;
2011-09-20 01:43:14 +00:00
module_param_array ( gpio_tab , ushort , NULL , 0444 ) ;
2011-04-22 12:04:55 +02:00
MODULE_PARM_DESC ( chosen_phy , \
" The address of the PHY to use for the ancillary clock features " ) ;
2011-09-20 01:43:14 +00:00
MODULE_PARM_DESC ( gpio_tab , \
" Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6 " ) ;
2011-04-22 12:04:55 +02:00
/* a list of clocks and a mutex to protect it */
static LIST_HEAD ( phyter_clocks ) ;
static DEFINE_MUTEX ( phyter_clocks_lock ) ;
static void rx_timestamp_work ( struct work_struct * work ) ;
/* extended register access functions */
# define BROADCAST_ADDR 31
static inline int broadcast_write ( struct mii_bus * bus , u32 regnum , u16 val )
{
return mdiobus_write ( bus , BROADCAST_ADDR , regnum , val ) ;
}
/* Caller must hold extreg_lock. */
static int ext_read ( struct phy_device * phydev , int page , u32 regnum )
{
struct dp83640_private * dp83640 = phydev - > priv ;
int val ;
if ( dp83640 - > clock - > page ! = page ) {
broadcast_write ( phydev - > bus , PAGESEL , page ) ;
dp83640 - > clock - > page = page ;
}
val = phy_read ( phydev , regnum ) ;
return val ;
}
/* Caller must hold extreg_lock. */
static void ext_write ( int broadcast , struct phy_device * phydev ,
int page , u32 regnum , u16 val )
{
struct dp83640_private * dp83640 = phydev - > priv ;
if ( dp83640 - > clock - > page ! = page ) {
broadcast_write ( phydev - > bus , PAGESEL , page ) ;
dp83640 - > clock - > page = page ;
}
if ( broadcast )
broadcast_write ( phydev - > bus , regnum , val ) ;
else
phy_write ( phydev , regnum , val ) ;
}
/* Caller must hold extreg_lock. */
static int tdr_write ( int bc , struct phy_device * dev ,
const struct timespec * ts , u16 cmd )
{
ext_write ( bc , dev , PAGE4 , PTP_TDR , ts - > tv_nsec & 0xffff ) ; /* ns[15:0] */
ext_write ( bc , dev , PAGE4 , PTP_TDR , ts - > tv_nsec > > 16 ) ; /* ns[31:16] */
ext_write ( bc , dev , PAGE4 , PTP_TDR , ts - > tv_sec & 0xffff ) ; /* sec[15:0] */
ext_write ( bc , dev , PAGE4 , PTP_TDR , ts - > tv_sec > > 16 ) ; /* sec[31:16]*/
ext_write ( bc , dev , PAGE4 , PTP_CTL , cmd ) ;
return 0 ;
}
/* convert phy timestamps into driver timestamps */
static void phy2rxts ( struct phy_rxts * p , struct rxts * rxts )
{
u32 sec ;
sec = p - > sec_lo ;
sec | = p - > sec_hi < < 16 ;
rxts - > ns = p - > ns_lo ;
rxts - > ns | = ( p - > ns_hi & 0x3fff ) < < 16 ;
rxts - > ns + = ( ( u64 ) sec ) * 1000000000ULL ;
rxts - > seqid = p - > seqid ;
rxts - > msgtype = ( p - > msgtype > > 12 ) & 0xf ;
rxts - > hash = p - > msgtype & 0x0fff ;
2011-08-06 21:03:04 +00:00
rxts - > tmo = jiffies + 2 ;
2011-04-22 12:04:55 +02:00
}
static u64 phy2txts ( struct phy_txts * p )
{
u64 ns ;
u32 sec ;
sec = p - > sec_lo ;
sec | = p - > sec_hi < < 16 ;
ns = p - > ns_lo ;
ns | = ( p - > ns_hi & 0x3fff ) < < 16 ;
ns + = ( ( u64 ) sec ) * 1000000000ULL ;
return ns ;
}
2011-09-20 01:43:14 +00:00
static void periodic_output ( struct dp83640_clock * clock ,
struct ptp_clock_request * clkreq , bool on )
{
struct dp83640_private * dp83640 = clock - > chosen ;
struct phy_device * phydev = dp83640 - > phydev ;
u32 sec , nsec , period ;
u16 gpio , ptp_trig , trigger , val ;
gpio = on ? gpio_tab [ PEROUT_GPIO ] : 0 ;
trigger = PER_TRIGGER ;
ptp_trig = TRIG_WR |
( trigger & TRIG_CSEL_MASK ) < < TRIG_CSEL_SHIFT |
( gpio & TRIG_GPIO_MASK ) < < TRIG_GPIO_SHIFT |
TRIG_PER |
TRIG_PULSE ;
val = ( trigger & TRIG_SEL_MASK ) < < TRIG_SEL_SHIFT ;
if ( ! on ) {
val | = TRIG_DIS ;
mutex_lock ( & clock - > extreg_lock ) ;
ext_write ( 0 , phydev , PAGE5 , PTP_TRIG , ptp_trig ) ;
ext_write ( 0 , phydev , PAGE4 , PTP_CTL , val ) ;
mutex_unlock ( & clock - > extreg_lock ) ;
return ;
}
sec = clkreq - > perout . start . sec ;
nsec = clkreq - > perout . start . nsec ;
period = clkreq - > perout . period . sec * 1000000000UL ;
period + = clkreq - > perout . period . nsec ;
mutex_lock ( & clock - > extreg_lock ) ;
ext_write ( 0 , phydev , PAGE5 , PTP_TRIG , ptp_trig ) ;
/*load trigger*/
val | = TRIG_LOAD ;
ext_write ( 0 , phydev , PAGE4 , PTP_CTL , val ) ;
ext_write ( 0 , phydev , PAGE4 , PTP_TDR , nsec & 0xffff ) ; /* ns[15:0] */
ext_write ( 0 , phydev , PAGE4 , PTP_TDR , nsec > > 16 ) ; /* ns[31:16] */
ext_write ( 0 , phydev , PAGE4 , PTP_TDR , sec & 0xffff ) ; /* sec[15:0] */
ext_write ( 0 , phydev , PAGE4 , PTP_TDR , sec > > 16 ) ; /* sec[31:16] */
ext_write ( 0 , phydev , PAGE4 , PTP_TDR , period & 0xffff ) ; /* ns[15:0] */
ext_write ( 0 , phydev , PAGE4 , PTP_TDR , period > > 16 ) ; /* ns[31:16] */
/*enable trigger*/
val & = ~ TRIG_LOAD ;
val | = TRIG_EN ;
ext_write ( 0 , phydev , PAGE4 , PTP_CTL , val ) ;
mutex_unlock ( & clock - > extreg_lock ) ;
}
2011-04-22 12:04:55 +02:00
/* ptp clock methods */
static int ptp_dp83640_adjfreq ( struct ptp_clock_info * ptp , s32 ppb )
{
struct dp83640_clock * clock =
container_of ( ptp , struct dp83640_clock , caps ) ;
struct phy_device * phydev = clock - > chosen - > phydev ;
u64 rate ;
int neg_adj = 0 ;
u16 hi , lo ;
if ( ppb < 0 ) {
neg_adj = 1 ;
ppb = - ppb ;
}
rate = ppb ;
rate < < = 26 ;
rate = div_u64 ( rate , 1953125 ) ;
hi = ( rate > > 16 ) & PTP_RATE_HI_MASK ;
if ( neg_adj )
hi | = PTP_RATE_DIR ;
lo = rate & 0xffff ;
mutex_lock ( & clock - > extreg_lock ) ;
ext_write ( 1 , phydev , PAGE4 , PTP_RATEH , hi ) ;
ext_write ( 1 , phydev , PAGE4 , PTP_RATEL , lo ) ;
mutex_unlock ( & clock - > extreg_lock ) ;
return 0 ;
}
static int ptp_dp83640_adjtime ( struct ptp_clock_info * ptp , s64 delta )
{
struct dp83640_clock * clock =
container_of ( ptp , struct dp83640_clock , caps ) ;
struct phy_device * phydev = clock - > chosen - > phydev ;
struct timespec ts ;
int err ;
delta + = ADJTIME_FIX ;
ts = ns_to_timespec ( delta ) ;
mutex_lock ( & clock - > extreg_lock ) ;
err = tdr_write ( 1 , phydev , & ts , PTP_STEP_CLK ) ;
mutex_unlock ( & clock - > extreg_lock ) ;
return err ;
}
static int ptp_dp83640_gettime ( struct ptp_clock_info * ptp , struct timespec * ts )
{
struct dp83640_clock * clock =
container_of ( ptp , struct dp83640_clock , caps ) ;
struct phy_device * phydev = clock - > chosen - > phydev ;
unsigned int val [ 4 ] ;
mutex_lock ( & clock - > extreg_lock ) ;
ext_write ( 0 , phydev , PAGE4 , PTP_CTL , PTP_RD_CLK ) ;
val [ 0 ] = ext_read ( phydev , PAGE4 , PTP_TDR ) ; /* ns[15:0] */
val [ 1 ] = ext_read ( phydev , PAGE4 , PTP_TDR ) ; /* ns[31:16] */
val [ 2 ] = ext_read ( phydev , PAGE4 , PTP_TDR ) ; /* sec[15:0] */
val [ 3 ] = ext_read ( phydev , PAGE4 , PTP_TDR ) ; /* sec[31:16] */
mutex_unlock ( & clock - > extreg_lock ) ;
ts - > tv_nsec = val [ 0 ] | ( val [ 1 ] < < 16 ) ;
ts - > tv_sec = val [ 2 ] | ( val [ 3 ] < < 16 ) ;
return 0 ;
}
static int ptp_dp83640_settime ( struct ptp_clock_info * ptp ,
const struct timespec * ts )
{
struct dp83640_clock * clock =
container_of ( ptp , struct dp83640_clock , caps ) ;
struct phy_device * phydev = clock - > chosen - > phydev ;
int err ;
mutex_lock ( & clock - > extreg_lock ) ;
err = tdr_write ( 1 , phydev , ts , PTP_LOAD_CLK ) ;
mutex_unlock ( & clock - > extreg_lock ) ;
return err ;
}
static int ptp_dp83640_enable ( struct ptp_clock_info * ptp ,
struct ptp_clock_request * rq , int on )
{
struct dp83640_clock * clock =
container_of ( ptp , struct dp83640_clock , caps ) ;
struct phy_device * phydev = clock - > chosen - > phydev ;
2011-09-20 01:43:14 +00:00
int index ;
u16 evnt , event_num , gpio_num ;
2011-04-22 12:04:55 +02:00
switch ( rq - > type ) {
case PTP_CLK_REQ_EXTTS :
2011-09-20 01:43:14 +00:00
index = rq - > extts . index ;
if ( index < 0 | | index > = N_EXT_TS )
2011-04-22 12:04:55 +02:00
return - EINVAL ;
2011-09-20 01:43:14 +00:00
event_num = EXT_EVENT + index ;
evnt = EVNT_WR | ( event_num & EVNT_SEL_MASK ) < < EVNT_SEL_SHIFT ;
2011-04-22 12:04:55 +02:00
if ( on ) {
2011-09-20 01:43:14 +00:00
gpio_num = gpio_tab [ EXTTS0_GPIO + index ] ;
evnt | = ( gpio_num & EVNT_GPIO_MASK ) < < EVNT_GPIO_SHIFT ;
2011-04-22 12:04:55 +02:00
evnt | = EVNT_RISE ;
}
ext_write ( 0 , phydev , PAGE5 , PTP_EVNT , evnt ) ;
return 0 ;
2011-09-20 01:43:14 +00:00
case PTP_CLK_REQ_PEROUT :
if ( rq - > perout . index ! = 0 )
return - EINVAL ;
periodic_output ( clock , rq , on ) ;
return 0 ;
2011-04-22 12:04:55 +02:00
default :
break ;
}
return - EOPNOTSUPP ;
}
static u8 status_frame_dst [ 6 ] = { 0x01 , 0x1B , 0x19 , 0x00 , 0x00 , 0x00 } ;
static u8 status_frame_src [ 6 ] = { 0x08 , 0x00 , 0x17 , 0x0B , 0x6B , 0x0F } ;
static void enable_status_frames ( struct phy_device * phydev , bool on )
{
u16 cfg0 = 0 , ver ;
if ( on )
cfg0 = PSF_EVNT_EN | PSF_RXTS_EN | PSF_TXTS_EN | ENDIAN_FLAG ;
ver = ( PSF_PTPVER & VERSIONPTP_MASK ) < < VERSIONPTP_SHIFT ;
ext_write ( 0 , phydev , PAGE5 , PSF_CFG0 , cfg0 ) ;
ext_write ( 0 , phydev , PAGE6 , PSF_CFG1 , ver ) ;
if ( ! phydev - > attached_dev ) {
2012-06-09 07:49:07 +00:00
pr_warn ( " expected to find an attached netdevice \n " ) ;
2011-04-22 12:04:55 +02:00
return ;
}
if ( on ) {
if ( dev_mc_add ( phydev - > attached_dev , status_frame_dst ) )
2012-06-09 07:49:07 +00:00
pr_warn ( " failed to add mc address \n " ) ;
2011-04-22 12:04:55 +02:00
} else {
if ( dev_mc_del ( phydev - > attached_dev , status_frame_dst ) )
2012-06-09 07:49:07 +00:00
pr_warn ( " failed to delete mc address \n " ) ;
2011-04-22 12:04:55 +02:00
}
}
static bool is_status_frame ( struct sk_buff * skb , int type )
{
struct ethhdr * h = eth_hdr ( skb ) ;
if ( PTP_CLASS_V2_L2 = = type & &
! memcmp ( h - > h_source , status_frame_src , sizeof ( status_frame_src ) ) )
return true ;
else
return false ;
}
static int expired ( struct rxts * rxts )
{
return time_after ( jiffies , rxts - > tmo ) ;
}
/* Caller must hold rx_lock. */
static void prune_rx_ts ( struct dp83640_private * dp83640 )
{
struct list_head * this , * next ;
struct rxts * rxts ;
list_for_each_safe ( this , next , & dp83640 - > rxts ) {
rxts = list_entry ( this , struct rxts , list ) ;
if ( expired ( rxts ) ) {
list_del_init ( & rxts - > list ) ;
list_add ( & rxts - > list , & dp83640 - > rxpool ) ;
}
}
}
/* synchronize the phyters so they act as one clock */
static void enable_broadcast ( struct phy_device * phydev , int init_page , int on )
{
int val ;
phy_write ( phydev , PAGESEL , 0 ) ;
val = phy_read ( phydev , PHYCR2 ) ;
if ( on )
val | = BC_WRITE ;
else
val & = ~ BC_WRITE ;
phy_write ( phydev , PHYCR2 , val ) ;
phy_write ( phydev , PAGESEL , init_page ) ;
}
static void recalibrate ( struct dp83640_clock * clock )
{
s64 now , diff ;
struct phy_txts event_ts ;
struct timespec ts ;
struct list_head * this ;
struct dp83640_private * tmp ;
struct phy_device * master = clock - > chosen - > phydev ;
2011-09-20 01:43:14 +00:00
u16 cal_gpio , cfg0 , evnt , ptp_trig , trigger , val ;
2011-04-22 12:04:55 +02:00
trigger = CAL_TRIGGER ;
2011-09-20 01:43:14 +00:00
cal_gpio = gpio_tab [ CALIBRATE_GPIO ] ;
2011-04-22 12:04:55 +02:00
mutex_lock ( & clock - > extreg_lock ) ;
/*
* enable broadcast , disable status frames , enable ptp clock
*/
list_for_each ( this , & clock - > phylist ) {
tmp = list_entry ( this , struct dp83640_private , list ) ;
enable_broadcast ( tmp - > phydev , clock - > page , 1 ) ;
tmp - > cfg0 = ext_read ( tmp - > phydev , PAGE5 , PSF_CFG0 ) ;
ext_write ( 0 , tmp - > phydev , PAGE5 , PSF_CFG0 , 0 ) ;
ext_write ( 0 , tmp - > phydev , PAGE4 , PTP_CTL , PTP_ENABLE ) ;
}
enable_broadcast ( master , clock - > page , 1 ) ;
cfg0 = ext_read ( master , PAGE5 , PSF_CFG0 ) ;
ext_write ( 0 , master , PAGE5 , PSF_CFG0 , 0 ) ;
ext_write ( 0 , master , PAGE4 , PTP_CTL , PTP_ENABLE ) ;
/*
* enable an event timestamp
*/
evnt = EVNT_WR | EVNT_RISE | EVNT_SINGLE ;
evnt | = ( CAL_EVENT & EVNT_SEL_MASK ) < < EVNT_SEL_SHIFT ;
evnt | = ( cal_gpio & EVNT_GPIO_MASK ) < < EVNT_GPIO_SHIFT ;
list_for_each ( this , & clock - > phylist ) {
tmp = list_entry ( this , struct dp83640_private , list ) ;
ext_write ( 0 , tmp - > phydev , PAGE5 , PTP_EVNT , evnt ) ;
}
ext_write ( 0 , master , PAGE5 , PTP_EVNT , evnt ) ;
/*
* configure a trigger
*/
ptp_trig = TRIG_WR | TRIG_IF_LATE | TRIG_PULSE ;
ptp_trig | = ( trigger & TRIG_CSEL_MASK ) < < TRIG_CSEL_SHIFT ;
ptp_trig | = ( cal_gpio & TRIG_GPIO_MASK ) < < TRIG_GPIO_SHIFT ;
ext_write ( 0 , master , PAGE5 , PTP_TRIG , ptp_trig ) ;
/* load trigger */
val = ( trigger & TRIG_SEL_MASK ) < < TRIG_SEL_SHIFT ;
val | = TRIG_LOAD ;
ext_write ( 0 , master , PAGE4 , PTP_CTL , val ) ;
/* enable trigger */
val & = ~ TRIG_LOAD ;
val | = TRIG_EN ;
ext_write ( 0 , master , PAGE4 , PTP_CTL , val ) ;
/* disable trigger */
val = ( trigger & TRIG_SEL_MASK ) < < TRIG_SEL_SHIFT ;
val | = TRIG_DIS ;
ext_write ( 0 , master , PAGE4 , PTP_CTL , val ) ;
/*
* read out and correct offsets
*/
val = ext_read ( master , PAGE4 , PTP_STS ) ;
2012-06-09 07:49:07 +00:00
pr_info ( " master PTP_STS 0x%04hx \n " , val ) ;
2011-04-22 12:04:55 +02:00
val = ext_read ( master , PAGE4 , PTP_ESTS ) ;
2012-06-09 07:49:07 +00:00
pr_info ( " master PTP_ESTS 0x%04hx \n " , val ) ;
2011-04-22 12:04:55 +02:00
event_ts . ns_lo = ext_read ( master , PAGE4 , PTP_EDATA ) ;
event_ts . ns_hi = ext_read ( master , PAGE4 , PTP_EDATA ) ;
event_ts . sec_lo = ext_read ( master , PAGE4 , PTP_EDATA ) ;
event_ts . sec_hi = ext_read ( master , PAGE4 , PTP_EDATA ) ;
now = phy2txts ( & event_ts ) ;
list_for_each ( this , & clock - > phylist ) {
tmp = list_entry ( this , struct dp83640_private , list ) ;
val = ext_read ( tmp - > phydev , PAGE4 , PTP_STS ) ;
2012-06-09 07:49:07 +00:00
pr_info ( " slave PTP_STS 0x%04hx \n " , val ) ;
2011-04-22 12:04:55 +02:00
val = ext_read ( tmp - > phydev , PAGE4 , PTP_ESTS ) ;
2012-06-09 07:49:07 +00:00
pr_info ( " slave PTP_ESTS 0x%04hx \n " , val ) ;
2011-04-22 12:04:55 +02:00
event_ts . ns_lo = ext_read ( tmp - > phydev , PAGE4 , PTP_EDATA ) ;
event_ts . ns_hi = ext_read ( tmp - > phydev , PAGE4 , PTP_EDATA ) ;
event_ts . sec_lo = ext_read ( tmp - > phydev , PAGE4 , PTP_EDATA ) ;
event_ts . sec_hi = ext_read ( tmp - > phydev , PAGE4 , PTP_EDATA ) ;
diff = now - ( s64 ) phy2txts ( & event_ts ) ;
pr_info ( " slave offset %lld nanoseconds \n " , diff ) ;
diff + = ADJTIME_FIX ;
ts = ns_to_timespec ( diff ) ;
tdr_write ( 0 , tmp - > phydev , & ts , PTP_STEP_CLK ) ;
}
/*
* restore status frames
*/
list_for_each ( this , & clock - > phylist ) {
tmp = list_entry ( this , struct dp83640_private , list ) ;
ext_write ( 0 , tmp - > phydev , PAGE5 , PSF_CFG0 , tmp - > cfg0 ) ;
}
ext_write ( 0 , master , PAGE5 , PSF_CFG0 , cfg0 ) ;
mutex_unlock ( & clock - > extreg_lock ) ;
}
/* time stamping methods */
2011-09-20 01:43:14 +00:00
static inline u16 exts_chan_to_edata ( int ch )
{
return 1 < < ( ( ch + EXT_EVENT ) * 2 ) ;
}
2011-06-14 23:55:19 +00:00
static int decode_evnt ( struct dp83640_private * dp83640 ,
void * data , u16 ests )
2011-04-22 12:04:55 +02:00
{
2011-06-14 23:55:19 +00:00
struct phy_txts * phy_txts ;
2011-04-22 12:04:55 +02:00
struct ptp_clock_event event ;
2011-09-20 01:43:14 +00:00
int i , parsed ;
2011-04-22 12:04:55 +02:00
int words = ( ests > > EVNT_TS_LEN_SHIFT ) & EVNT_TS_LEN_MASK ;
2011-06-14 23:55:19 +00:00
u16 ext_status = 0 ;
if ( ests & MULT_EVNT ) {
ext_status = * ( u16 * ) data ;
data + = sizeof ( ext_status ) ;
}
phy_txts = data ;
2011-04-22 12:04:55 +02:00
switch ( words ) { /* fall through in every case */
case 3 :
dp83640 - > edata . sec_hi = phy_txts - > sec_hi ;
case 2 :
dp83640 - > edata . sec_lo = phy_txts - > sec_lo ;
case 1 :
dp83640 - > edata . ns_hi = phy_txts - > ns_hi ;
case 0 :
dp83640 - > edata . ns_lo = phy_txts - > ns_lo ;
}
2011-09-20 01:43:14 +00:00
if ( ext_status ) {
parsed = words + 2 ;
} else {
parsed = words + 1 ;
i = ( ( ests > > EVNT_NUM_SHIFT ) & EVNT_NUM_MASK ) - EXT_EVENT ;
ext_status = exts_chan_to_edata ( i ) ;
}
2011-04-22 12:04:55 +02:00
event . type = PTP_CLOCK_EXTTS ;
event . timestamp = phy2txts ( & dp83640 - > edata ) ;
2011-09-20 01:43:14 +00:00
for ( i = 0 ; i < N_EXT_TS ; i + + ) {
if ( ext_status & exts_chan_to_edata ( i ) ) {
event . index = i ;
ptp_clock_event ( dp83640 - > clock - > ptp_clock , & event ) ;
}
}
2011-06-14 23:55:19 +00:00
2011-09-20 01:43:14 +00:00
return parsed * sizeof ( u16 ) ;
2011-04-22 12:04:55 +02:00
}
static void decode_rxts ( struct dp83640_private * dp83640 ,
struct phy_rxts * phy_rxts )
{
struct rxts * rxts ;
unsigned long flags ;
spin_lock_irqsave ( & dp83640 - > rx_lock , flags ) ;
prune_rx_ts ( dp83640 ) ;
if ( list_empty ( & dp83640 - > rxpool ) ) {
2012-06-09 07:49:07 +00:00
pr_debug ( " rx timestamp pool is empty \n " ) ;
2011-04-22 12:04:55 +02:00
goto out ;
}
rxts = list_first_entry ( & dp83640 - > rxpool , struct rxts , list ) ;
list_del_init ( & rxts - > list ) ;
phy2rxts ( phy_rxts , rxts ) ;
list_add_tail ( & rxts - > list , & dp83640 - > rxts ) ;
out :
spin_unlock_irqrestore ( & dp83640 - > rx_lock , flags ) ;
}
static void decode_txts ( struct dp83640_private * dp83640 ,
struct phy_txts * phy_txts )
{
struct skb_shared_hwtstamps shhwtstamps ;
struct sk_buff * skb ;
u64 ns ;
/* We must already have the skb that triggered this. */
skb = skb_dequeue ( & dp83640 - > tx_queue ) ;
if ( ! skb ) {
2012-06-09 07:49:07 +00:00
pr_debug ( " have timestamp but tx_queue empty \n " ) ;
2011-04-22 12:04:55 +02:00
return ;
}
ns = phy2txts ( phy_txts ) ;
memset ( & shhwtstamps , 0 , sizeof ( shhwtstamps ) ) ;
shhwtstamps . hwtstamp = ns_to_ktime ( ns ) ;
skb_complete_tx_timestamp ( skb , & shhwtstamps ) ;
}
static void decode_status_frame ( struct dp83640_private * dp83640 ,
struct sk_buff * skb )
{
struct phy_rxts * phy_rxts ;
struct phy_txts * phy_txts ;
u8 * ptr ;
int len , size ;
u16 ests , type ;
ptr = skb - > data + 2 ;
for ( len = skb_headlen ( skb ) - 2 ; len > sizeof ( type ) ; len - = size ) {
type = * ( u16 * ) ptr ;
ests = type & 0x0fff ;
type = type & 0xf000 ;
len - = sizeof ( type ) ;
ptr + = sizeof ( type ) ;
if ( PSF_RX = = type & & len > = sizeof ( * phy_rxts ) ) {
phy_rxts = ( struct phy_rxts * ) ptr ;
decode_rxts ( dp83640 , phy_rxts ) ;
size = sizeof ( * phy_rxts ) ;
} else if ( PSF_TX = = type & & len > = sizeof ( * phy_txts ) ) {
phy_txts = ( struct phy_txts * ) ptr ;
decode_txts ( dp83640 , phy_txts ) ;
size = sizeof ( * phy_txts ) ;
} else if ( PSF_EVNT = = type & & len > = sizeof ( * phy_txts ) ) {
2011-06-14 23:55:19 +00:00
size = decode_evnt ( dp83640 , ptr , ests ) ;
2011-04-22 12:04:55 +02:00
} else {
size = 0 ;
break ;
}
ptr + = size ;
}
}
2011-09-20 01:43:16 +00:00
static int is_sync ( struct sk_buff * skb , int type )
{
u8 * data = skb - > data , * msgtype ;
unsigned int offset = 0 ;
switch ( type ) {
case PTP_CLASS_V1_IPV4 :
case PTP_CLASS_V2_IPV4 :
offset = ETH_HLEN + IPV4_HLEN ( data ) + UDP_HLEN ;
break ;
case PTP_CLASS_V1_IPV6 :
case PTP_CLASS_V2_IPV6 :
offset = OFF_PTP6 ;
break ;
case PTP_CLASS_V2_L2 :
offset = ETH_HLEN ;
break ;
case PTP_CLASS_V2_VLAN :
offset = ETH_HLEN + VLAN_HLEN ;
break ;
default :
return 0 ;
}
if ( type & PTP_CLASS_V1 )
offset + = OFF_PTP_CONTROL ;
if ( skb - > len < offset + 1 )
return 0 ;
msgtype = data + offset ;
return ( * msgtype & 0xf ) = = 0 ;
}
2011-04-22 12:04:55 +02:00
static int match ( struct sk_buff * skb , unsigned int type , struct rxts * rxts )
{
u16 * seqid ;
unsigned int offset ;
u8 * msgtype , * data = skb_mac_header ( skb ) ;
/* check sequenceID, messageType, 12 bit hash of offset 20-29 */
switch ( type ) {
case PTP_CLASS_V1_IPV4 :
case PTP_CLASS_V2_IPV4 :
offset = ETH_HLEN + IPV4_HLEN ( data ) + UDP_HLEN ;
break ;
case PTP_CLASS_V1_IPV6 :
case PTP_CLASS_V2_IPV6 :
offset = OFF_PTP6 ;
break ;
case PTP_CLASS_V2_L2 :
offset = ETH_HLEN ;
break ;
case PTP_CLASS_V2_VLAN :
offset = ETH_HLEN + VLAN_HLEN ;
break ;
default :
return 0 ;
}
if ( skb - > len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof ( * seqid ) )
return 0 ;
if ( unlikely ( type & PTP_CLASS_V1 ) )
msgtype = data + offset + OFF_PTP_CONTROL ;
else
msgtype = data + offset ;
seqid = ( u16 * ) ( data + offset + OFF_PTP_SEQUENCE_ID ) ;
return ( rxts - > msgtype = = ( * msgtype & 0xf ) & &
rxts - > seqid = = ntohs ( * seqid ) ) ;
}
static void dp83640_free_clocks ( void )
{
struct dp83640_clock * clock ;
struct list_head * this , * next ;
mutex_lock ( & phyter_clocks_lock ) ;
list_for_each_safe ( this , next , & phyter_clocks ) {
clock = list_entry ( this , struct dp83640_clock , list ) ;
if ( ! list_empty ( & clock - > phylist ) ) {
2012-06-09 07:49:07 +00:00
pr_warn ( " phy list non-empty while unloading \n " ) ;
2011-04-22 12:04:55 +02:00
BUG ( ) ;
}
list_del ( & clock - > list ) ;
mutex_destroy ( & clock - > extreg_lock ) ;
mutex_destroy ( & clock - > clock_lock ) ;
put_device ( & clock - > bus - > dev ) ;
kfree ( clock ) ;
}
mutex_unlock ( & phyter_clocks_lock ) ;
}
static void dp83640_clock_init ( struct dp83640_clock * clock , struct mii_bus * bus )
{
INIT_LIST_HEAD ( & clock - > list ) ;
clock - > bus = bus ;
mutex_init ( & clock - > extreg_lock ) ;
mutex_init ( & clock - > clock_lock ) ;
INIT_LIST_HEAD ( & clock - > phylist ) ;
clock - > caps . owner = THIS_MODULE ;
sprintf ( clock - > caps . name , " dp83640 timer " ) ;
clock - > caps . max_adj = 1953124 ;
clock - > caps . n_alarm = 0 ;
clock - > caps . n_ext_ts = N_EXT_TS ;
2011-09-20 01:43:14 +00:00
clock - > caps . n_per_out = 1 ;
2011-04-22 12:04:55 +02:00
clock - > caps . pps = 0 ;
clock - > caps . adjfreq = ptp_dp83640_adjfreq ;
clock - > caps . adjtime = ptp_dp83640_adjtime ;
clock - > caps . gettime = ptp_dp83640_gettime ;
clock - > caps . settime = ptp_dp83640_settime ;
clock - > caps . enable = ptp_dp83640_enable ;
/*
* Get a reference to this bus instance .
*/
get_device ( & bus - > dev ) ;
}
static int choose_this_phy ( struct dp83640_clock * clock ,
struct phy_device * phydev )
{
if ( chosen_phy = = - 1 & & ! clock - > chosen )
return 1 ;
if ( chosen_phy = = phydev - > addr )
return 1 ;
return 0 ;
}
static struct dp83640_clock * dp83640_clock_get ( struct dp83640_clock * clock )
{
if ( clock )
mutex_lock ( & clock - > clock_lock ) ;
return clock ;
}
/*
* Look up and lock a clock by bus instance .
* If there is no clock for this bus , then create it first .
*/
static struct dp83640_clock * dp83640_clock_get_bus ( struct mii_bus * bus )
{
struct dp83640_clock * clock = NULL , * tmp ;
struct list_head * this ;
mutex_lock ( & phyter_clocks_lock ) ;
list_for_each ( this , & phyter_clocks ) {
tmp = list_entry ( this , struct dp83640_clock , list ) ;
if ( tmp - > bus = = bus ) {
clock = tmp ;
break ;
}
}
if ( clock )
goto out ;
clock = kzalloc ( sizeof ( struct dp83640_clock ) , GFP_KERNEL ) ;
if ( ! clock )
goto out ;
dp83640_clock_init ( clock , bus ) ;
list_add_tail ( & phyter_clocks , & clock - > list ) ;
out :
mutex_unlock ( & phyter_clocks_lock ) ;
return dp83640_clock_get ( clock ) ;
}
static void dp83640_clock_put ( struct dp83640_clock * clock )
{
mutex_unlock ( & clock - > clock_lock ) ;
}
static int dp83640_probe ( struct phy_device * phydev )
{
struct dp83640_clock * clock ;
struct dp83640_private * dp83640 ;
int err = - ENOMEM , i ;
if ( phydev - > addr = = BROADCAST_ADDR )
return 0 ;
clock = dp83640_clock_get_bus ( phydev - > bus ) ;
if ( ! clock )
goto no_clock ;
dp83640 = kzalloc ( sizeof ( struct dp83640_private ) , GFP_KERNEL ) ;
if ( ! dp83640 )
goto no_memory ;
dp83640 - > phydev = phydev ;
INIT_WORK ( & dp83640 - > ts_work , rx_timestamp_work ) ;
INIT_LIST_HEAD ( & dp83640 - > rxts ) ;
INIT_LIST_HEAD ( & dp83640 - > rxpool ) ;
for ( i = 0 ; i < MAX_RXTS ; i + + )
list_add ( & dp83640 - > rx_pool_data [ i ] . list , & dp83640 - > rxpool ) ;
phydev - > priv = dp83640 ;
spin_lock_init ( & dp83640 - > rx_lock ) ;
skb_queue_head_init ( & dp83640 - > rx_queue ) ;
skb_queue_head_init ( & dp83640 - > tx_queue ) ;
dp83640 - > clock = clock ;
if ( choose_this_phy ( clock , phydev ) ) {
clock - > chosen = dp83640 ;
clock - > ptp_clock = ptp_clock_register ( & clock - > caps ) ;
if ( IS_ERR ( clock - > ptp_clock ) ) {
err = PTR_ERR ( clock - > ptp_clock ) ;
goto no_register ;
}
} else
list_add_tail ( & dp83640 - > list , & clock - > phylist ) ;
if ( clock - > chosen & & ! list_empty ( & clock - > phylist ) )
recalibrate ( clock ) ;
else
enable_broadcast ( dp83640 - > phydev , clock - > page , 1 ) ;
dp83640_clock_put ( clock ) ;
return 0 ;
no_register :
clock - > chosen = NULL ;
kfree ( dp83640 ) ;
no_memory :
dp83640_clock_put ( clock ) ;
no_clock :
return err ;
}
static void dp83640_remove ( struct phy_device * phydev )
{
struct dp83640_clock * clock ;
struct list_head * this , * next ;
struct dp83640_private * tmp , * dp83640 = phydev - > priv ;
2011-10-21 00:49:17 +00:00
struct sk_buff * skb ;
2011-04-22 12:04:55 +02:00
if ( phydev - > addr = = BROADCAST_ADDR )
return ;
enable_status_frames ( phydev , false ) ;
cancel_work_sync ( & dp83640 - > ts_work ) ;
2011-10-21 00:49:17 +00:00
while ( ( skb = skb_dequeue ( & dp83640 - > rx_queue ) ) ! = NULL )
kfree_skb ( skb ) ;
while ( ( skb = skb_dequeue ( & dp83640 - > tx_queue ) ) ! = NULL )
skb_complete_tx_timestamp ( skb , NULL ) ;
2011-04-22 12:04:55 +02:00
clock = dp83640_clock_get ( dp83640 - > clock ) ;
if ( dp83640 = = clock - > chosen ) {
ptp_clock_unregister ( clock - > ptp_clock ) ;
clock - > chosen = NULL ;
} else {
list_for_each_safe ( this , next , & clock - > phylist ) {
tmp = list_entry ( this , struct dp83640_private , list ) ;
if ( tmp = = dp83640 ) {
list_del_init ( & tmp - > list ) ;
break ;
}
}
}
dp83640_clock_put ( clock ) ;
kfree ( dp83640 ) ;
}
static int dp83640_hwtstamp ( struct phy_device * phydev , struct ifreq * ifr )
{
struct dp83640_private * dp83640 = phydev - > priv ;
struct hwtstamp_config cfg ;
u16 txcfg0 , rxcfg0 ;
if ( copy_from_user ( & cfg , ifr - > ifr_data , sizeof ( cfg ) ) )
return - EFAULT ;
if ( cfg . flags ) /* reserved for future extensions */
return - EINVAL ;
2011-09-20 01:43:16 +00:00
if ( cfg . tx_type < 0 | | cfg . tx_type > HWTSTAMP_TX_ONESTEP_SYNC )
2011-04-22 12:04:55 +02:00
return - ERANGE ;
2011-09-20 01:43:16 +00:00
dp83640 - > hwts_tx_en = cfg . tx_type ;
2011-04-22 12:04:55 +02:00
switch ( cfg . rx_filter ) {
case HWTSTAMP_FILTER_NONE :
dp83640 - > hwts_rx_en = 0 ;
dp83640 - > layer = 0 ;
dp83640 - > version = 0 ;
break ;
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT :
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC :
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ :
dp83640 - > hwts_rx_en = 1 ;
dp83640 - > layer = LAYER4 ;
dp83640 - > version = 1 ;
break ;
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT :
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC :
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ :
dp83640 - > hwts_rx_en = 1 ;
dp83640 - > layer = LAYER4 ;
dp83640 - > version = 2 ;
break ;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT :
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC :
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ :
dp83640 - > hwts_rx_en = 1 ;
dp83640 - > layer = LAYER2 ;
dp83640 - > version = 2 ;
break ;
case HWTSTAMP_FILTER_PTP_V2_EVENT :
case HWTSTAMP_FILTER_PTP_V2_SYNC :
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ :
dp83640 - > hwts_rx_en = 1 ;
dp83640 - > layer = LAYER4 | LAYER2 ;
dp83640 - > version = 2 ;
break ;
default :
return - ERANGE ;
}
txcfg0 = ( dp83640 - > version & TX_PTP_VER_MASK ) < < TX_PTP_VER_SHIFT ;
rxcfg0 = ( dp83640 - > version & TX_PTP_VER_MASK ) < < TX_PTP_VER_SHIFT ;
if ( dp83640 - > layer & LAYER2 ) {
txcfg0 | = TX_L2_EN ;
rxcfg0 | = RX_L2_EN ;
}
if ( dp83640 - > layer & LAYER4 ) {
txcfg0 | = TX_IPV6_EN | TX_IPV4_EN ;
rxcfg0 | = RX_IPV6_EN | RX_IPV4_EN ;
}
if ( dp83640 - > hwts_tx_en )
txcfg0 | = TX_TS_EN ;
2011-09-20 01:43:16 +00:00
if ( dp83640 - > hwts_tx_en = = HWTSTAMP_TX_ONESTEP_SYNC )
txcfg0 | = SYNC_1STEP | CHK_1STEP ;
2011-04-22 12:04:55 +02:00
if ( dp83640 - > hwts_rx_en )
rxcfg0 | = RX_TS_EN ;
mutex_lock ( & dp83640 - > clock - > extreg_lock ) ;
if ( dp83640 - > hwts_tx_en | | dp83640 - > hwts_rx_en ) {
enable_status_frames ( phydev , true ) ;
ext_write ( 0 , phydev , PAGE4 , PTP_CTL , PTP_ENABLE ) ;
}
ext_write ( 0 , phydev , PAGE5 , PTP_TXCFG0 , txcfg0 ) ;
ext_write ( 0 , phydev , PAGE5 , PTP_RXCFG0 , rxcfg0 ) ;
mutex_unlock ( & dp83640 - > clock - > extreg_lock ) ;
return copy_to_user ( ifr - > ifr_data , & cfg , sizeof ( cfg ) ) ? - EFAULT : 0 ;
}
static void rx_timestamp_work ( struct work_struct * work )
{
struct dp83640_private * dp83640 =
container_of ( work , struct dp83640_private , ts_work ) ;
struct list_head * this , * next ;
struct rxts * rxts ;
struct skb_shared_hwtstamps * shhwtstamps ;
struct sk_buff * skb ;
unsigned int type ;
unsigned long flags ;
/* Deliver each deferred packet, with or without a time stamp. */
while ( ( skb = skb_dequeue ( & dp83640 - > rx_queue ) ) ! = NULL ) {
type = SKB_PTP_TYPE ( skb ) ;
spin_lock_irqsave ( & dp83640 - > rx_lock , flags ) ;
list_for_each_safe ( this , next , & dp83640 - > rxts ) {
rxts = list_entry ( this , struct rxts , list ) ;
if ( match ( skb , type , rxts ) ) {
shhwtstamps = skb_hwtstamps ( skb ) ;
memset ( shhwtstamps , 0 , sizeof ( * shhwtstamps ) ) ;
shhwtstamps - > hwtstamp = ns_to_ktime ( rxts - > ns ) ;
list_del_init ( & rxts - > list ) ;
list_add ( & rxts - > list , & dp83640 - > rxpool ) ;
break ;
}
}
spin_unlock_irqrestore ( & dp83640 - > rx_lock , flags ) ;
2012-01-09 23:52:15 +00:00
netif_rx_ni ( skb ) ;
2011-04-22 12:04:55 +02:00
}
/* Clear out expired time stamps. */
spin_lock_irqsave ( & dp83640 - > rx_lock , flags ) ;
prune_rx_ts ( dp83640 ) ;
spin_unlock_irqrestore ( & dp83640 - > rx_lock , flags ) ;
}
static bool dp83640_rxtstamp ( struct phy_device * phydev ,
struct sk_buff * skb , int type )
{
struct dp83640_private * dp83640 = phydev - > priv ;
if ( ! dp83640 - > hwts_rx_en )
return false ;
if ( is_status_frame ( skb , type ) ) {
decode_status_frame ( dp83640 , skb ) ;
2011-06-14 23:55:20 +00:00
kfree_skb ( skb ) ;
return true ;
2011-04-22 12:04:55 +02:00
}
SKB_PTP_TYPE ( skb ) = type ;
skb_queue_tail ( & dp83640 - > rx_queue , skb ) ;
schedule_work ( & dp83640 - > ts_work ) ;
return true ;
}
static void dp83640_txtstamp ( struct phy_device * phydev ,
struct sk_buff * skb , int type )
{
struct dp83640_private * dp83640 = phydev - > priv ;
2011-09-20 01:43:16 +00:00
switch ( dp83640 - > hwts_tx_en ) {
case HWTSTAMP_TX_ONESTEP_SYNC :
if ( is_sync ( skb , type ) ) {
2011-10-21 00:49:16 +00:00
skb_complete_tx_timestamp ( skb , NULL ) ;
2011-09-20 01:43:16 +00:00
return ;
}
/* fall through */
case HWTSTAMP_TX_ON :
skb_queue_tail ( & dp83640 - > tx_queue , skb ) ;
schedule_work ( & dp83640 - > ts_work ) ;
break ;
case HWTSTAMP_TX_OFF :
default :
2011-10-21 00:49:16 +00:00
skb_complete_tx_timestamp ( skb , NULL ) ;
2011-09-20 01:43:16 +00:00
break ;
2011-04-22 12:04:55 +02:00
}
}
2012-04-03 22:59:18 +00:00
static int dp83640_ts_info ( struct phy_device * dev , struct ethtool_ts_info * info )
{
struct dp83640_private * dp83640 = dev - > priv ;
info - > so_timestamping =
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE ;
info - > phc_index = ptp_clock_index ( dp83640 - > clock - > ptp_clock ) ;
info - > tx_types =
( 1 < < HWTSTAMP_TX_OFF ) |
( 1 < < HWTSTAMP_TX_ON ) |
( 1 < < HWTSTAMP_TX_ONESTEP_SYNC ) ;
info - > rx_filters =
( 1 < < HWTSTAMP_FILTER_NONE ) |
( 1 < < HWTSTAMP_FILTER_PTP_V1_L4_EVENT ) |
( 1 < < HWTSTAMP_FILTER_PTP_V1_L4_SYNC ) |
( 1 < < HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ ) |
( 1 < < HWTSTAMP_FILTER_PTP_V2_L4_EVENT ) |
( 1 < < HWTSTAMP_FILTER_PTP_V2_L4_SYNC ) |
( 1 < < HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ ) |
( 1 < < HWTSTAMP_FILTER_PTP_V2_L2_EVENT ) |
( 1 < < HWTSTAMP_FILTER_PTP_V2_L2_SYNC ) |
( 1 < < HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ ) |
( 1 < < HWTSTAMP_FILTER_PTP_V2_EVENT ) |
( 1 < < HWTSTAMP_FILTER_PTP_V2_SYNC ) |
( 1 < < HWTSTAMP_FILTER_PTP_V2_DELAY_REQ ) ;
return 0 ;
}
2011-04-22 12:04:55 +02:00
static struct phy_driver dp83640_driver = {
. phy_id = DP83640_PHY_ID ,
. phy_id_mask = 0xfffffff0 ,
. name = " NatSemi DP83640 " ,
. features = PHY_BASIC_FEATURES ,
. flags = 0 ,
. probe = dp83640_probe ,
. remove = dp83640_remove ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
2012-04-03 22:59:18 +00:00
. ts_info = dp83640_ts_info ,
2011-04-22 12:04:55 +02:00
. hwtstamp = dp83640_hwtstamp ,
. rxtstamp = dp83640_rxtstamp ,
. txtstamp = dp83640_txtstamp ,
. driver = { . owner = THIS_MODULE , }
} ;
static int __init dp83640_init ( void )
{
return phy_driver_register ( & dp83640_driver ) ;
}
static void __exit dp83640_exit ( void )
{
dp83640_free_clocks ( ) ;
phy_driver_unregister ( & dp83640_driver ) ;
}
MODULE_DESCRIPTION ( " National Semiconductor DP83640 PHY driver " ) ;
2012-03-16 22:39:29 +00:00
MODULE_AUTHOR ( " Richard Cochran <richardcochran@gmail.at> " ) ;
2011-04-22 12:04:55 +02:00
MODULE_LICENSE ( " GPL " ) ;
module_init ( dp83640_init ) ;
module_exit ( dp83640_exit ) ;
2011-05-23 13:32:11 -07:00
static struct mdio_device_id __maybe_unused dp83640_tbl [ ] = {
2011-04-22 12:04:55 +02:00
{ DP83640_PHY_ID , 0xfffffff0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( mdio , dp83640_tbl ) ;