diff options
Diffstat (limited to 'drivers/net/phy/dp83867.c')
-rw-r--r-- | drivers/net/phy/dp83867.c | 610 |
1 files changed, 435 insertions, 175 deletions
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index b3328b7e44..5dc5bac125 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -1,16 +1,7 @@ -/* - * Driver for the Texas Instruments DP83867 PHY +// SPDX-License-Identifier: GPL-2.0 +/* Driver for the Texas Instruments DP83867 PHY * * Copyright (C) 2015 Texas Instruments Inc. - * - * 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. - * - * 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. */ #include <common.h> @@ -18,24 +9,46 @@ #include <linux/phy.h> #include <dt-bindings/net/ti-dp83867.h> +#include <linux/mdio.h> #define DP83867_PHY_ID 0x2000a231 #define DP83867_DEVADDR 0x1f #define MII_DP83867_PHYCTRL 0x10 +#define MII_DP83867_PHYSTS 0x11 #define MII_DP83867_MICR 0x12 #define MII_DP83867_ISR 0x13 -#define MII_DP83867_CFG2 0x14 -#define MII_DP83867_BISCR 0x16 -#define DP83867_CTRL 0x1f +#define DP83867_CFG2 0x14 +#define DP83867_LEDCR1 0x18 +#define DP83867_LEDCR2 0x19 #define DP83867_CFG3 0x1e +#define DP83867_CTRL 0x1f /* Extended Registers */ +#define DP83867_FLD_THR_CFG 0x002e #define DP83867_CFG4 0x0031 +#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) + #define DP83867_RGMIICTL 0x0032 #define DP83867_STRAP_STS1 0x006E +#define DP83867_STRAP_STS2 0x006f #define DP83867_RGMIIDCTL 0x0086 +#define DP83867_DSP_FFE_CFG 0x012c +#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 #define DP83867_IO_MUX_CFG 0x0170 +#define DP83867_SGMIICTL 0x00D3 +#define DP83867_10M_SGMII_CFG 0x016F +#define DP83867_10M_SGMII_RATE_ADAPT_MASK BIT(7) #define DP83867_SW_RESET BIT(15) #define DP83867_SW_RESTART BIT(14) @@ -58,48 +71,86 @@ #define DP83867_RGMII_TX_CLK_DELAY_EN BIT(1) #define DP83867_RGMII_RX_CLK_DELAY_EN BIT(0) +/* SGMIICTL bits */ +#define DP83867_SGMII_TYPE BIT(14) + +/* 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) + /* STRAP_STS1 bits */ #define DP83867_STRAP_STS1_RESERVED BIT(11) +/* 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) +#define DP83867_STRAP_STS2_STRAP_FLD BIT(10) + /* PHY CTRL bits */ -#define DP83867_PHYCR_FIFO_DEPTH_SHIFT 14 -#define DP83867_PHYCR_FIFO_DEPTH_MASK (3 << 14) -#define DP83867_MDI_CROSSOVER 5 -#define DP83867_MDI_CROSSOVER_AUTO 0b10 -#define DP83867_MDI_CROSSOVER_MDIX 0b01 -#define DP83867_PHYCTRL_SGMIIEN 0x0800 -#define DP83867_PHYCTRL_RXFIFO_SHIFT 12 -#define DP83867_PHYCTRL_TXFIFO_SHIFT 14 +#define DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT 14 +#define DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT 12 +#define DP83867_PHYCR_FIFO_DEPTH_MAX 0x03 +#define DP83867_PHYCR_TX_FIFO_DEPTH_MASK GENMASK(15, 14) +#define DP83867_PHYCR_RX_FIFO_DEPTH_MASK GENMASK(13, 12) #define DP83867_PHYCR_RESERVED_MASK BIT(11) +#define DP83867_PHYCR_FORCE_LINK_GOOD BIT(10) /* RGMIIDCTL bits */ +#define DP83867_RGMII_TX_CLK_DELAY_MAX 0xf #define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4 +#define DP83867_RGMII_TX_CLK_DELAY_INV (DP83867_RGMII_TX_CLK_DELAY_MAX + 1) +#define DP83867_RGMII_RX_CLK_DELAY_MAX 0xf +#define DP83867_RGMII_RX_CLK_DELAY_SHIFT 0 +#define DP83867_RGMII_RX_CLK_DELAY_INV (DP83867_RGMII_RX_CLK_DELAY_MAX + 1) + +/* IO_MUX_CFG bits */ +#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK 0x1f +#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 +#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f +#define DP83867_IO_MUX_CFG_CLK_O_DISABLE BIT(6) +#define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK (0x1f << 8) +#define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT 8 + +/* 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 MII_DP83867_CFG2_SPEEDOPT_10EN 0x0040 -#define MII_DP83867_CFG2_SGMII_AUTONEGEN 0x0080 -#define MII_DP83867_CFG2_SPEEDOPT_ENH 0x0100 -#define MII_DP83867_CFG2_SPEEDOPT_CNT 0x0800 -#define MII_DP83867_CFG2_SPEEDOPT_INTLOW 0x2000 -#define MII_DP83867_CFG2_MASK 0x003F +#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 +#define DP83867_SGMII_AUTONEG_EN BIT(7) + +/* CFG3 bits */ +#define DP83867_CFG3_INT_OE BIT(7) +#define DP83867_CFG3_ROBUST_AUTO_MDIX BIT(9) /* CFG4 bits */ -#define DP83867_CFG4_SGMII_AUTONEG_TIMER_MASK 0x60 -#define DP83867_CFG4_SGMII_AUTONEG_TIMER_16MS 0x00 -#define DP83867_CFG4_SGMII_AUTONEG_TIMER_2US 0x20 -#define DP83867_CFG4_SGMII_AUTONEG_TIMER_800US 0x40 -#define DP83867_CFG4_SGMII_AUTONEG_TIMER_11MS 0x60 -#define DP83867_CFG4_RESVDBIT7 BIT(7) -#define DP83867_CFG4_RESVDBIT8 BIT(8) +#define DP83867_CFG4_PORT_MIRROR_EN BIT(0) -/* IO_MUX_CFG bits */ -#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f +/* FLD_THR_CFG */ +#define DP83867_FLD_THR_CFG_ENERGY_LOST_THR_MASK 0x7 -#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 -#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f +#define DP83867_LED_COUNT 4 -/* CFG4 bits */ -#define DP83867_CFG4_PORT_MIRROR_EN BIT(0) +/* LED_DRV bits */ +#define DP83867_LED_DRV_EN(x) BIT((x) * 4) +#define DP83867_LED_DRV_VAL(x) BIT((x) * 4 + 1) enum { DP83867_PORT_MIRROING_KEEP, @@ -108,27 +159,112 @@ enum { }; struct dp83867_private { - int rx_id_delay; - int tx_id_delay; - int fifo_depth; + u32 rx_id_delay; + u32 tx_id_delay; + u32 tx_fifo_depth; + u32 rx_fifo_depth; int io_impedance; int port_mirroring; bool rxctrl_strap_quirk; + bool set_clk_output; + u32 clk_output_sel; + bool sgmii_ref_clk_en; }; -static int dp83867_config_port_mirroring(struct phy_device *phydev) +static int dp83867_read_status(struct phy_device *phydev) { - struct dp83867_private *dp83867 = (struct dp83867_private *)phydev->priv; - u16 val; + int status = phy_read(phydev, MII_DP83867_PHYSTS); + int ret; - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR); + 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_config_port_mirroring(struct phy_device *phydev) +{ + struct dp83867_private *dp83867 = phydev->priv; if (dp83867->port_mirroring == DP83867_PORT_MIRROING_EN) - val |= DP83867_CFG4_PORT_MIRROR_EN; + phy_set_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, + DP83867_CFG4_PORT_MIRROR_EN); else - val &= ~DP83867_CFG4_PORT_MIRROR_EN; + phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, + DP83867_CFG4_PORT_MIRROR_EN); + return 0; +} - phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, val); +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; +} + +static int dp83867_of_init_io_impedance(struct phy_device *phydev) +{ + struct dp83867_private *dp83867 = phydev->priv; + struct device *dev = &phydev->dev; + struct device_node *of_node = dev->of_node; + + 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; } @@ -136,36 +272,59 @@ static int dp83867_config_port_mirroring(struct phy_device *phydev) static int dp83867_of_init(struct phy_device *phydev) { struct dp83867_private *dp83867 = phydev->priv; - struct device_d *dev = &phydev->dev; - struct device_node *of_node = dev->device_node; + struct device *dev = &phydev->dev; + struct device_node *of_node = dev->of_node; int ret; if (!of_node) return -ENODEV; - dp83867->io_impedance = -EINVAL; - /* Optional configuration */ - 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; + ret = of_property_read_u32(of_node, "ti,clk-output-sel", + &dp83867->clk_output_sel); + /* 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. + */ + 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; + } + } + + ret = dp83867_of_init_io_impedance(phydev); + if (ret) + return ret; - dp83867->rxctrl_strap_quirk = - of_property_read_bool(of_node, - "ti,dp83867-rxctrl-strap-quirk"); + dp83867->rxctrl_strap_quirk = of_property_read_bool(of_node, + "ti,dp83867-rxctrl-strap-quirk"); + dp83867->sgmii_ref_clk_en = of_property_read_bool(of_node, + "ti,sgmii-ref-clock-output-enable"); + + 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 && (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)) - return ret; + &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; + } + 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 && (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) - return ret; + &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; + } if (of_property_read_bool(of_node, "enet-phy-lane-swap")) dp83867->port_mirroring = DP83867_PORT_MIRROING_EN; @@ -173,151 +332,252 @@ static int dp83867_of_init(struct phy_device *phydev) if (of_property_read_bool(of_node, "enet-phy-lane-no-swap")) dp83867->port_mirroring = DP83867_PORT_MIRROING_DIS; - return of_property_read_u32(of_node, "ti,fifo-depth", - &dp83867->fifo_depth); -} + ret = of_property_read_u32(of_node, "ti,fifo-depth", + &dp83867->tx_fifo_depth); + if (ret) { + 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; + } -static inline bool phy_interface_is_rgmii(struct phy_device *phydev) -{ - return phydev->interface >= PHY_INTERFACE_MODE_RGMII && - phydev->interface <= PHY_INTERFACE_MODE_RGMII_TXID; + 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); + return -EINVAL; + } + + return 0; } -static inline bool phy_interface_is_sgmii(struct phy_device *phydev) +static int dp83867_probe(struct phy_device *phydev) { - return phydev->interface == PHY_INTERFACE_MODE_SGMII || - phydev->interface == PHY_INTERFACE_MODE_QSGMII; + struct dp83867_private *dp83867; + + dp83867 = xzalloc(sizeof(*dp83867)); + + phydev->priv = dp83867; + + return dp83867_of_init(phydev); } static int dp83867_config_init(struct phy_device *phydev) { - struct dp83867_private *dp83867; - int ret; - u16 val, delay, cfg2; + struct dp83867_private *dp83867 = phydev->priv; + int ret, val, bs; + u16 delay; - if (!phydev->priv) { - dp83867 = kzalloc(sizeof(*dp83867), GFP_KERNEL); - if (!dp83867) - return -ENOMEM; + /* 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; + + ret = dp83867_verify_rgmii_cfg(phydev); + if (ret) + return ret; - phydev->priv = dp83867; - ret = dp83867_of_init(phydev); + /* RX_DV/RX_CTRL strapped in mode 1 or mode 2 workaround */ + if (dp83867->rxctrl_strap_quirk) + phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, + BIT(7)); + + 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; - } else { - dp83867 = (struct dp83867_private *)phydev->priv; } - /* Restart the PHY. */ - val = phy_read(phydev, DP83867_CTRL); - phy_write(phydev, DP83867_CTRL, val | DP83867_SW_RESTART); + if (phy_interface_is_rgmii(phydev) || + phydev->interface == PHY_INTERFACE_MODE_SGMII) { + val = phy_read(phydev, MII_DP83867_PHYCTRL); + if (val < 0) + return val; - if (dp83867->rxctrl_strap_quirk) { - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, - DP83867_DEVADDR); - val &= ~BIT(7); - phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, - 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); + } - if (phy_interface_is_rgmii(phydev)) { - val = DP83867_MDI_CROSSOVER_AUTO << DP83867_MDI_CROSSOVER | - dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT; ret = phy_write(phydev, MII_DP83867_PHYCTRL, val); if (ret) return ret; - } else if (phy_interface_is_sgmii(phydev)) { - phy_write(phydev, MII_BMCR, BMCR_ANENABLE | - BMCR_FULLDPLX | - BMCR_SPEED1000); - - cfg2 = phy_read(phydev, MII_DP83867_CFG2); - cfg2 &= MII_DP83867_CFG2_MASK; - cfg2 |= MII_DP83867_CFG2_SPEEDOPT_10EN | - MII_DP83867_CFG2_SGMII_AUTONEGEN | - MII_DP83867_CFG2_SPEEDOPT_ENH | - MII_DP83867_CFG2_SPEEDOPT_CNT | - MII_DP83867_CFG2_SPEEDOPT_INTLOW; - - phy_write(phydev, MII_DP83867_CFG2, cfg2); - - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, 0x0); - - val = DP83867_PHYCTRL_SGMIIEN | - DP83867_MDI_CROSSOVER_MDIX << DP83867_MDI_CROSSOVER | - dp83867->fifo_depth << DP83867_PHYCTRL_RXFIFO_SHIFT | - dp83867->fifo_depth << DP83867_PHYCTRL_TXFIFO_SHIFT; - - phy_write(phydev, MII_DP83867_PHYCTRL, val); - phy_write(phydev, MII_DP83867_BISCR, 0x0); } if (phy_interface_is_rgmii(phydev)) { - val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR); - - switch (phydev->interface) { - case PHY_INTERFACE_MODE_RGMII_ID: - val |= (DP83867_RGMII_TX_CLK_DELAY_EN - | DP83867_RGMII_RX_CLK_DELAY_EN); - break; - case PHY_INTERFACE_MODE_RGMII_TXID: + val = phy_read(phydev, MII_DP83867_PHYCTRL); + if (val < 0) + return val; + + /* 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). + */ + + bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1); + if (bs & DP83867_STRAP_STS1_RESERVED) + val &= ~DP83867_PHYCR_RESERVED_MASK; + + ret = phy_write(phydev, MII_DP83867_PHYCTRL, val); + if (ret) + return ret; + + /* 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 + */ + val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL); + + val &= ~(DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN); + 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; - break; - case PHY_INTERFACE_MODE_RGMII_RXID: + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) val |= DP83867_RGMII_RX_CLK_DELAY_EN; - break; - default: - break; - } - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, val); - delay = (dp83867->rx_id_delay | - (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val); - phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL, - DP83867_DEVADDR, delay); + 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; - if (dp83867->io_impedance >= 0) { - val = phy_read_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR); - val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; - val |= dp83867->io_impedance - & DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL, + delay); + } - phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR, val); - } + /* 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); + + 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; + + /* 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 16ms. + */ + ret = phy_modify_mmd(phydev, DP83867_DEVADDR, + DP83867_CFG4, + DP83867_CFG4_SGMII_ANEG_MASK, + DP83867_CFG4_SGMII_ANEG_TIMER_16MS); + + if (ret) + return ret; + + 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); + + /* This is a SW workaround for link instability if RX_CTRL is + * not strapped to mode 3 or 4 in HW. This is required for SGMII + * in addition to clearing bit 7, handled above. + */ + if (dp83867->rxctrl_strap_quirk) + phy_set_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, + BIT(8)); } - genphy_config_aneg(phydev); + val = phy_read(phydev, DP83867_CFG3); + + val |= DP83867_CFG3_ROBUST_AUTO_MDIX; + phy_write(phydev, DP83867_CFG3, val); if (dp83867->port_mirroring != DP83867_PORT_MIRROING_KEEP) dp83867_config_port_mirroring(phydev); - dev_info(&phydev->dev, "DP83867\n"); + /* Clock output selection if muxing property is set */ + 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; + } + + phy_modify_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG, + mask, val); + } return 0; } static struct phy_driver dp83867_driver[] = { - { - .phy_id = DP83867_PHY_ID, - .phy_id_mask = 0xfffffff0, - .drv.name = "TI DP83867", - .features = PHY_GBIT_FEATURES, + { + .phy_id = DP83867_PHY_ID, + .phy_id_mask = 0xfffffff0, + .drv.name = "TI DP83867", + .features = PHY_GBIT_FEATURES, - .config_init = dp83867_config_init, + .probe = dp83867_probe, + .config_init = dp83867_config_init, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, - }, + .read_status = dp83867_read_status, + }, }; +device_phy_drivers(dp83867_driver); -static int dp83867_phy_init(void) -{ - return phy_drivers_register(dp83867_driver, ARRAY_SIZE(dp83867_driver)); -} -fs_initcall(dp83867_phy_init); +MODULE_DESCRIPTION("Texas Instruments DP83867 PHY driver"); +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); +MODULE_LICENSE("GPL v2"); |