2019-01-21 19:10:19 +01:00
// SPDX-License-Identifier: GPL-2.0
2020-09-03 14:51:12 -05:00
/* Driver for the Texas Instruments DP83867 PHY
2015-06-02 09:34:37 -05:00
*
* Copyright ( C ) 2015 Texas Instruments Inc .
*/
# include <linux/ethtool.h>
# include <linux/kernel.h>
# include <linux/mii.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/phy.h>
2019-02-25 12:15:10 +03:00
# include <linux/delay.h>
2019-10-28 08:08:14 +00:00
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
2020-02-18 08:11:30 -06:00
# include <linux/bitfield.h>
2022-06-14 10:46:12 +02:00
# include <linux/nvmem-consumer.h>
2015-06-02 09:34:37 -05:00
# include <dt-bindings/net/ti-dp83867.h>
# define DP83867_PHY_ID 0x2000a231
# define DP83867_DEVADDR 0x1f
# define MII_DP83867_PHYCTRL 0x10
2020-02-18 08:11:30 -06:00
# define MII_DP83867_PHYSTS 0x11
2015-06-02 09:34:37 -05:00
# define MII_DP83867_MICR 0x12
# define MII_DP83867_ISR 0x13
2019-10-28 08:08:14 +00:00
# define DP83867_CFG2 0x14
2017-01-05 14:48:07 -06:00
# define DP83867_CFG3 0x1e
2019-10-28 08:08:14 +00:00
# define DP83867_CTRL 0x1f
2015-06-02 09:34:37 -05:00
/* Extended Registers */
2020-03-17 20:04:54 +02:00
# define DP83867_FLD_THR_CFG 0x002e
# define DP83867_CFG4 0x0031
2019-05-28 13:00:50 +03:00
# define DP83867_CFG4_SGMII_ANEG_MASK (BIT(5) | BIT(6))
# define DP83867_CFG4_SGMII_ANEG_TIMER_11MS (3 << 5)
# define DP83867_CFG4_SGMII_ANEG_TIMER_800US (2 << 5)
# define DP83867_CFG4_SGMII_ANEG_TIMER_2US (1 << 5)
# define DP83867_CFG4_SGMII_ANEG_TIMER_16MS (0 << 5)
2015-06-02 09:34:37 -05:00
# define DP83867_RGMIICTL 0x0032
2017-02-07 06:20:24 +01:00
# define DP83867_STRAP_STS1 0x006E
2019-05-22 18:43:23 +00:00
# define DP83867_STRAP_STS2 0x006f
2015-06-02 09:34:37 -05:00
# define DP83867_RGMIIDCTL 0x0086
2019-10-28 08:08:14 +00:00
# define DP83867_RXFCFG 0x0134
# define DP83867_RXFPMD1 0x0136
# define DP83867_RXFPMD2 0x0137
# define DP83867_RXFPMD3 0x0138
# define DP83867_RXFSOP1 0x0139
# define DP83867_RXFSOP2 0x013A
# define DP83867_RXFSOP3 0x013B
2016-10-18 16:50:18 +05:30
# define DP83867_IO_MUX_CFG 0x0170
2019-09-09 20:19:24 +03:00
# define DP83867_SGMIICTL 0x00D3
2019-05-28 13:00:49 +03:00
# define DP83867_10M_SGMII_CFG 0x016F
# define DP83867_10M_SGMII_RATE_ADAPT_MASK BIT(7)
2015-06-02 09:34:37 -05:00
# define DP83867_SW_RESET BIT(15)
# define DP83867_SW_RESTART BIT(14)
/* MICR Interrupt bits */
# define MII_DP83867_MICR_AN_ERR_INT_EN BIT(15)
# define MII_DP83867_MICR_SPEED_CHNG_INT_EN BIT(14)
# define MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN BIT(13)
# define MII_DP83867_MICR_PAGE_RXD_INT_EN BIT(12)
# define MII_DP83867_MICR_AUTONEG_COMP_INT_EN BIT(11)
# define MII_DP83867_MICR_LINK_STS_CHNG_INT_EN BIT(10)
# define MII_DP83867_MICR_FALSE_CARRIER_INT_EN BIT(8)
# define MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN BIT(4)
# define MII_DP83867_MICR_WOL_INT_EN BIT(3)
# define MII_DP83867_MICR_XGMII_ERR_INT_EN BIT(2)
# define MII_DP83867_MICR_POL_CHNG_INT_EN BIT(1)
# define MII_DP83867_MICR_JABBER_INT_EN BIT(0)
/* RGMIICTL bits */
# define DP83867_RGMII_TX_CLK_DELAY_EN BIT(1)
# define DP83867_RGMII_RX_CLK_DELAY_EN BIT(0)
2019-09-09 20:19:24 +03:00
/* SGMIICTL bits */
# define DP83867_SGMII_TYPE BIT(14)
2019-10-28 08:08:14 +00:00
/* RXFCFG bits*/
# define DP83867_WOL_MAGIC_EN BIT(0)
# define DP83867_WOL_BCAST_EN BIT(2)
# define DP83867_WOL_UCAST_EN BIT(4)
# define DP83867_WOL_SEC_EN BIT(5)
# define DP83867_WOL_ENH_MAC BIT(7)
2017-02-07 06:20:24 +01:00
/* STRAP_STS1 bits */
# define DP83867_STRAP_STS1_RESERVED BIT(11)
2019-05-22 18:43:23 +00:00
/* STRAP_STS2 bits */
# define DP83867_STRAP_STS2_CLK_SKEW_TX_MASK GENMASK(6, 4)
# define DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT 4
# define DP83867_STRAP_STS2_CLK_SKEW_RX_MASK GENMASK(2, 0)
# define DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT 0
# define DP83867_STRAP_STS2_CLK_SKEW_NONE BIT(2)
2020-03-17 20:04:54 +02:00
# define DP83867_STRAP_STS2_STRAP_FLD BIT(10)
2019-05-22 18:43:23 +00:00
2015-06-02 09:34:37 -05:00
/* PHY CTRL bits */
2019-12-09 14:10:25 -06:00
# define DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT 14
# define DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT 12
2019-05-22 18:43:26 +00:00
# define DP83867_PHYCR_FIFO_DEPTH_MAX 0x03
2019-12-09 14:10:25 -06:00
# define DP83867_PHYCR_TX_FIFO_DEPTH_MASK GENMASK(15, 14)
# define DP83867_PHYCR_RX_FIFO_DEPTH_MASK GENMASK(13, 12)
2017-02-07 06:20:24 +01:00
# define DP83867_PHYCR_RESERVED_MASK BIT(11)
2020-01-16 14:16:31 +01:00
# define DP83867_PHYCR_FORCE_LINK_GOOD BIT(10)
2015-06-02 09:34:37 -05:00
/* RGMIIDCTL bits */
2019-05-22 18:43:23 +00:00
# define DP83867_RGMII_TX_CLK_DELAY_MAX 0xf
2015-06-02 09:34:37 -05:00
# define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4
2019-12-06 14:34:32 +02:00
# define DP83867_RGMII_TX_CLK_DELAY_INV (DP83867_RGMII_TX_CLK_DELAY_MAX + 1)
2019-05-22 18:43:23 +00:00
# define DP83867_RGMII_RX_CLK_DELAY_MAX 0xf
# define DP83867_RGMII_RX_CLK_DELAY_SHIFT 0
2019-12-06 14:34:32 +02:00
# define DP83867_RGMII_RX_CLK_DELAY_INV (DP83867_RGMII_RX_CLK_DELAY_MAX + 1)
2016-10-18 16:50:18 +05:30
/* IO_MUX_CFG bits */
2019-05-22 18:43:25 +00:00
# define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK 0x1f
2016-10-18 16:50:18 +05:30
# define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0
# define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f
2019-05-22 18:43:22 +00:00
# define DP83867_IO_MUX_CFG_CLK_O_DISABLE BIT(6)
2018-02-14 17:07:11 +01:00
# define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK (0x1f << 8)
# define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT 8
2016-10-18 16:50:18 +05:30
2020-02-18 08:11:30 -06:00
/* PHY STS bits */
# define DP83867_PHYSTS_1000 BIT(15)
# define DP83867_PHYSTS_100 BIT(14)
# define DP83867_PHYSTS_DUPLEX BIT(13)
# define DP83867_PHYSTS_LINK BIT(10)
/* CFG2 bits */
# define DP83867_DOWNSHIFT_EN (BIT(8) | BIT(9))
# define DP83867_DOWNSHIFT_ATTEMPT_MASK (BIT(10) | BIT(11))
# define DP83867_DOWNSHIFT_1_COUNT_VAL 0
# define DP83867_DOWNSHIFT_2_COUNT_VAL 1
# define DP83867_DOWNSHIFT_4_COUNT_VAL 2
# define DP83867_DOWNSHIFT_8_COUNT_VAL 3
# define DP83867_DOWNSHIFT_1_COUNT 1
# define DP83867_DOWNSHIFT_2_COUNT 2
# define DP83867_DOWNSHIFT_4_COUNT 4
# define DP83867_DOWNSHIFT_8_COUNT 8
2022-05-26 17:03:47 +08:00
# define DP83867_SGMII_AUTONEG_EN BIT(7)
2020-02-18 08:11:30 -06:00
2019-10-23 17:48:45 +03:00
/* CFG3 bits */
# define DP83867_CFG3_INT_OE BIT(7)
# define DP83867_CFG3_ROBUST_AUTO_MDIX BIT(9)
2017-02-07 06:20:23 +01:00
/* CFG4 bits */
# define DP83867_CFG4_PORT_MIRROR_EN BIT(0)
2020-03-17 20:04:54 +02:00
/* FLD_THR_CFG */
# define DP83867_FLD_THR_CFG_ENERGY_LOST_THR_MASK 0x7
2017-02-07 06:20:23 +01:00
enum {
DP83867_PORT_MIRROING_KEEP ,
DP83867_PORT_MIRROING_EN ,
DP83867_PORT_MIRROING_DIS ,
} ;
2015-06-02 09:34:37 -05:00
struct dp83867_private {
2019-05-22 18:43:24 +00:00
u32 rx_id_delay ;
u32 tx_id_delay ;
2019-12-09 14:10:25 -06:00
u32 tx_fifo_depth ;
u32 rx_fifo_depth ;
2016-10-18 16:50:18 +05:30
int io_impedance ;
2017-02-07 06:20:23 +01:00
int port_mirroring ;
2017-07-04 16:23:24 +05:30
bool rxctrl_strap_quirk ;
2019-05-22 18:43:22 +00:00
bool set_clk_output ;
u32 clk_output_sel ;
2019-09-09 20:19:24 +03:00
bool sgmii_ref_clk_en ;
2015-06-02 09:34:37 -05:00
} ;
static int dp83867_ack_interrupt ( struct phy_device * phydev )
{
int err = phy_read ( phydev , MII_DP83867_ISR ) ;
if ( err < 0 )
return err ;
return 0 ;
}
2019-10-28 08:08:14 +00:00
static int dp83867_set_wol ( struct phy_device * phydev ,
struct ethtool_wolinfo * wol )
{
struct net_device * ndev = phydev - > attached_dev ;
u16 val_rxcfg , val_micr ;
2021-10-22 16:20:58 -07:00
const u8 * mac ;
2019-10-28 08:08:14 +00:00
val_rxcfg = phy_read_mmd ( phydev , DP83867_DEVADDR , DP83867_RXFCFG ) ;
val_micr = phy_read ( phydev , MII_DP83867_MICR ) ;
if ( wol - > wolopts & ( WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_UCAST |
WAKE_BCAST ) ) {
val_rxcfg | = DP83867_WOL_ENH_MAC ;
val_micr | = MII_DP83867_MICR_WOL_INT_EN ;
if ( wol - > wolopts & WAKE_MAGIC ) {
2021-10-22 16:20:58 -07:00
mac = ( const u8 * ) ndev - > dev_addr ;
2019-10-28 08:08:14 +00:00
if ( ! is_valid_ether_addr ( mac ) )
return - EINVAL ;
phy_write_mmd ( phydev , DP83867_DEVADDR , DP83867_RXFPMD1 ,
( mac [ 1 ] < < 8 | mac [ 0 ] ) ) ;
phy_write_mmd ( phydev , DP83867_DEVADDR , DP83867_RXFPMD2 ,
( mac [ 3 ] < < 8 | mac [ 2 ] ) ) ;
phy_write_mmd ( phydev , DP83867_DEVADDR , DP83867_RXFPMD3 ,
( mac [ 5 ] < < 8 | mac [ 4 ] ) ) ;
val_rxcfg | = DP83867_WOL_MAGIC_EN ;
} else {
val_rxcfg & = ~ DP83867_WOL_MAGIC_EN ;
}
if ( wol - > wolopts & WAKE_MAGICSECURE ) {
phy_write_mmd ( phydev , DP83867_DEVADDR , DP83867_RXFSOP1 ,
( wol - > sopass [ 1 ] < < 8 ) | wol - > sopass [ 0 ] ) ;
2020-09-02 14:27:04 -05:00
phy_write_mmd ( phydev , DP83867_DEVADDR , DP83867_RXFSOP2 ,
2019-10-28 08:08:14 +00:00
( wol - > sopass [ 3 ] < < 8 ) | wol - > sopass [ 2 ] ) ;
2020-09-02 14:27:04 -05:00
phy_write_mmd ( phydev , DP83867_DEVADDR , DP83867_RXFSOP3 ,
2019-10-28 08:08:14 +00:00
( wol - > sopass [ 5 ] < < 8 ) | wol - > sopass [ 4 ] ) ;
val_rxcfg | = DP83867_WOL_SEC_EN ;
} else {
val_rxcfg & = ~ DP83867_WOL_SEC_EN ;
}
if ( wol - > wolopts & WAKE_UCAST )
val_rxcfg | = DP83867_WOL_UCAST_EN ;
else
val_rxcfg & = ~ DP83867_WOL_UCAST_EN ;
if ( wol - > wolopts & WAKE_BCAST )
val_rxcfg | = DP83867_WOL_BCAST_EN ;
else
val_rxcfg & = ~ DP83867_WOL_BCAST_EN ;
} else {
val_rxcfg & = ~ DP83867_WOL_ENH_MAC ;
val_micr & = ~ MII_DP83867_MICR_WOL_INT_EN ;
}
phy_write_mmd ( phydev , DP83867_DEVADDR , DP83867_RXFCFG , val_rxcfg ) ;
phy_write ( phydev , MII_DP83867_MICR , val_micr ) ;
return 0 ;
}
static void dp83867_get_wol ( struct phy_device * phydev ,
struct ethtool_wolinfo * wol )
{
u16 value , sopass_val ;
wol - > supported = ( WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC |
WAKE_MAGICSECURE ) ;
wol - > wolopts = 0 ;
value = phy_read_mmd ( phydev , DP83867_DEVADDR , DP83867_RXFCFG ) ;
if ( value & DP83867_WOL_UCAST_EN )
wol - > wolopts | = WAKE_UCAST ;
if ( value & DP83867_WOL_BCAST_EN )
wol - > wolopts | = WAKE_BCAST ;
if ( value & DP83867_WOL_MAGIC_EN )
wol - > wolopts | = WAKE_MAGIC ;
if ( value & DP83867_WOL_SEC_EN ) {
sopass_val = phy_read_mmd ( phydev , DP83867_DEVADDR ,
DP83867_RXFSOP1 ) ;
wol - > sopass [ 0 ] = ( sopass_val & 0xff ) ;
wol - > sopass [ 1 ] = ( sopass_val > > 8 ) ;
sopass_val = phy_read_mmd ( phydev , DP83867_DEVADDR ,
DP83867_RXFSOP2 ) ;
wol - > sopass [ 2 ] = ( sopass_val & 0xff ) ;
wol - > sopass [ 3 ] = ( sopass_val > > 8 ) ;
sopass_val = phy_read_mmd ( phydev , DP83867_DEVADDR ,
DP83867_RXFSOP3 ) ;
wol - > sopass [ 4 ] = ( sopass_val & 0xff ) ;
wol - > sopass [ 5 ] = ( sopass_val > > 8 ) ;
wol - > wolopts | = WAKE_MAGICSECURE ;
}
if ( ! ( value & DP83867_WOL_ENH_MAC ) )
wol - > wolopts = 0 ;
}
2015-06-02 09:34:37 -05:00
static int dp83867_config_intr ( struct phy_device * phydev )
{
2020-11-23 17:38:14 +02:00
int micr_status , err ;
2015-06-02 09:34:37 -05:00
if ( phydev - > interrupts = = PHY_INTERRUPT_ENABLED ) {
2020-11-23 17:38:14 +02:00
err = dp83867_ack_interrupt ( phydev ) ;
if ( err )
return err ;
2015-06-02 09:34:37 -05:00
micr_status = phy_read ( phydev , MII_DP83867_MICR ) ;
if ( micr_status < 0 )
return micr_status ;
micr_status | =
( MII_DP83867_MICR_AN_ERR_INT_EN |
MII_DP83867_MICR_SPEED_CHNG_INT_EN |
2017-01-05 14:48:07 -06:00
MII_DP83867_MICR_AUTONEG_COMP_INT_EN |
MII_DP83867_MICR_LINK_STS_CHNG_INT_EN |
2015-06-02 09:34:37 -05:00
MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN |
MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN ) ;
2020-11-23 17:38:14 +02:00
err = phy_write ( phydev , MII_DP83867_MICR , micr_status ) ;
} else {
micr_status = 0x0 ;
err = phy_write ( phydev , MII_DP83867_MICR , micr_status ) ;
if ( err )
return err ;
err = dp83867_ack_interrupt ( phydev ) ;
2015-06-02 09:34:37 -05:00
}
2020-11-23 17:38:14 +02:00
return err ;
2015-06-02 09:34:37 -05:00
}
2020-11-23 17:38:13 +02:00
static irqreturn_t dp83867_handle_interrupt ( struct phy_device * phydev )
{
int irq_status , irq_enabled ;
irq_status = phy_read ( phydev , MII_DP83867_ISR ) ;
if ( irq_status < 0 ) {
phy_error ( phydev ) ;
return IRQ_NONE ;
}
irq_enabled = phy_read ( phydev , MII_DP83867_MICR ) ;
if ( irq_enabled < 0 ) {
phy_error ( phydev ) ;
return IRQ_NONE ;
}
if ( ! ( irq_status & irq_enabled ) )
return IRQ_NONE ;
phy_trigger_machine ( phydev ) ;
return IRQ_HANDLED ;
}
2020-02-18 08:11:30 -06:00
static int dp83867_read_status ( struct phy_device * phydev )
{
int status = phy_read ( phydev , MII_DP83867_PHYSTS ) ;
int ret ;
ret = genphy_read_status ( phydev ) ;
if ( ret )
return ret ;
if ( status < 0 )
return status ;
if ( status & DP83867_PHYSTS_DUPLEX )
phydev - > duplex = DUPLEX_FULL ;
else
phydev - > duplex = DUPLEX_HALF ;
if ( status & DP83867_PHYSTS_1000 )
phydev - > speed = SPEED_1000 ;
else if ( status & DP83867_PHYSTS_100 )
phydev - > speed = SPEED_100 ;
else
phydev - > speed = SPEED_10 ;
return 0 ;
}
static int dp83867_get_downshift ( struct phy_device * phydev , u8 * data )
{
int val , cnt , enable , count ;
val = phy_read ( phydev , DP83867_CFG2 ) ;
if ( val < 0 )
return val ;
enable = FIELD_GET ( DP83867_DOWNSHIFT_EN , val ) ;
cnt = FIELD_GET ( DP83867_DOWNSHIFT_ATTEMPT_MASK , val ) ;
switch ( cnt ) {
case DP83867_DOWNSHIFT_1_COUNT_VAL :
count = DP83867_DOWNSHIFT_1_COUNT ;
break ;
case DP83867_DOWNSHIFT_2_COUNT_VAL :
count = DP83867_DOWNSHIFT_2_COUNT ;
break ;
case DP83867_DOWNSHIFT_4_COUNT_VAL :
count = DP83867_DOWNSHIFT_4_COUNT ;
break ;
case DP83867_DOWNSHIFT_8_COUNT_VAL :
count = DP83867_DOWNSHIFT_8_COUNT ;
break ;
default :
return - EINVAL ;
2020-04-24 17:08:50 +08:00
}
2020-02-18 08:11:30 -06:00
* data = enable ? count : DOWNSHIFT_DEV_DISABLE ;
return 0 ;
}
static int dp83867_set_downshift ( struct phy_device * phydev , u8 cnt )
{
int val , count ;
if ( cnt > DP83867_DOWNSHIFT_8_COUNT )
return - E2BIG ;
if ( ! cnt )
return phy_clear_bits ( phydev , DP83867_CFG2 ,
DP83867_DOWNSHIFT_EN ) ;
switch ( cnt ) {
2020-09-03 14:51:12 -05:00
case DP83867_DOWNSHIFT_1_COUNT :
count = DP83867_DOWNSHIFT_1_COUNT_VAL ;
break ;
case DP83867_DOWNSHIFT_2_COUNT :
count = DP83867_DOWNSHIFT_2_COUNT_VAL ;
break ;
case DP83867_DOWNSHIFT_4_COUNT :
count = DP83867_DOWNSHIFT_4_COUNT_VAL ;
break ;
case DP83867_DOWNSHIFT_8_COUNT :
count = DP83867_DOWNSHIFT_8_COUNT_VAL ;
break ;
default :
phydev_err ( phydev ,
" Downshift count must be 1, 2, 4 or 8 \n " ) ;
return - EINVAL ;
2020-04-24 17:08:50 +08:00
}
2020-02-18 08:11:30 -06:00
val = DP83867_DOWNSHIFT_EN ;
val | = FIELD_PREP ( DP83867_DOWNSHIFT_ATTEMPT_MASK , count ) ;
return phy_modify ( phydev , DP83867_CFG2 ,
DP83867_DOWNSHIFT_EN | DP83867_DOWNSHIFT_ATTEMPT_MASK ,
val ) ;
}
static int dp83867_get_tunable ( struct phy_device * phydev ,
2020-09-03 14:51:12 -05:00
struct ethtool_tunable * tuna , void * data )
2020-02-18 08:11:30 -06:00
{
switch ( tuna - > id ) {
case ETHTOOL_PHY_DOWNSHIFT :
return dp83867_get_downshift ( phydev , data ) ;
default :
return - EOPNOTSUPP ;
}
}
static int dp83867_set_tunable ( struct phy_device * phydev ,
2020-09-03 14:51:12 -05:00
struct ethtool_tunable * tuna , const void * data )
2020-02-18 08:11:30 -06:00
{
switch ( tuna - > id ) {
case ETHTOOL_PHY_DOWNSHIFT :
return dp83867_set_downshift ( phydev , * ( const u8 * ) data ) ;
default :
return - EOPNOTSUPP ;
}
}
2017-02-07 06:20:23 +01:00
static int dp83867_config_port_mirroring ( struct phy_device * phydev )
{
struct dp83867_private * dp83867 =
( struct dp83867_private * ) phydev - > priv ;
if ( dp83867 - > port_mirroring = = DP83867_PORT_MIRROING_EN )
2019-02-06 07:38:43 +01:00
phy_set_bits_mmd ( phydev , DP83867_DEVADDR , DP83867_CFG4 ,
DP83867_CFG4_PORT_MIRROR_EN ) ;
2017-02-07 06:20:23 +01:00
else
2019-02-06 07:38:43 +01:00
phy_clear_bits_mmd ( phydev , DP83867_DEVADDR , DP83867_CFG4 ,
DP83867_CFG4_PORT_MIRROR_EN ) ;
2017-02-07 06:20:23 +01:00
return 0 ;
}
2019-12-06 14:34:32 +02:00
static int dp83867_verify_rgmii_cfg ( struct phy_device * phydev )
{
struct dp83867_private * dp83867 = phydev - > priv ;
/* Existing behavior was to use default pin strapping delay in rgmii
* mode , but rgmii should have meant no delay . Warn existing users .
*/
if ( phydev - > interface = = PHY_INTERFACE_MODE_RGMII ) {
const u16 val = phy_read_mmd ( phydev , DP83867_DEVADDR ,
DP83867_STRAP_STS2 ) ;
const u16 txskew = ( val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK ) > >
DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT ;
const u16 rxskew = ( val & DP83867_STRAP_STS2_CLK_SKEW_RX_MASK ) > >
DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT ;
if ( txskew ! = DP83867_STRAP_STS2_CLK_SKEW_NONE | |
rxskew ! = DP83867_STRAP_STS2_CLK_SKEW_NONE )
phydev_warn ( phydev ,
" PHY has delays via pin strapping, but phy-mode = 'rgmii' \n "
" Should be 'rgmii-id' to use internal delays txskew:%x rxskew:%x \n " ,
txskew , rxskew ) ;
}
/* RX delay *must* be specified if internal delay of RX is used. */
if ( ( phydev - > interface = = PHY_INTERFACE_MODE_RGMII_ID | |
phydev - > interface = = PHY_INTERFACE_MODE_RGMII_RXID ) & &
dp83867 - > rx_id_delay = = DP83867_RGMII_RX_CLK_DELAY_INV ) {
phydev_err ( phydev , " ti,rx-internal-delay must be specified \n " ) ;
return - EINVAL ;
}
/* TX delay *must* be specified if internal delay of TX is used. */
if ( ( phydev - > interface = = PHY_INTERFACE_MODE_RGMII_ID | |
phydev - > interface = = PHY_INTERFACE_MODE_RGMII_TXID ) & &
dp83867 - > tx_id_delay = = DP83867_RGMII_TX_CLK_DELAY_INV ) {
phydev_err ( phydev , " ti,tx-internal-delay must be specified \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2020-06-05 09:01:05 -05:00
# if IS_ENABLED(CONFIG_OF_MDIO)
2022-06-14 10:46:12 +02:00
static int dp83867_of_init_io_impedance ( struct phy_device * phydev )
{
struct dp83867_private * dp83867 = phydev - > priv ;
struct device * dev = & phydev - > mdio . dev ;
struct device_node * of_node = dev - > of_node ;
struct nvmem_cell * cell ;
u8 * buf , val ;
int ret ;
cell = of_nvmem_cell_get ( of_node , " io_impedance_ctrl " ) ;
if ( IS_ERR ( cell ) ) {
ret = PTR_ERR ( cell ) ;
2022-08-05 11:48:43 +03:00
if ( ret ! = - ENOENT & & ret ! = - EOPNOTSUPP )
2022-06-14 10:46:12 +02:00
return phydev_err_probe ( phydev , ret ,
" failed to get nvmem cell io_impedance_ctrl \n " ) ;
/* If no nvmem cell, check for the boolean properties. */
if ( of_property_read_bool ( of_node , " ti,max-output-impedance " ) )
dp83867 - > io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX ;
else if ( of_property_read_bool ( of_node , " ti,min-output-impedance " ) )
dp83867 - > io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN ;
else
dp83867 - > io_impedance = - 1 ; /* leave at default */
return 0 ;
}
buf = nvmem_cell_read ( cell , NULL ) ;
nvmem_cell_put ( cell ) ;
if ( IS_ERR ( buf ) )
return PTR_ERR ( buf ) ;
val = * buf ;
kfree ( buf ) ;
if ( ( val & DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK ) ! = val ) {
phydev_err ( phydev , " nvmem cell 'io_impedance_ctrl' contents out of range \n " ) ;
return - ERANGE ;
}
dp83867 - > io_impedance = val ;
return 0 ;
}
2015-06-02 09:34:37 -05:00
static int dp83867_of_init ( struct phy_device * phydev )
{
struct dp83867_private * dp83867 = phydev - > priv ;
2016-01-06 20:11:16 +01:00
struct device * dev = & phydev - > mdio . dev ;
2015-06-02 09:34:37 -05:00
struct device_node * of_node = dev - > of_node ;
int ret ;
2015-12-07 04:38:58 +01:00
if ( ! of_node )
2015-06-02 09:34:37 -05:00
return - ENODEV ;
2016-10-18 16:50:18 +05:30
/* Optional configuration */
2018-02-14 17:07:11 +01:00
ret = of_property_read_u32 ( of_node , " ti,clk-output-sel " ,
& dp83867 - > clk_output_sel ) ;
2019-05-22 18:43:22 +00:00
/* If not set, keep default */
if ( ! ret ) {
dp83867 - > set_clk_output = true ;
/* Valid values are 0 to DP83867_CLK_O_SEL_REF_CLK or
* DP83867_CLK_O_SEL_OFF .
2018-02-14 17:07:11 +01:00
*/
2019-05-22 18:43:22 +00:00
if ( dp83867 - > clk_output_sel > DP83867_CLK_O_SEL_REF_CLK & &
dp83867 - > clk_output_sel ! = DP83867_CLK_O_SEL_OFF ) {
phydev_err ( phydev , " ti,clk-output-sel value %u out of range \n " ,
dp83867 - > clk_output_sel ) ;
return - EINVAL ;
}
}
2018-02-14 17:07:11 +01:00
2022-06-14 10:46:12 +02:00
ret = dp83867_of_init_io_impedance ( phydev ) ;
if ( ret )
return ret ;
2016-10-18 16:50:18 +05:30
2017-07-04 16:23:24 +05:30
dp83867 - > rxctrl_strap_quirk = of_property_read_bool ( of_node ,
2020-09-03 14:51:12 -05:00
" ti,dp83867-rxctrl-strap-quirk " ) ;
2017-07-04 16:23:24 +05:30
2019-09-09 20:19:24 +03:00
dp83867 - > sgmii_ref_clk_en = of_property_read_bool ( of_node ,
2020-09-03 14:51:12 -05:00
" ti,sgmii-ref-clock-output-enable " ) ;
2019-05-22 18:43:23 +00:00
2019-12-06 14:34:32 +02:00
dp83867 - > rx_id_delay = DP83867_RGMII_RX_CLK_DELAY_INV ;
ret = of_property_read_u32 ( of_node , " ti,rx-internal-delay " ,
& dp83867 - > rx_id_delay ) ;
if ( ! ret & & dp83867 - > rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX ) {
phydev_err ( phydev ,
" ti,rx-internal-delay value of %u out of range \n " ,
dp83867 - > rx_id_delay ) ;
return - EINVAL ;
2019-05-22 18:43:23 +00:00
}
2019-12-06 14:34:32 +02:00
dp83867 - > tx_id_delay = DP83867_RGMII_TX_CLK_DELAY_INV ;
ret = of_property_read_u32 ( of_node , " ti,tx-internal-delay " ,
& dp83867 - > tx_id_delay ) ;
if ( ! ret & & dp83867 - > tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX ) {
phydev_err ( phydev ,
" ti,tx-internal-delay value of %u out of range \n " ,
dp83867 - > tx_id_delay ) ;
return - EINVAL ;
2019-05-22 18:43:23 +00:00
}
2015-06-02 09:34:37 -05:00
2017-02-07 06:20:23 +01:00
if ( of_property_read_bool ( of_node , " enet-phy-lane-swap " ) )
dp83867 - > port_mirroring = DP83867_PORT_MIRROING_EN ;
if ( of_property_read_bool ( of_node , " enet-phy-lane-no-swap " ) )
dp83867 - > port_mirroring = DP83867_PORT_MIRROING_DIS ;
2019-05-22 18:43:26 +00:00
ret = of_property_read_u32 ( of_node , " ti,fifo-depth " ,
2019-12-09 14:10:25 -06:00
& dp83867 - > tx_fifo_depth ) ;
2019-05-22 18:43:26 +00:00
if ( ret ) {
2019-12-09 14:10:25 -06:00
ret = of_property_read_u32 ( of_node , " tx-fifo-depth " ,
& dp83867 - > tx_fifo_depth ) ;
if ( ret )
dp83867 - > tx_fifo_depth =
DP83867_PHYCR_FIFO_DEPTH_4_B_NIB ;
2019-05-22 18:43:26 +00:00
}
2019-12-09 14:10:25 -06:00
if ( dp83867 - > tx_fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX ) {
phydev_err ( phydev , " tx-fifo-depth value %u out of range \n " ,
dp83867 - > tx_fifo_depth ) ;
return - EINVAL ;
}
ret = of_property_read_u32 ( of_node , " rx-fifo-depth " ,
& dp83867 - > rx_fifo_depth ) ;
if ( ret )
dp83867 - > rx_fifo_depth = DP83867_PHYCR_FIFO_DEPTH_4_B_NIB ;
if ( dp83867 - > rx_fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX ) {
phydev_err ( phydev , " rx-fifo-depth value %u out of range \n " ,
dp83867 - > rx_fifo_depth ) ;
2019-05-22 18:43:26 +00:00
return - EINVAL ;
}
2019-12-09 14:10:25 -06:00
2019-05-22 18:43:26 +00:00
return 0 ;
2015-06-02 09:34:37 -05:00
}
# else
static int dp83867_of_init ( struct phy_device * phydev )
{
2021-10-13 14:59:41 +08:00
struct dp83867_private * dp83867 = phydev - > priv ;
u16 delay ;
/* For non-OF device, the RX and TX ID values are either strapped
* or take from default value . So , we init RX & TX ID values here
* so that the RGMIIDCTL is configured correctly later in
* dp83867_config_init ( ) ;
*/
delay = phy_read_mmd ( phydev , DP83867_DEVADDR , DP83867_RGMIIDCTL ) ;
dp83867 - > rx_id_delay = delay & DP83867_RGMII_RX_CLK_DELAY_MAX ;
dp83867 - > tx_id_delay = ( delay > > DP83867_RGMII_TX_CLK_DELAY_SHIFT ) &
DP83867_RGMII_TX_CLK_DELAY_MAX ;
/* Per datasheet, IO impedance is default to 50-ohm, so we set the
* same here or else the default ' 0 ' means highest IO impedance
* which is wrong .
*/
dp83867 - > io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN / 2 ;
2015-06-02 09:34:37 -05:00
return 0 ;
}
# endif /* CONFIG_OF_MDIO */
2019-05-22 18:43:27 +00:00
static int dp83867_probe ( struct phy_device * phydev )
2015-06-02 09:34:37 -05:00
{
struct dp83867_private * dp83867 ;
2019-05-22 18:43:27 +00:00
dp83867 = devm_kzalloc ( & phydev - > mdio . dev , sizeof ( * dp83867 ) ,
GFP_KERNEL ) ;
if ( ! dp83867 )
return - ENOMEM ;
phydev - > priv = dp83867 ;
2019-10-23 17:48:46 +03:00
return dp83867_of_init ( phydev ) ;
2019-05-22 18:43:27 +00:00
}
static int dp83867_config_init ( struct phy_device * phydev )
{
struct dp83867_private * dp83867 = phydev - > priv ;
2017-02-07 06:20:24 +01:00
int ret , val , bs ;
2016-07-01 22:35:03 +02:00
u16 delay ;
2015-06-02 09:34:37 -05:00
2020-02-18 08:11:30 -06:00
/* Force speed optimization for the PHY even if it strapped */
ret = phy_modify ( phydev , DP83867_CFG2 , DP83867_DOWNSHIFT_EN ,
DP83867_DOWNSHIFT_EN ) ;
if ( ret )
return ret ;
2019-12-06 14:34:32 +02:00
ret = dp83867_verify_rgmii_cfg ( phydev ) ;
if ( ret )
return ret ;
2017-07-04 16:23:24 +05:30
/* RX_DV/RX_CTRL strapped in mode 1 or mode 2 workaround */
2019-02-06 07:38:43 +01:00
if ( dp83867 - > rxctrl_strap_quirk )
phy_clear_bits_mmd ( phydev , DP83867_DEVADDR , DP83867_CFG4 ,
BIT ( 7 ) ) ;
2017-07-04 16:23:24 +05:30
2020-03-17 20:04:54 +02:00
bs = phy_read_mmd ( phydev , DP83867_DEVADDR , DP83867_STRAP_STS2 ) ;
if ( bs & DP83867_STRAP_STS2_STRAP_FLD ) {
/* When using strap to enable FLD, the ENERGY_LOST_FLD_THR will
* be set to 0x2 . This may causes the PHY link to be unstable -
* the default value 0x1 need to be restored .
*/
ret = phy_modify_mmd ( phydev , DP83867_DEVADDR ,
DP83867_FLD_THR_CFG ,
DP83867_FLD_THR_CFG_ENERGY_LOST_THR_MASK ,
0x1 ) ;
if ( ret )
return ret ;
}
2019-12-09 14:10:25 -06:00
if ( phy_interface_is_rgmii ( phydev ) | |
phydev - > interface = = PHY_INTERFACE_MODE_SGMII ) {
val = phy_read ( phydev , MII_DP83867_PHYCTRL ) ;
if ( val < 0 )
return val ;
val & = ~ DP83867_PHYCR_TX_FIFO_DEPTH_MASK ;
val | = ( dp83867 - > tx_fifo_depth < <
DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT ) ;
if ( phydev - > interface = = PHY_INTERFACE_MODE_SGMII ) {
val & = ~ DP83867_PHYCR_RX_FIFO_DEPTH_MASK ;
val | = ( dp83867 - > rx_fifo_depth < <
DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT ) ;
}
ret = phy_write ( phydev , MII_DP83867_PHYCTRL , val ) ;
if ( ret )
return ret ;
}
2015-06-02 09:34:37 -05:00
if ( phy_interface_is_rgmii ( phydev ) ) {
2016-07-01 22:35:03 +02:00
val = phy_read ( phydev , MII_DP83867_PHYCTRL ) ;
if ( val < 0 )
return val ;
2017-02-07 06:20:24 +01:00
/* The code below checks if "port mirroring" N/A MODE4 has been
* enabled during power on bootstrap .
*
* Such N / A mode enabled by mistake can put PHY IC in some
* internal testing mode and disable RGMII transmission .
*
* In this particular case one needs to check STRAP_STS1
* register ' s bit 11 ( marked as RESERVED ) .
*/
2017-03-21 16:36:53 +00:00
bs = phy_read_mmd ( phydev , DP83867_DEVADDR , DP83867_STRAP_STS1 ) ;
2017-02-07 06:20:24 +01:00
if ( bs & DP83867_STRAP_STS1_RESERVED )
val & = ~ DP83867_PHYCR_RESERVED_MASK ;
2016-07-01 22:35:03 +02:00
ret = phy_write ( phydev , MII_DP83867_PHYCTRL , val ) ;
2015-06-02 09:34:37 -05:00
if ( ret )
return ret ;
2019-05-31 10:49:43 -07:00
/* If rgmii mode with no internal delay is selected, we do NOT use
* aligned mode as one might expect . Instead we use the PHY ' s default
* based on pin strapping . And the " mode 0 " default is to * use *
* internal delay with a value of 7 ( 2.00 ns ) .
*
* Set up RGMII delays
*/
2017-03-21 16:36:53 +00:00
val = phy_read_mmd ( phydev , DP83867_DEVADDR , DP83867_RGMIICTL ) ;
2015-06-02 09:34:37 -05:00
2019-05-22 18:43:23 +00:00
val & = ~ ( DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN ) ;
2015-06-02 09:34:37 -05:00
if ( phydev - > interface = = PHY_INTERFACE_MODE_RGMII_ID )
val | = ( DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN ) ;
if ( phydev - > interface = = PHY_INTERFACE_MODE_RGMII_TXID )
val | = DP83867_RGMII_TX_CLK_DELAY_EN ;
if ( phydev - > interface = = PHY_INTERFACE_MODE_RGMII_RXID )
val | = DP83867_RGMII_RX_CLK_DELAY_EN ;
2017-03-21 16:36:53 +00:00
phy_write_mmd ( phydev , DP83867_DEVADDR , DP83867_RGMIICTL , val ) ;
2015-06-02 09:34:37 -05:00
2019-12-06 14:34:32 +02:00
delay = 0 ;
if ( dp83867 - > rx_id_delay ! = DP83867_RGMII_RX_CLK_DELAY_INV )
delay | = dp83867 - > rx_id_delay ;
if ( dp83867 - > tx_id_delay ! = DP83867_RGMII_TX_CLK_DELAY_INV )
delay | = dp83867 - > tx_id_delay < <
DP83867_RGMII_TX_CLK_DELAY_SHIFT ;
2015-06-02 09:34:37 -05:00
2017-03-21 16:36:53 +00:00
phy_write_mmd ( phydev , DP83867_DEVADDR , DP83867_RGMIIDCTL ,
delay ) ;
2015-06-02 09:34:37 -05:00
}
2019-05-22 18:43:25 +00:00
/* If specified, set io impedance */
if ( dp83867 - > io_impedance > = 0 )
phy_modify_mmd ( phydev , DP83867_DEVADDR , DP83867_IO_MUX_CFG ,
DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK ,
dp83867 - > io_impedance ) ;
2019-05-28 13:00:49 +03:00
if ( phydev - > interface = = PHY_INTERFACE_MODE_SGMII ) {
/* For support SPEED_10 in SGMII mode
* DP83867_10M_SGMII_RATE_ADAPT bit
* has to be cleared by software . That
* does not affect SPEED_100 and
* SPEED_1000 .
*/
ret = phy_modify_mmd ( phydev , DP83867_DEVADDR ,
DP83867_10M_SGMII_CFG ,
DP83867_10M_SGMII_RATE_ADAPT_MASK ,
0 ) ;
if ( ret )
return ret ;
2019-05-28 13:00:50 +03:00
/* After reset SGMII Autoneg timer is set to 2us (bits 6 and 5
* are 01 ) . That is not enough to finalize autoneg on some
* devices . Increase this timer duration to maximum 16 ms .
*/
ret = phy_modify_mmd ( phydev , DP83867_DEVADDR ,
DP83867_CFG4 ,
DP83867_CFG4_SGMII_ANEG_MASK ,
DP83867_CFG4_SGMII_ANEG_TIMER_16MS ) ;
if ( ret )
return ret ;
2019-09-09 20:19:24 +03:00
val = phy_read_mmd ( phydev , DP83867_DEVADDR , DP83867_SGMIICTL ) ;
/* SGMII type is set to 4-wire mode by default.
* If we place appropriate property in dts ( see above )
* switch on 6 - wire mode .
*/
if ( dp83867 - > sgmii_ref_clk_en )
val | = DP83867_SGMII_TYPE ;
else
val & = ~ DP83867_SGMII_TYPE ;
phy_write_mmd ( phydev , DP83867_DEVADDR , DP83867_SGMIICTL , val ) ;
2019-05-28 13:00:49 +03:00
}
2019-10-23 17:48:45 +03:00
val = phy_read ( phydev , DP83867_CFG3 ) ;
2017-01-05 14:48:07 -06:00
/* Enable Interrupt output INT_OE in CFG3 register */
2019-10-23 17:48:45 +03:00
if ( phy_interrupt_is_valid ( phydev ) )
val | = DP83867_CFG3_INT_OE ;
val | = DP83867_CFG3_ROBUST_AUTO_MDIX ;
phy_write ( phydev , DP83867_CFG3 , val ) ;
2017-01-05 14:48:07 -06:00
2017-02-07 06:20:23 +01:00
if ( dp83867 - > port_mirroring ! = DP83867_PORT_MIRROING_KEEP )
dp83867_config_port_mirroring ( phydev ) ;
2018-02-14 17:07:11 +01:00
/* Clock output selection if muxing property is set */
2019-05-22 18:43:22 +00:00
if ( dp83867 - > set_clk_output ) {
u16 mask = DP83867_IO_MUX_CFG_CLK_O_DISABLE ;
if ( dp83867 - > clk_output_sel = = DP83867_CLK_O_SEL_OFF ) {
val = DP83867_IO_MUX_CFG_CLK_O_DISABLE ;
} else {
mask | = DP83867_IO_MUX_CFG_CLK_O_SEL_MASK ;
val = dp83867 - > clk_output_sel < <
DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT ;
}
2019-02-06 07:38:43 +01:00
phy_modify_mmd ( phydev , DP83867_DEVADDR , DP83867_IO_MUX_CFG ,
2019-05-22 18:43:22 +00:00
mask , val ) ;
}
2018-02-14 17:07:11 +01:00
2015-06-02 09:34:37 -05:00
return 0 ;
}
static int dp83867_phy_reset ( struct phy_device * phydev )
{
int err ;
2021-06-09 19:43:42 -05:00
err = phy_write ( phydev , DP83867_CTRL , DP83867_SW_RESTART ) ;
2015-06-02 09:34:37 -05:00
if ( err < 0 )
return err ;
2019-02-25 12:15:10 +03:00
usleep_range ( 10 , 20 ) ;
2020-01-16 14:16:31 +01:00
return phy_modify ( phydev , MII_DP83867_PHYCTRL ,
DP83867_PHYCR_FORCE_LINK_GOOD , 0 ) ;
2015-06-02 09:34:37 -05:00
}
2022-05-26 17:03:47 +08:00
static void dp83867_link_change_notify ( struct phy_device * phydev )
{
/* There is a limitation in DP83867 PHY device where SGMII AN is
* only triggered once after the device is booted up . Even after the
* PHY TPI is down and up again , SGMII AN is not triggered and
* hence no new in - band message from PHY to MAC side SGMII .
* This could cause an issue during power up , when PHY is up prior
* to MAC . At this condition , once MAC side SGMII is up , MAC side
* SGMII wouldn ` t receive new in - band message from TI PHY with
* correct link status , speed and duplex info .
* Thus , implemented a SW solution here to retrigger SGMII Auto - Neg
* whenever there is a link change .
*/
if ( phydev - > interface = = PHY_INTERFACE_MODE_SGMII ) {
int val = 0 ;
val = phy_clear_bits ( phydev , DP83867_CFG2 ,
DP83867_SGMII_AUTONEG_EN ) ;
if ( val < 0 )
return ;
phy_set_bits ( phydev , DP83867_CFG2 ,
DP83867_SGMII_AUTONEG_EN ) ;
}
}
2015-06-02 09:34:37 -05:00
static struct phy_driver dp83867_driver [ ] = {
{
. phy_id = DP83867_PHY_ID ,
. phy_id_mask = 0xfffffff0 ,
. name = " TI DP83867 " ,
2019-04-12 20:47:03 +02:00
/* PHY_GBIT_FEATURES */
2015-06-02 09:34:37 -05:00
2019-05-22 18:43:27 +00:00
. probe = dp83867_probe ,
2015-06-02 09:34:37 -05:00
. config_init = dp83867_config_init ,
. soft_reset = dp83867_phy_reset ,
2020-02-18 08:11:30 -06:00
. read_status = dp83867_read_status ,
. get_tunable = dp83867_get_tunable ,
. set_tunable = dp83867_set_tunable ,
2019-10-28 08:08:14 +00:00
. get_wol = dp83867_get_wol ,
. set_wol = dp83867_set_wol ,
2015-06-02 09:34:37 -05:00
/* IRQ related */
. config_intr = dp83867_config_intr ,
2020-11-23 17:38:13 +02:00
. handle_interrupt = dp83867_handle_interrupt ,
2015-06-02 09:34:37 -05:00
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2022-05-26 17:03:47 +08:00
. link_change_notify = dp83867_link_change_notify ,
2015-06-02 09:34:37 -05:00
} ,
} ;
module_phy_driver ( dp83867_driver ) ;
static struct mdio_device_id __maybe_unused dp83867_tbl [ ] = {
{ DP83867_PHY_ID , 0xfffffff0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( mdio , dp83867_tbl ) ;
MODULE_DESCRIPTION ( " Texas Instruments DP83867 PHY driver " ) ;
MODULE_AUTHOR ( " Dan Murphy <dmurphy@ti.com " ) ;
2019-01-21 19:10:19 +01:00
MODULE_LICENSE ( " GPL v2 " ) ;