2011-04-22 14:04:55 +04: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 11:49:07 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2011-04-22 14:04:55 +04: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>
2014-04-01 18:20:23 +04:00
# include <linux/if_vlan.h>
2011-04-22 14:04:55 +04:00
# 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-07 01:03:04 +04:00
# define MAX_RXTS 64
2011-09-20 05:43:14 +04:00
# define N_EXT_TS 6
2014-06-27 14:05:30 +04:00
# define N_PER_OUT 7
2011-04-22 14:04:55 +04:00
# define PSF_PTPVER 2
# define PSF_EVNT 0x4000
# define PSF_RX 0x2000
# define PSF_TX 0x1000
# define EXT_EVENT 1
2011-09-20 05:43:14 +04:00
# define CAL_EVENT 7
2015-05-25 12:55:43 +03:00
# define CAL_TRIGGER 1
2014-03-21 01:21:58 +04:00
# define DP83640_N_PINS 12
2011-04-22 14:04:55 +04:00
2012-12-04 14:21:38 +04:00
# define MII_DP83640_MICR 0x11
# define MII_DP83640_MISR 0x12
# define MII_DP83640_MICR_OE 0x1
# define MII_DP83640_MICR_IE 0x2
# define MII_DP83640_MISR_RHF_INT_EN 0x01
# define MII_DP83640_MISR_FHF_INT_EN 0x02
# define MII_DP83640_MISR_ANC_INT_EN 0x04
# define MII_DP83640_MISR_DUP_INT_EN 0x08
# define MII_DP83640_MISR_SPD_INT_EN 0x10
# define MII_DP83640_MISR_LINK_INT_EN 0x20
# define MII_DP83640_MISR_ED_INT_EN 0x40
# define MII_DP83640_MISR_LQ_INT_EN 0x80
2011-04-22 14:04:55 +04: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
2014-07-22 17:20:45 +04:00
struct dp83640_skb_info {
int ptp_type ;
unsigned long tmo ;
} ;
2011-04-22 14:04:55 +04:00
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 05:43:14 +04:00
enum {
CALIBRATE_GPIO ,
PEROUT_GPIO ,
EXTTS0_GPIO ,
EXTTS1_GPIO ,
EXTTS2_GPIO ,
EXTTS3_GPIO ,
EXTTS4_GPIO ,
EXTTS5_GPIO ,
GPIO_TABLE_SIZE
} ;
2011-04-22 14:04:55 +04:00
static int chosen_phy = - 1 ;
2011-09-20 05:43:14 +04:00
static ushort gpio_tab [ GPIO_TABLE_SIZE ] = {
1 , 2 , 3 , 4 , 8 , 9 , 10 , 11
} ;
2011-04-22 14:04:55 +04:00
module_param ( chosen_phy , int , 0444 ) ;
2011-09-20 05:43:14 +04:00
module_param_array ( gpio_tab , ushort , NULL , 0444 ) ;
2011-04-22 14:04:55 +04:00
MODULE_PARM_DESC ( chosen_phy , \
" The address of the PHY to use for the ancillary clock features " ) ;
2011-09-20 05:43:14 +04:00
MODULE_PARM_DESC ( gpio_tab , \
" Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6 " ) ;
2011-04-22 14:04:55 +04:00
2014-03-21 01:21:58 +04:00
static void dp83640_gpio_defaults ( struct ptp_pin_desc * pd )
{
int i , index ;
for ( i = 0 ; i < DP83640_N_PINS ; i + + ) {
snprintf ( pd [ i ] . name , sizeof ( pd [ i ] . name ) , " GPIO%d " , 1 + i ) ;
pd [ i ] . index = i ;
}
for ( i = 0 ; i < GPIO_TABLE_SIZE ; i + + ) {
if ( gpio_tab [ i ] < 1 | | gpio_tab [ i ] > DP83640_N_PINS ) {
pr_err ( " gpio_tab[%d]=%hu out of range " , i , gpio_tab [ i ] ) ;
return ;
}
}
index = gpio_tab [ CALIBRATE_GPIO ] - 1 ;
pd [ index ] . func = PTP_PF_PHYSYNC ;
pd [ index ] . chan = 0 ;
index = gpio_tab [ PEROUT_GPIO ] - 1 ;
pd [ index ] . func = PTP_PF_PEROUT ;
pd [ index ] . chan = 0 ;
for ( i = EXTTS0_GPIO ; i < GPIO_TABLE_SIZE ; i + + ) {
index = gpio_tab [ i ] - 1 ;
pd [ index ] . func = PTP_PF_EXTTS ;
pd [ index ] . chan = i - EXTTS0_GPIO ;
}
}
2011-04-22 14:04:55 +04: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 ,
2015-03-30 00:12:10 +03:00
const struct timespec64 * ts , u16 cmd )
2011-04-22 14:04:55 +04:00
{
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-07 01:03:04 +04:00
rxts - > tmo = jiffies + 2 ;
2011-04-22 14:04:55 +04: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 ;
}
2014-03-21 01:22:00 +04:00
static int periodic_output ( struct dp83640_clock * clock ,
2014-06-27 14:05:30 +04:00
struct ptp_clock_request * clkreq , bool on ,
int trigger )
2011-09-20 05:43:14 +04:00
{
struct dp83640_private * dp83640 = clock - > chosen ;
struct phy_device * phydev = dp83640 - > phydev ;
2014-03-21 01:21:57 +04:00
u32 sec , nsec , pwidth ;
2014-06-27 14:05:30 +04:00
u16 gpio , ptp_trig , val ;
2011-09-20 05:43:14 +04:00
2014-03-21 01:22:00 +04:00
if ( on ) {
2014-06-27 14:05:30 +04:00
gpio = 1 + ptp_find_pin ( clock - > ptp_clock , PTP_PF_PEROUT ,
trigger ) ;
2014-03-21 01:22:00 +04:00
if ( gpio < 1 )
return - EINVAL ;
} else {
gpio = 0 ;
}
2011-09-20 05:43:14 +04:00
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 ) ;
2014-03-21 01:22:00 +04:00
return 0 ;
2011-09-20 05:43:14 +04:00
}
sec = clkreq - > perout . start . sec ;
nsec = clkreq - > perout . start . nsec ;
2014-03-21 01:21:57 +04:00
pwidth = clkreq - > perout . period . sec * 1000000000UL ;
pwidth + = clkreq - > perout . period . nsec ;
pwidth / = 2 ;
2011-09-20 05:43:14 +04:00
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] */
2014-03-21 01:21:57 +04:00
ext_write ( 0 , phydev , PAGE4 , PTP_TDR , pwidth & 0xffff ) ; /* ns[15:0] */
ext_write ( 0 , phydev , PAGE4 , PTP_TDR , pwidth > > 16 ) ; /* ns[31:16] */
2014-06-27 14:05:29 +04:00
/* Triggers 0 and 1 has programmable pulsewidth2 */
if ( trigger < 2 ) {
ext_write ( 0 , phydev , PAGE4 , PTP_TDR , pwidth & 0xffff ) ;
ext_write ( 0 , phydev , PAGE4 , PTP_TDR , pwidth > > 16 ) ;
}
2011-09-20 05:43:14 +04:00
/*enable trigger*/
val & = ~ TRIG_LOAD ;
val | = TRIG_EN ;
ext_write ( 0 , phydev , PAGE4 , PTP_CTL , val ) ;
mutex_unlock ( & clock - > extreg_lock ) ;
2014-03-21 01:22:00 +04:00
return 0 ;
2011-09-20 05:43:14 +04:00
}
2011-04-22 14:04:55 +04: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 ;
2015-03-30 00:12:10 +03:00
struct timespec64 ts ;
2011-04-22 14:04:55 +04:00
int err ;
delta + = ADJTIME_FIX ;
2015-03-30 00:12:10 +03:00
ts = ns_to_timespec64 ( delta ) ;
2011-04-22 14:04:55 +04:00
mutex_lock ( & clock - > extreg_lock ) ;
err = tdr_write ( 1 , phydev , & ts , PTP_STEP_CLK ) ;
mutex_unlock ( & clock - > extreg_lock ) ;
return err ;
}
2015-03-30 00:12:10 +03:00
static int ptp_dp83640_gettime ( struct ptp_clock_info * ptp ,
struct timespec64 * ts )
2011-04-22 14:04:55 +04:00
{
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 ,
2015-03-30 00:12:10 +03:00
const struct timespec64 * ts )
2011-04-22 14:04:55 +04:00
{
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 ;
2014-03-21 01:21:56 +04:00
unsigned int index ;
2011-09-20 05:43:14 +04:00
u16 evnt , event_num , gpio_num ;
2011-04-22 14:04:55 +04:00
switch ( rq - > type ) {
case PTP_CLK_REQ_EXTTS :
2011-09-20 05:43:14 +04:00
index = rq - > extts . index ;
2014-03-21 01:21:56 +04:00
if ( index > = N_EXT_TS )
2011-04-22 14:04:55 +04:00
return - EINVAL ;
2011-09-20 05:43:14 +04:00
event_num = EXT_EVENT + index ;
evnt = EVNT_WR | ( event_num & EVNT_SEL_MASK ) < < EVNT_SEL_SHIFT ;
2011-04-22 14:04:55 +04:00
if ( on ) {
2014-03-21 01:21:59 +04:00
gpio_num = 1 + ptp_find_pin ( clock - > ptp_clock ,
PTP_PF_EXTTS , index ) ;
if ( gpio_num < 1 )
return - EINVAL ;
2011-09-20 05:43:14 +04:00
evnt | = ( gpio_num & EVNT_GPIO_MASK ) < < EVNT_GPIO_SHIFT ;
2014-02-03 18:36:50 +04:00
if ( rq - > extts . flags & PTP_FALLING_EDGE )
evnt | = EVNT_FALL ;
else
evnt | = EVNT_RISE ;
2011-04-22 14:04:55 +04:00
}
2015-05-25 12:55:44 +03:00
mutex_lock ( & clock - > extreg_lock ) ;
2011-04-22 14:04:55 +04:00
ext_write ( 0 , phydev , PAGE5 , PTP_EVNT , evnt ) ;
2015-05-25 12:55:44 +03:00
mutex_unlock ( & clock - > extreg_lock ) ;
2011-04-22 14:04:55 +04:00
return 0 ;
2011-09-20 05:43:14 +04:00
case PTP_CLK_REQ_PEROUT :
2014-06-27 14:05:30 +04:00
if ( rq - > perout . index > = N_PER_OUT )
2011-09-20 05:43:14 +04:00
return - EINVAL ;
2014-06-27 14:05:30 +04:00
return periodic_output ( clock , rq , on , rq - > perout . index ) ;
2011-09-20 05:43:14 +04:00
2011-04-22 14:04:55 +04:00
default :
break ;
}
return - EOPNOTSUPP ;
}
2014-03-21 01:21:58 +04:00
static int ptp_dp83640_verify ( struct ptp_clock_info * ptp , unsigned int pin ,
enum ptp_pin_function func , unsigned int chan )
{
2014-06-27 14:05:31 +04:00
struct dp83640_clock * clock =
container_of ( ptp , struct dp83640_clock , caps ) ;
if ( clock - > caps . pin_config [ pin ] . func = = PTP_PF_PHYSYNC & &
! list_empty ( & clock - > phylist ) )
return 1 ;
if ( func = = PTP_PF_PHYSYNC )
return 1 ;
2014-03-21 01:21:58 +04:00
return 0 ;
}
2011-04-22 14:04:55 +04:00
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 )
{
2015-05-25 12:55:44 +03:00
struct dp83640_private * dp83640 = phydev - > priv ;
struct dp83640_clock * clock = dp83640 - > clock ;
2011-04-22 14:04:55 +04:00
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 ;
2015-05-25 12:55:44 +03:00
mutex_lock ( & clock - > extreg_lock ) ;
2011-04-22 14:04:55 +04:00
ext_write ( 0 , phydev , PAGE5 , PSF_CFG0 , cfg0 ) ;
ext_write ( 0 , phydev , PAGE6 , PSF_CFG1 , ver ) ;
2015-05-25 12:55:44 +03:00
mutex_unlock ( & clock - > extreg_lock ) ;
2011-04-22 14:04:55 +04:00
if ( ! phydev - > attached_dev ) {
2012-06-09 11:49:07 +04:00
pr_warn ( " expected to find an attached netdevice \n " ) ;
2011-04-22 14:04:55 +04:00
return ;
}
if ( on ) {
if ( dev_mc_add ( phydev - > attached_dev , status_frame_dst ) )
2012-06-09 11:49:07 +04:00
pr_warn ( " failed to add mc address \n " ) ;
2011-04-22 14:04:55 +04:00
} else {
if ( dev_mc_del ( phydev - > attached_dev , status_frame_dst ) )
2012-06-09 11:49:07 +04:00
pr_warn ( " failed to delete mc address \n " ) ;
2011-04-22 14:04:55 +04: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 ;
2015-03-30 00:12:10 +03:00
struct timespec64 ts ;
2011-04-22 14:04:55 +04:00
struct list_head * this ;
struct dp83640_private * tmp ;
struct phy_device * master = clock - > chosen - > phydev ;
2011-09-20 05:43:14 +04:00
u16 cal_gpio , cfg0 , evnt , ptp_trig , trigger , val ;
2011-04-22 14:04:55 +04:00
trigger = CAL_TRIGGER ;
2014-06-27 14:05:32 +04:00
cal_gpio = 1 + ptp_find_pin ( clock - > ptp_clock , PTP_PF_PHYSYNC , 0 ) ;
if ( cal_gpio < 1 ) {
2015-02-24 17:11:26 +03:00
pr_err ( " PHY calibration pin not available - PHY is not calibrated. " ) ;
2014-06-27 14:05:32 +04:00
return ;
}
2011-04-22 14:04:55 +04: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 11:49:07 +04:00
pr_info ( " master PTP_STS 0x%04hx \n " , val ) ;
2011-04-22 14:04:55 +04:00
val = ext_read ( master , PAGE4 , PTP_ESTS ) ;
2012-06-09 11:49:07 +04:00
pr_info ( " master PTP_ESTS 0x%04hx \n " , val ) ;
2011-04-22 14:04:55 +04: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 11:49:07 +04:00
pr_info ( " slave PTP_STS 0x%04hx \n " , val ) ;
2011-04-22 14:04:55 +04:00
val = ext_read ( tmp - > phydev , PAGE4 , PTP_ESTS ) ;
2012-06-09 11:49:07 +04:00
pr_info ( " slave PTP_ESTS 0x%04hx \n " , val ) ;
2011-04-22 14:04:55 +04: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 ;
2015-03-30 00:12:10 +03:00
ts = ns_to_timespec64 ( diff ) ;
2011-04-22 14:04:55 +04:00
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 05:43:14 +04:00
static inline u16 exts_chan_to_edata ( int ch )
{
return 1 < < ( ( ch + EXT_EVENT ) * 2 ) ;
}
2011-06-15 03:55:19 +04:00
static int decode_evnt ( struct dp83640_private * dp83640 ,
2014-08-21 17:17:04 +04:00
void * data , int len , u16 ests )
2011-04-22 14:04:55 +04:00
{
2011-06-15 03:55:19 +04:00
struct phy_txts * phy_txts ;
2011-04-22 14:04:55 +04:00
struct ptp_clock_event event ;
2011-09-20 05:43:14 +04:00
int i , parsed ;
2011-04-22 14:04:55 +04:00
int words = ( ests > > EVNT_TS_LEN_SHIFT ) & EVNT_TS_LEN_MASK ;
2011-06-15 03:55:19 +04:00
u16 ext_status = 0 ;
2014-08-21 17:17:04 +04:00
/* calculate length of the event timestamp status message */
if ( ests & MULT_EVNT )
parsed = ( words + 2 ) * sizeof ( u16 ) ;
else
parsed = ( words + 1 ) * sizeof ( u16 ) ;
/* check if enough data is available */
if ( len < parsed )
return len ;
2011-06-15 03:55:19 +04:00
if ( ests & MULT_EVNT ) {
ext_status = * ( u16 * ) data ;
data + = sizeof ( ext_status ) ;
}
phy_txts = data ;
2011-04-22 14:04:55 +04: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 ;
}
2014-08-21 17:17:04 +04:00
if ( ! ext_status ) {
2011-09-20 05:43:14 +04:00
i = ( ( ests > > EVNT_NUM_SHIFT ) & EVNT_NUM_MASK ) - EXT_EVENT ;
ext_status = exts_chan_to_edata ( i ) ;
}
2011-04-22 14:04:55 +04:00
event . type = PTP_CLOCK_EXTTS ;
event . timestamp = phy2txts ( & dp83640 - > edata ) ;
2014-07-11 10:18:26 +04:00
/* Compensate for input path and synchronization delays */
event . timestamp - = 35 ;
2011-09-20 05:43:14 +04: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-15 03:55:19 +04:00
2014-08-21 17:17:04 +04:00
return parsed ;
2011-04-22 14:04:55 +04:00
}
2014-07-22 17:20:45 +04:00
static int match ( struct sk_buff * skb , unsigned int type , struct rxts * rxts )
{
u16 * seqid ;
unsigned int offset = 0 ;
u8 * msgtype , * data = skb_mac_header ( skb ) ;
/* check sequenceID, messageType, 12 bit hash of offset 20-29 */
if ( type & PTP_CLASS_VLAN )
offset + = VLAN_HLEN ;
switch ( type & PTP_CLASS_PMASK ) {
case PTP_CLASS_IPV4 :
2014-11-12 13:33:52 +03:00
offset + = ETH_HLEN + IPV4_HLEN ( data + offset ) + UDP_HLEN ;
2014-07-22 17:20:45 +04:00
break ;
case PTP_CLASS_IPV6 :
offset + = ETH_HLEN + IP6_HLEN + UDP_HLEN ;
break ;
case PTP_CLASS_L2 :
offset + = ETH_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 ) ;
}
2011-04-22 14:04:55 +04:00
static void decode_rxts ( struct dp83640_private * dp83640 ,
struct phy_rxts * phy_rxts )
{
struct rxts * rxts ;
2014-07-22 17:20:45 +04:00
struct skb_shared_hwtstamps * shhwtstamps = NULL ;
struct sk_buff * skb ;
2011-04-22 14:04:55 +04:00
unsigned long flags ;
spin_lock_irqsave ( & dp83640 - > rx_lock , flags ) ;
prune_rx_ts ( dp83640 ) ;
if ( list_empty ( & dp83640 - > rxpool ) ) {
2012-06-09 11:49:07 +04:00
pr_debug ( " rx timestamp pool is empty \n " ) ;
2011-04-22 14:04:55 +04:00
goto out ;
}
rxts = list_first_entry ( & dp83640 - > rxpool , struct rxts , list ) ;
list_del_init ( & rxts - > list ) ;
phy2rxts ( phy_rxts , rxts ) ;
2014-07-22 17:20:45 +04:00
2015-05-25 12:55:45 +03:00
spin_lock ( & dp83640 - > rx_queue . lock ) ;
2014-07-22 17:20:45 +04:00
skb_queue_walk ( & dp83640 - > rx_queue , skb ) {
struct dp83640_skb_info * skb_info ;
skb_info = ( struct dp83640_skb_info * ) skb - > cb ;
if ( match ( skb , skb_info - > ptp_type , rxts ) ) {
__skb_unlink ( skb , & dp83640 - > rx_queue ) ;
shhwtstamps = skb_hwtstamps ( skb ) ;
memset ( shhwtstamps , 0 , sizeof ( * shhwtstamps ) ) ;
shhwtstamps - > hwtstamp = ns_to_ktime ( rxts - > ns ) ;
netif_rx_ni ( skb ) ;
list_add ( & rxts - > list , & dp83640 - > rxpool ) ;
break ;
}
}
2015-05-25 12:55:45 +03:00
spin_unlock ( & dp83640 - > rx_queue . lock ) ;
2014-07-22 17:20:45 +04:00
if ( ! shhwtstamps )
list_add_tail ( & rxts - > list , & dp83640 - > rxts ) ;
2011-04-22 14:04:55 +04:00
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 11:49:07 +04:00
pr_debug ( " have timestamp but tx_queue empty \n " ) ;
2011-04-22 14:04:55 +04: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 ) ;
2014-08-21 17:17:04 +04:00
} else if ( PSF_EVNT = = type ) {
2011-04-22 14:04:55 +04:00
2014-08-21 17:17:04 +04:00
size = decode_evnt ( dp83640 , ptr , len , ests ) ;
2011-04-22 14:04:55 +04:00
} else {
size = 0 ;
break ;
}
ptr + = size ;
}
}
2011-09-20 05:43:16 +04:00
static int is_sync ( struct sk_buff * skb , int type )
{
u8 * data = skb - > data , * msgtype ;
unsigned int offset = 0 ;
2014-06-27 13:59:10 +04:00
if ( type & PTP_CLASS_VLAN )
offset + = VLAN_HLEN ;
switch ( type & PTP_CLASS_PMASK ) {
case PTP_CLASS_IPV4 :
2014-11-12 13:33:52 +03:00
offset + = ETH_HLEN + IPV4_HLEN ( data + offset ) + UDP_HLEN ;
2011-09-20 05:43:16 +04:00
break ;
2014-06-27 13:59:10 +04:00
case PTP_CLASS_IPV6 :
offset + = ETH_HLEN + IP6_HLEN + UDP_HLEN ;
2011-09-20 05:43:16 +04:00
break ;
2014-06-27 13:59:10 +04:00
case PTP_CLASS_L2 :
offset + = ETH_HLEN ;
2011-09-20 05:43:16 +04:00
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 14:04:55 +04:00
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 11:49:07 +04:00
pr_warn ( " phy list non-empty while unloading \n " ) ;
2011-04-22 14:04:55 +04:00
BUG ( ) ;
}
list_del ( & clock - > list ) ;
mutex_destroy ( & clock - > extreg_lock ) ;
mutex_destroy ( & clock - > clock_lock ) ;
put_device ( & clock - > bus - > dev ) ;
2014-03-21 01:21:58 +04:00
kfree ( clock - > caps . pin_config ) ;
2011-04-22 14:04:55 +04:00
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 ;
2014-06-27 14:05:30 +04:00
clock - > caps . n_per_out = N_PER_OUT ;
2014-03-21 01:21:58 +04:00
clock - > caps . n_pins = DP83640_N_PINS ;
2011-04-22 14:04:55 +04:00
clock - > caps . pps = 0 ;
clock - > caps . adjfreq = ptp_dp83640_adjfreq ;
clock - > caps . adjtime = ptp_dp83640_adjtime ;
2015-03-30 00:12:10 +03:00
clock - > caps . gettime64 = ptp_dp83640_gettime ;
clock - > caps . settime64 = ptp_dp83640_settime ;
2011-04-22 14:04:55 +04:00
clock - > caps . enable = ptp_dp83640_enable ;
2014-03-21 01:21:58 +04:00
clock - > caps . verify = ptp_dp83640_verify ;
/*
* Convert the module param defaults into a dynamic pin configuration .
*/
dp83640_gpio_defaults ( clock - > caps . pin_config ) ;
2011-04-22 14:04:55 +04:00
/*
* 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 ;
2014-03-21 01:21:58 +04:00
clock - > caps . pin_config = kzalloc ( sizeof ( struct ptp_pin_desc ) *
DP83640_N_PINS , GFP_KERNEL ) ;
if ( ! clock - > caps . pin_config ) {
kfree ( clock ) ;
clock = NULL ;
goto out ;
}
2011-04-22 14:04:55 +04:00
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 ;
2012-09-22 11:02:03 +04:00
clock - > ptp_clock = ptp_clock_register ( & clock - > caps , & phydev - > dev ) ;
2011-04-22 14:04:55 +04:00
if ( IS_ERR ( clock - > ptp_clock ) ) {
err = PTR_ERR ( clock - > ptp_clock ) ;
goto no_register ;
}
} else
list_add_tail ( & dp83640 - > list , & clock - > phylist ) ;
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 ;
if ( phydev - > addr = = BROADCAST_ADDR )
return ;
enable_status_frames ( phydev , false ) ;
cancel_work_sync ( & dp83640 - > ts_work ) ;
2014-09-08 19:25:34 +04:00
skb_queue_purge ( & dp83640 - > rx_queue ) ;
skb_queue_purge ( & dp83640 - > tx_queue ) ;
2011-10-21 04:49:17 +04:00
2011-04-22 14:04:55 +04: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 ) ;
}
2014-02-03 18:36:58 +04:00
static int dp83640_config_init ( struct phy_device * phydev )
{
2014-02-13 18:26:57 +04:00
struct dp83640_private * dp83640 = phydev - > priv ;
struct dp83640_clock * clock = dp83640 - > clock ;
if ( clock - > chosen & & ! list_empty ( & clock - > phylist ) )
recalibrate ( clock ) ;
2015-05-25 12:55:44 +03:00
else {
mutex_lock ( & clock - > extreg_lock ) ;
2014-02-13 18:26:57 +04:00
enable_broadcast ( phydev , clock - > page , 1 ) ;
2015-05-25 12:55:44 +03:00
mutex_unlock ( & clock - > extreg_lock ) ;
}
2014-02-13 18:26:57 +04:00
2014-02-03 18:36:58 +04:00
enable_status_frames ( phydev , true ) ;
2015-05-25 12:55:44 +03:00
mutex_lock ( & clock - > extreg_lock ) ;
2014-02-03 18:36:58 +04:00
ext_write ( 0 , phydev , PAGE4 , PTP_CTL , PTP_ENABLE ) ;
2015-05-25 12:55:44 +03:00
mutex_unlock ( & clock - > extreg_lock ) ;
2014-02-03 18:36:58 +04:00
return 0 ;
}
2012-12-04 14:21:38 +04:00
static int dp83640_ack_interrupt ( struct phy_device * phydev )
{
int err = phy_read ( phydev , MII_DP83640_MISR ) ;
if ( err < 0 )
return err ;
return 0 ;
}
static int dp83640_config_intr ( struct phy_device * phydev )
{
int micr ;
int misr ;
int err ;
if ( phydev - > interrupts = = PHY_INTERRUPT_ENABLED ) {
misr = phy_read ( phydev , MII_DP83640_MISR ) ;
if ( misr < 0 )
return misr ;
misr | =
( MII_DP83640_MISR_ANC_INT_EN |
MII_DP83640_MISR_DUP_INT_EN |
MII_DP83640_MISR_SPD_INT_EN |
MII_DP83640_MISR_LINK_INT_EN ) ;
err = phy_write ( phydev , MII_DP83640_MISR , misr ) ;
if ( err < 0 )
return err ;
micr = phy_read ( phydev , MII_DP83640_MICR ) ;
if ( micr < 0 )
return micr ;
micr | =
( MII_DP83640_MICR_OE |
MII_DP83640_MICR_IE ) ;
return phy_write ( phydev , MII_DP83640_MICR , micr ) ;
} else {
micr = phy_read ( phydev , MII_DP83640_MICR ) ;
if ( micr < 0 )
return micr ;
micr & =
~ ( MII_DP83640_MICR_OE |
MII_DP83640_MICR_IE ) ;
err = phy_write ( phydev , MII_DP83640_MICR , micr ) ;
if ( err < 0 )
return err ;
misr = phy_read ( phydev , MII_DP83640_MISR ) ;
if ( misr < 0 )
return misr ;
misr & =
~ ( MII_DP83640_MISR_ANC_INT_EN |
MII_DP83640_MISR_DUP_INT_EN |
MII_DP83640_MISR_SPD_INT_EN |
MII_DP83640_MISR_LINK_INT_EN ) ;
return phy_write ( phydev , MII_DP83640_MISR , misr ) ;
}
}
2011-04-22 14:04:55 +04:00
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 05:43:16 +04:00
if ( cfg . tx_type < 0 | | cfg . tx_type > HWTSTAMP_TX_ONESTEP_SYNC )
2011-04-22 14:04:55 +04:00
return - ERANGE ;
2011-09-20 05:43:16 +04:00
dp83640 - > hwts_tx_en = cfg . tx_type ;
2011-04-22 14:04:55 +04: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 05:43:16 +04:00
if ( dp83640 - > hwts_tx_en = = HWTSTAMP_TX_ONESTEP_SYNC )
txcfg0 | = SYNC_1STEP | CHK_1STEP ;
2011-04-22 14:04:55 +04:00
if ( dp83640 - > hwts_rx_en )
rxcfg0 | = RX_TS_EN ;
mutex_lock ( & dp83640 - > clock - > extreg_lock ) ;
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 sk_buff * skb ;
2014-07-22 17:20:45 +04:00
/* Deliver expired packets. */
while ( ( skb = skb_dequeue ( & dp83640 - > rx_queue ) ) ) {
struct dp83640_skb_info * skb_info ;
skb_info = ( struct dp83640_skb_info * ) skb - > cb ;
if ( ! time_after ( jiffies , skb_info - > tmo ) ) {
skb_queue_head ( & dp83640 - > rx_queue , skb ) ;
break ;
2011-04-22 14:04:55 +04:00
}
2014-07-22 17:20:45 +04:00
2012-01-10 03:52:15 +04:00
netif_rx_ni ( skb ) ;
2011-04-22 14:04:55 +04:00
}
2014-07-22 17:20:45 +04:00
if ( ! skb_queue_empty ( & dp83640 - > rx_queue ) )
schedule_work ( & dp83640 - > ts_work ) ;
2011-04-22 14:04:55 +04:00
}
static bool dp83640_rxtstamp ( struct phy_device * phydev ,
struct sk_buff * skb , int type )
{
struct dp83640_private * dp83640 = phydev - > priv ;
2014-07-22 17:20:45 +04:00
struct dp83640_skb_info * skb_info = ( struct dp83640_skb_info * ) skb - > cb ;
struct list_head * this , * next ;
struct rxts * rxts ;
struct skb_shared_hwtstamps * shhwtstamps = NULL ;
unsigned long flags ;
2011-04-22 14:04:55 +04:00
if ( is_status_frame ( skb , type ) ) {
decode_status_frame ( dp83640 , skb ) ;
2011-06-15 03:55:20 +04:00
kfree_skb ( skb ) ;
return true ;
2011-04-22 14:04:55 +04:00
}
2014-06-25 16:40:16 +04:00
if ( ! dp83640 - > hwts_rx_en )
return false ;
2014-07-22 17:20:45 +04:00
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 ) ;
netif_rx_ni ( skb ) ;
list_del_init ( & rxts - > list ) ;
list_add ( & rxts - > list , & dp83640 - > rxpool ) ;
break ;
}
}
spin_unlock_irqrestore ( & dp83640 - > rx_lock , flags ) ;
if ( ! shhwtstamps ) {
skb_info - > ptp_type = type ;
skb_info - > tmo = jiffies + 2 ;
skb_queue_tail ( & dp83640 - > rx_queue , skb ) ;
schedule_work ( & dp83640 - > ts_work ) ;
}
2011-04-22 14:04:55 +04:00
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 05:43:16 +04:00
switch ( dp83640 - > hwts_tx_en ) {
case HWTSTAMP_TX_ONESTEP_SYNC :
if ( is_sync ( skb , type ) ) {
2014-09-04 21:31:35 +04:00
kfree_skb ( skb ) ;
2011-09-20 05:43:16 +04:00
return ;
}
/* fall through */
case HWTSTAMP_TX_ON :
2014-02-03 18:36:35 +04:00
skb_shinfo ( skb ) - > tx_flags | = SKBTX_IN_PROGRESS ;
2011-09-20 05:43:16 +04:00
skb_queue_tail ( & dp83640 - > tx_queue , skb ) ;
break ;
case HWTSTAMP_TX_OFF :
default :
2014-09-04 21:31:35 +04:00
kfree_skb ( skb ) ;
2011-09-20 05:43:16 +04:00
break ;
2011-04-22 14:04:55 +04:00
}
}
2012-04-04 02:59:18 +04: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_V2_L4_EVENT ) |
( 1 < < HWTSTAMP_FILTER_PTP_V2_L2_EVENT ) |
2015-04-23 00:40:37 +03:00
( 1 < < HWTSTAMP_FILTER_PTP_V2_EVENT ) ;
2012-04-04 02:59:18 +04:00
return 0 ;
}
2011-04-22 14:04:55 +04:00
static struct phy_driver dp83640_driver = {
. phy_id = DP83640_PHY_ID ,
. phy_id_mask = 0xfffffff0 ,
. name = " NatSemi DP83640 " ,
. features = PHY_BASIC_FEATURES ,
2012-12-04 14:21:38 +04:00
. flags = PHY_HAS_INTERRUPT ,
2011-04-22 14:04:55 +04:00
. probe = dp83640_probe ,
. remove = dp83640_remove ,
2014-02-03 18:36:58 +04:00
. config_init = dp83640_config_init ,
2011-04-22 14:04:55 +04:00
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
2012-12-04 14:21:38 +04:00
. ack_interrupt = dp83640_ack_interrupt ,
. config_intr = dp83640_config_intr ,
2012-04-04 02:59:18 +04:00
. ts_info = dp83640_ts_info ,
2011-04-22 14:04:55 +04: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 " ) ;
2014-03-21 01:21:56 +04:00
MODULE_AUTHOR ( " Richard Cochran <richardcochran@gmail.com> " ) ;
2011-04-22 14:04:55 +04:00
MODULE_LICENSE ( " GPL " ) ;
module_init ( dp83640_init ) ;
module_exit ( dp83640_exit ) ;
2011-05-24 00:32:11 +04:00
static struct mdio_device_id __maybe_unused dp83640_tbl [ ] = {
2011-04-22 14:04:55 +04:00
{ DP83640_PHY_ID , 0xfffffff0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( mdio , dp83640_tbl ) ;