diff options
Diffstat (limited to 'drivers/net/phy')
30 files changed, 2070 insertions, 425 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 6cb162a437..8e12671801 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # PHY Layer Configuration # @@ -28,6 +29,18 @@ config DP83867_PHY help Currently supports the DP83867 PHY. +config DP83TD510_PHY + tristate "Texas Instruments DP83TD510 Ethernet 10Base-T1L PHY" + help + 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 @@ -43,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 7053b5762c..ce15e1bab7 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-y += phy.o mdio_bus.o phy-core.o obj-$(CONFIG_AR8327N_PHY) += ar8327.o obj-$(CONFIG_AT803X_PHY) += at803x.o @@ -5,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 @@ -16,4 +18,6 @@ obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o 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 5f3a2e2cf2..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) @@ -268,9 +268,4 @@ static struct phy_driver ar8327n_driver[] = { .aneg_done = &ar8327n_aneg_done, }}; -static int atheros_phy_init(void) -{ - return phy_drivers_register(ar8327n_driver, - ARRAY_SIZE(ar8327n_driver)); -} -fs_initcall(atheros_phy_init); +device_phy_drivers(ar8327n_driver); diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index b43cb0d23e..8d6b879a27 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -11,6 +11,9 @@ #include <init.h> #include <linux/phy.h> #include <linux/string.h> +#include <linux/bitfield.h> +#include <linux/mdio.h> +#include <dt-bindings/net/qca-ar803x.h> #define AT803X_INTR_ENABLE 0x12 #define AT803X_INTR_STATUS 0x13 @@ -24,64 +27,322 @@ #define AT803X_FUNC_DATA 0x4003 #define AT803X_DEBUG_ADDR 0x1D #define AT803X_DEBUG_DATA 0x1E -#define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 -#define AT803X_DEBUG_RGMII_TX_CLK_DLY (1 << 8) +#define AT803X_DEBUG_REG_0 0x00 +#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) +#define AT803X_DEBUG_REG_5 0x05 +#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) -static int at803x_config_init(struct phy_device *phydev) +#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. + * The DSP as a clock reference is used in synchronous ethernet + * applications. + * + * By default the PLL is only enabled if there is a link. Otherwise + * the PHY will go into low power state and disabled the PLL. You can + * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always + * enabled. + */ +#define AT803X_MMD7_CLK25M 0x8016 +#define AT803X_CLK_OUT_MASK GENMASK(4, 2) +#define AT803X_CLK_OUT_25MHZ_XTAL 0 +#define AT803X_CLK_OUT_25MHZ_DSP 1 +#define AT803X_CLK_OUT_50MHZ_PLL 2 +#define AT803X_CLK_OUT_50MHZ_DSP 3 +#define AT803X_CLK_OUT_62_5MHZ_PLL 4 +#define AT803X_CLK_OUT_62_5MHZ_DSP 5 +#define AT803X_CLK_OUT_125MHZ_PLL 6 +#define AT803X_CLK_OUT_125MHZ_DSP 7 + +/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask + * but doesn't support choosing between XTAL/PLL and DSP. + */ +#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 +#define AT803X_CLK_OUT_STRENGTH_QUARTER 2 + +#define ATH9331_PHY_ID 0x004dd041 +#define ATH8030_PHY_ID 0x004dd076 +#define ATH8031_PHY_ID 0x004dd074 +#define ATH8032_PHY_ID 0x004dd023 +#define ATH8035_PHY_ID 0x004dd072 +#define AT8030_PHY_ID_MASK 0xffffffef + +struct at803x_priv { + u16 clk_25m_reg; + u16 clk_25m_mask; +}; + +static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) { int ret; - ret = genphy_config_init(phydev); + ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); if (ret < 0) return ret; - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { - ret = phy_write(phydev, AT803X_DEBUG_ADDR, - AT803X_DEBUG_SYSTEM_MODE_CTRL); - if (ret) - return ret; - ret = phy_write(phydev, AT803X_DEBUG_DATA, - AT803X_DEBUG_RGMII_TX_CLK_DLY); - if (ret) - return ret; + return phy_read(phydev, AT803X_DEBUG_DATA); +} + +static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, + u16 clear, u16 set) +{ + u16 val; + int ret; + + ret = at803x_debug_reg_read(phydev, reg); + if (ret < 0) + return ret; + + val = ret & 0xffff; + val &= ~clear; + val |= set; + + return phy_write(phydev, AT803X_DEBUG_DATA, val); +} + +static int at803x_enable_rx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0, + AT803X_DEBUG_RX_CLK_DLY_EN); +} + +static int at803x_enable_tx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0, + AT803X_DEBUG_TX_CLK_DLY_EN); +} + +static int at803x_disable_rx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, + AT803X_DEBUG_RX_CLK_DLY_EN, 0); +} + +static int at803x_disable_tx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, + 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); + + return (phydev->phy_id & drv->phy_id_mask) + == (phy_id & drv->phy_id_mask); +} + +static int at803x_parse_dt(struct phy_device *phydev) +{ + 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; + int ret; + + ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq); + if (!ret) { + switch (freq) { + case 25000000: + sel = AT803X_CLK_OUT_25MHZ_XTAL; + break; + case 50000000: + sel = AT803X_CLK_OUT_50MHZ_PLL; + break; + case 62500000: + sel = AT803X_CLK_OUT_62_5MHZ_PLL; + break; + case 125000000: + sel = AT803X_CLK_OUT_125MHZ_PLL; + break; + default: + dev_err(dev, "invalid qca,clk-out-frequency\n"); + return -EINVAL; + } + + priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); + priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; + + /* Fixup for the AR8030/AR8035. This chip has another mask and + * doesn't support the DSP reference. Eg. the lowest bit of the + * mask. The upper two bits select the same frequencies. Mask + * the lowest bit here. + * + * Warning: + * There was no datasheet for the AR8030 available so this is + * just a guess. But the AR8035 is listed as pin compatible + * to the AR8030 so there might be a good chance it works on + * the AR8030 too. + */ + if (at803x_match_phy_id(phydev, ATH8030_PHY_ID) || + at803x_match_phy_id(phydev, ATH8035_PHY_ID)) { + priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; + priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; + } } + ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); + if (!ret) { + priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK; + switch (strength) { + case AR803X_STRENGTH_FULL: + priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL; + break; + case AR803X_STRENGTH_HALF: + priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF; + break; + case AR803X_STRENGTH_QUARTER: + priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER; + break; + default: + dev_err(dev, "invalid qca,clk-out-strength\n"); + return -EINVAL; + } + } + + return 0; +} + +static int at803x_probe(struct phy_device *phydev) +{ + struct at803x_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + 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; + int val; + + if (!priv->clk_25m_mask) + return 0; + + 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(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val); + return 0; } +static int at803x_config_init(struct phy_device *phydev) +{ + int ret; + + ret = genphy_config_init(phydev); + if (ret < 0) + return ret; + + /* The RX and TX delay default is: + * after HW reset: RX delay enabled and TX delay disabled + * after SW reset: RX delay enabled, while TX delay retains the + * value before reset. + */ + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + ret = at803x_enable_rx_delay(phydev); + else + ret = at803x_disable_rx_delay(phydev); + if (ret < 0) + return ret; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + ret = at803x_enable_tx_delay(phydev); + else + ret = at803x_disable_tx_delay(phydev); + if (ret < 0) + return ret; + + 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[] = { { /* ATHEROS 8035 */ - .phy_id = 0x004dd072, - .phy_id_mask = 0xffffffef, + .phy_id = ATH8035_PHY_ID, + .phy_id_mask = AT8030_PHY_ID_MASK, .drv.name = "Atheros 8035 ethernet", + .probe = at803x_probe, .config_init = at803x_config_init, .features = PHY_GBIT_FEATURES, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, }, { /* ATHEROS 8030 */ - .phy_id = 0x004dd076, - .phy_id_mask = 0xffffffef, + .phy_id = ATH8030_PHY_ID, + .phy_id_mask = AT8030_PHY_ID_MASK, .drv.name = "Atheros 8030 ethernet", .config_init = at803x_config_init, + .probe = at803x_probe, .features = PHY_GBIT_FEATURES, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, }, { /* ATHEROS 8031 */ - .phy_id = 0x004dd074, - .phy_id_mask = 0xffffffef, + .phy_id = ATH8031_PHY_ID, + .phy_id_mask = AT8030_PHY_ID_MASK, .drv.name = "Atheros 8031 ethernet", + .probe = at803x_probe, .config_init = at803x_config_init, .features = PHY_GBIT_FEATURES, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, } }; -static int atheros_phy_init(void) -{ - return phy_drivers_register(at803x_driver, - ARRAY_SIZE(at803x_driver)); -} -fs_initcall(atheros_phy_init); +device_phy_drivers(at803x_driver); diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c index febaffa52c..794e5f2c96 100644 --- a/drivers/net/phy/davicom.c +++ b/drivers/net/phy/davicom.c @@ -127,9 +127,4 @@ static struct phy_driver dm91xx_driver[] = { .features = PHY_BASIC_FEATURES, } }; -static int dm9161_init(void) -{ - return phy_drivers_register(dm91xx_driver, - ARRAY_SIZE(dm91xx_driver)); -} -fs_initcall(dm9161_init); +device_phy_drivers(dm91xx_driver); 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"); diff --git a/drivers/net/phy/dp83td510.c b/drivers/net/phy/dp83td510.c new file mode 100644 index 0000000000..44c551e795 --- /dev/null +++ b/drivers/net/phy/dp83td510.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <common.h> +#include <linux/phy.h> + +#define DP83TD510E_PHY_ID 0x20000181 + +#define DP83TD510E_PHY_STS 0x10 +#define DP83TD510E_LINK_STATUS BIT(0) + +static int dp83td510_read_status(struct phy_device *phydev) +{ + u16 phy_sts; + + phy_sts = phy_read(phydev, DP83TD510E_PHY_STS); + + phydev->link = !!(phy_sts & DP83TD510E_LINK_STATUS); + if (phydev->link) { + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_10; + } else { + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + } + + return 0; +} + +static int dp83td510_config_init(struct phy_device *phydev) +{ + phydev->supported = SUPPORTED_10baseT_Full | SUPPORTED_Autoneg; + phydev->advertising = SUPPORTED_10baseT_Full | SUPPORTED_Autoneg; + + return 0; +} + +static struct phy_driver dp83td510_driver[] = { + { + PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID), + .drv.name = "TI DP83TD510E", + .read_status = dp83td510_read_status, + .config_init = dp83td510_config_init, + } +}; +device_phy_drivers(dp83td510_driver); 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/lxt.c b/drivers/net/phy/lxt.c index b661ae7316..9b023c8c40 100644 --- a/drivers/net/phy/lxt.c +++ b/drivers/net/phy/lxt.c @@ -19,9 +19,4 @@ static struct phy_driver lxt97x_driver[] = { .features = PHY_BASIC_FEATURES, } }; -static int lxt97x_phy_init(void) -{ - return phy_drivers_register(lxt97x_driver, - ARRAY_SIZE(lxt97x_driver)); -} -fs_initcall(lxt97x_phy_init); +device_phy_drivers(lxt97x_driver); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 73d6453b36..c0b819b109 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/net/phy/marvell.c * @@ -168,12 +169,6 @@ static int marvell_read_status(struct phy_device *phydev) #define MII_88E1510_GEN_CTRL_REG_1 0x14 -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; -}; - /* * Set and/or override some configuration registers based on the * marvell,reg-init property stored in the of_node for the phydev. @@ -193,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; @@ -739,9 +734,4 @@ static struct phy_driver marvell_drivers[] = { }, }; -static int __init marvell_phy_init(void) -{ - return phy_drivers_register(marvell_drivers, - ARRAY_SIZE(marvell_drivers)); -} -fs_initcall(marvell_phy_init); +device_phy_drivers(marvell_drivers); 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 affa31ae2c..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,25 +50,25 @@ 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 inforamtion from DT\n"); + dev_dbg(dev, "failed to get MDC information from DT\n"); goto free_info; } 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 inforamtion from DT\n"); + dev_dbg(dev, "failed to get MDIO information from DT\n"); goto free_info; } 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 3480e2ffb4..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; @@ -239,6 +307,8 @@ int mdiobus_register(struct mii_bus *bus) return -EINVAL; } + slice_init(&bus->slice, dev_name(&bus->dev)); + if (bus->reset) bus->reset(bus); @@ -246,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; @@ -272,6 +340,8 @@ void mdiobus_unregister(struct mii_bus *bus) bus->phy_map[i] = NULL; } + slice_exit(&bus->slice); + list_del(&bus->list); } EXPORT_SYMBOL(mdiobus_unregister); @@ -329,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; @@ -345,18 +415,61 @@ 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; } +/** + * mdiobus_read - Convenience function for reading a given MII mgmt register + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to read + */ +int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) +{ + int ret; + + slice_acquire(&bus->slice); + + ret = bus->read(bus, addr, regnum); + + slice_release(&bus->slice); + + return ret; +} + +/** + * mdiobus_write - Convenience function for writing a given MII mgmt register + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to write + * @val: value to write to @regnum + */ +int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) +{ + int ret; + + slice_acquire(&bus->slice); + + ret = bus->write(bus, addr, regnum, val); + + slice_release(&bus->slice); + + return ret; +} + static ssize_t phydev_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags) { int i = count; @@ -396,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)) @@ -427,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); @@ -481,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 8f0b81d8fa..a203669353 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -14,24 +14,31 @@ #include <common.h> #include <init.h> +#include <linux/clk.h> #include <linux/mii.h> #include <linux/ethtool.h> #include <linux/phy.h> +#include <linux/mdio.h> #include <linux/micrel_phy.h> +#include <linux/bitfield.h> /* Operation Mode Strap Override */ #define MII_KSZPHY_OMSO 0x16 #define KSZPHY_OMSO_B_CAST_OFF BIT(9) +#define KSZPHY_OMSO_NAND_TREE_ON BIT(5) #define KSZPHY_OMSO_RMII_OVERRIDE BIT(1) #define KSZPHY_OMSO_MII_OVERRIDE BIT(0) /* general PHY control reg in vendor specific block. */ -#define MII_KSZPHY_CTRL 0x1F +#define MII_KSZPHY_CTRL 0x1f /* bitmap of PHY register to set interrupt mode */ #define KSZPHY_CTRL_INT_ACTIVE_HIGH BIT(9) #define KSZ9021_CTRL_INT_ACTIVE_HIGH BIT(14) #define KS8737_CTRL_INT_ACTIVE_HIGH BIT(14) -#define KSZ8051_RMII_50MHZ_CLK BIT(7) +#define KSZPHY_RMII_REF_CLK_SEL BIT(7) + +/* PHY Control 1 */ +#define MII_KSZPHY_CTRL_1 0x1e /* Write/read to/from extended registers */ #define MII_KSZPHY_EXTREG 0x0b @@ -47,6 +54,47 @@ #define PS_TO_REG 200 +struct kszphy_type { + u32 led_mode_reg; + bool has_broadcast_disable; + bool has_nand_tree_disable; + bool has_rmii_ref_clk_sel; +}; + +struct kszphy_priv { + const struct kszphy_type *type; + int led_mode; + bool rmii_ref_clk_sel; + bool rmii_ref_clk_sel_val; +}; + +static const struct kszphy_type ksz8001_type = { + .led_mode_reg = MII_KSZPHY_CTRL_1, +}; + +static const struct kszphy_type ksz8021_type = { + .led_mode_reg = MII_KSZPHY_CTRL, + .has_broadcast_disable = true, + .has_nand_tree_disable = true, + .has_rmii_ref_clk_sel = true, +}; + +static const struct kszphy_type ksz8041_type = { + .led_mode_reg = MII_KSZPHY_CTRL_1, +}; + +static const struct kszphy_type ksz8051_type = { + .led_mode_reg = MII_KSZPHY_CTRL, + .has_nand_tree_disable = true, +}; + +static const struct kszphy_type ksz8081_type = { + .led_mode_reg = MII_KSZPHY_CTRL, + .has_broadcast_disable = true, + .has_nand_tree_disable = true, + .has_rmii_ref_clk_sel = true, +}; + static int kszphy_extended_write(struct phy_device *phydev, u32 regnum, u16 val) { @@ -61,35 +109,152 @@ static int kszphy_extended_read(struct phy_device *phydev, return phy_read(phydev, MII_KSZPHY_EXTREG_READ); } -static int kszphy_config_init(struct phy_device *phydev) +static int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val) { + int ctrl; + + ctrl = phy_read(phydev, MII_KSZPHY_CTRL); + if (ctrl < 0) + return ctrl; + + if (val) + ctrl |= KSZPHY_RMII_REF_CLK_SEL; + else + ctrl &= ~KSZPHY_RMII_REF_CLK_SEL; + + return phy_write(phydev, MII_KSZPHY_CTRL, ctrl); +} + +/* 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 *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)) { + if (val > 0x03) { + dev_err(dev, "led-mode 0x%02x out of range\n", val); + return -1; + } + return phy_modify(phydev, reg, 0x03 << shift, val << shift); + } return 0; } -static int ksz8021_config_init(struct phy_device *phydev) +static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val) { - u16 val; + const struct device *dev = &phydev->dev; + int rc, temp, shift; + + switch (reg) { + case MII_KSZPHY_CTRL_1: + shift = 14; + break; + case MII_KSZPHY_CTRL: + shift = 4; + break; + default: + return -EINVAL; + } + + temp = phy_read(phydev, reg); + if (temp < 0) { + rc = temp; + goto out; + } - val = phy_read(phydev, MII_KSZPHY_OMSO); - val |= KSZPHY_OMSO_B_CAST_OFF; - phy_write(phydev, MII_KSZPHY_OMSO, val); + temp &= ~(3 << shift); + temp |= val << shift; + rc = phy_write(phydev, reg, temp); +out: + if (rc < 0) + dev_err(dev, "failed to set led mode\n"); - return 0; + return rc; } -static int ks8051_config_init(struct phy_device *phydev) +/* Disable PHY address 0 as the broadcast address, so that it can be used as a + * unique (non-broadcast) address on a shared bus. + */ +static int kszphy_broadcast_disable(struct phy_device *phydev) { - int regval; + const struct device *dev = &phydev->dev; + int ret; + + ret = phy_read(phydev, MII_KSZPHY_OMSO); + if (ret < 0) + goto out; - if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) { - regval = phy_read(phydev, MII_KSZPHY_CTRL); - regval |= KSZ8051_RMII_50MHZ_CLK; - phy_write(phydev, MII_KSZPHY_CTRL, regval); + ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF); +out: + if (ret) + dev_err(dev, "failed to disable broadcast address\n"); + + return ret; +} + +static int kszphy_nand_tree_disable(struct phy_device *phydev) +{ + const struct device *dev = &phydev->dev; + int ret; + + ret = phy_read(phydev, MII_KSZPHY_OMSO); + if (ret < 0) + goto out; + + if (!(ret & KSZPHY_OMSO_NAND_TREE_ON)) + return 0; + + ret = phy_write(phydev, MII_KSZPHY_OMSO, + ret & ~KSZPHY_OMSO_NAND_TREE_ON); +out: + if (ret) + dev_err(dev, "failed to disable NAND tree mode\n"); + + return ret; +} + +/* Some config bits need to be set again on resume, handle them here. */ +static int kszphy_config_reset(struct phy_device *phydev) +{ + struct kszphy_priv *priv = phydev->priv; + int ret; + + if (priv->rmii_ref_clk_sel) { + ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val); + if (ret) { + dev_err(&phydev->dev, + "failed to set rmii reference clock\n"); + return ret; + } } + if (priv->led_mode >= 0) + kszphy_setup_led(phydev, priv->type->led_mode_reg, priv->led_mode); + return 0; } +static int kszphy_config_init(struct phy_device *phydev) +{ + struct kszphy_priv *priv = phydev->priv; + const struct kszphy_type *type; + + if (!priv) + return 0; + + type = priv->type; + + if (type->has_broadcast_disable) + kszphy_broadcast_disable(phydev); + + if (type->has_nand_tree_disable) + kszphy_nand_tree_disable(phydev); + + return kszphy_config_reset(phydev); +} + static int ksz9021_load_values_from_of(struct phy_device *phydev, const struct device_node *of_node, u16 reg, const char *field[]) @@ -113,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" @@ -128,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, @@ -141,6 +306,8 @@ static int ksz9021_config_init(struct phy_device *phydev) ksz9021_load_values_from_of(phydev, of_node, MII_KSZPHY_TX_DATA_PAD_SKEW, tx_pad_skew_names); + + kszphy_led_mode(phydev, 0x11, 6); } return 0; @@ -155,9 +322,50 @@ static int ksz9021_config_init(struct phy_device *phydev) /* MMD Address 0x2 */ #define MII_KSZ9031RN_CONTROL_PAD_SKEW 4 +#define MII_KSZ9031RN_RX_CTL_M GENMASK(7, 4) +#define MII_KSZ9031RN_TX_CTL_M GENMASK(3, 0) + #define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5 +#define MII_KSZ9031RN_RXD3 GENMASK(15, 12) +#define MII_KSZ9031RN_RXD2 GENMASK(11, 8) +#define MII_KSZ9031RN_RXD1 GENMASK(7, 4) +#define MII_KSZ9031RN_RXD0 GENMASK(3, 0) + #define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6 +#define MII_KSZ9031RN_TXD3 GENMASK(15, 12) +#define MII_KSZ9031RN_TXD2 GENMASK(11, 8) +#define MII_KSZ9031RN_TXD1 GENMASK(7, 4) +#define MII_KSZ9031RN_TXD0 GENMASK(3, 0) + #define MII_KSZ9031RN_CLK_PAD_SKEW 8 +#define MII_KSZ9031RN_GTX_CLK GENMASK(9, 5) +#define MII_KSZ9031RN_RX_CLK GENMASK(4, 0) + +/* KSZ9031 has internal RGMII_IDRX = 1.2ns and RGMII_IDTX = 0ns. To + * provide different RGMII options we need to configure delay offset + * for each pad relative to build in delay. + */ +/* keep rx as "No delay adjustment" and set rx_clk to +0.60ns to get delays of + * 1.80ns + */ +#define RX_ID 0x7 +#define RX_CLK_ID 0x19 + +/* set rx to +0.30ns and rx_clk to -0.90ns to compensate the + * internal 1.2ns delay. + */ +#define RX_ND 0xc +#define RX_CLK_ND 0x0 + +/* set tx to -0.42ns and tx_clk to +0.96ns to get 1.38ns delay */ +#define TX_ID 0x0 +#define TX_CLK_ID 0x1f + +/* set tx and tx_clk to "No delay adjustment" to keep 0ns + * dealy + */ +#define TX_ND 0x7 +#define TX_CLK_ND 0xf static int ksz9031_of_load_skew_values(struct phy_device *phydev, const struct device_node *of_node, @@ -179,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, 2); + newval = phy_read_mmd(phydev, MDIO_MMD_WIS, reg); else newval = 0; @@ -193,23 +401,78 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev, << (field_sz * i)); } - phy_write_mmd_indirect(phydev, reg, 2, 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); } +static int ksz9031_config_rgmii_delay(struct phy_device *phydev) +{ + u16 rx, tx, rx_clk, tx_clk; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + tx = TX_ND; + tx_clk = TX_CLK_ND; + rx = RX_ND; + rx_clk = RX_CLK_ND; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + tx = TX_ID; + tx_clk = TX_CLK_ID; + rx = RX_ID; + rx_clk = RX_CLK_ID; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + tx = TX_ND; + tx_clk = TX_CLK_ND; + rx = RX_ID; + rx_clk = RX_CLK_ID; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + tx = TX_ID; + tx_clk = TX_CLK_ID; + rx = RX_ND; + rx_clk = RX_CLK_ND; + break; + default: + return 0; + } + + 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", @@ -222,10 +485,16 @@ 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)) { + ret = ksz9031_config_rgmii_delay(phydev); + if (ret < 0) + return ret; + } + ksz9031_of_load_skew_values(phydev, of_node, MII_KSZ9031RN_CLK_PAD_SKEW, 5, clk_skews, 2); @@ -277,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) @@ -285,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); @@ -340,13 +789,66 @@ static int ksz8873mll_config_init(struct phy_device *phydev) return 0; } +static int kszphy_probe(struct phy_device *phydev) +{ + 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; + struct clk *clk; + int ret; + + priv = xzalloc(sizeof(*priv)); + + phydev->priv = priv; + + priv->type = type; + + if (type->led_mode_reg) { + ret = of_property_read_u32(np, "micrel,led-mode", + &priv->led_mode); + if (ret) + priv->led_mode = -1; + + if (priv->led_mode > 3) { + dev_err(dev, "invalid led mode: 0x%02x\n", + priv->led_mode); + priv->led_mode = -1; + } + } else { + priv->led_mode = -1; + } + + clk = clk_get(dev, "rmii-ref"); + /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */ + if (!IS_ERR_OR_NULL(clk)) { + unsigned long rate = clk_get_rate(clk); + bool rmii_ref_clk_sel_25_mhz; + + priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel; + rmii_ref_clk_sel_25_mhz = of_property_read_bool(np, + "micrel,rmii-reference-clock-select-25-mhz"); + + if (rate > 24500000 && rate < 25500000) { + priv->rmii_ref_clk_sel_val = rmii_ref_clk_sel_25_mhz; + } else if (rate > 49500000 && rate < 50500000) { + priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz; + } else { + dev_err(dev, "Clock rate out of range: %ld\n", rate); + return -EINVAL; + } + } + + return 0; +} + static struct phy_driver ksphy_driver[] = { { .phy_id = PHY_ID_KS8737, .phy_id_mask = 0x00fffff0, .drv.name = "Micrel KS8737", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), - .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { @@ -355,7 +857,9 @@ static struct phy_driver ksphy_driver[] = { .drv.name = "Micrel KSZ8021", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), - .config_init = ksz8021_config_init, + .driver_data = &ksz8021_type, + .probe = kszphy_probe, + .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { @@ -364,7 +868,9 @@ static struct phy_driver ksphy_driver[] = { .drv.name = "Micrel KSZ8031", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), - .config_init = ksz8021_config_init, + .driver_data = &ksz8021_type, + .probe = kszphy_probe, + .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { @@ -373,6 +879,8 @@ static struct phy_driver ksphy_driver[] = { .drv.name = "Micrel KSZ8041", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), + .driver_data = &ksz8041_type, + .probe = kszphy_probe, .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, @@ -382,22 +890,28 @@ static struct phy_driver ksphy_driver[] = { .drv.name = "Micrel KSZ8051", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), - .config_init = ks8051_config_init, + .driver_data = &ksz8051_type, + .probe = kszphy_probe, + .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { .phy_id = PHY_ID_KSZ8081, .phy_id_mask = MICREL_PHY_ID_MASK, .drv.name = "Micrel KSZ8081/91", + .driver_data = &ksz8081_type, .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), - .config_init = ksz8021_config_init, + .probe = kszphy_probe, + .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { .phy_id = PHY_ID_KSZ8001, - .drv.name = "Micrel KSZ8001 or KS8721", .phy_id_mask = 0x00ffffff, + .drv.name = "Micrel KSZ8001 or KS8721", + .driver_data = &ksz8001_type, .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), + .probe = kszphy_probe, .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, @@ -424,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", @@ -433,9 +955,4 @@ static struct phy_driver ksphy_driver[] = { .read_status = ksz8873mll_read_status, } }; -static int ksphy_init(void) -{ - return phy_drivers_register(ksphy_driver, - ARRAY_SIZE(ksphy_driver)); -} -fs_initcall(ksphy_init); +device_phy_drivers(ksphy_driver); 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/Makefile b/drivers/net/phy/mv88e6xxx/Makefile index e1d4b1b9d7..4f569509e5 100644 --- a/drivers/net/phy/mv88e6xxx/Makefile +++ b/drivers/net/phy/mv88e6xxx/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-y += mv88e6xxx.o mv88e6xxx-objs := chip.o diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c index b1bffe5cbc..b9b02c52f2 100644 --- a/drivers/net/phy/mv88e6xxx/chip.c +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <linux/mii.h> @@ -35,6 +36,7 @@ enum mv88e6xxx_model { MV88E6190X, MV88E6191, MV88E6240, + MV88E6250, MV88E6290, MV88E6320, MV88E6321, @@ -223,6 +225,18 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_link_state = mv88e6352_port_link_state, }; +static const struct mv88e6xxx_ops mv88e6250_ops = { + /* MV88E6XXX_FAMILY_6250 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6250_port_set_speed, +}; + static const struct mv88e6xxx_ops mv88e6290_ops = { /* MV88E6XXX_FAMILY_6390 */ .get_eeprom = mv88e6xxx_g2_get_eeprom8, @@ -524,6 +538,17 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .ops = &mv88e6240_ops, }, + [MV88E6250] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6250, + .family = MV88E6XXX_FAMILY_6250, + .name = "Marvell 88E6250", + .num_ports = 7, + .port_base_addr = 0x08, + .global1_addr = 0xf, + .global2_addr = 0x7, + .ops = &mv88e6250_ops, + }, + [MV88E6290] = { .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6290, .family = MV88E6XXX_FAMILY_6390, @@ -779,10 +804,9 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) return 0; } -static int mv88e6xxx_eeprom_read(struct device_d *dev, const int offset, - void *val, int bytes) +static int mv88e6xxx_eeprom_read(void *ctx, unsigned offset, void *val, size_t bytes) { - struct mv88e6xxx_chip *chip = dev->parent->priv; + struct mv88e6xxx_chip *chip = ctx; struct ethtool_eeprom eeprom = { .offset = offset, .len = bytes, @@ -794,10 +818,9 @@ static int mv88e6xxx_eeprom_read(struct device_d *dev, const int offset, return chip->info->ops->get_eeprom(chip, &eeprom, val); } -static int mv88e6xxx_eeprom_write(struct device_d *dev, const int offset, - const void *val, int bytes) +static int mv88e6xxx_eeprom_write(void *ctx, unsigned offset, const void *val, size_t bytes) { - struct mv88e6xxx_chip *chip = dev->parent->priv; + struct mv88e6xxx_chip *chip = ctx; struct ethtool_eeprom eeprom = { .offset = offset, .len = bytes, @@ -809,14 +832,9 @@ static int mv88e6xxx_eeprom_write(struct device_d *dev, const int offset, 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; @@ -883,11 +901,13 @@ static int mv88e6xxx_probe(struct device_d *dev) struct nvmem_config config = { .name = basprintf("%s-eeprom", dev_name(dev)), .dev = dev, + .priv = chip, .word_size = 1, .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))) @@ -917,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) @@ -932,13 +952,18 @@ static const struct of_device_id mv88e6xxx_of_match[] = { .data = &mv88e6xxx_table[MV88E6085], }, { + .compatible = "marvell,mv88e6250", + .data = &mv88e6xxx_table[MV88E6250], + }, + { .compatible = "marvell,mv88e6190", .data = &mv88e6xxx_table[MV88E6190], }, {}, }; +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 57f74a39a0..aec6c2891f 100644 --- a/drivers/net/phy/mv88e6xxx/chip.h +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef _MV88E6XXX_CHIP_H #define _MV88E6XXX_CHIP_H @@ -18,6 +19,7 @@ enum mv88e6xxx_family { MV88E6XXX_FAMILY_6165, /* 6123 6161 6165 */ MV88E6XXX_FAMILY_6185, /* 6108 6121 6122 6131 6152 6155 6182 6185 */ MV88E6XXX_FAMILY_6320, /* 6320 6321 */ + MV88E6XXX_FAMILY_6250, /* 6250 */ MV88E6XXX_FAMILY_6341, /* 6141 6341 */ MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */ @@ -48,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/global2.c b/drivers/net/phy/mv88e6xxx/global2.c index 970a7291e7..2728a66eea 100644 --- a/drivers/net/phy/mv88e6xxx/global2.c +++ b/drivers/net/phy/mv88e6xxx/global2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <linux/ethtool.h> #include <linux/bitfield.h> diff --git a/drivers/net/phy/mv88e6xxx/global2.h b/drivers/net/phy/mv88e6xxx/global2.h index 4e23b04232..0f2dc53c29 100644 --- a/drivers/net/phy/mv88e6xxx/global2.h +++ b/drivers/net/phy/mv88e6xxx/global2.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef _MV88E6XXX_GLOBAL2_H #define _MV88E6XXX_GLOBAL2_H diff --git a/drivers/net/phy/mv88e6xxx/port.c b/drivers/net/phy/mv88e6xxx/port.c index 52f95d622c..29ea4ec882 100644 --- a/drivers/net/phy/mv88e6xxx/port.c +++ b/drivers/net/phy/mv88e6xxx/port.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> @@ -276,6 +277,18 @@ int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) return mv88e6xxx_port_set_speed(chip, port, speed, false, false); } +/* Support 10, 100 (e.g. 88E6250 family) */ +int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 100; + + if (speed > 100) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) { @@ -547,21 +560,17 @@ static struct phy_driver mv88e6xxx_port_driver = { .read_status = mv88e6xxx_port_read_status, }; -static int __init mv88e6xxx_port_driver_register(void) -{ - return phy_driver_register(&mv88e6xxx_port_driver); -} -fs_initcall(mv88e6xxx_port_driver_register); +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; - switch_node = of_find_node_by_name(np, "ports"); + switch_node = of_find_node_by_name_address(np, "ports"); if (!switch_node) return -EINVAL; @@ -571,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; } @@ -650,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; @@ -660,4 +669,4 @@ int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip) } return 0; -}
\ No newline at end of file +} diff --git a/drivers/net/phy/mv88e6xxx/port.h b/drivers/net/phy/mv88e6xxx/port.h index 07d937ecbd..4bc5072948 100644 --- a/drivers/net/phy/mv88e6xxx/port.h +++ b/drivers/net/phy/mv88e6xxx/port.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef _MV88E6XXX_PORT_H #define _MV88E6XXX_PORT_H @@ -89,6 +90,7 @@ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6250 0x2500 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6290 0x2900 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6321 0x3100 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6141 0x3400 @@ -120,6 +122,7 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c index 83390b99ab..d74cd81933 100644 --- a/drivers/net/phy/national.c +++ b/drivers/net/phy/national.c @@ -84,8 +84,4 @@ static struct phy_driver dp83865_driver = { .config_init = ns_config_init, }; -static int ns_phy_init(void) -{ - return phy_driver_register(&dp83865_driver); -} -fs_initcall(ns_phy_init); +device_phy_driver(dp83865_driver); diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 21daaa9a2b..ab52de35b5 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <linux/phy.h> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 57c2f8044f..abd78b2c80 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -18,11 +18,8 @@ #include <net.h> #include <malloc.h> #include <linux/phy.h> -#include <linux/phy.h> #include <linux/err.h> -#define PHY_AN_TIMEOUT 10 - static struct phy_driver genphy_driver; /** @@ -54,6 +51,13 @@ int phy_update_status(struct phy_device *phydev) return ret; } + /* + * If the phy is a fixed-link, set it to active state to trigger + * MAC configuration + */ + if (!phydev->bus && !phydev->link) + phydev->link = 1; + if (phydev->speed == oldspeed && phydev->duplex == oldduplex && phydev->link == oldlink) return 0; @@ -302,8 +306,8 @@ void phy_unregister_device(struct phy_device *phydev) phydev->registered = 0; } -static struct phy_device *of_phy_register_fixed_link(struct device_node *np, - struct eth_device *edev) +struct phy_device *of_phy_register_fixed_link(struct device_node *np, + struct eth_device *edev) { struct phy_device *phydev; @@ -311,7 +315,7 @@ static struct phy_device *of_phy_register_fixed_link(struct device_node *np, phydev->dev.parent = &edev->dev; phydev->registered = 1; - phydev->link = 1; + phydev->link = 0; if (of_property_read_u32(np, "speed", &phydev->speed)) return NULL; @@ -324,7 +328,7 @@ static 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; @@ -332,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); } @@ -350,8 +356,9 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev) return NULL; 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; @@ -360,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); } @@ -400,10 +407,6 @@ static int phy_device_attach(struct phy_device *phy, struct eth_device *edev, phy->adjust_link = adjust_link; - /* If the phy is a fixed-link, then call adjust_link directly */ - if (!phy->bus && adjust_link) - adjust_link(edev); - return 0; } @@ -459,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; } @@ -571,7 +574,7 @@ int phy_wait_aneg_done(struct phy_device *phydev) } do { - genphy_update_link(phydev); + phy_update_status(phydev); if (phydev->link == 1) return 0; } while (!is_timeout(start, PHY_AN_TIMEOUT * SECOND)); @@ -672,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); @@ -816,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)); @@ -847,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); @@ -873,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; @@ -930,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; @@ -981,8 +1127,4 @@ static struct phy_driver genphy_driver = { SUPPORTED_BNC, }; -static int generic_phy_register(void) -{ - return phy_driver_register(&genphy_driver); -} -device_initcall(generic_phy_register); +device_phy_driver(genphy_driver); diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 4ae050128c..c23947b7cb 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * drivers/net/phy/realtek.c * @@ -6,12 +7,6 @@ * Author: Johnson Leung <r58129@freescale.com> * * Copyright (c) 2004 Freescale Semiconductor, 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, or (at your - * option) any later version. - * */ #include <common.h> #include <init.h> @@ -34,6 +29,7 @@ #define RTL8211F_INSR 0x1d #define RTL8211F_TX_DELAY BIT(8) +#define RTL8211F_RX_DELAY BIT(3) #define RTL8201F_ISR 0x1e #define RTL8201F_IER 0x13 @@ -84,19 +80,50 @@ static int rtl8211c_config_init(struct phy_device *phydev) static int rtl8211f_config_init(struct phy_device *phydev) { + struct device *dev = &phydev->dev; + u16 val_txdly, val_rxdly; int ret; - u16 val = 0; - ret = genphy_config_init(phydev); - if (ret < 0) + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + val_txdly = 0; + val_rxdly = 0; + break; + + case PHY_INTERFACE_MODE_RGMII_RXID: + val_txdly = 0; + val_rxdly = RTL8211F_RX_DELAY; + break; + + case PHY_INTERFACE_MODE_RGMII_TXID: + val_txdly = RTL8211F_TX_DELAY; + val_rxdly = 0; + break; + + case PHY_INTERFACE_MODE_RGMII_ID: + val_txdly = RTL8211F_TX_DELAY; + val_rxdly = RTL8211F_RX_DELAY; + break; + + default: /* the rest of the modes imply leaving delay as is. */ + return 0; + } + + ret = phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, + val_txdly); + if (ret < 0) { + dev_err(dev, "Failed to update the TX delay register\n"); return ret; + } - /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */ - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) - val = RTL8211F_TX_DELAY; + ret = phy_modify_paged(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY, + val_rxdly); + if (ret < 0) { + dev_err(dev, "Failed to update the RX delay register\n"); + return ret; + } - return phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, val); + return 0; } static int rtl8366rb_config_init(struct phy_device *phydev) @@ -128,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, @@ -164,9 +197,4 @@ static struct phy_driver realtek_drvs[] = { }, }; -static int __init realtek_phy_init(void) -{ - return phy_drivers_register(realtek_drvs, - ARRAY_SIZE(realtek_drvs)); -} -fs_initcall(realtek_phy_init); +device_phy_drivers(realtek_drvs); diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index d6705e4fe2..1e1f3d5274 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -135,8 +135,4 @@ static struct phy_driver smsc_phy_driver[] = { .config_init = lan87xx_config_init, } }; -static int __init smsc_init(void) -{ - return phy_drivers_register(smsc_phy_driver, ARRAY_SIZE(smsc_phy_driver)); -} -fs_initcall(smsc_init); +device_phy_drivers(smsc_phy_driver); |