diff --git a/Documentation/devicetree/bindings/net/ethernet-phy.yaml b/Documentation/devicetree/bindings/net/ethernet-phy.yaml index 9b1f1147ca360c0e861dc27088fd2b6358bcde9a..a9e547ac790512918b07316606d05beb0af42ab1 100644 --- a/Documentation/devicetree/bindings/net/ethernet-phy.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-phy.yaml @@ -162,6 +162,18 @@ properties: description: Specifies a reference to a node representing a SFP cage. + rx-internal-delay-ps: + description: | + RGMII Receive PHY Clock Delay defined in pico seconds. This is used for + PHY's that have configurable RX internal delays. If this property is + present then the PHY applies the RX delay. + + tx-internal-delay-ps: + description: | + RGMII Transmit PHY Clock Delay defined in pico seconds. This is used for + PHY's that have configurable TX internal delays. If this property is + present then the PHY applies the TX delay. + required: - reg diff --git a/Documentation/devicetree/bindings/net/ti,dp83869.yaml b/Documentation/devicetree/bindings/net/ti,dp83869.yaml index 5b69ef03bbf7b99e973b94189e73f6bde94e21e1..71e90a3e46522561d942563f169fd6cd9d5eac91 100644 --- a/Documentation/devicetree/bindings/net/ti,dp83869.yaml +++ b/Documentation/devicetree/bindings/net/ti,dp83869.yaml @@ -8,7 +8,7 @@ $schema: "http://devicetree.org/meta-schemas/core.yaml#" title: TI DP83869 ethernet PHY allOf: - - $ref: "ethernet-controller.yaml#" + - $ref: "ethernet-phy.yaml#" maintainers: - Dan Murphy @@ -64,6 +64,18 @@ properties: Operational mode for the PHY. If this is not set then the operational mode is set by the straps. see dt-bindings/net/ti-dp83869.h for values + rx-internal-delay-ps: + description: Delay is in pico seconds + enum: [ 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, + 3250, 3500, 3750, 4000 ] + default: 2000 + + tx-internal-delay-ps: + description: Delay is in pico seconds + enum: [ 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, + 3250, 3500, 3750, 4000 ] + default: 2000 + required: - reg @@ -80,5 +92,7 @@ examples: ti,op-mode = ; ti,max-output-impedance = "true"; ti,clk-output-sel = ; + rx-internal-delay-ps = <2000>; + tx-internal-delay-ps = <2000>; }; }; diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c index 1dd19d0cb269f5a945b37e09cec3824f2f9e1eb1..37643c468e1991f49c04783ef8a725bb7cb32810 100644 --- a/drivers/net/phy/dp83822.c +++ b/drivers/net/phy/dp83822.c @@ -26,7 +26,9 @@ #define MII_DP83822_PHYSCR 0x11 #define MII_DP83822_MISR1 0x12 #define MII_DP83822_MISR2 0x13 +#define MII_DP83822_RCSR 0x17 #define MII_DP83822_RESET_CTRL 0x1f +#define MII_DP83822_GENCFG 0x465 #define DP83822_HW_RESET BIT(15) #define DP83822_SW_RESET BIT(14) @@ -77,6 +79,10 @@ #define DP83822_WOL_INDICATION_SEL BIT(8) #define DP83822_WOL_CLR_INDICATION BIT(11) +/* RSCR bits */ +#define DP83822_RX_CLK_SHIFT BIT(12) +#define DP83822_TX_CLK_SHIFT BIT(11) + static int dp83822_ack_interrupt(struct phy_device *phydev) { int err; @@ -255,7 +261,7 @@ static int dp83822_config_intr(struct phy_device *phydev) return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status); } -static int dp83822_config_init(struct phy_device *phydev) +static int dp8382x_disable_wol(struct phy_device *phydev) { int value = DP83822_WOL_EN | DP83822_WOL_MAGIC_EN | DP83822_WOL_SECURE_ON; @@ -264,6 +270,46 @@ static int dp83822_config_init(struct phy_device *phydev) MII_DP83822_WOL_CFG, value); } +static int dp83822_config_init(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int rgmii_delay; + s32 rx_int_delay; + s32 tx_int_delay; + int err = 0; + + if (phy_interface_is_rgmii(phydev)) { + rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, + true); + + if (rx_int_delay <= 0) + rgmii_delay = 0; + else + rgmii_delay = DP83822_RX_CLK_SHIFT; + + tx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, + false); + if (tx_int_delay <= 0) + rgmii_delay &= ~DP83822_TX_CLK_SHIFT; + else + rgmii_delay |= DP83822_TX_CLK_SHIFT; + + if (rgmii_delay) { + err = phy_set_bits_mmd(phydev, DP83822_DEVADDR, + MII_DP83822_RCSR, rgmii_delay); + if (err) + return err; + } + } + + return dp8382x_disable_wol(phydev); +} + +static int dp8382x_config_init(struct phy_device *phydev) +{ + return dp8382x_disable_wol(phydev); +} + static int dp83822_phy_reset(struct phy_device *phydev) { int err; @@ -272,9 +318,7 @@ static int dp83822_phy_reset(struct phy_device *phydev) if (err < 0) return err; - dp83822_config_init(phydev); - - return 0; + return phydev->drv->config_init(phydev); } static int dp83822_suspend(struct phy_device *phydev) @@ -318,14 +362,29 @@ static int dp83822_resume(struct phy_device *phydev) .resume = dp83822_resume, \ } +#define DP8382X_PHY_DRIVER(_id, _name) \ + { \ + PHY_ID_MATCH_MODEL(_id), \ + .name = (_name), \ + /* PHY_BASIC_FEATURES */ \ + .soft_reset = dp83822_phy_reset, \ + .config_init = dp8382x_config_init, \ + .get_wol = dp83822_get_wol, \ + .set_wol = dp83822_set_wol, \ + .ack_interrupt = dp83822_ack_interrupt, \ + .config_intr = dp83822_config_intr, \ + .suspend = dp83822_suspend, \ + .resume = dp83822_resume, \ + } + static struct phy_driver dp83822_driver[] = { DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"), - DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"), - DP83822_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"), - DP83822_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"), - DP83822_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"), - DP83822_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"), - DP83822_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"), + DP8382X_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"), + DP8382X_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"), + DP8382X_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"), + DP8382X_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"), + DP8382X_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"), + DP8382X_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"), }; module_phy_driver(dp83822_driver); diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index 53ed3abc26c90cd549250b520adb1d07cee14cb6..58103152c6010e844589d81785ac0b7727b25ea7 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -64,6 +64,10 @@ #define DP83869_RGMII_TX_CLK_DELAY_EN BIT(1) #define DP83869_RGMII_RX_CLK_DELAY_EN BIT(0) +/* RGMIIDCTL */ +#define DP83869_RGMII_CLK_DELAY_SHIFT 4 +#define DP83869_CLK_DELAY_DEF 7 + /* STRAP_STS1 bits */ #define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0) #define DP83869_STRAP_STS1_RESERVED BIT(11) @@ -78,9 +82,6 @@ #define DP83869_PHYCR_FIFO_DEPTH_MASK GENMASK(15, 12) #define DP83869_PHYCR_RESERVED_MASK BIT(11) -/* RGMIIDCTL bits */ -#define DP83869_RGMII_TX_CLK_DELAY_SHIFT 4 - /* IO_MUX_CFG bits */ #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f @@ -108,6 +109,8 @@ enum { struct dp83869_private { int tx_fifo_depth; int rx_fifo_depth; + s32 rx_int_delay; + s32 tx_int_delay; int io_impedance; int port_mirroring; bool rxctrl_strap_quirk; @@ -177,11 +180,16 @@ static int dp83869_set_strapped_mode(struct phy_device *phydev) } #if IS_ENABLED(CONFIG_OF_MDIO) +static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500, + 1750, 2000, 2250, 2500, 2750, 3000, + 3250, 3500, 3750, 4000}; + static int dp83869_of_init(struct phy_device *phydev) { struct dp83869_private *dp83869 = phydev->priv; struct device *dev = &phydev->mdio.dev; struct device_node *of_node = dev->of_node; + int delay_size = ARRAY_SIZE(dp83869_internal_delay); int ret; if (!of_node) @@ -235,6 +243,20 @@ static int dp83869_of_init(struct phy_device *phydev) &dp83869->tx_fifo_depth)) dp83869->tx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB; + dp83869->rx_int_delay = phy_get_internal_delay(phydev, dev, + &dp83869_internal_delay[0], + delay_size, true); + if (dp83869->rx_int_delay < 0) + dp83869->rx_int_delay = + dp83869_internal_delay[DP83869_CLK_DELAY_DEF]; + + dp83869->tx_int_delay = phy_get_internal_delay(phydev, dev, + &dp83869_internal_delay[0], + delay_size, false); + if (dp83869->tx_int_delay < 0) + dp83869->tx_int_delay = + dp83869_internal_delay[DP83869_CLK_DELAY_DEF]; + return ret; } #else @@ -397,6 +419,31 @@ static int dp83869_config_init(struct phy_device *phydev) dp83869->clk_output_sel << DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT); + if (phy_interface_is_rgmii(phydev)) { + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL, + dp83869->rx_int_delay | + dp83869->tx_int_delay << DP83869_RGMII_CLK_DELAY_SHIFT); + if (ret) + return ret; + + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL); + val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN | + DP83869_RGMII_RX_CLK_DELAY_EN); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + val |= (DP83869_RGMII_TX_CLK_DELAY_EN | + DP83869_RGMII_RX_CLK_DELAY_EN); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + val |= DP83869_RGMII_TX_CLK_DELAY_EN; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + val |= DP83869_RGMII_RX_CLK_DELAY_EN; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL, + val); + } + return ret; } diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 29ef4456ac253160f24458515b4f28656bf52024..6d47485e68f94451a10b5346c87e316d4ad4284e 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -31,6 +31,7 @@ #include #include #include +#include MODULE_DESCRIPTION("PHY library"); MODULE_AUTHOR("Andy Fleming"); @@ -2708,6 +2709,104 @@ void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause) } EXPORT_SYMBOL(phy_get_pause); +#if IS_ENABLED(CONFIG_OF_MDIO) +static int phy_get_int_delay_property(struct device *dev, const char *name) +{ + s32 int_delay; + int ret; + + ret = device_property_read_u32(dev, name, &int_delay); + if (ret) + return ret; + + return int_delay; +} +#else +static int phy_get_int_delay_property(struct device *dev, const char *name) +{ + return -EINVAL; +} +#endif + +/** + * phy_get_delay_index - returns the index of the internal delay + * @phydev: phy_device struct + * @dev: pointer to the devices device struct + * @delay_values: array of delays the PHY supports + * @size: the size of the delay array + * @is_rx: boolean to indicate to get the rx internal delay + * + * Returns the index within the array of internal delay passed in. + * If the device property is not present then the interface type is checked + * if the interface defines use of internal delay then a 1 is returned otherwise + * a 0 is returned. + * The array must be in ascending order. If PHY does not have an ascending order + * array then size = 0 and the value of the delay property is returned. + * Return -EINVAL if the delay is invalid or cannot be found. + */ +s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, + const int *delay_values, int size, bool is_rx) +{ + s32 delay; + int i; + + if (is_rx) { + delay = phy_get_int_delay_property(dev, "rx-internal-delay-ps"); + if (delay < 0 && size == 0) { + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + return 1; + else + return 0; + } + + } else { + delay = phy_get_int_delay_property(dev, "tx-internal-delay-ps"); + if (delay < 0 && size == 0) { + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + return 1; + else + return 0; + } + } + + if (delay < 0) + return delay; + + if (delay && size == 0) + return delay; + + if (delay < delay_values[0] || delay > delay_values[size - 1]) { + phydev_err(phydev, "Delay %d is out of range\n", delay); + return -EINVAL; + } + + if (delay == delay_values[0]) + return 0; + + for (i = 1; i < size; i++) { + if (delay == delay_values[i]) + return i; + + /* Find an approximate index by looking up the table */ + if (delay > delay_values[i - 1] && + delay < delay_values[i]) { + if (delay - delay_values[i - 1] < + delay_values[i] - delay) + return i - 1; + else + return i; + } + } + + phydev_err(phydev, "error finding internal delay index for %d\n", + delay); + + return -EINVAL; +} +EXPORT_SYMBOL(phy_get_internal_delay); + static bool phy_drv_supports_irq(struct phy_driver *phydrv) { return phydrv->config_intr && phydrv->ack_interrupt; diff --git a/include/linux/phy.h b/include/linux/phy.h index 6fb8f302978defe7b53a4207add772c3d4322443..2c00374dc99650933822ead4a7a490884099c92d 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -1443,6 +1443,10 @@ void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx); bool phy_validate_pause(struct phy_device *phydev, struct ethtool_pauseparam *pp); void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause); + +s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, + const int *delay_values, int size, bool is_rx); + void phy_resolve_pause(unsigned long *local_adv, unsigned long *partner_adv, bool *tx_pause, bool *rx_pause);