diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/Kconfig | 11 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/phy/ar8327.c | 4 | ||||
-rw-r--r-- | drivers/net/phy/at803x.c | 49 | ||||
-rw-r--r-- | drivers/net/phy/dp83867.c | 566 | ||||
-rw-r--r-- | drivers/net/phy/dp83tg720.c | 103 | ||||
-rw-r--r-- | drivers/net/phy/marvell.c | 4 | ||||
-rw-r--r-- | drivers/net/phy/mdio-bitbang.c | 8 | ||||
-rw-r--r-- | drivers/net/phy/mdio-gpio.c | 25 | ||||
-rw-r--r-- | drivers/net/phy/mdio-mux-gpio.c | 11 | ||||
-rw-r--r-- | drivers/net/phy/mdio-mux.c | 4 | ||||
-rw-r--r-- | drivers/net/phy/mdio-mvebu.c | 9 | ||||
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 146 | ||||
-rw-r--r-- | drivers/net/phy/micrel.c | 266 | ||||
-rw-r--r-- | drivers/net/phy/motorcomm.c | 129 | ||||
-rw-r--r-- | drivers/net/phy/mv88e6xxx/chip.c | 17 | ||||
-rw-r--r-- | drivers/net/phy/mv88e6xxx/chip.h | 2 | ||||
-rw-r--r-- | drivers/net/phy/mv88e6xxx/port.c | 10 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 177 | ||||
-rw-r--r-- | drivers/net/phy/realtek.c | 8 |
20 files changed, 1256 insertions, 295 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index cd20e1de27..8e12671801 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -35,6 +35,12 @@ config DP83TD510_PHY Support for the DP83TD510 Ethernet 10Base-T1L PHY. This PHY supports a 10M single pair Ethernet connection for up to 1000 meter cable. +config DP83TG720_PHY + tristate "Texas Instruments DP83TG720 Ethernet 1000Base-T1 PHY" + help + Support for the DP83TG720 Ethernet 10000Base-T1 PHY. This PHY supports + a 1000M single pair Ethernet. + config LXT_PHY bool "Driver for the Intel LXT PHYs" help @@ -50,6 +56,11 @@ config MICREL_PHY help Supports the KSZ9021, VSC8201, KS8001 PHYs. +config MOTORCOMM_PHY + bool "Driver for Motorcomm PHYs" + help + Currently supports the YT8511 PHY. + config NATIONAL_PHY bool "Driver for National Semiconductor PHYs" help diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 83f46f11d3..ce15e1bab7 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_DAVICOM_PHY) += davicom.o obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_MARVELL_PHY) += marvell.o obj-$(CONFIG_MICREL_PHY) += micrel.o +obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_REALTEK_PHY) += realtek.o obj-$(CONFIG_SMSC_PHY) += smsc.o @@ -18,4 +19,5 @@ obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o obj-$(CONFIG_DP83867_PHY) += dp83867.o obj-$(CONFIG_DP83TD510_PHY) += dp83td510.o +obj-$(CONFIG_DP83TG720_PHY) += dp83tg720.o diff --git a/drivers/net/phy/ar8327.c b/drivers/net/phy/ar8327.c index f13d574b30..7717861c74 100644 --- a/drivers/net/phy/ar8327.c +++ b/drivers/net/phy/ar8327.c @@ -132,7 +132,7 @@ static int ar8327n_phy_is_link_alive(struct phy_device *phydev, int phy_addr) static int ar8327n_phy_setup(struct phy_device *phydev) { - struct device_d *dev = &phydev->dev; + struct device *dev = &phydev->dev; int phy_addr; /* start auto negotiation on each phy */ @@ -194,7 +194,7 @@ static int ar8327n_get_link(struct phy_device *phydev) static int ar8327n_config_init(struct phy_device *phydev) { - struct device_d *dev = &phydev->dev; + struct device *dev = &phydev->dev; int phy_addr = 0; if (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 93a8bb9df1..8d6b879a27 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -32,6 +32,9 @@ #define AT803X_DEBUG_REG_5 0x05 #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) +#define AT803X_DEBUG_REG_HIB_CTRL 0x0b +#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) + /* AT803x supports either the XTAL input pad, an internal PLL or the * DSP as clock reference for the clock output pad. The XTAL reference * is only used for 25 MHz output, all other frequencies need the PLL. @@ -59,6 +62,9 @@ */ #define AT8035_CLK_OUT_MASK GENMASK(4, 3) +#define AT803X_MMD3_SMARTEEE_CTL3 0x805d +#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8) + #define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7) #define AT803X_CLK_OUT_STRENGTH_FULL 0 #define AT803X_CLK_OUT_STRENGTH_HALF 1 @@ -128,6 +134,15 @@ static int at803x_disable_tx_delay(struct phy_device *phydev) AT803X_DEBUG_TX_CLK_DLY_EN, 0); } +static int at803x_hibernation_mode_config(struct phy_device *phydev) +{ + /* The default after hardware reset is hibernation mode enabled. After + * software reset, the value is retained. + */ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, + AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0); +} + static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id) { struct phy_driver *drv = to_phy_driver(phydev->dev.driver); @@ -138,8 +153,8 @@ static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id) static int at803x_parse_dt(struct phy_device *phydev) { - const struct device_d *dev = &phydev->dev; - const struct device_node *node = dev->device_node; + const struct device *dev = &phydev->dev; + const struct device_node *node = dev->of_node; struct at803x_priv *priv = phydev->priv; unsigned int sel; u32 freq, strength; @@ -221,6 +236,12 @@ static int at803x_probe(struct phy_device *phydev) return at803x_parse_dt(phydev); } +static int at803x_smarteee_config(struct phy_device *phydev) +{ + return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3, + AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0); +} + static int at803x_clk_out_config(struct phy_device *phydev) { struct at803x_priv *priv = phydev->priv; @@ -229,14 +250,14 @@ static int at803x_clk_out_config(struct phy_device *phydev) if (!priv->clk_25m_mask) return 0; - val = phy_read_mmd_indirect(phydev, AT803X_MMD7_CLK25M, MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M); if (val < 0) return val; val &= ~priv->clk_25m_mask; val |= priv->clk_25m_reg; - phy_write_mmd_indirect(phydev, AT803X_MMD7_CLK25M, MDIO_MMD_AN, val); + phy_write_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val); return 0; } @@ -270,7 +291,25 @@ static int at803x_config_init(struct phy_device *phydev) if (ret < 0) return ret; - return at803x_clk_out_config(phydev); + ret = at803x_smarteee_config(phydev); + if (ret < 0) + return ret; + + ret = at803x_clk_out_config(phydev); + if (ret < 0) + return ret; + + ret = at803x_hibernation_mode_config(phydev); + if (ret < 0) + return ret; + + /* Ar803x extended next page bit is enabled by default. Cisco + * multigig switches read this bit and attempt to negotiate 10Gbps + * rates even if the next page bit is disabled. This is incorrect + * behaviour but we still need to accommodate it. XNP is only needed + * for 10Gbps support, so disable XNP. + */ + return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); } static struct phy_driver at803x_driver[] = { diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 2435df1b47..5dc5bac125 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -1,6 +1,5 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * 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. */ @@ -13,23 +12,43 @@ #include <linux/mdio.h> #define DP83867_PHY_ID 0x2000a231 -#define DP83867_DEVADDR MDIO_MMD_VEND2 +#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) @@ -52,53 +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) - -/* PHY STS bits */ -#define DP83867_PHYSTS_SPEED_1000 BIT(15) -#define DP83867_PHYSTS_SPEED_100 BIT(14) -#define DP83867_PHYSTS_DUPLEX_FULL BIT(13) +#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 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_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, @@ -107,39 +159,41 @@ 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_read_status(struct phy_device *phydev) { - int status; + int status = phy_read(phydev, MII_DP83867_PHYSTS); int ret; - ret = genphy_update_link(phydev); + ret = genphy_read_status(phydev); if (ret) return ret; - status = phy_read(phydev, MII_DP83867_PHYSTS); if (status < 0) return status; - phydev->speed = SPEED_10; - phydev->duplex = DUPLEX_HALF; + if (status & DP83867_PHYSTS_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; - if (status & DP83867_PHYSTS_SPEED_1000) + if (status & DP83867_PHYSTS_1000) phydev->speed = SPEED_1000; - else if (status & DP83867_PHYSTS_SPEED_100) + else if (status & DP83867_PHYSTS_100) phydev->speed = SPEED_100; - - if (status & DP83867_PHYSTS_DUPLEX_FULL) - phydev->duplex = DUPLEX_FULL; - - phydev->pause = phydev->asym_pause = 0; + else + phydev->speed = SPEED_10; return 0; } @@ -147,16 +201,70 @@ static int dp83867_read_status(struct phy_device *phydev) static int dp83867_config_port_mirroring(struct phy_device *phydev) { struct dp83867_private *dp83867 = phydev->priv; - u16 val; - - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR); 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; } @@ -164,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; + 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; + 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; @@ -201,125 +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; + } + + 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; + 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; - phydev->priv = dp83867; - ret = dp83867_of_init(phydev); + ret = dp83867_verify_rgmii_cfg(phydev); + if (ret) + return ret; + + /* 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 = 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; + + 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 (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); + ret = phy_write(phydev, MII_DP83867_PHYCTRL, val); + if (ret) + return ret; } if (phy_interface_is_rgmii(phydev)) { - val = DP83867_MDI_CROSSOVER_AUTO << DP83867_MDI_CROSSOVER | - dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT; + 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; - val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR); + /* 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); - 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: + 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); - } - } else if (phy_interface_is_sgmii(phydev)) { - phy_write(phydev, MII_BMCR, - BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000); + /* 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; - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, 0x0); + /* 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); - 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; + if (ret) + return ret; - phy_write(phydev, MII_DP83867_PHYCTRL, val); - phy_write(phydev, MII_DP83867_BISCR, 0x0); + 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)); } - val = phy_read(phydev, MII_DP83867_CFG2); - val |= DP83867_DOWNSHIFT_EN; - phy_write(phydev, MII_DP83867_CFG2, val); + 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); + /* 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, - .config_init = dp83867_config_init, - .read_status = dp83867_read_status, + .phy_id = DP83867_PHY_ID, + .phy_id_mask = 0xfffffff0, + .drv.name = "TI DP83867", + .features = PHY_GBIT_FEATURES, + + .probe = dp83867_probe, + .config_init = dp83867_config_init, + + .read_status = dp83867_read_status, }, }; - device_phy_drivers(dp83867_driver); + +MODULE_DESCRIPTION("Texas Instruments DP83867 PHY driver"); +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/phy/dp83tg720.c b/drivers/net/phy/dp83tg720.c new file mode 100644 index 0000000000..0571f4cb52 --- /dev/null +++ b/drivers/net/phy/dp83tg720.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Driver for the Texas Instruments DP83TG720 PHY + * Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> + */ +#include <common.h> +#include <linux/mdio.h> +#include <linux/phy.h> + +#define DP83TG720S_PHY_ID 0x2000a284 + +/* MDIO_MMD_VEND2 registers */ +#define DP83TG720S_MII_REG_10 0x10 +#define DP83TG720S_LINK_STATUS BIT(0) + +#define DP83TG720S_RGMII_DELAY_CTRL 0x602 +/* In RGMII mode, Enable or disable the internal delay for RXD */ +#define DP83TG720S_RGMII_RX_CLK_SEL BIT(1) +/* In RGMII mode, Enable or disable the internal delay for TXD */ +#define DP83TG720S_RGMII_TX_CLK_SEL BIT(0) + +#define DP83TG720S_PHY_RESET 0x1f +#define DP83TG720S_HW_RESET BIT(15) + +static int dp83tg720_config_rgmii_delay(struct phy_device *phydev) +{ + u16 rgmii_delay_mask; + u16 rgmii_delay = 0; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + rgmii_delay = 0; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + rgmii_delay = DP83TG720S_RGMII_RX_CLK_SEL | + DP83TG720S_RGMII_TX_CLK_SEL; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + rgmii_delay = DP83TG720S_RGMII_RX_CLK_SEL; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + rgmii_delay = DP83TG720S_RGMII_TX_CLK_SEL; + break; + default: + return 0; + } + + rgmii_delay_mask = DP83TG720S_RGMII_RX_CLK_SEL | + DP83TG720S_RGMII_TX_CLK_SEL; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, + DP83TG720S_RGMII_DELAY_CTRL, rgmii_delay_mask, + rgmii_delay); +} + +static int dp83tg720_phy_init(struct phy_device *phydev) +{ + /* HW reset is needed to recover link if previous link was lost. SW + * reset is not enough. + */ + phy_write(phydev, DP83TG720S_PHY_RESET, DP83TG720S_HW_RESET); + + phydev->supported = SUPPORTED_1000baseT_Full; + phydev->advertising = SUPPORTED_1000baseT_Full; + + if (phy_interface_is_rgmii(phydev)) + return dp83tg720_config_rgmii_delay(phydev); + + return 0; +} + +static int dp83tg720_read_status(struct phy_device *phydev) +{ + u16 phy_sts; + + phy_sts = phy_read(phydev, DP83TG720S_MII_REG_10); + phydev->link = !!(phy_sts & DP83TG720S_LINK_STATUS); + if (!phydev->link) { + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + + /* According to the "DP83TC81x, DP83TG72x Software + * Implementation Guide", the PHY needs to be reset after a + * link loss or if no link is created after at least 100ms. + */ + dp83tg720_phy_init(phydev); + return 0; + } + + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_1000; + + return 0; +} + +static struct phy_driver dp83tg720_driver[] = { + { + PHY_ID_MATCH_MODEL(DP83TG720S_PHY_ID), + .drv.name = "TI DP83TG720S", + .read_status = dp83tg720_read_status, + .config_init = dp83tg720_phy_init, + } +}; +device_phy_drivers(dp83tg720_driver); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index d81632dfc7..c0b819b109 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -188,10 +188,10 @@ static int marvell_of_reg_init(struct phy_device *phydev) const __be32 *paddr; int len, i, saved_page, current_page, page_changed, ret; - if (!phydev->dev.device_node) + if (!phydev->dev.of_node) return 0; - paddr = of_get_property(phydev->dev.device_node, + paddr = of_get_property(phydev->dev.of_node, "marvell,reg-init", &len); if (!paddr || len < (4 * sizeof(*paddr))) return 0; diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c index 839a7d1eb8..656557589d 100644 --- a/drivers/net/phy/mdio-bitbang.c +++ b/drivers/net/phy/mdio-bitbang.c @@ -158,7 +158,7 @@ static int mdiobb_read(struct mii_bus *bus, int phy, int reg) reg = mdiobb_cmd_addr(ctrl, phy, reg); mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg); } else - mdiobb_cmd(ctrl, MDIO_READ, phy, reg); + mdiobb_cmd(ctrl, ctrl->op_c22_read, phy, reg); ctrl->ops->set_mdio_dir(ctrl, 0); @@ -188,7 +188,7 @@ static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val) reg = mdiobb_cmd_addr(ctrl, phy, reg); mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg); } else - mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg); + mdiobb_cmd(ctrl, ctrl->op_c22_write, phy, reg); /* send the turnaround (10) */ mdiobb_send_bit(ctrl, 1); @@ -219,6 +219,10 @@ struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl) bus->write = mdiobb_write; bus->reset = mdiobb_reset; bus->priv = ctrl; + if (!ctrl->override_op_c22) { + ctrl->op_c22_read = MDIO_READ; + ctrl->op_c22_write = MDIO_WRITE; + } return bus; } diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 80d2394f4b..a28fb961e4 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -42,7 +42,7 @@ struct mdio_gpio_info { int mdc_active_low, mdio_active_low, mdo_active_low; }; -static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) +static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device *dev) { int ret; enum of_gpio_flags flags; @@ -50,7 +50,7 @@ static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) info = xzalloc(sizeof(*info)); - ret = of_get_gpio_flags(dev->device_node, 0, &flags); + ret = of_get_gpio_flags(dev->of_node, 0, &flags); if (ret < 0) { dev_dbg(dev, "failed to get MDC information from DT\n"); goto free_info; @@ -59,7 +59,7 @@ static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) info->mdc = ret; info->mdc_active_low = flags & OF_GPIO_ACTIVE_LOW; - ret = of_get_gpio_flags(dev->device_node, 1, &flags); + ret = of_get_gpio_flags(dev->of_node, 1, &flags); if (ret < 0) { dev_dbg(dev, "failed to get MDIO information from DT\n"); goto free_info; @@ -68,7 +68,7 @@ static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) info->mdio = ret; info->mdio_active_low = flags & OF_GPIO_ACTIVE_LOW; - ret = of_get_gpio_flags(dev->device_node, 2, &flags); + ret = of_get_gpio_flags(dev->of_node, 2, &flags); if (ret > 0) { dev_dbg(dev, "found MDO information in DT\n"); info->mdo = ret; @@ -142,10 +142,10 @@ static struct mdiobb_ops mdio_gpio_ops = { .get_mdio_data = mdio_get, }; -static int mdio_gpio_probe(struct device_d *dev) +static int mdio_gpio_probe(struct device *dev) { int ret; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct mdio_gpio_info *info; struct mii_bus *bus; @@ -193,9 +193,16 @@ static int mdio_gpio_probe(struct device_d *dev) goto free_mdo; } + if (np && + of_device_is_compatible(np, "microchip,mdio-smi0")) { + info->ctrl.op_c22_read = 0; + info->ctrl.op_c22_write = 0; + info->ctrl.override_op_c22 = 1; + } + bus = alloc_mdio_bitbang(&info->ctrl); bus->parent = dev; - bus->dev.device_node = np; + bus->dev.of_node = np; dev->priv = bus; @@ -217,10 +224,12 @@ free_info: static const struct of_device_id gpio_mdio_dt_ids[] = { { .compatible = "virtual,mdio-gpio", }, + { .compatible = "microchip,mdio-smi0" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, gpio_mdio_dt_ids); -static struct driver_d mdio_gpio_driver = { +static struct driver mdio_gpio_driver = { .name = "mdio-gpio", .probe = mdio_gpio_probe, .of_compatible = DRV_OF_COMPAT(gpio_mdio_dt_ids), diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c index a36782c0b6..3dd04830a5 100644 --- a/drivers/net/phy/mdio-mux-gpio.c +++ b/drivers/net/phy/mdio-mux-gpio.c @@ -67,14 +67,14 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child, return 0; } -static int mdio_mux_gpio_probe(struct device_d *dev) +static int mdio_mux_gpio_probe(struct device *dev) { struct mdio_mux_gpio_state *s; int i, r; s = xzalloc(sizeof(*s)); - s->gpios_num = of_gpio_count(dev->device_node); + s->gpios_num = of_gpio_count(dev->of_node); if (s->gpios_num <= 0) { dev_err(dev, "No GPIOs specified\n"); r = -EINVAL; @@ -86,7 +86,7 @@ static int mdio_mux_gpio_probe(struct device_d *dev) for (i = 0; i < s->gpios_num; i++) { enum of_gpio_flags flags; - r = of_get_gpio_flags(dev->device_node, i, &flags); + r = of_get_gpio_flags(dev->of_node, i, &flags); if (!gpio_is_valid(r)) { r = (r < 0) ? r : -EINVAL; goto free_mem; @@ -105,7 +105,7 @@ static int mdio_mux_gpio_probe(struct device_d *dev) goto free_gpios; - r = mdio_mux_init(dev, dev->device_node, + r = mdio_mux_init(dev, dev->of_node, mdio_mux_gpio_switch_fn, s, NULL); if (r < 0) goto free_gpios; @@ -126,8 +126,9 @@ static const struct of_device_id mdio_mux_gpio_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, mdio_mux_gpio_match); -static struct driver_d mdio_mux_gpio_driver = { +static struct driver mdio_mux_gpio_driver = { .name = "mdio-mux-gpio", .probe = mdio_mux_gpio_probe, .of_compatible = mdio_mux_gpio_match, diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index 1e6278ef35..c4088c16ca 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -56,7 +56,7 @@ static int mdio_mux_write(struct mii_bus *bus, int phy_id, return mdio_mux_read_or_write(bus, phy_id, regnum, &val); } -int mdio_mux_init(struct device_d *dev, +int mdio_mux_init(struct device *dev, struct device_node *mux_node, int (*switch_fn)(int cur, int desired, void *data), void *data, @@ -117,7 +117,7 @@ int mdio_mux_init(struct device_d *dev, cb->mii_bus.parent = dev; cb->mii_bus.read = mdio_mux_read; cb->mii_bus.write = mdio_mux_write; - cb->mii_bus.dev.device_node = child_bus_node; + cb->mii_bus.dev.of_node = child_bus_node; r = mdiobus_register(&cb->mii_bus); if (r) { diff --git a/drivers/net/phy/mdio-mvebu.c b/drivers/net/phy/mdio-mvebu.c index 289ff4b05d..cd90ddd221 100644 --- a/drivers/net/phy/mdio-mvebu.c +++ b/drivers/net/phy/mdio-mvebu.c @@ -103,7 +103,7 @@ static int mvebu_mdio_write(struct mii_bus *bus, int addr, int reg, u16 data) return 0; } -static int mvebu_mdio_probe(struct device_d *dev) +static int mvebu_mdio_probe(struct device *dev) { struct mdio_priv *priv; @@ -119,7 +119,7 @@ static int mvebu_mdio_probe(struct device_d *dev) return PTR_ERR(priv->clk); clk_enable(priv->clk); - priv->miibus.dev.device_node = dev->device_node; + priv->miibus.dev.of_node = dev->of_node; priv->miibus.priv = priv; priv->miibus.parent = dev; priv->miibus.read = mvebu_mdio_read; @@ -128,7 +128,7 @@ static int mvebu_mdio_probe(struct device_d *dev) return mdiobus_register(&priv->miibus); } -static void mvebu_mdio_remove(struct device_d *dev) +static void mvebu_mdio_remove(struct device *dev) { struct mdio_priv *priv = dev->priv; @@ -141,8 +141,9 @@ static struct of_device_id mvebu_mdio_dt_ids[] = { { .compatible = "marvell,orion-mdio" }, { } }; +MODULE_DEVICE_TABLE(of, mvebu_mdio_dt_ids); -static struct driver_d mvebu_mdio_driver = { +static struct driver mvebu_mdio_driver = { .name = "mvebu-mdio", .probe = mvebu_mdio_probe, .remove = mvebu_mdio_remove, diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 99d23ffedf..eed7c779e7 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -19,16 +19,68 @@ #include <clock.h> #include <net.h> #include <errno.h> +#include <linux/mdio.h> #include <linux/phy.h> #include <linux/err.h> #include <of_device.h> +#include <pinctrl.h> #define DEFAULT_GPIO_RESET_ASSERT 1000 /* us */ #define DEFAULT_GPIO_RESET_DEASSERT 1000 /* us */ LIST_HEAD(mii_bus_list); -int mdiobus_detect(struct device_d *dev) +static struct phy_device *mdio_device_create(struct mii_bus *bus, int addr) +{ + struct phy_device *phydev; + + phydev = xzalloc(sizeof(*phydev)); + + phydev->addr = addr; + phydev->bus = bus; + phydev->dev.bus = &mdio_bus_type; + + dev_set_name(&phydev->dev, "mdio%d-dev%02x", phydev->bus->dev.id, + phydev->addr); + phydev->dev.id = DEVICE_ID_SINGLE; + + return phydev; +} + +static int mdio_register_device(struct phy_device *phydev) +{ + int ret; + + if (phydev->registered) + return -EBUSY; + + if (!phydev->dev.parent) + phydev->dev.parent = &phydev->bus->dev; + + ret = register_device(&phydev->dev); + if (ret) + return ret; + + if (phydev->bus) + phydev->bus->phy_map[phydev->addr] = phydev; + + phydev->registered = 1; + + if (phydev->dev.driver) + return 0; + + return ret; +} + +int mdio_driver_register(struct phy_driver *phydrv) +{ + phydrv->drv.bus = &mdio_bus_type; + phydrv->is_phy = false; + + return register_driver(&phydrv->drv); +} + +int mdiobus_detect(struct device *dev) { struct mii_bus *mii = to_mii_bus(dev); int i, ret; @@ -67,7 +119,8 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi * Associate the OF node with the device structure so it * can be looked up later */ - phy->dev.device_node = child; + child->dev = &phy->dev; + phy->dev.of_node = child; /* * All data is now stored in the phy struct; @@ -83,6 +136,29 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi return 0; } +static int of_mdiobus_register_device(struct mii_bus *mdio, + struct device_node *child, u32 addr) +{ + struct phy_device *mdiodev; + int ret; + + mdiodev = mdio_device_create(mdio, addr); + if (IS_ERR(mdiodev)) + return PTR_ERR(mdiodev); + + child->dev = &mdiodev->dev; + mdiodev->dev.of_node = child; + + ret = mdio_register_device(mdiodev); + if (ret) + return ret; + + dev_dbg(&mdio->dev, "registered mdio device %s at address %i\n", + child->name, addr); + + return 0; +} + /* * Node is considered a PHY node if: * o Compatible string of "ethernet-phy-idX.X" @@ -175,35 +251,27 @@ static int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) /* Loop over the child nodes and register a phy_device for each one */ for_each_available_child_of_node(np, child) { - if (!of_mdiobus_child_is_phy(child)) { - if (of_get_property(child, "compatible", NULL)) { - if (!of_platform_device_create(child, - &mdio->dev)) { - dev_err(&mdio->dev, - "Failed to create device " - "for %s\n", - child->full_name); - } - } - - continue; - } - ret = of_property_read_u32(child, "reg", &addr); if (ret) { - dev_dbg(&mdio->dev, "%s has invalid PHY address\n", - child->full_name); + dev_dbg(&mdio->dev, "%pOF has invalid PHY address\n", + child); continue; } if (addr >= PHY_MAX_ADDR) { - dev_err(&mdio->dev, "%s PHY address %i is too large\n", - child->full_name, addr); + dev_err(&mdio->dev, "%pOF PHY address %i is too large\n", + child, addr); continue; } - of_mdiobus_reset_phy(mdio, child); - of_mdiobus_register_phy(mdio, child, addr); + of_pinctrl_select_state_default(child); + + if (of_mdiobus_child_is_phy(child)) { + of_mdiobus_reset_phy(mdio, child); + of_mdiobus_register_phy(mdio, child, addr); + } else { + of_mdiobus_register_device(mdio, child, addr); + } } return 0; @@ -248,16 +316,14 @@ int mdiobus_register(struct mii_bus *bus) pr_info("%s: probed\n", dev_name(&bus->dev)); - if (bus->dev.device_node) { + if (!bus->dev.of_node) + bus->dev.of_node = bus->parent->of_node; + + if (bus->dev.of_node) { + bus->dev.of_node->dev = &bus->dev; + /* Register PHY's as child node to mdio node */ - of_mdiobus_register(bus, bus->dev.device_node); - } - else if (bus->parent->device_node) { - /* - * Register PHY's as child node to the ethernet node, - * if there was no mdio node - */ - of_mdiobus_register(bus, bus->parent->device_node); + of_mdiobus_register(bus, bus->dev.of_node); } return 0; @@ -333,7 +399,7 @@ struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np) return NULL; for_each_mii_bus(mii) - if (mii->dev.device_node == mdio_bus_np) + if (mii->dev.of_node == mdio_bus_np) return mii; return NULL; @@ -349,14 +415,18 @@ EXPORT_SYMBOL(of_mdio_find_bus); * Description: Given a PHY device, and a PHY driver, return 0 if * the driver supports the device. Otherwise, return 1. */ -static int mdio_bus_match(struct device_d *dev, struct driver_d *drv) +static int mdio_bus_match(struct device *dev, struct driver *drv) { struct phy_device *phydev = to_phy_device(dev); struct phy_driver *phydrv = to_phy_driver(drv); - if ((phydrv->phy_id & phydrv->phy_id_mask) == - (phydev->phy_id & phydrv->phy_id_mask)) + if (phydrv->is_phy) { + if ((phydrv->phy_id & phydrv->phy_id_mask) == + (phydev->phy_id & phydrv->phy_id_mask)) return 0; + } else { + return device_match(dev, drv); + } return 1; } @@ -439,7 +509,7 @@ static struct cdev_operations phydev_ops = { static void of_set_phy_supported(struct phy_device *phydev) { - struct device_node *node = phydev->dev.device_node; + struct device_node *node = phydev->dev.of_node; u32 max_speed; if (!IS_ENABLED(CONFIG_OFDEVICE)) @@ -470,7 +540,7 @@ static void of_set_phy_supported(struct phy_device *phydev) } } -static int mdio_bus_probe(struct device_d *_dev) +static int mdio_bus_probe(struct device *_dev) { struct phy_device *dev = to_phy_device(_dev); struct phy_driver *drv = to_phy_driver(_dev->driver); @@ -524,7 +594,7 @@ err: return ret; } -static void mdio_bus_remove(struct device_d *_dev) +static void mdio_bus_remove(struct device *_dev) { struct phy_device *dev = to_phy_device(_dev); struct phy_driver *drv = to_phy_driver(_dev->driver); diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index cf593ee6a6..a203669353 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -128,8 +128,8 @@ static int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val) /* Handle LED mode, shift = position of first led mode bit, usually 4 or 14 */ static int kszphy_led_mode(struct phy_device *phydev, int reg, int shift) { - const struct device_d *dev = &phydev->dev; - const struct device_node *of_node = dev->device_node ? : dev->parent->device_node; + const struct device *dev = &phydev->dev; + const struct device_node *of_node = dev->of_node ? : dev->parent->of_node; u32 val; if (!of_property_read_u32(of_node, "micrel,led-mode", &val)) { @@ -144,7 +144,7 @@ static int kszphy_led_mode(struct phy_device *phydev, int reg, int shift) static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val) { - const struct device_d *dev = &phydev->dev; + const struct device *dev = &phydev->dev; int rc, temp, shift; switch (reg) { @@ -179,7 +179,7 @@ out: */ static int kszphy_broadcast_disable(struct phy_device *phydev) { - const struct device_d *dev = &phydev->dev; + const struct device *dev = &phydev->dev; int ret; ret = phy_read(phydev, MII_KSZPHY_OMSO); @@ -196,7 +196,7 @@ out: static int kszphy_nand_tree_disable(struct phy_device *phydev) { - const struct device_d *dev = &phydev->dev; + const struct device *dev = &phydev->dev; int ret; ret = phy_read(phydev, MII_KSZPHY_OMSO); @@ -278,8 +278,8 @@ static int ksz9021_load_values_from_of(struct phy_device *phydev, static int ksz9021_config_init(struct phy_device *phydev) { - const struct device_d *dev = &phydev->dev; - const struct device_node *of_node = dev->device_node; + const struct device *dev = &phydev->dev; + const struct device_node *of_node = dev->of_node; const char *clk_pad_skew_names[] = { "txen-skew-ps", "txc-skew-ps", "rxdv-skew-ps", "rxc-skew-ps" @@ -293,8 +293,8 @@ static int ksz9021_config_init(struct phy_device *phydev) "txd2-skew-ps", "txd3-skew-ps" }; - if (!of_node && dev->parent->device_node) - of_node = dev->parent->device_node; + if (!of_node && dev->parent->of_node) + of_node = dev->parent->of_node; if (of_node) { ksz9021_load_values_from_of(phydev, of_node, @@ -387,7 +387,7 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev, return 0; if (matches < numfields) - newval = phy_read_mmd_indirect(phydev, reg, MDIO_MMD_WIS); + newval = phy_read_mmd(phydev, MDIO_MMD_WIS, reg); else newval = 0; @@ -401,15 +401,15 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev, << (field_sz * i)); } - phy_write_mmd_indirect(phydev, reg, MDIO_MMD_WIS, newval); + phy_write_mmd(phydev, MDIO_MMD_WIS, reg, newval); return 0; } static int ksz9031_center_flp_timing(struct phy_device *phydev) { /* Center KSZ9031RNX FLP timing at 16ms. */ - phy_write_mmd_indirect(phydev, MII_KSZ9031RN_FLP_BURST_TX_HI, 0, 0x0006); - phy_write_mmd_indirect(phydev, MII_KSZ9031RN_FLP_BURST_TX_LO, 0, 0x1a80); + phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_HI, 0x0006); + phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_LO, 0x1a80); return genphy_restart_aneg(phydev); } @@ -447,32 +447,32 @@ static int ksz9031_config_rgmii_delay(struct phy_device *phydev) return 0; } - phy_write_mmd_indirect(phydev, MII_KSZ9031RN_CONTROL_PAD_SKEW, 2, - FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) | - FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx)); - - phy_write_mmd_indirect(phydev, MII_KSZ9031RN_RX_DATA_PAD_SKEW, 2, - FIELD_PREP(MII_KSZ9031RN_RXD3, rx) | - FIELD_PREP(MII_KSZ9031RN_RXD2, rx) | - FIELD_PREP(MII_KSZ9031RN_RXD1, rx) | - FIELD_PREP(MII_KSZ9031RN_RXD0, rx)); - - phy_write_mmd_indirect(phydev, MII_KSZ9031RN_TX_DATA_PAD_SKEW, 2, - FIELD_PREP(MII_KSZ9031RN_TXD3, tx) | - FIELD_PREP(MII_KSZ9031RN_TXD2, tx) | - FIELD_PREP(MII_KSZ9031RN_TXD1, tx) | - FIELD_PREP(MII_KSZ9031RN_TXD0, tx)); - - phy_write_mmd_indirect(phydev, MII_KSZ9031RN_CLK_PAD_SKEW, 2, - FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) | - FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk)); + phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_CONTROL_PAD_SKEW, + FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) | + FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx)); + + phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_RX_DATA_PAD_SKEW, + FIELD_PREP(MII_KSZ9031RN_RXD3, rx) | + FIELD_PREP(MII_KSZ9031RN_RXD2, rx) | + FIELD_PREP(MII_KSZ9031RN_RXD1, rx) | + FIELD_PREP(MII_KSZ9031RN_RXD0, rx)); + + phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_TX_DATA_PAD_SKEW, + FIELD_PREP(MII_KSZ9031RN_TXD3, tx) | + FIELD_PREP(MII_KSZ9031RN_TXD2, tx) | + FIELD_PREP(MII_KSZ9031RN_TXD1, tx) | + FIELD_PREP(MII_KSZ9031RN_TXD0, tx)); + + phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_CLK_PAD_SKEW, + FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) | + FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk)); return 0; } static int ksz9031_config_init(struct phy_device *phydev) { - const struct device_d *dev = &phydev->dev; - const struct device_node *of_node = dev->device_node; + const struct device *dev = &phydev->dev; + const struct device_node *of_node = dev->of_node; static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"}; static const char *rx_data_skews[4] = { "rxd0-skew-ps", "rxd1-skew-ps", @@ -485,8 +485,8 @@ static int ksz9031_config_init(struct phy_device *phydev) static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"}; int ret; - if (!of_node && dev->parent->device_node) - of_node = dev->parent->device_node; + if (!of_node && dev->parent->of_node) + of_node = dev->parent->of_node; if (of_node) { if (phy_interface_is_rgmii(phydev)) { @@ -546,6 +546,186 @@ err_force_master: return ret; } +#define KSZ9131_SKEW_5BIT_MAX 2400 +#define KSZ9131_SKEW_4BIT_MAX 800 +#define KSZ9131_OFFSET 700 +#define KSZ9131_STEP 100 + +static int ksz9131_of_load_skew_values(struct phy_device *phydev, + const struct device_node *of_node, + u16 reg, size_t field_sz, + const char *field[], u8 numfields) +{ + int val[4] = {-(1 + KSZ9131_OFFSET), -(2 + KSZ9131_OFFSET), + -(3 + KSZ9131_OFFSET), -(4 + KSZ9131_OFFSET)}; + int skewval, skewmax = 0; + int matches = 0; + u16 maxval; + u16 newval; + u16 mask; + int i; + + /* psec properties in dts should mean x pico seconds */ + if (field_sz == 5) + skewmax = KSZ9131_SKEW_5BIT_MAX; + else + skewmax = KSZ9131_SKEW_4BIT_MAX; + + for (i = 0; i < numfields; i++) + if (!of_property_read_s32(of_node, field[i], &skewval)) { + if (skewval < -KSZ9131_OFFSET) + skewval = -KSZ9131_OFFSET; + else if (skewval > skewmax) + skewval = skewmax; + + val[i] = skewval + KSZ9131_OFFSET; + matches++; + } + + if (!matches) + return 0; + + if (matches < numfields) + newval = phy_read_mmd(phydev, 2, reg); + else + newval = 0; + + maxval = (field_sz == 4) ? 0xf : 0x1f; + for (i = 0; i < numfields; i++) + if (val[i] != -(i + 1 + KSZ9131_OFFSET)) { + mask = 0xffff; + mask ^= maxval << (field_sz * i); + newval = (newval & mask) | + (((val[i] / KSZ9131_STEP) & maxval) + << (field_sz * i)); + } + + return phy_write_mmd(phydev, 2, reg, newval); +} + +#define KSZ9131RN_MMD_COMMON_CTRL_REG 2 +#define KSZ9131RN_RXC_DLL_CTRL 76 +#define KSZ9131RN_TXC_DLL_CTRL 77 +#define KSZ9131RN_DLL_CTRL_BYPASS BIT_MASK(12) +#define KSZ9131RN_DLL_ENABLE_DELAY 0 +#define KSZ9131RN_DLL_DISABLE_DELAY BIT(12) + +static int ksz9131_config_rgmii_delay(struct phy_device *phydev) +{ + u16 rxcdll_val, txcdll_val; + int ret; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + break; + default: + return 0; + } + + ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, + rxcdll_val); + if (ret < 0) + return ret; + + return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, + txcdll_val); +} + +/* Silicon Errata DS80000693B + * + * When LEDs are configured in Individual Mode, LED1 is ON in a no-link + * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves + * according to the datasheet (off if there is no link). + */ +static int ksz9131_led_errata(struct phy_device *phydev) +{ + int reg; + + reg = phy_read_mmd(phydev, 2, 0); + if (reg < 0) + return reg; + + if (!(reg & BIT(4))) + return 0; + + return phy_set_bits(phydev, 0x1e, BIT(9)); +} + +static int ksz9131_config_init(struct phy_device *phydev) +{ + const struct device *dev = &phydev->dev; + const struct device_node *of_node = dev->of_node; + static const char *clk_skews[2] = {"rxc-skew-psec", "txc-skew-psec"}; + static const char *rx_data_skews[4] = { + "rxd0-skew-psec", "rxd1-skew-psec", + "rxd2-skew-psec", "rxd3-skew-psec" + }; + static const char *tx_data_skews[4] = { + "txd0-skew-psec", "txd1-skew-psec", + "txd2-skew-psec", "txd3-skew-psec" + }; + static const char *control_skews[2] = {"txen-skew-psec", "rxdv-skew-psec"}; + int ret; + + if (!of_node && dev->parent->of_node) + of_node = dev->parent->of_node; + + if (!of_node) + return 0; + + if (phy_interface_is_rgmii(phydev)) { + ret = ksz9131_config_rgmii_delay(phydev); + if (ret < 0) + return ret; + } + + ret = ksz9131_of_load_skew_values(phydev, of_node, + MII_KSZ9031RN_CLK_PAD_SKEW, 5, + clk_skews, 2); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, of_node, + MII_KSZ9031RN_CONTROL_PAD_SKEW, 4, + control_skews, 2); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, of_node, + MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4, + rx_data_skews, 4); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, of_node, + MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4, + tx_data_skews, 4); + if (ret < 0) + return ret; + + ret = ksz9131_led_errata(phydev); + if (ret < 0) + return ret; + + return 0; +} + #define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6) #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4) @@ -554,7 +734,7 @@ static int ksz8873mll_read_status(struct phy_device *phydev) int regval; /* dummy read */ - regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); + (void)phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); @@ -611,8 +791,8 @@ static int ksz8873mll_config_init(struct phy_device *phydev) static int kszphy_probe(struct phy_device *phydev) { - struct device_d *dev = &phydev->dev; - struct device_node *np = dev->device_node; + struct device *dev = &phydev->dev; + struct device_node *np = dev->of_node; struct phy_driver *drv = to_phy_driver(dev->driver); const struct kszphy_type *type = drv->driver_data; struct kszphy_priv *priv; @@ -758,6 +938,14 @@ static struct phy_driver ksphy_driver[] = { .config_aneg = genphy_config_aneg, .read_status = ksz9031_read_status, }, { + .phy_id = PHY_ID_KSZ9131, + .phy_id_mask = 0x00fffff0, + .drv.name = "Microchip KSZ9131 Gigabit PHY", + .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), + .config_init = ksz9131_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, +}, { .phy_id = PHY_ID_KSZ8873MLL, .phy_id_mask = 0x00fffff0, .drv.name = "Micrel KSZ8873MLL Switch", diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c new file mode 100644 index 0000000000..d4cd05a1f6 --- /dev/null +++ b/drivers/net/phy/motorcomm.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * drivers/net/phy/motorcomm.c + * + * Driver for Motorcomm PHYs + * + * Author: Peter Geis <pgwipeout@gmail.com> + */ + +#include <common.h> +#include <init.h> +#include <linux/phy.h> +#include <linux/mdio.h> + +#define PHY_ID_YT8511 0x0000010a + +#define YT8511_PAGE_SELECT 0x1e +#define YT8511_PAGE 0x1f +#define YT8511_EXT_CLK_GATE 0x0c +#define YT8511_EXT_DELAY_DRIVE 0x0d +#define YT8511_EXT_SLEEP_CTRL 0x27 + +/* 2b00 25m from pll + * 2b01 25m from xtl *default* + * 2b10 62.m from pll + * 2b11 125m from pll + */ +#define YT8511_CLK_125M (BIT(2) | BIT(1)) +#define YT8511_PLLON_SLP BIT(14) + +/* RX Delay enabled = 1.8ns 1000T, 8ns 10/100T */ +#define YT8511_DELAY_RX BIT(0) + +/* TX Gig-E Delay is bits 7:4, default 0x5 + * TX Fast-E Delay is bits 15:12, default 0xf + * Delay = 150ps * N - 250ps + * On = 2000ps, off = 50ps + */ +#define YT8511_DELAY_GE_TX_EN (0xf << 4) +#define YT8511_DELAY_GE_TX_DIS (0x2 << 4) +#define YT8511_DELAY_FE_TX_EN (0xf << 12) +#define YT8511_DELAY_FE_TX_DIS (0x2 << 12) + +static int yt8511_read_page(struct phy_device *phydev) +{ + return phy_read(phydev, YT8511_PAGE_SELECT); +}; + +static int yt8511_write_page(struct phy_device *phydev, int page) +{ + return phy_write(phydev, YT8511_PAGE_SELECT, page); +}; + +static int yt8511_config_init(struct phy_device *phydev) +{ + int oldpage, ret = 0; + unsigned int ge, fe; + + oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE); + if (oldpage < 0) + goto err_restore_page; + + /* set rgmii delay mode */ + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + ge = YT8511_DELAY_GE_TX_DIS; + fe = YT8511_DELAY_FE_TX_DIS; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_DIS; + fe = YT8511_DELAY_FE_TX_DIS; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + ge = YT8511_DELAY_GE_TX_EN; + fe = YT8511_DELAY_FE_TX_EN; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN; + fe = YT8511_DELAY_FE_TX_EN; + break; + default: /* do not support other modes */ + ret = -EOPNOTSUPP; + goto err_restore_page; + } + + ret = phy_modify(phydev, YT8511_PAGE, (YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN), ge); + if (ret < 0) + goto err_restore_page; + + /* set clock mode to 125mhz */ + ret = phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M); + if (ret < 0) + goto err_restore_page; + + /* fast ethernet delay is in a separate page */ + ret = phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_DELAY_DRIVE); + if (ret < 0) + goto err_restore_page; + + ret = phy_modify(phydev, YT8511_PAGE, YT8511_DELAY_FE_TX_EN, fe); + if (ret < 0) + goto err_restore_page; + + /* leave pll enabled in sleep */ + ret = phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_SLEEP_CTRL); + if (ret < 0) + goto err_restore_page; + + ret = phy_modify(phydev, YT8511_PAGE, 0, YT8511_PLLON_SLP); + if (ret < 0) + goto err_restore_page; + +err_restore_page: + return phy_restore_page(phydev, oldpage, ret); +} + +static struct phy_driver motorcomm_phy_drvs[] = { + { + .phy_id = PHY_ID_YT8511, + .phy_id_mask = 0xffffffff, + .drv.name = "YT8511 Gigabit Ethernet", + .config_init = yt8511_config_init, + .features = PHY_GBIT_FEATURES, + .read_page = yt8511_read_page, + .write_page = yt8511_write_page, + }, +}; + +device_phy_drivers(motorcomm_phy_drvs); diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c index a7d707095b..b9b02c52f2 100644 --- a/drivers/net/phy/mv88e6xxx/chip.c +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -832,14 +832,9 @@ static int mv88e6xxx_eeprom_write(void *ctx, unsigned offset, const void *val, s return chip->info->ops->set_eeprom(chip, &eeprom, (void *)val); } -static const struct nvmem_bus mv88e6xxx_eeprom_nvmem_bus = { - .write = mv88e6xxx_eeprom_write, - .read = mv88e6xxx_eeprom_read, -}; - -static int mv88e6xxx_probe(struct device_d *dev) +static int mv88e6xxx_probe(struct device *dev) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct device_node *mdio_node; struct mv88e6xxx_chip *chip; enum of_gpio_flags of_flags; @@ -911,7 +906,8 @@ static int mv88e6xxx_probe(struct device_d *dev) .stride = 1, .size = eeprom_len, .read_only = false, - .bus = &mv88e6xxx_eeprom_nvmem_bus, + .reg_write = mv88e6xxx_eeprom_write, + .reg_read = mv88e6xxx_eeprom_read, }; if (IS_ERR(nvmem_register(&config))) @@ -941,7 +937,7 @@ static int mv88e6xxx_probe(struct device_d *dev) mdio_node = of_get_child_by_name(np, "mdio"); if (mdio_node) - chip->miibus.dev.device_node = mdio_node; + chip->miibus.dev.of_node = mdio_node; err = mv88e6xxx_port_probe(chip); if (err) @@ -965,8 +961,9 @@ static const struct of_device_id mv88e6xxx_of_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match); -static struct driver_d mv88e6xxx_driver = { +static struct driver mv88e6xxx_driver = { .name = "mv88e6085", .probe = mv88e6xxx_probe, .of_compatible = mv88e6xxx_of_match, diff --git a/drivers/net/phy/mv88e6xxx/chip.h b/drivers/net/phy/mv88e6xxx/chip.h index 30fdac9a9f..aec6c2891f 100644 --- a/drivers/net/phy/mv88e6xxx/chip.h +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -50,7 +50,7 @@ struct mv88e6xxx_chip { const struct mv88e6xxx_info *info; struct mii_bus *parent_miibus; struct mii_bus miibus; - struct device_d *dev; + struct device *dev; int reset; /* Array of port structures. */ diff --git a/drivers/net/phy/mv88e6xxx/port.c b/drivers/net/phy/mv88e6xxx/port.c index 93a789e658..29ea4ec882 100644 --- a/drivers/net/phy/mv88e6xxx/port.c +++ b/drivers/net/phy/mv88e6xxx/port.c @@ -564,8 +564,8 @@ device_phy_driver(mv88e6xxx_port_driver); int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip) { - struct device_d *dev = chip->dev; - struct device_node *np = dev->device_node; + struct device *dev = chip->dev; + struct device_node *np = dev->of_node; struct device_node *port_node, *switch_node; struct device_node *port_nodes[DSA_MAX_PORTS] = { NULL }; int err, i; @@ -580,8 +580,8 @@ int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip) err = of_property_read_u32(port_node, "reg", &nr); if (err) { dev_err(dev, - "Error: Failed to find reg for child %s\n", - port_node->full_name); + "Error: Failed to find reg for child %pOF\n", + port_node); continue; } @@ -659,7 +659,7 @@ int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip) phydev = phy_device_create(chip->parent_miibus, chip->info->port_base_addr + i, MV88E6XXX_SWITCH_PORT_PHY_ID); - phydev->dev.device_node = port_nodes[i]; + phydev->dev.of_node = port_nodes[i]; phydev->dev.priv = chip; phydev->duplex = DUPLEX_UNFORCED; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 60b5839b40..abd78b2c80 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -20,8 +20,6 @@ #include <linux/phy.h> #include <linux/err.h> -#define PHY_AN_TIMEOUT 10 - static struct phy_driver genphy_driver; /** @@ -330,7 +328,7 @@ struct phy_device *of_phy_register_fixed_link(struct device_node *np, static struct phy_device *of_mdio_find_phy(struct eth_device *edev) { - struct device_d *dev; + struct device *dev; struct device_node *phy_node; struct mii_bus *bus; int addr; @@ -338,16 +336,18 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev) if (!IS_ENABLED(CONFIG_OFDEVICE)) return NULL; - if (!edev->parent || !edev->parent->device_node) + if (!edev->parent || !edev->parent->of_node) return NULL; - phy_node = of_parse_phandle(edev->parent->device_node, "phy-handle", 0); + phy_node = of_parse_phandle(edev->parent->of_node, "phy-handle", 0); if (!phy_node) - phy_node = of_parse_phandle(edev->parent->device_node, "phy", 0); + phy_node = of_parse_phandle(edev->parent->of_node, "phy", 0); if (!phy_node) - phy_node = of_parse_phandle(edev->parent->device_node, "phy-device", 0); + phy_node = of_parse_phandle(edev->parent->of_node, + "phy-device", 0); if (!phy_node) { - phy_node = of_get_child_by_name(edev->parent->device_node, "fixed-link"); + phy_node = of_get_child_by_name(edev->parent->of_node, + "fixed-link"); if (phy_node) return of_phy_register_fixed_link(phy_node, edev); } @@ -358,7 +358,7 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev) if (!of_property_read_u32(phy_node, "reg", &addr)) { of_device_ensure_probed(phy_node->parent); for_each_mii_bus(bus) { - if (bus->parent->device_node == phy_node->parent) { + if (bus->dev.of_node == phy_node->parent) { struct phy_device *phy = mdiobus_scan(bus, addr); if (!IS_ERR(phy)) return phy; @@ -367,7 +367,7 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev) } bus_for_each_device(&mdio_bus_type, dev) { - if (dev->device_node == phy_node) + if (dev->of_node == phy_node) return container_of(dev, struct phy_device, dev); } @@ -462,7 +462,7 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, ret = -ENODEV; out: if (ret) - puts("Unable to find a PHY (unknown ID?)\n"); + dev_err(&edev->dev, "Unable to find a PHY (unknown ID?)\n"); return ret; } @@ -675,7 +675,7 @@ int genphy_aneg_done(struct phy_device *phydev) /* Restart auto-negotiation if remote fault */ if (bmsr & BMSR_RFAULT) { - puts("PHY remote fault detected\n" + dev_info(&phydev->dev, "PHY remote fault detected\n" "PHY restarting auto-negotiation\n"); phy_write(phydev, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); @@ -819,14 +819,14 @@ int genphy_read_status(struct phy_device *phydev) return 0; } -static inline void mmd_phy_indirect(struct phy_device *phydev, int prtad, - int devad) +static inline void mmd_phy_indirect(struct phy_device *phydev, int devad, + u16 regnum) { /* Write the desired MMD Devad */ phy_write(phydev, MII_MMD_CTRL, devad); /* Write the desired MMD register address */ - phy_write(phydev, MII_MMD_DATA, prtad); + phy_write(phydev, MII_MMD_DATA, regnum); /* Select the Function : DATA with no post increment */ phy_write(phydev, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); @@ -850,7 +850,10 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) { u32 ret; - mmd_phy_indirect(phydev, prtad, devad); + phydev_warn(phydev, "%s is deprectated use phy_read_mmd instead\n", + __func__); + + mmd_phy_indirect(phydev, devad, prtad); /* Read the content of the MMD's selected register */ ret = phy_read(phydev, MII_MMD_DATA); @@ -876,12 +879,150 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, int devad, u16 data) { - mmd_phy_indirect(phydev, prtad, devad); + phydev_warn(phydev, "%s is deprectated use phy_write_mmd instead\n", + __func__); + + mmd_phy_indirect(phydev, devad, prtad); /* Write the data into MMD's selected register */ phy_write(phydev, MII_MMD_DATA, data); } +/** + * phy_modify_mmd_indirect - Convenience function for modifying a MMD register + * @phydev: phy device + * @prtad: MMD Address + * @devad: MMD DEVAD + * @mask: bit mask of bits to clear + * @set: new value of bits set in @mask + * + */ +int phy_modify_mmd_indirect(struct phy_device *phydev, int prtad, int devad, + u16 mask, u16 set) +{ + int ret; + + phydev_warn(phydev, "%s is deprectated use phy_modify_mmd instead\n", + __func__); + + ret = phy_read_mmd_indirect(phydev, prtad, devad); + if (ret < 0) + return ret; + + phy_write_mmd_indirect(phydev, prtad, devad, (ret & ~mask) | set); + + return 0; +} + +/** + * phy_read_mmd - Convenience function for reading a register + * from an MMD on a given PHY. + * @phydev: The phy_device struct + * @devad: The MMD to read from + * @regnum: The register on the MMD to read + * + * Same rules as for phy_read(); + */ +int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) +{ + struct mii_bus *bus = phydev->bus; + int phy_addr = phydev->addr; + + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + + if (phydev->is_c45) { + phydev_warn(phydev, "Clause45 is not supported yet\n"); + return -EOPNOTSUPP; + } + + mmd_phy_indirect(phydev, devad, regnum); + + /* Read the content of the MMD's selected register */ + return mdiobus_read(bus, phy_addr, MII_MMD_DATA); +} +EXPORT_SYMBOL(phy_read_mmd); + +/** + * phy_write_mmd - Convenience function for writing a register + * on an MMD on a given PHY. + * @phydev: The phy_device struct + * @devad: The MMD to read from + * @regnum: The register on the MMD to read + * @val: value to write to @regnum + * + * Same rules as for phy_write(); + */ +int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) +{ + struct mii_bus *bus = phydev->bus; + int phy_addr = phydev->addr; + + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + + if (phydev->is_c45) { + phydev_warn(phydev, "Clause45 is not supported yet\n"); + return -EOPNOTSUPP; + } + + mmd_phy_indirect(phydev, devad, regnum); + + /* Write the data into MMD's selected register */ + mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); + + return 0; +} +EXPORT_SYMBOL(phy_write_mmd); + +/** + * phy_modify_mmd_changed - Function for modifying a register on MMD + * @phydev: the phy_device struct + * @devad: the MMD containing register to modify + * @regnum: register number to modify + * @mask: bit mask of bits to clear + * @set: new value of bits set in mask to write to @regnum + * + * Returns negative errno, 0 if there was no change, and 1 in case of change + */ +int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum, + u16 mask, u16 set) +{ + int new, ret; + + ret = phy_read_mmd(phydev, devad, regnum); + if (ret < 0) + return ret; + + new = (ret & ~mask) | set; + if (new == ret) + return 0; + + ret = phy_write_mmd(phydev, devad, regnum, new); + + return ret < 0 ? ret : 1; +} +EXPORT_SYMBOL_GPL(phy_modify_mmd_changed); + +/** + * phy_modify_mmd - Convenience function for modifying a register on MMD + * @phydev: the phy_device struct + * @devad: the MMD containing register to modify + * @regnum: register number to modify + * @mask: bit mask of bits to clear + * @set: new value of bits set in mask to write to @regnum + */ +int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, + u16 mask, u16 set) +{ + int ret; + + ret = phy_modify_mmd_changed(phydev, devad, regnum, mask, set); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(phy_modify_mmd); + int genphy_config_init(struct phy_device *phydev) { int val; @@ -933,6 +1074,8 @@ int phy_driver_register(struct phy_driver *phydrv) { phydrv->drv.bus = &mdio_bus_type; + phydrv->is_phy = true; + if (!phydrv->config_init) phydrv->config_init = genphy_config_init; diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 4df3db1387..c23947b7cb 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -80,7 +80,7 @@ static int rtl8211c_config_init(struct phy_device *phydev) static int rtl8211f_config_init(struct phy_device *phydev) { - struct device_d *dev = &phydev->dev; + struct device *dev = &phydev->dev; u16 val_txdly, val_rxdly; int ret; @@ -155,6 +155,12 @@ static struct phy_driver realtek_drvs[] = { .read_page = rtl821x_read_page, .write_page = rtl821x_write_page, }, { + PHY_ID_MATCH_EXACT(0x001cc840), + .drv.name = "RTL8226B_RTL8221B 2.5Gbps PHY", + .features = PHY_GBIT_FEATURES, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + }, { PHY_ID_MATCH_EXACT(0x001cc910), .drv.name = "RTL8211 Gigabit Ethernet", .features = PHY_GBIT_FEATURES, |