Commit 161e3106 authored by David S. Miller's avatar David S. Miller

Merge branch 'Enable-Fiber-on-DP83822-PHY'

Dan Murphy says:

====================
Enable Fiber on DP83822 PHY

The DP83822 Ethernet PHY has the ability to connect via a Fiber port.  The
derivative PHYs DP83825 and DP83826 do not have this ability. In fiber mode
the DP83822 disables auto negotiation and has a fixed 100Mbps speed with
support for full or half duplex modes.

A devicetree binding was added to set the signal polarity for the fiber
connection.  This property is only applicable if the FX_EN strap is set in
hardware other wise the signal loss detection is disabled on the PHY.

If the FX_EN is not strapped the device can be configured to run in fiber mode
via the device tree. All be it the PHY will not perform signal loss detection.

v2 review from a long time ago can be found here - https://lore.kernel.org/patchwork/patch/1270958/
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 316cdaa1 5dc39fd5
# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause)
# Copyright (C) 2020 Texas Instruments Incorporated
%YAML 1.2
---
$id: "http://devicetree.org/schemas/net/ti,dp83822.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: TI DP83822 ethernet PHY
maintainers:
- Dan Murphy <dmurphy@ti.com>
description: |
The DP83822 is a low-power, single-port, 10/100 Mbps Ethernet PHY. It
provides all of the physical layer functions needed to transmit and receive
data over standard, twisted-pair cables or to connect to an external,
fiber-optic transceiver. Additionally, the DP83822 provides flexibility to
connect to a MAC through a standard MII, RMII, or RGMII interface
Specifications about the Ethernet PHY can be found at:
http://www.ti.com/lit/ds/symlink/dp83822i.pdf
allOf:
- $ref: "ethernet-phy.yaml#"
properties:
reg:
maxItems: 1
ti,link-loss-low:
type: boolean
description: |
DP83822 PHY in Fiber mode only.
Sets the DP83822 to detect a link drop condition when the signal goes
high. If not set then link drop will occur when the signal goes low.
This property is only applicable if the fiber mode support is strapped
to on.
ti,fiber-mode:
type: boolean
description: |
DP83822 PHY only.
If present the DP83822 PHY is configured to operate in fiber mode
Fiber mode support can also be strapped. If the strap pin is not set
correctly or not set at all then this boolean can be used to enable it.
If the fiber mode is not strapped then signal detection for the PHY
is disabled.
In fiber mode, auto-negotiation is disabled and the PHY can only work in
100base-fx (full and half duplex) modes.
rx-internal-delay-ps:
description: |
DP83822 PHY only.
Setting this property to a non-zero number sets the RX internal delay
for the PHY. The internal delay for the PHY is fixed to 3.5ns relative
to receive data.
tx-internal-delay-ps:
description: |
DP83822 PHY only.
Setting this property to a non-zero number sets the TX internal delay
for the PHY. The internal delay for the PHY is fixed to 3.5ns relative
to transmit data.
required:
- reg
examples:
- |
mdio0 {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@0 {
reg = <0>;
rx-internal-delay-ps = <1>;
tx-internal-delay-ps = <1>;
};
};
...
...@@ -23,16 +23,31 @@ ...@@ -23,16 +23,31 @@
#define DP83822_DEVADDR 0x1f #define DP83822_DEVADDR 0x1f
#define MII_DP83822_CTRL_2 0x0a
#define MII_DP83822_PHYSTS 0x10
#define MII_DP83822_PHYSCR 0x11 #define MII_DP83822_PHYSCR 0x11
#define MII_DP83822_MISR1 0x12 #define MII_DP83822_MISR1 0x12
#define MII_DP83822_MISR2 0x13 #define MII_DP83822_MISR2 0x13
#define MII_DP83822_FCSCR 0x14
#define MII_DP83822_RCSR 0x17 #define MII_DP83822_RCSR 0x17
#define MII_DP83822_RESET_CTRL 0x1f #define MII_DP83822_RESET_CTRL 0x1f
#define MII_DP83822_GENCFG 0x465 #define MII_DP83822_GENCFG 0x465
#define MII_DP83822_SOR1 0x467
/* GENCFG */
#define DP83822_SIG_DET_LOW BIT(0)
/* Control Register 2 bits */
#define DP83822_FX_ENABLE BIT(14)
#define DP83822_HW_RESET BIT(15) #define DP83822_HW_RESET BIT(15)
#define DP83822_SW_RESET BIT(14) #define DP83822_SW_RESET BIT(14)
/* PHY STS bits */
#define DP83822_PHYSTS_DUPLEX BIT(2)
#define DP83822_PHYSTS_10 BIT(1)
#define DP83822_PHYSTS_LINK BIT(0)
/* PHYSCR Register Fields */ /* PHYSCR Register Fields */
#define DP83822_PHYSCR_INT_OE BIT(0) /* Interrupt Output Enable */ #define DP83822_PHYSCR_INT_OE BIT(0) /* Interrupt Output Enable */
#define DP83822_PHYSCR_INTEN BIT(1) /* Interrupt Enable */ #define DP83822_PHYSCR_INTEN BIT(1) /* Interrupt Enable */
...@@ -83,6 +98,28 @@ ...@@ -83,6 +98,28 @@
#define DP83822_RX_CLK_SHIFT BIT(12) #define DP83822_RX_CLK_SHIFT BIT(12)
#define DP83822_TX_CLK_SHIFT BIT(11) #define DP83822_TX_CLK_SHIFT BIT(11)
/* SOR1 mode */
#define DP83822_STRAP_MODE1 0
#define DP83822_STRAP_MODE2 BIT(0)
#define DP83822_STRAP_MODE3 BIT(1)
#define DP83822_STRAP_MODE4 GENMASK(1, 0)
#define DP83822_COL_STRAP_MASK GENMASK(11, 10)
#define DP83822_COL_SHIFT 10
#define DP83822_RX_ER_STR_MASK GENMASK(9, 8)
#define DP83822_RX_ER_SHIFT 8
#define MII_DP83822_FIBER_ADVERTISE (ADVERTISED_TP | ADVERTISED_MII | \
ADVERTISED_FIBRE | ADVERTISED_BNC | \
ADVERTISED_Pause | ADVERTISED_Asym_Pause | \
ADVERTISED_100baseT_Full)
struct dp83822_private {
bool fx_signal_det_low;
int fx_enabled;
u16 fx_sd_enable;
};
static int dp83822_ack_interrupt(struct phy_device *phydev) static int dp83822_ack_interrupt(struct phy_device *phydev)
{ {
int err; int err;
...@@ -197,6 +234,7 @@ static void dp83822_get_wol(struct phy_device *phydev, ...@@ -197,6 +234,7 @@ static void dp83822_get_wol(struct phy_device *phydev,
static int dp83822_config_intr(struct phy_device *phydev) static int dp83822_config_intr(struct phy_device *phydev)
{ {
struct dp83822_private *dp83822 = phydev->priv;
int misr_status; int misr_status;
int physcr_status; int physcr_status;
int err; int err;
...@@ -208,13 +246,16 @@ static int dp83822_config_intr(struct phy_device *phydev) ...@@ -208,13 +246,16 @@ static int dp83822_config_intr(struct phy_device *phydev)
misr_status |= (DP83822_RX_ERR_HF_INT_EN | misr_status |= (DP83822_RX_ERR_HF_INT_EN |
DP83822_FALSE_CARRIER_HF_INT_EN | DP83822_FALSE_CARRIER_HF_INT_EN |
DP83822_ANEG_COMPLETE_INT_EN |
DP83822_DUP_MODE_CHANGE_INT_EN |
DP83822_SPEED_CHANGED_INT_EN |
DP83822_LINK_STAT_INT_EN | DP83822_LINK_STAT_INT_EN |
DP83822_ENERGY_DET_INT_EN | DP83822_ENERGY_DET_INT_EN |
DP83822_LINK_QUAL_INT_EN); DP83822_LINK_QUAL_INT_EN);
if (!dp83822->fx_enabled)
misr_status |= DP83822_ANEG_COMPLETE_INT_EN |
DP83822_DUP_MODE_CHANGE_INT_EN |
DP83822_SPEED_CHANGED_INT_EN;
err = phy_write(phydev, MII_DP83822_MISR1, misr_status); err = phy_write(phydev, MII_DP83822_MISR1, misr_status);
if (err < 0) if (err < 0)
return err; return err;
...@@ -224,14 +265,16 @@ static int dp83822_config_intr(struct phy_device *phydev) ...@@ -224,14 +265,16 @@ static int dp83822_config_intr(struct phy_device *phydev)
return misr_status; return misr_status;
misr_status |= (DP83822_JABBER_DET_INT_EN | misr_status |= (DP83822_JABBER_DET_INT_EN |
DP83822_WOL_PKT_INT_EN |
DP83822_SLEEP_MODE_INT_EN | DP83822_SLEEP_MODE_INT_EN |
DP83822_MDI_XOVER_INT_EN |
DP83822_LB_FIFO_INT_EN | DP83822_LB_FIFO_INT_EN |
DP83822_PAGE_RX_INT_EN | DP83822_PAGE_RX_INT_EN |
DP83822_ANEG_ERR_INT_EN |
DP83822_EEE_ERROR_CHANGE_INT_EN); DP83822_EEE_ERROR_CHANGE_INT_EN);
if (!dp83822->fx_enabled)
misr_status |= DP83822_MDI_XOVER_INT_EN |
DP83822_ANEG_ERR_INT_EN |
DP83822_WOL_PKT_INT_EN;
err = phy_write(phydev, MII_DP83822_MISR2, misr_status); err = phy_write(phydev, MII_DP83822_MISR2, misr_status);
if (err < 0) if (err < 0)
return err; return err;
...@@ -270,13 +313,60 @@ static int dp8382x_disable_wol(struct phy_device *phydev) ...@@ -270,13 +313,60 @@ static int dp8382x_disable_wol(struct phy_device *phydev)
MII_DP83822_WOL_CFG, value); MII_DP83822_WOL_CFG, value);
} }
static int dp83822_read_status(struct phy_device *phydev)
{
struct dp83822_private *dp83822 = phydev->priv;
int status = phy_read(phydev, MII_DP83822_PHYSTS);
int ctrl2;
int ret;
if (dp83822->fx_enabled) {
if (status & DP83822_PHYSTS_LINK) {
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
} else {
ctrl2 = phy_read(phydev, MII_DP83822_CTRL_2);
if (ctrl2 < 0)
return ctrl2;
if (!(ctrl2 & DP83822_FX_ENABLE)) {
ret = phy_write(phydev, MII_DP83822_CTRL_2,
DP83822_FX_ENABLE | ctrl2);
if (ret < 0)
return ret;
}
}
}
ret = genphy_read_status(phydev);
if (ret)
return ret;
if (status < 0)
return status;
if (status & DP83822_PHYSTS_DUPLEX)
phydev->duplex = DUPLEX_FULL;
else
phydev->duplex = DUPLEX_HALF;
if (status & DP83822_PHYSTS_10)
phydev->speed = SPEED_10;
else
phydev->speed = SPEED_100;
return 0;
}
static int dp83822_config_init(struct phy_device *phydev) static int dp83822_config_init(struct phy_device *phydev)
{ {
struct dp83822_private *dp83822 = phydev->priv;
struct device *dev = &phydev->mdio.dev; struct device *dev = &phydev->mdio.dev;
int rgmii_delay; int rgmii_delay;
s32 rx_int_delay; s32 rx_int_delay;
s32 tx_int_delay; s32 tx_int_delay;
int err = 0; int err = 0;
int bmcr;
if (phy_interface_is_rgmii(phydev)) { if (phy_interface_is_rgmii(phydev)) {
rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0,
...@@ -302,6 +392,53 @@ static int dp83822_config_init(struct phy_device *phydev) ...@@ -302,6 +392,53 @@ static int dp83822_config_init(struct phy_device *phydev)
} }
} }
if (dp83822->fx_enabled) {
err = phy_modify(phydev, MII_DP83822_CTRL_2,
DP83822_FX_ENABLE, 1);
if (err < 0)
return err;
/* Only allow advertising what this PHY supports */
linkmode_and(phydev->advertising, phydev->advertising,
phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
phydev->advertising);
/* Auto neg is not supported in fiber mode */
bmcr = phy_read(phydev, MII_BMCR);
if (bmcr < 0)
return bmcr;
if (bmcr & BMCR_ANENABLE) {
err = phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
if (err < 0)
return err;
}
phydev->autoneg = AUTONEG_DISABLE;
linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
phydev->supported);
linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
phydev->advertising);
/* Setup fiber advertisement */
err = phy_modify_changed(phydev, MII_ADVERTISE,
MII_DP83822_FIBER_ADVERTISE,
MII_DP83822_FIBER_ADVERTISE);
if (err < 0)
return err;
if (dp83822->fx_signal_det_low) {
err = phy_set_bits_mmd(phydev, DP83822_DEVADDR,
MII_DP83822_GENCFG,
DP83822_SIG_DET_LOW);
if (err)
return err;
}
}
return dp8382x_disable_wol(phydev); return dp8382x_disable_wol(phydev);
} }
...@@ -314,13 +451,85 @@ static int dp83822_phy_reset(struct phy_device *phydev) ...@@ -314,13 +451,85 @@ static int dp83822_phy_reset(struct phy_device *phydev)
{ {
int err; int err;
err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_HW_RESET); err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_SW_RESET);
if (err < 0) if (err < 0)
return err; return err;
return phydev->drv->config_init(phydev); return phydev->drv->config_init(phydev);
} }
#ifdef CONFIG_OF_MDIO
static int dp83822_of_init(struct phy_device *phydev)
{
struct dp83822_private *dp83822 = phydev->priv;
struct device *dev = &phydev->mdio.dev;
/* Signal detection for the PHY is only enabled if the FX_EN and the
* SD_EN pins are strapped. Signal detection can only enabled if FX_EN
* is strapped otherwise signal detection is disabled for the PHY.
*/
if (dp83822->fx_enabled && dp83822->fx_sd_enable)
dp83822->fx_signal_det_low = device_property_present(dev,
"ti,link-loss-low");
if (!dp83822->fx_enabled)
dp83822->fx_enabled = device_property_present(dev,
"ti,fiber-mode");
return 0;
}
#else
static int dp83822_of_init(struct phy_device *phydev)
{
return 0;
}
#endif /* CONFIG_OF_MDIO */
static int dp83822_read_straps(struct phy_device *phydev)
{
struct dp83822_private *dp83822 = phydev->priv;
int fx_enabled, fx_sd_enable;
int val;
val = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_SOR1);
if (val < 0)
return val;
fx_enabled = (val & DP83822_COL_STRAP_MASK) >> DP83822_COL_SHIFT;
if (fx_enabled == DP83822_STRAP_MODE2 ||
fx_enabled == DP83822_STRAP_MODE3)
dp83822->fx_enabled = 1;
if (dp83822->fx_enabled) {
fx_sd_enable = (val & DP83822_RX_ER_STR_MASK) >> DP83822_RX_ER_SHIFT;
if (fx_sd_enable == DP83822_STRAP_MODE3 ||
fx_sd_enable == DP83822_STRAP_MODE4)
dp83822->fx_sd_enable = 1;
}
return 0;
}
static int dp83822_probe(struct phy_device *phydev)
{
struct dp83822_private *dp83822;
int ret;
dp83822 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83822),
GFP_KERNEL);
if (!dp83822)
return -ENOMEM;
phydev->priv = dp83822;
ret = dp83822_read_straps(phydev);
if (ret)
return ret;
dp83822_of_init(phydev);
return 0;
}
static int dp83822_suspend(struct phy_device *phydev) static int dp83822_suspend(struct phy_device *phydev)
{ {
int value; int value;
...@@ -352,8 +561,10 @@ static int dp83822_resume(struct phy_device *phydev) ...@@ -352,8 +561,10 @@ static int dp83822_resume(struct phy_device *phydev)
PHY_ID_MATCH_MODEL(_id), \ PHY_ID_MATCH_MODEL(_id), \
.name = (_name), \ .name = (_name), \
/* PHY_BASIC_FEATURES */ \ /* PHY_BASIC_FEATURES */ \
.probe = dp83822_probe, \
.soft_reset = dp83822_phy_reset, \ .soft_reset = dp83822_phy_reset, \
.config_init = dp83822_config_init, \ .config_init = dp83822_config_init, \
.read_status = dp83822_read_status, \
.get_wol = dp83822_get_wol, \ .get_wol = dp83822_get_wol, \
.set_wol = dp83822_set_wol, \ .set_wol = dp83822_set_wol, \
.ack_interrupt = dp83822_ack_interrupt, \ .ack_interrupt = dp83822_ack_interrupt, \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment