Message ID | 20211222075803.211141-1-christian.gmeiner@gmail.com |
---|---|
State | Accepted |
Commit | f3e22eea815d99f025aed505eefeffbbf699f845 |
Delegated to: | Ramon Fried |
Headers | show |
Series | net: phy: add TI DP83869HM ethernet driver | expand |
On Wed, Dec 22, 2021 at 9:58 AM Christian Gmeiner <christian.gmeiner@gmail.com> wrote: > > From: Dominic Rath <rath@ibv-augsburg.net> > > This driver is based on an older downstream TI kernel, with > changes and cleanups to work with mainline device-tree bindings. > > Signed-off-by: Dominic Rath <rath@ibv-augsburg.net> > Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com> > --- > drivers/net/phy/Kconfig | 6 + > drivers/net/phy/Makefile | 1 + > drivers/net/phy/dp83869.c | 507 +++++++++++++++++++++++++++ > drivers/net/phy/ti_phy_init.c | 4 + > drivers/net/phy/ti_phy_init.h | 1 + > include/dt-bindings/net/ti-dp83869.h | 60 ++++ > 6 files changed, 579 insertions(+) > create mode 100644 drivers/net/phy/dp83869.c > create mode 100644 include/dt-bindings/net/ti-dp83869.h > > diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig > index 68ee7d7a2d..c6e58058d9 100644 > --- a/drivers/net/phy/Kconfig > +++ b/drivers/net/phy/Kconfig > @@ -290,6 +290,12 @@ config PHY_TI_DP83867 > ---help--- > Adds support for the TI DP83867 1Gbit PHY. > > +config PHY_TI_DP83869 > + select PHY_TI > + bool "Texas Instruments Ethernet DP83869 PHY support" > + ---help--- > + Adds support for the TI DP83869 1Gbit PHY. > + > config PHY_TI_GENERIC > select PHY_TI > bool "Texas Instruments Generic Ethernet PHYs support" > diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile > index 218b8c7669..77f7f60621 100644 > --- a/drivers/net/phy/Makefile > +++ b/drivers/net/phy/Makefile > @@ -29,6 +29,7 @@ obj-$(CONFIG_PHY_SMSC) += smsc.o > obj-$(CONFIG_PHY_TERANETICS) += teranetics.o > obj-$(CONFIG_PHY_TI) += ti_phy_init.o > obj-$(CONFIG_PHY_TI_DP83867) += dp83867.o > +obj-$(CONFIG_PHY_TI_DP83869) += dp83869.o > obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o > obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o > obj-$(CONFIG_PHY_VITESSE) += vitesse.o > diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c > new file mode 100644 > index 0000000000..c9461185cf > --- /dev/null > +++ b/drivers/net/phy/dp83869.c > @@ -0,0 +1,507 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * TI PHY drivers > + * > + */ > + > +#include <common.h> > +#include <phy.h> > +#include <linux/compat.h> > +#include <malloc.h> > + > +#include <dm.h> > +#include <dt-bindings/net/ti-dp83869.h> > + > +/* TI DP83869 */ > +#define DP83869_DEVADDR 0x1f > + > +#define MII_DP83869_PHYCTRL 0x10 > +#define MII_DP83869_MICR 0x12 > +#define MII_DP83869_CFG2 0x14 > +#define MII_DP83869_BISCR 0x16 > +#define DP83869_CTRL 0x1f > +#define DP83869_CFG4 0x1e > + > +/* Extended Registers */ > +#define DP83869_GEN_CFG3 0x0031 > +#define DP83869_RGMIICTL 0x0032 > +#define DP83869_STRAP_STS1 0x006E > +#define DP83869_RGMIIDCTL 0x0086 > +#define DP83869_IO_MUX_CFG 0x0170 > +#define DP83869_OP_MODE 0x01df > +#define DP83869_FX_CTRL 0x0c00 > + > +#define DP83869_SW_RESET BIT(15) > +#define DP83869_SW_RESTART BIT(14) > + > +/* MICR Interrupt bits */ > +#define MII_DP83869_MICR_AN_ERR_INT_EN BIT(15) > +#define MII_DP83869_MICR_SPEED_CHNG_INT_EN BIT(14) > +#define MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN BIT(13) > +#define MII_DP83869_MICR_PAGE_RXD_INT_EN BIT(12) > +#define MII_DP83869_MICR_AUTONEG_COMP_INT_EN BIT(11) > +#define MII_DP83869_MICR_LINK_STS_CHNG_INT_EN BIT(10) > +#define MII_DP83869_MICR_FALSE_CARRIER_INT_EN BIT(8) > +#define MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN BIT(4) > +#define MII_DP83869_MICR_WOL_INT_EN BIT(3) > +#define MII_DP83869_MICR_XGMII_ERR_INT_EN BIT(2) > +#define MII_DP83869_MICR_POL_CHNG_INT_EN BIT(1) > +#define MII_DP83869_MICR_JABBER_INT_EN BIT(0) > + > +#define MII_DP83869_BMCR_DEFAULT (BMCR_ANENABLE | \ > + BMCR_FULLDPLX | \ > + BMCR_SPEED1000) > + > +/* This is the same bit mask as the BMCR so re-use the BMCR default */ > +#define DP83869_FX_CTRL_DEFAULT MII_DP83869_BMCR_DEFAULT > + > +/* CFG1 bits */ > +#define DP83869_CFG1_DEFAULT (ADVERTISE_1000HALF | \ > + ADVERTISE_1000FULL | \ > + CTL1000_AS_MASTER) > + > +/* RGMIICTL bits */ > +#define DP83869_RGMII_TX_CLK_DELAY_EN BIT(1) > +#define DP83869_RGMII_RX_CLK_DELAY_EN BIT(0) > + > +/* STRAP_STS1 bits */ > +#define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0) > +#define DP83869_STRAP_STS1_RESERVED BIT(11) > +#define DP83869_STRAP_MIRROR_ENABLED BIT(12) > + > +/* PHY CTRL bits */ > +#define DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT 12 > +#define DP83869_PHYCR_RX_FIFO_DEPTH_MASK GENMASK(13, 12) > +#define DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT 14 > +#define DP83869_PHYCR_TX_FIFO_DEPTH_MASK GENMASK(15, 14) > +#define DP83869_PHYCR_RESERVED_MASK BIT(11) > +#define DP83869_PHYCR_MDI_CROSSOVER_SHIFT 5 > +#define DP83869_PHYCR_MDI_CROSSOVER_MDIX 2 > +#define DP83869_PHY_CTRL_DEFAULT 0x48 > + > +/* RGMIIDCTL bits */ > +#define DP83869_RGMII_TX_CLK_DELAY_SHIFT 4 > +#define DP83869_CLK_DELAY_DEF 7 > + > +/* CFG2 bits */ > +#define MII_DP83869_CFG2_SPEEDOPT_10EN 0x0040 > +#define MII_DP83869_CFG2_SGMII_AUTONEGEN 0x0080 > +#define MII_DP83869_CFG2_SPEEDOPT_ENH 0x0100 > +#define MII_DP83869_CFG2_SPEEDOPT_CNT 0x0800 > +#define MII_DP83869_CFG2_SPEEDOPT_INTLOW 0x2000 > +#define MII_DP83869_CFG2_MASK 0x003F > + > +/* User setting - can be taken from DTS */ > +#define DEFAULT_FIFO_DEPTH DP83869_PHYCR_FIFO_DEPTH_4_B_NIB > + > +/* IO_MUX_CFG bits */ > +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f > + > +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 > +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f > +#define DP83869_IO_MUX_CFG_CLK_O_DISABLE BIT(6) > +#define DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT 8 > +#define DP83869_IO_MUX_CFG_CLK_O_SEL_MASK \ > + GENMASK(0x1f, DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT) > + > +/* CFG3 bits */ > +#define DP83869_CFG3_PORT_MIRROR_EN BIT(0) > + > +/* OP MODE bits */ > +#define DP83869_OP_MODE_MII BIT(5) > +#define DP83869_SGMII_RGMII_BRIDGE BIT(6) > + > +enum { > + DP83869_PORT_MIRRORING_KEEP, > + DP83869_PORT_MIRRORING_EN, > + DP83869_PORT_MIRRORING_DIS, > +}; > + > +struct dp83869_private { > + int tx_fifo_depth; > + int rx_fifo_depth; > + s32 rx_int_delay; > + s32 tx_int_delay; > + int io_impedance; > + int port_mirroring; > + bool set_clk_output; > + int clk_output_sel; > + int mode; > +}; > + > +static int dp83869_readext(struct phy_device *phydev, int addr, int devad, int reg) > +{ > + return phy_read_mmd(phydev, devad, reg); > +} > + > +static int dp83869_writeext(struct phy_device *phydev, int addr, int devad, int reg, u16 val) > +{ > + return phy_write_mmd(phydev, devad, reg, val); > +} > + > +static int dp83869_config_port_mirroring(struct phy_device *phydev) > +{ > + struct dp83869_private *dp83869 = > + (struct dp83869_private *)phydev->priv; > + u16 val; > + > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_CFG4); > + > + if (dp83869->port_mirroring == DP83869_PORT_MIRRORING_EN) > + val |= DP83869_CFG3_PORT_MIRROR_EN; > + else > + val &= ~DP83869_CFG3_PORT_MIRROR_EN; > + > + phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_CFG4, val); > + > + return 0; > +} > + > +#ifdef CONFIG_DM_ETH > +static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500, > + 1750, 2000, 2250, 2500, 2750, 3000, > + 3250, 3500, 3750, 4000}; > + > +static int dp83869_set_strapped_mode(struct phy_device *phydev) > +{ > + struct dp83869_private *dp83869 = phydev->priv; > + int val; > + > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); > + if (val < 0) > + return val; > + > + dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK; > + > + return 0; > +} > + > +/** > + * dp83869_data_init - Convenience function for setting PHY specific data > + * > + * @phydev: the phy_device struct > + */ > +static int dp83869_of_init(struct phy_device *phydev) > +{ > + struct dp83869_private * const dp83869 = phydev->priv; > + const int delay_entries = ARRAY_SIZE(dp83869_internal_delay); > + int ret; > + ofnode node; > + > + node = phy_get_ofnode(phydev); > + if (!ofnode_valid(node)) > + return -EINVAL; > + > + dp83869->io_impedance = -EINVAL; > + > + /* Optional configuration, set to default if required */ > + dp83869->clk_output_sel = ofnode_read_u32_default(node, "ti,clk-output-sel", > + DP83869_CLK_O_SEL_CHN_A_RCLK); > + > + if (dp83869->clk_output_sel > DP83869_CLK_O_SEL_REF_CLK && > + dp83869->clk_output_sel != DP83869_CLK_O_SEL_OFF) > + dp83869->clk_output_sel = DP83869_CLK_O_SEL_REF_CLK; > + > + /* If operation mode is not set use setting from straps */ > + ret = ofnode_read_s32(node, "ti,op-mode", &dp83869->mode); > + if (ret == 0) { > + if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || > + dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) > + return -EINVAL; > + } else { > + ret = dp83869_set_strapped_mode(phydev); > + if (ret) > + return ret; > + } > + > + if (ofnode_read_bool(node, "ti,max-output-impedance")) > + dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX; > + else if (ofnode_read_bool(node, "ti,min-output-impedance")) > + dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN; > + > + if (ofnode_read_bool(node, "enet-phy-lane-swap")) { > + dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; > + } else { > + ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); > + > + if (ret < 0) > + return ret; > + > + if (ret & DP83869_STRAP_MIRROR_ENABLED) > + dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; > + else > + dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS; > + } > + > + dp83869->rx_fifo_depth = ofnode_read_s32_default(node, "rx-fifo-depth", > + DP83869_PHYCR_FIFO_DEPTH_4_B_NIB); > + > + dp83869->tx_fifo_depth = ofnode_read_s32_default(node, "tx-fifo-depth", > + DP83869_PHYCR_FIFO_DEPTH_4_B_NIB); > + > + /* 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) { > + dp83869->rx_int_delay = ofnode_read_u32_default(node, "rx-internal-delay-ps", > + DP83869_CLK_DELAY_DEF); > + if (dp83869->rx_int_delay > delay_entries) { > + dp83869->rx_int_delay = DP83869_CLK_DELAY_DEF; > + pr_debug("rx-internal-delay-ps not set/invalid, default to %ups\n", > + dp83869_internal_delay[dp83869->rx_int_delay]); > + } > + > + dp83869->rx_int_delay = dp83869_internal_delay[dp83869->rx_int_delay]; > + } > + > + /* TX 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_TXID) { > + dp83869->tx_int_delay = ofnode_read_u32_default(node, "tx-internal-delay-ps", > + DP83869_CLK_DELAY_DEF); > + if (dp83869->tx_int_delay > delay_entries) { > + dp83869->tx_int_delay = DP83869_CLK_DELAY_DEF; > + pr_debug("tx-internal-delay-ps not set/invalid, default to %ups\n", > + dp83869_internal_delay[dp83869->tx_int_delay]); > + } > + > + dp83869->tx_int_delay = dp83869_internal_delay[dp83869->tx_int_delay]; > + } > + > + return 0; > +} > +#else > +static int dp83869_of_init(struct phy_device *phydev) > +{ > + struct dp83869_private *dp83869 = phydev->priv; > + > + dp83869->rx_int_delay = DP83869_RGMIIDCTL_2_25_NS; > + dp83869->tx_int_delay = DP83869_RGMIIDCTL_2_75_NS; > + dp83869->fifo_depth = DEFAULT_FIFO_DEPTH; > + dp83869->io_impedance = -EINVAL; > + > + return 0; > +} > +#endif /* CONFIG_OF_MDIO */ > + > +static int dp83869_configure_rgmii(struct phy_device *phydev, > + struct dp83869_private *dp83869) > +{ > + int ret = 0, val; > + > + if (phy_interface_is_rgmii(phydev)) { > + val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL); > + if (val < 0) > + return val; > + > + val &= ~(DP83869_PHYCR_TX_FIFO_DEPTH_MASK | DP83869_PHYCR_RX_FIFO_DEPTH_MASK); > + val |= (dp83869->tx_fifo_depth << DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT); > + val |= (dp83869->rx_fifo_depth << DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT); > + > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, val); > + if (ret) > + return ret; > + } > + > + if (dp83869->io_impedance >= 0) { > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG); > + > + val &= ~DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL; > + val |= dp83869->io_impedance & DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL; > + > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG, val); > + > + if (ret) > + return ret; > + } > + > + return ret; > +} > + > +static int dp83869_configure_mode(struct phy_device *phydev, > + struct dp83869_private *dp83869) > +{ > + int phy_ctrl_val; > + int ret, val; > + > + if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || > + dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) > + return -EINVAL; > + > + /* Below init sequence for each operational mode is defined in > + * section 9.4.8 of the datasheet. > + */ > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, > + dp83869->mode); > + if (ret) > + return ret; > + > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, MII_DP83869_BMCR_DEFAULT); > + if (ret) > + return ret; > + > + phy_ctrl_val = (dp83869->rx_fifo_depth << DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT | > + dp83869->tx_fifo_depth << DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT | > + DP83869_PHY_CTRL_DEFAULT); > + > + switch (dp83869->mode) { > + case DP83869_RGMII_COPPER_ETHERNET: > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, > + phy_ctrl_val); > + if (ret) > + return ret; > + > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, DP83869_CFG1_DEFAULT); > + if (ret) > + return ret; > + > + ret = dp83869_configure_rgmii(phydev, dp83869); > + if (ret) > + return ret; > + break; > + case DP83869_RGMII_SGMII_BRIDGE: > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE); > + > + val |= DP83869_SGMII_RGMII_BRIDGE; > + > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, val); > + > + if (ret) > + return ret; > + > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, > + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); > + if (ret) > + return ret; > + > + break; > + case DP83869_1000M_MEDIA_CONVERT: > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, > + phy_ctrl_val); > + if (ret) > + return ret; > + > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, > + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); > + if (ret) > + return ret; > + break; > + case DP83869_100M_MEDIA_CONVERT: > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, > + phy_ctrl_val); > + if (ret) > + return ret; > + break; > + case DP83869_SGMII_COPPER_ETHERNET: > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, > + phy_ctrl_val); > + if (ret) > + return ret; > + > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, DP83869_CFG1_DEFAULT); > + if (ret) > + return ret; > + > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, > + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); > + if (ret) > + return ret; > + > + break; > + default: > + return -EINVAL; > + } > + > + return ret; > +} > + > +static int dp83869_config(struct phy_device *phydev) > +{ > + struct dp83869_private *dp83869; > + unsigned int val; > + int ret; > + > + dp83869 = (struct dp83869_private *)phydev->priv; > + > + ret = dp83869_of_init(phydev); > + if (ret) > + return ret; > + > + ret = dp83869_configure_mode(phydev, dp83869); > + if (ret) > + return ret; > + > + if (dp83869->port_mirroring != DP83869_PORT_MIRRORING_KEEP) > + dp83869_config_port_mirroring(phydev); > + > + /* Clock output selection if muxing property is set */ > + if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) { > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG); > + > + val &= ~DP83869_IO_MUX_CFG_CLK_O_SEL_MASK; > + val |= dp83869->clk_output_sel << DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT; > + > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG, val); > + > + if (ret) > + return ret; > + } > + > + if (phy_interface_is_rgmii(phydev)) { > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL, > + dp83869->rx_int_delay | > + dp83869->tx_int_delay << DP83869_RGMII_TX_CLK_DELAY_SHIFT); > + if (ret) > + return ret; > + > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL); > + val |= (DP83869_RGMII_TX_CLK_DELAY_EN | > + DP83869_RGMII_RX_CLK_DELAY_EN); > + > + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) > + val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN | > + DP83869_RGMII_RX_CLK_DELAY_EN); > + > + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) > + val &= ~DP83869_RGMII_TX_CLK_DELAY_EN; > + > + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) > + val &= ~DP83869_RGMII_RX_CLK_DELAY_EN; > + > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL, > + val); > + } > + > + genphy_config_aneg(phydev); > + return 0; > +} > + > +static int dp83869_probe(struct phy_device *phydev) > +{ > + struct dp83869_private *dp83869; > + > + dp83869 = kzalloc(sizeof(*dp83869), GFP_KERNEL); > + if (!dp83869) > + return -ENOMEM; > + > + phydev->priv = dp83869; > + return 0; > +} > + > +static struct phy_driver DP83869_driver = { > + .name = "TI DP83869", > + .uid = 0x2000a0f1, > + .mask = 0xfffffff0, > + .features = PHY_GBIT_FEATURES, > + .probe = dp83869_probe, > + .config = &dp83869_config, > + .startup = &genphy_startup, > + .shutdown = &genphy_shutdown, > + .readext = dp83869_readext, > + .writeext = dp83869_writeext > +}; > + > +int phy_dp83869_init(void) > +{ > + phy_register(&DP83869_driver); > + return 0; > +} > diff --git a/drivers/net/phy/ti_phy_init.c b/drivers/net/phy/ti_phy_init.c > index 50eff77692..075b19a39f 100644 > --- a/drivers/net/phy/ti_phy_init.c > +++ b/drivers/net/phy/ti_phy_init.c > @@ -88,6 +88,10 @@ int phy_ti_init(void) > phy_dp83867_init(); > #endif > > +#ifdef CONFIG_PHY_TI_DP83869 > + phy_dp83869_init(); > +#endif > + > #ifdef CONFIG_PHY_TI_GENERIC > phy_register(&dp83822_driver); > phy_register(&dp83825s_driver); > diff --git a/drivers/net/phy/ti_phy_init.h b/drivers/net/phy/ti_phy_init.h > index 6c7f6c640a..bcb3d320d3 100644 > --- a/drivers/net/phy/ti_phy_init.h > +++ b/drivers/net/phy/ti_phy_init.h > @@ -11,5 +11,6 @@ > #define _TI_GEN_PHY_H > > int phy_dp83867_init(void); > +int phy_dp83869_init(void); > > #endif /* _TI_GEN_PHY_H */ > diff --git a/include/dt-bindings/net/ti-dp83869.h b/include/dt-bindings/net/ti-dp83869.h > new file mode 100644 > index 0000000000..b3a5ac4a17 > --- /dev/null > +++ b/include/dt-bindings/net/ti-dp83869.h > @@ -0,0 +1,60 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * TI DP83869 PHY drivers > + * > + */ > + > +#ifndef _DT_BINDINGS_TI_DP83869_H > +#define _DT_BINDINGS_TI_DP83869_H > + > +/* PHY CTRL bits */ > +#define DP83869_PHYCR_FIFO_DEPTH_3_B_NIB 0x00 > +#define DP83869_PHYCR_FIFO_DEPTH_4_B_NIB 0x01 > +#define DP83869_PHYCR_FIFO_DEPTH_6_B_NIB 0x02 > +#define DP83869_PHYCR_FIFO_DEPTH_8_B_NIB 0x03 > + > +/* RGMIIDCTL internal delay for rx and tx */ > +#define DP83869_RGMIIDCTL_250_PS 0x0 > +#define DP83869_RGMIIDCTL_500_PS 0x1 > +#define DP83869_RGMIIDCTL_750_PS 0x2 > +#define DP83869_RGMIIDCTL_1_NS 0x3 > +#define DP83869_RGMIIDCTL_1_25_NS 0x4 > +#define DP83869_RGMIIDCTL_1_50_NS 0x5 > +#define DP83869_RGMIIDCTL_1_75_NS 0x6 > +#define DP83869_RGMIIDCTL_2_00_NS 0x7 > +#define DP83869_RGMIIDCTL_2_25_NS 0x8 > +#define DP83869_RGMIIDCTL_2_50_NS 0x9 > +#define DP83869_RGMIIDCTL_2_75_NS 0xa > +#define DP83869_RGMIIDCTL_3_00_NS 0xb > +#define DP83869_RGMIIDCTL_3_25_NS 0xc > +#define DP83869_RGMIIDCTL_3_50_NS 0xd > +#define DP83869_RGMIIDCTL_3_75_NS 0xe > +#define DP83869_RGMIIDCTL_4_00_NS 0xf > + > +/* IO_MUX_CFG - Clock output selection */ > +#define DP83869_CLK_O_SEL_CHN_A_RCLK 0x0 > +#define DP83869_CLK_O_SEL_CHN_B_RCLK 0x1 > +#define DP83869_CLK_O_SEL_CHN_C_RCLK 0x2 > +#define DP83869_CLK_O_SEL_CHN_D_RCLK 0x3 > +#define DP83869_CLK_O_SEL_CHN_A_RCLK_DIV5 0x4 > +#define DP83869_CLK_O_SEL_CHN_B_RCLK_DIV5 0x5 > +#define DP83869_CLK_O_SEL_CHN_C_RCLK_DIV5 0x6 > +#define DP83869_CLK_O_SEL_CHN_D_RCLK_DIV5 0x7 > +#define DP83869_CLK_O_SEL_CHN_A_TCLK 0x8 > +#define DP83869_CLK_O_SEL_CHN_B_TCLK 0x9 > +#define DP83869_CLK_O_SEL_CHN_C_TCLK 0xA > +#define DP83869_CLK_O_SEL_CHN_D_TCLK 0xB > +#define DP83869_CLK_O_SEL_REF_CLK 0xC > +/* Special flag to indicate clock should be off */ > +#define DP83869_CLK_O_SEL_OFF 0xFFFFFFFF > + > +/* OPMODE - Operation mode */ > +#define DP83869_RGMII_COPPER_ETHERNET 0x00 > +#define DP83869_RGMII_1000_BASE 0x01 > +#define DP83869_RGMII_100_BASE 0x02 > +#define DP83869_RGMII_SGMII_BRIDGE 0x03 > +#define DP83869_1000M_MEDIA_CONVERT 0x04 > +#define DP83869_100M_MEDIA_CONVERT 0x05 > +#define DP83869_SGMII_COPPER_ETHERNET 0x06 > + > +#endif > -- > 2.33.1 > Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
On Wed, Dec 29, 2021 at 9:26 PM Ramon Fried <rfried.dev@gmail.com> wrote: > > On Wed, Dec 22, 2021 at 9:58 AM Christian Gmeiner > <christian.gmeiner@gmail.com> wrote: > > > > From: Dominic Rath <rath@ibv-augsburg.net> > > > > This driver is based on an older downstream TI kernel, with > > changes and cleanups to work with mainline device-tree bindings. > > > > Signed-off-by: Dominic Rath <rath@ibv-augsburg.net> > > Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com> > > --- > > drivers/net/phy/Kconfig | 6 + > > drivers/net/phy/Makefile | 1 + > > drivers/net/phy/dp83869.c | 507 +++++++++++++++++++++++++++ > > drivers/net/phy/ti_phy_init.c | 4 + > > drivers/net/phy/ti_phy_init.h | 1 + > > include/dt-bindings/net/ti-dp83869.h | 60 ++++ > > 6 files changed, 579 insertions(+) > > create mode 100644 drivers/net/phy/dp83869.c > > create mode 100644 include/dt-bindings/net/ti-dp83869.h > > > > diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig > > index 68ee7d7a2d..c6e58058d9 100644 > > --- a/drivers/net/phy/Kconfig > > +++ b/drivers/net/phy/Kconfig > > @@ -290,6 +290,12 @@ config PHY_TI_DP83867 > > ---help--- > > Adds support for the TI DP83867 1Gbit PHY. > > > > +config PHY_TI_DP83869 > > + select PHY_TI > > + bool "Texas Instruments Ethernet DP83869 PHY support" > > + ---help--- > > + Adds support for the TI DP83869 1Gbit PHY. > > + > > config PHY_TI_GENERIC > > select PHY_TI > > bool "Texas Instruments Generic Ethernet PHYs support" > > diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile > > index 218b8c7669..77f7f60621 100644 > > --- a/drivers/net/phy/Makefile > > +++ b/drivers/net/phy/Makefile > > @@ -29,6 +29,7 @@ obj-$(CONFIG_PHY_SMSC) += smsc.o > > obj-$(CONFIG_PHY_TERANETICS) += teranetics.o > > obj-$(CONFIG_PHY_TI) += ti_phy_init.o > > obj-$(CONFIG_PHY_TI_DP83867) += dp83867.o > > +obj-$(CONFIG_PHY_TI_DP83869) += dp83869.o > > obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o > > obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o > > obj-$(CONFIG_PHY_VITESSE) += vitesse.o > > diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c > > new file mode 100644 > > index 0000000000..c9461185cf > > --- /dev/null > > +++ b/drivers/net/phy/dp83869.c > > @@ -0,0 +1,507 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * TI PHY drivers > > + * > > + */ > > + > > +#include <common.h> > > +#include <phy.h> > > +#include <linux/compat.h> > > +#include <malloc.h> > > + > > +#include <dm.h> > > +#include <dt-bindings/net/ti-dp83869.h> > > + > > +/* TI DP83869 */ > > +#define DP83869_DEVADDR 0x1f > > + > > +#define MII_DP83869_PHYCTRL 0x10 > > +#define MII_DP83869_MICR 0x12 > > +#define MII_DP83869_CFG2 0x14 > > +#define MII_DP83869_BISCR 0x16 > > +#define DP83869_CTRL 0x1f > > +#define DP83869_CFG4 0x1e > > + > > +/* Extended Registers */ > > +#define DP83869_GEN_CFG3 0x0031 > > +#define DP83869_RGMIICTL 0x0032 > > +#define DP83869_STRAP_STS1 0x006E > > +#define DP83869_RGMIIDCTL 0x0086 > > +#define DP83869_IO_MUX_CFG 0x0170 > > +#define DP83869_OP_MODE 0x01df > > +#define DP83869_FX_CTRL 0x0c00 > > + > > +#define DP83869_SW_RESET BIT(15) > > +#define DP83869_SW_RESTART BIT(14) > > + > > +/* MICR Interrupt bits */ > > +#define MII_DP83869_MICR_AN_ERR_INT_EN BIT(15) > > +#define MII_DP83869_MICR_SPEED_CHNG_INT_EN BIT(14) > > +#define MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN BIT(13) > > +#define MII_DP83869_MICR_PAGE_RXD_INT_EN BIT(12) > > +#define MII_DP83869_MICR_AUTONEG_COMP_INT_EN BIT(11) > > +#define MII_DP83869_MICR_LINK_STS_CHNG_INT_EN BIT(10) > > +#define MII_DP83869_MICR_FALSE_CARRIER_INT_EN BIT(8) > > +#define MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN BIT(4) > > +#define MII_DP83869_MICR_WOL_INT_EN BIT(3) > > +#define MII_DP83869_MICR_XGMII_ERR_INT_EN BIT(2) > > +#define MII_DP83869_MICR_POL_CHNG_INT_EN BIT(1) > > +#define MII_DP83869_MICR_JABBER_INT_EN BIT(0) > > + > > +#define MII_DP83869_BMCR_DEFAULT (BMCR_ANENABLE | \ > > + BMCR_FULLDPLX | \ > > + BMCR_SPEED1000) > > + > > +/* This is the same bit mask as the BMCR so re-use the BMCR default */ > > +#define DP83869_FX_CTRL_DEFAULT MII_DP83869_BMCR_DEFAULT > > + > > +/* CFG1 bits */ > > +#define DP83869_CFG1_DEFAULT (ADVERTISE_1000HALF | \ > > + ADVERTISE_1000FULL | \ > > + CTL1000_AS_MASTER) > > + > > +/* RGMIICTL bits */ > > +#define DP83869_RGMII_TX_CLK_DELAY_EN BIT(1) > > +#define DP83869_RGMII_RX_CLK_DELAY_EN BIT(0) > > + > > +/* STRAP_STS1 bits */ > > +#define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0) > > +#define DP83869_STRAP_STS1_RESERVED BIT(11) > > +#define DP83869_STRAP_MIRROR_ENABLED BIT(12) > > + > > +/* PHY CTRL bits */ > > +#define DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT 12 > > +#define DP83869_PHYCR_RX_FIFO_DEPTH_MASK GENMASK(13, 12) > > +#define DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT 14 > > +#define DP83869_PHYCR_TX_FIFO_DEPTH_MASK GENMASK(15, 14) > > +#define DP83869_PHYCR_RESERVED_MASK BIT(11) > > +#define DP83869_PHYCR_MDI_CROSSOVER_SHIFT 5 > > +#define DP83869_PHYCR_MDI_CROSSOVER_MDIX 2 > > +#define DP83869_PHY_CTRL_DEFAULT 0x48 > > + > > +/* RGMIIDCTL bits */ > > +#define DP83869_RGMII_TX_CLK_DELAY_SHIFT 4 > > +#define DP83869_CLK_DELAY_DEF 7 > > + > > +/* CFG2 bits */ > > +#define MII_DP83869_CFG2_SPEEDOPT_10EN 0x0040 > > +#define MII_DP83869_CFG2_SGMII_AUTONEGEN 0x0080 > > +#define MII_DP83869_CFG2_SPEEDOPT_ENH 0x0100 > > +#define MII_DP83869_CFG2_SPEEDOPT_CNT 0x0800 > > +#define MII_DP83869_CFG2_SPEEDOPT_INTLOW 0x2000 > > +#define MII_DP83869_CFG2_MASK 0x003F > > + > > +/* User setting - can be taken from DTS */ > > +#define DEFAULT_FIFO_DEPTH DP83869_PHYCR_FIFO_DEPTH_4_B_NIB > > + > > +/* IO_MUX_CFG bits */ > > +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f > > + > > +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 > > +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f > > +#define DP83869_IO_MUX_CFG_CLK_O_DISABLE BIT(6) > > +#define DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT 8 > > +#define DP83869_IO_MUX_CFG_CLK_O_SEL_MASK \ > > + GENMASK(0x1f, DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT) > > + > > +/* CFG3 bits */ > > +#define DP83869_CFG3_PORT_MIRROR_EN BIT(0) > > + > > +/* OP MODE bits */ > > +#define DP83869_OP_MODE_MII BIT(5) > > +#define DP83869_SGMII_RGMII_BRIDGE BIT(6) > > + > > +enum { > > + DP83869_PORT_MIRRORING_KEEP, > > + DP83869_PORT_MIRRORING_EN, > > + DP83869_PORT_MIRRORING_DIS, > > +}; > > + > > +struct dp83869_private { > > + int tx_fifo_depth; > > + int rx_fifo_depth; > > + s32 rx_int_delay; > > + s32 tx_int_delay; > > + int io_impedance; > > + int port_mirroring; > > + bool set_clk_output; > > + int clk_output_sel; > > + int mode; > > +}; > > + > > +static int dp83869_readext(struct phy_device *phydev, int addr, int devad, int reg) > > +{ > > + return phy_read_mmd(phydev, devad, reg); > > +} > > + > > +static int dp83869_writeext(struct phy_device *phydev, int addr, int devad, int reg, u16 val) > > +{ > > + return phy_write_mmd(phydev, devad, reg, val); > > +} > > + > > +static int dp83869_config_port_mirroring(struct phy_device *phydev) > > +{ > > + struct dp83869_private *dp83869 = > > + (struct dp83869_private *)phydev->priv; > > + u16 val; > > + > > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_CFG4); > > + > > + if (dp83869->port_mirroring == DP83869_PORT_MIRRORING_EN) > > + val |= DP83869_CFG3_PORT_MIRROR_EN; > > + else > > + val &= ~DP83869_CFG3_PORT_MIRROR_EN; > > + > > + phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_CFG4, val); > > + > > + return 0; > > +} > > + > > +#ifdef CONFIG_DM_ETH > > +static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500, > > + 1750, 2000, 2250, 2500, 2750, 3000, > > + 3250, 3500, 3750, 4000}; > > + > > +static int dp83869_set_strapped_mode(struct phy_device *phydev) > > +{ > > + struct dp83869_private *dp83869 = phydev->priv; > > + int val; > > + > > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); > > + if (val < 0) > > + return val; > > + > > + dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK; > > + > > + return 0; > > +} > > + > > +/** > > + * dp83869_data_init - Convenience function for setting PHY specific data > > + * > > + * @phydev: the phy_device struct > > + */ > > +static int dp83869_of_init(struct phy_device *phydev) > > +{ > > + struct dp83869_private * const dp83869 = phydev->priv; > > + const int delay_entries = ARRAY_SIZE(dp83869_internal_delay); > > + int ret; > > + ofnode node; > > + > > + node = phy_get_ofnode(phydev); > > + if (!ofnode_valid(node)) > > + return -EINVAL; > > + > > + dp83869->io_impedance = -EINVAL; > > + > > + /* Optional configuration, set to default if required */ > > + dp83869->clk_output_sel = ofnode_read_u32_default(node, "ti,clk-output-sel", > > + DP83869_CLK_O_SEL_CHN_A_RCLK); > > + > > + if (dp83869->clk_output_sel > DP83869_CLK_O_SEL_REF_CLK && > > + dp83869->clk_output_sel != DP83869_CLK_O_SEL_OFF) > > + dp83869->clk_output_sel = DP83869_CLK_O_SEL_REF_CLK; > > + > > + /* If operation mode is not set use setting from straps */ > > + ret = ofnode_read_s32(node, "ti,op-mode", &dp83869->mode); > > + if (ret == 0) { > > + if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || > > + dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) > > + return -EINVAL; > > + } else { > > + ret = dp83869_set_strapped_mode(phydev); > > + if (ret) > > + return ret; > > + } > > + > > + if (ofnode_read_bool(node, "ti,max-output-impedance")) > > + dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX; > > + else if (ofnode_read_bool(node, "ti,min-output-impedance")) > > + dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN; > > + > > + if (ofnode_read_bool(node, "enet-phy-lane-swap")) { > > + dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; > > + } else { > > + ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); > > + > > + if (ret < 0) > > + return ret; > > + > > + if (ret & DP83869_STRAP_MIRROR_ENABLED) > > + dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; > > + else > > + dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS; > > + } > > + > > + dp83869->rx_fifo_depth = ofnode_read_s32_default(node, "rx-fifo-depth", > > + DP83869_PHYCR_FIFO_DEPTH_4_B_NIB); > > + > > + dp83869->tx_fifo_depth = ofnode_read_s32_default(node, "tx-fifo-depth", > > + DP83869_PHYCR_FIFO_DEPTH_4_B_NIB); > > + > > + /* 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) { > > + dp83869->rx_int_delay = ofnode_read_u32_default(node, "rx-internal-delay-ps", > > + DP83869_CLK_DELAY_DEF); > > + if (dp83869->rx_int_delay > delay_entries) { > > + dp83869->rx_int_delay = DP83869_CLK_DELAY_DEF; > > + pr_debug("rx-internal-delay-ps not set/invalid, default to %ups\n", > > + dp83869_internal_delay[dp83869->rx_int_delay]); > > + } > > + > > + dp83869->rx_int_delay = dp83869_internal_delay[dp83869->rx_int_delay]; > > + } > > + > > + /* TX 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_TXID) { > > + dp83869->tx_int_delay = ofnode_read_u32_default(node, "tx-internal-delay-ps", > > + DP83869_CLK_DELAY_DEF); > > + if (dp83869->tx_int_delay > delay_entries) { > > + dp83869->tx_int_delay = DP83869_CLK_DELAY_DEF; > > + pr_debug("tx-internal-delay-ps not set/invalid, default to %ups\n", > > + dp83869_internal_delay[dp83869->tx_int_delay]); > > + } > > + > > + dp83869->tx_int_delay = dp83869_internal_delay[dp83869->tx_int_delay]; > > + } > > + > > + return 0; > > +} > > +#else > > +static int dp83869_of_init(struct phy_device *phydev) > > +{ > > + struct dp83869_private *dp83869 = phydev->priv; > > + > > + dp83869->rx_int_delay = DP83869_RGMIIDCTL_2_25_NS; > > + dp83869->tx_int_delay = DP83869_RGMIIDCTL_2_75_NS; > > + dp83869->fifo_depth = DEFAULT_FIFO_DEPTH; > > + dp83869->io_impedance = -EINVAL; > > + > > + return 0; > > +} > > +#endif /* CONFIG_OF_MDIO */ > > + > > +static int dp83869_configure_rgmii(struct phy_device *phydev, > > + struct dp83869_private *dp83869) > > +{ > > + int ret = 0, val; > > + > > + if (phy_interface_is_rgmii(phydev)) { > > + val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL); > > + if (val < 0) > > + return val; > > + > > + val &= ~(DP83869_PHYCR_TX_FIFO_DEPTH_MASK | DP83869_PHYCR_RX_FIFO_DEPTH_MASK); > > + val |= (dp83869->tx_fifo_depth << DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT); > > + val |= (dp83869->rx_fifo_depth << DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT); > > + > > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, val); > > + if (ret) > > + return ret; > > + } > > + > > + if (dp83869->io_impedance >= 0) { > > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG); > > + > > + val &= ~DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL; > > + val |= dp83869->io_impedance & DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL; > > + > > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG, val); > > + > > + if (ret) > > + return ret; > > + } > > + > > + return ret; > > +} > > + > > +static int dp83869_configure_mode(struct phy_device *phydev, > > + struct dp83869_private *dp83869) > > +{ > > + int phy_ctrl_val; > > + int ret, val; > > + > > + if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || > > + dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) > > + return -EINVAL; > > + > > + /* Below init sequence for each operational mode is defined in > > + * section 9.4.8 of the datasheet. > > + */ > > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, > > + dp83869->mode); > > + if (ret) > > + return ret; > > + > > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, MII_DP83869_BMCR_DEFAULT); > > + if (ret) > > + return ret; > > + > > + phy_ctrl_val = (dp83869->rx_fifo_depth << DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT | > > + dp83869->tx_fifo_depth << DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT | > > + DP83869_PHY_CTRL_DEFAULT); > > + > > + switch (dp83869->mode) { > > + case DP83869_RGMII_COPPER_ETHERNET: > > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, > > + phy_ctrl_val); > > + if (ret) > > + return ret; > > + > > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, DP83869_CFG1_DEFAULT); > > + if (ret) > > + return ret; > > + > > + ret = dp83869_configure_rgmii(phydev, dp83869); > > + if (ret) > > + return ret; > > + break; > > + case DP83869_RGMII_SGMII_BRIDGE: > > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE); > > + > > + val |= DP83869_SGMII_RGMII_BRIDGE; > > + > > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, val); > > + > > + if (ret) > > + return ret; > > + > > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, > > + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); > > + if (ret) > > + return ret; > > + > > + break; > > + case DP83869_1000M_MEDIA_CONVERT: > > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, > > + phy_ctrl_val); > > + if (ret) > > + return ret; > > + > > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, > > + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); > > + if (ret) > > + return ret; > > + break; > > + case DP83869_100M_MEDIA_CONVERT: > > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, > > + phy_ctrl_val); > > + if (ret) > > + return ret; > > + break; > > + case DP83869_SGMII_COPPER_ETHERNET: > > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, > > + phy_ctrl_val); > > + if (ret) > > + return ret; > > + > > + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, DP83869_CFG1_DEFAULT); > > + if (ret) > > + return ret; > > + > > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, > > + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); > > + if (ret) > > + return ret; > > + > > + break; > > + default: > > + return -EINVAL; > > + } > > + > > + return ret; > > +} > > + > > +static int dp83869_config(struct phy_device *phydev) > > +{ > > + struct dp83869_private *dp83869; > > + unsigned int val; > > + int ret; > > + > > + dp83869 = (struct dp83869_private *)phydev->priv; > > + > > + ret = dp83869_of_init(phydev); > > + if (ret) > > + return ret; > > + > > + ret = dp83869_configure_mode(phydev, dp83869); > > + if (ret) > > + return ret; > > + > > + if (dp83869->port_mirroring != DP83869_PORT_MIRRORING_KEEP) > > + dp83869_config_port_mirroring(phydev); > > + > > + /* Clock output selection if muxing property is set */ > > + if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) { > > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG); > > + > > + val &= ~DP83869_IO_MUX_CFG_CLK_O_SEL_MASK; > > + val |= dp83869->clk_output_sel << DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT; > > + > > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG, val); > > + > > + if (ret) > > + return ret; > > + } > > + > > + if (phy_interface_is_rgmii(phydev)) { > > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL, > > + dp83869->rx_int_delay | > > + dp83869->tx_int_delay << DP83869_RGMII_TX_CLK_DELAY_SHIFT); > > + if (ret) > > + return ret; > > + > > + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL); > > + val |= (DP83869_RGMII_TX_CLK_DELAY_EN | > > + DP83869_RGMII_RX_CLK_DELAY_EN); > > + > > + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) > > + val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN | > > + DP83869_RGMII_RX_CLK_DELAY_EN); > > + > > + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) > > + val &= ~DP83869_RGMII_TX_CLK_DELAY_EN; > > + > > + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) > > + val &= ~DP83869_RGMII_RX_CLK_DELAY_EN; > > + > > + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL, > > + val); > > + } > > + > > + genphy_config_aneg(phydev); > > + return 0; > > +} > > + > > +static int dp83869_probe(struct phy_device *phydev) > > +{ > > + struct dp83869_private *dp83869; > > + > > + dp83869 = kzalloc(sizeof(*dp83869), GFP_KERNEL); > > + if (!dp83869) > > + return -ENOMEM; > > + > > + phydev->priv = dp83869; > > + return 0; > > +} > > + > > +static struct phy_driver DP83869_driver = { > > + .name = "TI DP83869", > > + .uid = 0x2000a0f1, > > + .mask = 0xfffffff0, > > + .features = PHY_GBIT_FEATURES, > > + .probe = dp83869_probe, > > + .config = &dp83869_config, > > + .startup = &genphy_startup, > > + .shutdown = &genphy_shutdown, > > + .readext = dp83869_readext, > > + .writeext = dp83869_writeext > > +}; > > + > > +int phy_dp83869_init(void) > > +{ > > + phy_register(&DP83869_driver); > > + return 0; > > +} > > diff --git a/drivers/net/phy/ti_phy_init.c b/drivers/net/phy/ti_phy_init.c > > index 50eff77692..075b19a39f 100644 > > --- a/drivers/net/phy/ti_phy_init.c > > +++ b/drivers/net/phy/ti_phy_init.c > > @@ -88,6 +88,10 @@ int phy_ti_init(void) > > phy_dp83867_init(); > > #endif > > > > +#ifdef CONFIG_PHY_TI_DP83869 > > + phy_dp83869_init(); > > +#endif > > + > > #ifdef CONFIG_PHY_TI_GENERIC > > phy_register(&dp83822_driver); > > phy_register(&dp83825s_driver); > > diff --git a/drivers/net/phy/ti_phy_init.h b/drivers/net/phy/ti_phy_init.h > > index 6c7f6c640a..bcb3d320d3 100644 > > --- a/drivers/net/phy/ti_phy_init.h > > +++ b/drivers/net/phy/ti_phy_init.h > > @@ -11,5 +11,6 @@ > > #define _TI_GEN_PHY_H > > > > int phy_dp83867_init(void); > > +int phy_dp83869_init(void); > > > > #endif /* _TI_GEN_PHY_H */ > > diff --git a/include/dt-bindings/net/ti-dp83869.h b/include/dt-bindings/net/ti-dp83869.h > > new file mode 100644 > > index 0000000000..b3a5ac4a17 > > --- /dev/null > > +++ b/include/dt-bindings/net/ti-dp83869.h > > @@ -0,0 +1,60 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * TI DP83869 PHY drivers > > + * > > + */ > > + > > +#ifndef _DT_BINDINGS_TI_DP83869_H > > +#define _DT_BINDINGS_TI_DP83869_H > > + > > +/* PHY CTRL bits */ > > +#define DP83869_PHYCR_FIFO_DEPTH_3_B_NIB 0x00 > > +#define DP83869_PHYCR_FIFO_DEPTH_4_B_NIB 0x01 > > +#define DP83869_PHYCR_FIFO_DEPTH_6_B_NIB 0x02 > > +#define DP83869_PHYCR_FIFO_DEPTH_8_B_NIB 0x03 > > + > > +/* RGMIIDCTL internal delay for rx and tx */ > > +#define DP83869_RGMIIDCTL_250_PS 0x0 > > +#define DP83869_RGMIIDCTL_500_PS 0x1 > > +#define DP83869_RGMIIDCTL_750_PS 0x2 > > +#define DP83869_RGMIIDCTL_1_NS 0x3 > > +#define DP83869_RGMIIDCTL_1_25_NS 0x4 > > +#define DP83869_RGMIIDCTL_1_50_NS 0x5 > > +#define DP83869_RGMIIDCTL_1_75_NS 0x6 > > +#define DP83869_RGMIIDCTL_2_00_NS 0x7 > > +#define DP83869_RGMIIDCTL_2_25_NS 0x8 > > +#define DP83869_RGMIIDCTL_2_50_NS 0x9 > > +#define DP83869_RGMIIDCTL_2_75_NS 0xa > > +#define DP83869_RGMIIDCTL_3_00_NS 0xb > > +#define DP83869_RGMIIDCTL_3_25_NS 0xc > > +#define DP83869_RGMIIDCTL_3_50_NS 0xd > > +#define DP83869_RGMIIDCTL_3_75_NS 0xe > > +#define DP83869_RGMIIDCTL_4_00_NS 0xf > > + > > +/* IO_MUX_CFG - Clock output selection */ > > +#define DP83869_CLK_O_SEL_CHN_A_RCLK 0x0 > > +#define DP83869_CLK_O_SEL_CHN_B_RCLK 0x1 > > +#define DP83869_CLK_O_SEL_CHN_C_RCLK 0x2 > > +#define DP83869_CLK_O_SEL_CHN_D_RCLK 0x3 > > +#define DP83869_CLK_O_SEL_CHN_A_RCLK_DIV5 0x4 > > +#define DP83869_CLK_O_SEL_CHN_B_RCLK_DIV5 0x5 > > +#define DP83869_CLK_O_SEL_CHN_C_RCLK_DIV5 0x6 > > +#define DP83869_CLK_O_SEL_CHN_D_RCLK_DIV5 0x7 > > +#define DP83869_CLK_O_SEL_CHN_A_TCLK 0x8 > > +#define DP83869_CLK_O_SEL_CHN_B_TCLK 0x9 > > +#define DP83869_CLK_O_SEL_CHN_C_TCLK 0xA > > +#define DP83869_CLK_O_SEL_CHN_D_TCLK 0xB > > +#define DP83869_CLK_O_SEL_REF_CLK 0xC > > +/* Special flag to indicate clock should be off */ > > +#define DP83869_CLK_O_SEL_OFF 0xFFFFFFFF > > + > > +/* OPMODE - Operation mode */ > > +#define DP83869_RGMII_COPPER_ETHERNET 0x00 > > +#define DP83869_RGMII_1000_BASE 0x01 > > +#define DP83869_RGMII_100_BASE 0x02 > > +#define DP83869_RGMII_SGMII_BRIDGE 0x03 > > +#define DP83869_1000M_MEDIA_CONVERT 0x04 > > +#define DP83869_100M_MEDIA_CONVERT 0x05 > > +#define DP83869_SGMII_COPPER_ETHERNET 0x06 > > + > > +#endif > > -- > > 2.33.1 > > > Reviewed-by: Ramon Fried <rfried.dev@gmail.com> Applied to u-boot-net/next Thanks !
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 68ee7d7a2d..c6e58058d9 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -290,6 +290,12 @@ config PHY_TI_DP83867 ---help--- Adds support for the TI DP83867 1Gbit PHY. +config PHY_TI_DP83869 + select PHY_TI + bool "Texas Instruments Ethernet DP83869 PHY support" + ---help--- + Adds support for the TI DP83869 1Gbit PHY. + config PHY_TI_GENERIC select PHY_TI bool "Texas Instruments Generic Ethernet PHYs support" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 218b8c7669..77f7f60621 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_PHY_SMSC) += smsc.o obj-$(CONFIG_PHY_TERANETICS) += teranetics.o obj-$(CONFIG_PHY_TI) += ti_phy_init.o obj-$(CONFIG_PHY_TI_DP83867) += dp83867.o +obj-$(CONFIG_PHY_TI_DP83869) += dp83869.o obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o obj-$(CONFIG_PHY_VITESSE) += vitesse.o diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c new file mode 100644 index 0000000000..c9461185cf --- /dev/null +++ b/drivers/net/phy/dp83869.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI PHY drivers + * + */ + +#include <common.h> +#include <phy.h> +#include <linux/compat.h> +#include <malloc.h> + +#include <dm.h> +#include <dt-bindings/net/ti-dp83869.h> + +/* TI DP83869 */ +#define DP83869_DEVADDR 0x1f + +#define MII_DP83869_PHYCTRL 0x10 +#define MII_DP83869_MICR 0x12 +#define MII_DP83869_CFG2 0x14 +#define MII_DP83869_BISCR 0x16 +#define DP83869_CTRL 0x1f +#define DP83869_CFG4 0x1e + +/* Extended Registers */ +#define DP83869_GEN_CFG3 0x0031 +#define DP83869_RGMIICTL 0x0032 +#define DP83869_STRAP_STS1 0x006E +#define DP83869_RGMIIDCTL 0x0086 +#define DP83869_IO_MUX_CFG 0x0170 +#define DP83869_OP_MODE 0x01df +#define DP83869_FX_CTRL 0x0c00 + +#define DP83869_SW_RESET BIT(15) +#define DP83869_SW_RESTART BIT(14) + +/* MICR Interrupt bits */ +#define MII_DP83869_MICR_AN_ERR_INT_EN BIT(15) +#define MII_DP83869_MICR_SPEED_CHNG_INT_EN BIT(14) +#define MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN BIT(13) +#define MII_DP83869_MICR_PAGE_RXD_INT_EN BIT(12) +#define MII_DP83869_MICR_AUTONEG_COMP_INT_EN BIT(11) +#define MII_DP83869_MICR_LINK_STS_CHNG_INT_EN BIT(10) +#define MII_DP83869_MICR_FALSE_CARRIER_INT_EN BIT(8) +#define MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN BIT(4) +#define MII_DP83869_MICR_WOL_INT_EN BIT(3) +#define MII_DP83869_MICR_XGMII_ERR_INT_EN BIT(2) +#define MII_DP83869_MICR_POL_CHNG_INT_EN BIT(1) +#define MII_DP83869_MICR_JABBER_INT_EN BIT(0) + +#define MII_DP83869_BMCR_DEFAULT (BMCR_ANENABLE | \ + BMCR_FULLDPLX | \ + BMCR_SPEED1000) + +/* This is the same bit mask as the BMCR so re-use the BMCR default */ +#define DP83869_FX_CTRL_DEFAULT MII_DP83869_BMCR_DEFAULT + +/* CFG1 bits */ +#define DP83869_CFG1_DEFAULT (ADVERTISE_1000HALF | \ + ADVERTISE_1000FULL | \ + CTL1000_AS_MASTER) + +/* RGMIICTL bits */ +#define DP83869_RGMII_TX_CLK_DELAY_EN BIT(1) +#define DP83869_RGMII_RX_CLK_DELAY_EN BIT(0) + +/* STRAP_STS1 bits */ +#define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0) +#define DP83869_STRAP_STS1_RESERVED BIT(11) +#define DP83869_STRAP_MIRROR_ENABLED BIT(12) + +/* PHY CTRL bits */ +#define DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT 12 +#define DP83869_PHYCR_RX_FIFO_DEPTH_MASK GENMASK(13, 12) +#define DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT 14 +#define DP83869_PHYCR_TX_FIFO_DEPTH_MASK GENMASK(15, 14) +#define DP83869_PHYCR_RESERVED_MASK BIT(11) +#define DP83869_PHYCR_MDI_CROSSOVER_SHIFT 5 +#define DP83869_PHYCR_MDI_CROSSOVER_MDIX 2 +#define DP83869_PHY_CTRL_DEFAULT 0x48 + +/* RGMIIDCTL bits */ +#define DP83869_RGMII_TX_CLK_DELAY_SHIFT 4 +#define DP83869_CLK_DELAY_DEF 7 + +/* CFG2 bits */ +#define MII_DP83869_CFG2_SPEEDOPT_10EN 0x0040 +#define MII_DP83869_CFG2_SGMII_AUTONEGEN 0x0080 +#define MII_DP83869_CFG2_SPEEDOPT_ENH 0x0100 +#define MII_DP83869_CFG2_SPEEDOPT_CNT 0x0800 +#define MII_DP83869_CFG2_SPEEDOPT_INTLOW 0x2000 +#define MII_DP83869_CFG2_MASK 0x003F + +/* User setting - can be taken from DTS */ +#define DEFAULT_FIFO_DEPTH DP83869_PHYCR_FIFO_DEPTH_4_B_NIB + +/* IO_MUX_CFG bits */ +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f + +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f +#define DP83869_IO_MUX_CFG_CLK_O_DISABLE BIT(6) +#define DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT 8 +#define DP83869_IO_MUX_CFG_CLK_O_SEL_MASK \ + GENMASK(0x1f, DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT) + +/* CFG3 bits */ +#define DP83869_CFG3_PORT_MIRROR_EN BIT(0) + +/* OP MODE bits */ +#define DP83869_OP_MODE_MII BIT(5) +#define DP83869_SGMII_RGMII_BRIDGE BIT(6) + +enum { + DP83869_PORT_MIRRORING_KEEP, + DP83869_PORT_MIRRORING_EN, + DP83869_PORT_MIRRORING_DIS, +}; + +struct dp83869_private { + int tx_fifo_depth; + int rx_fifo_depth; + s32 rx_int_delay; + s32 tx_int_delay; + int io_impedance; + int port_mirroring; + bool set_clk_output; + int clk_output_sel; + int mode; +}; + +static int dp83869_readext(struct phy_device *phydev, int addr, int devad, int reg) +{ + return phy_read_mmd(phydev, devad, reg); +} + +static int dp83869_writeext(struct phy_device *phydev, int addr, int devad, int reg, u16 val) +{ + return phy_write_mmd(phydev, devad, reg, val); +} + +static int dp83869_config_port_mirroring(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = + (struct dp83869_private *)phydev->priv; + u16 val; + + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_CFG4); + + if (dp83869->port_mirroring == DP83869_PORT_MIRRORING_EN) + val |= DP83869_CFG3_PORT_MIRROR_EN; + else + val &= ~DP83869_CFG3_PORT_MIRROR_EN; + + phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_CFG4, val); + + return 0; +} + +#ifdef CONFIG_DM_ETH +static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500, + 1750, 2000, 2250, 2500, 2750, 3000, + 3250, 3500, 3750, 4000}; + +static int dp83869_set_strapped_mode(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = phydev->priv; + int val; + + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); + if (val < 0) + return val; + + dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK; + + return 0; +} + +/** + * dp83869_data_init - Convenience function for setting PHY specific data + * + * @phydev: the phy_device struct + */ +static int dp83869_of_init(struct phy_device *phydev) +{ + struct dp83869_private * const dp83869 = phydev->priv; + const int delay_entries = ARRAY_SIZE(dp83869_internal_delay); + int ret; + ofnode node; + + node = phy_get_ofnode(phydev); + if (!ofnode_valid(node)) + return -EINVAL; + + dp83869->io_impedance = -EINVAL; + + /* Optional configuration, set to default if required */ + dp83869->clk_output_sel = ofnode_read_u32_default(node, "ti,clk-output-sel", + DP83869_CLK_O_SEL_CHN_A_RCLK); + + if (dp83869->clk_output_sel > DP83869_CLK_O_SEL_REF_CLK && + dp83869->clk_output_sel != DP83869_CLK_O_SEL_OFF) + dp83869->clk_output_sel = DP83869_CLK_O_SEL_REF_CLK; + + /* If operation mode is not set use setting from straps */ + ret = ofnode_read_s32(node, "ti,op-mode", &dp83869->mode); + if (ret == 0) { + if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || + dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) + return -EINVAL; + } else { + ret = dp83869_set_strapped_mode(phydev); + if (ret) + return ret; + } + + if (ofnode_read_bool(node, "ti,max-output-impedance")) + dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX; + else if (ofnode_read_bool(node, "ti,min-output-impedance")) + dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN; + + if (ofnode_read_bool(node, "enet-phy-lane-swap")) { + dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; + } else { + ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); + + if (ret < 0) + return ret; + + if (ret & DP83869_STRAP_MIRROR_ENABLED) + dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; + else + dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS; + } + + dp83869->rx_fifo_depth = ofnode_read_s32_default(node, "rx-fifo-depth", + DP83869_PHYCR_FIFO_DEPTH_4_B_NIB); + + dp83869->tx_fifo_depth = ofnode_read_s32_default(node, "tx-fifo-depth", + DP83869_PHYCR_FIFO_DEPTH_4_B_NIB); + + /* 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) { + dp83869->rx_int_delay = ofnode_read_u32_default(node, "rx-internal-delay-ps", + DP83869_CLK_DELAY_DEF); + if (dp83869->rx_int_delay > delay_entries) { + dp83869->rx_int_delay = DP83869_CLK_DELAY_DEF; + pr_debug("rx-internal-delay-ps not set/invalid, default to %ups\n", + dp83869_internal_delay[dp83869->rx_int_delay]); + } + + dp83869->rx_int_delay = dp83869_internal_delay[dp83869->rx_int_delay]; + } + + /* TX 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_TXID) { + dp83869->tx_int_delay = ofnode_read_u32_default(node, "tx-internal-delay-ps", + DP83869_CLK_DELAY_DEF); + if (dp83869->tx_int_delay > delay_entries) { + dp83869->tx_int_delay = DP83869_CLK_DELAY_DEF; + pr_debug("tx-internal-delay-ps not set/invalid, default to %ups\n", + dp83869_internal_delay[dp83869->tx_int_delay]); + } + + dp83869->tx_int_delay = dp83869_internal_delay[dp83869->tx_int_delay]; + } + + return 0; +} +#else +static int dp83869_of_init(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = phydev->priv; + + dp83869->rx_int_delay = DP83869_RGMIIDCTL_2_25_NS; + dp83869->tx_int_delay = DP83869_RGMIIDCTL_2_75_NS; + dp83869->fifo_depth = DEFAULT_FIFO_DEPTH; + dp83869->io_impedance = -EINVAL; + + return 0; +} +#endif /* CONFIG_OF_MDIO */ + +static int dp83869_configure_rgmii(struct phy_device *phydev, + struct dp83869_private *dp83869) +{ + int ret = 0, val; + + if (phy_interface_is_rgmii(phydev)) { + val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL); + if (val < 0) + return val; + + val &= ~(DP83869_PHYCR_TX_FIFO_DEPTH_MASK | DP83869_PHYCR_RX_FIFO_DEPTH_MASK); + val |= (dp83869->tx_fifo_depth << DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT); + val |= (dp83869->rx_fifo_depth << DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT); + + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, val); + if (ret) + return ret; + } + + if (dp83869->io_impedance >= 0) { + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG); + + val &= ~DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL; + val |= dp83869->io_impedance & DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG, val); + + if (ret) + return ret; + } + + return ret; +} + +static int dp83869_configure_mode(struct phy_device *phydev, + struct dp83869_private *dp83869) +{ + int phy_ctrl_val; + int ret, val; + + if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || + dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) + return -EINVAL; + + /* Below init sequence for each operational mode is defined in + * section 9.4.8 of the datasheet. + */ + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, + dp83869->mode); + if (ret) + return ret; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, MII_DP83869_BMCR_DEFAULT); + if (ret) + return ret; + + phy_ctrl_val = (dp83869->rx_fifo_depth << DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT | + dp83869->tx_fifo_depth << DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT | + DP83869_PHY_CTRL_DEFAULT); + + switch (dp83869->mode) { + case DP83869_RGMII_COPPER_ETHERNET: + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, + phy_ctrl_val); + if (ret) + return ret; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, DP83869_CFG1_DEFAULT); + if (ret) + return ret; + + ret = dp83869_configure_rgmii(phydev, dp83869); + if (ret) + return ret; + break; + case DP83869_RGMII_SGMII_BRIDGE: + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE); + + val |= DP83869_SGMII_RGMII_BRIDGE; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, val); + + if (ret) + return ret; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); + if (ret) + return ret; + + break; + case DP83869_1000M_MEDIA_CONVERT: + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, + phy_ctrl_val); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); + if (ret) + return ret; + break; + case DP83869_100M_MEDIA_CONVERT: + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, + phy_ctrl_val); + if (ret) + return ret; + break; + case DP83869_SGMII_COPPER_ETHERNET: + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, + phy_ctrl_val); + if (ret) + return ret; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, DP83869_CFG1_DEFAULT); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); + if (ret) + return ret; + + break; + default: + return -EINVAL; + } + + return ret; +} + +static int dp83869_config(struct phy_device *phydev) +{ + struct dp83869_private *dp83869; + unsigned int val; + int ret; + + dp83869 = (struct dp83869_private *)phydev->priv; + + ret = dp83869_of_init(phydev); + if (ret) + return ret; + + ret = dp83869_configure_mode(phydev, dp83869); + if (ret) + return ret; + + if (dp83869->port_mirroring != DP83869_PORT_MIRRORING_KEEP) + dp83869_config_port_mirroring(phydev); + + /* Clock output selection if muxing property is set */ + if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) { + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG); + + val &= ~DP83869_IO_MUX_CFG_CLK_O_SEL_MASK; + val |= dp83869->clk_output_sel << DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG, val); + + if (ret) + return ret; + } + + if (phy_interface_is_rgmii(phydev)) { + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL, + dp83869->rx_int_delay | + dp83869->tx_int_delay << DP83869_RGMII_TX_CLK_DELAY_SHIFT); + if (ret) + return ret; + + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL); + val |= (DP83869_RGMII_TX_CLK_DELAY_EN | + DP83869_RGMII_RX_CLK_DELAY_EN); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN | + DP83869_RGMII_RX_CLK_DELAY_EN); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + val &= ~DP83869_RGMII_TX_CLK_DELAY_EN; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + val &= ~DP83869_RGMII_RX_CLK_DELAY_EN; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL, + val); + } + + genphy_config_aneg(phydev); + return 0; +} + +static int dp83869_probe(struct phy_device *phydev) +{ + struct dp83869_private *dp83869; + + dp83869 = kzalloc(sizeof(*dp83869), GFP_KERNEL); + if (!dp83869) + return -ENOMEM; + + phydev->priv = dp83869; + return 0; +} + +static struct phy_driver DP83869_driver = { + .name = "TI DP83869", + .uid = 0x2000a0f1, + .mask = 0xfffffff0, + .features = PHY_GBIT_FEATURES, + .probe = dp83869_probe, + .config = &dp83869_config, + .startup = &genphy_startup, + .shutdown = &genphy_shutdown, + .readext = dp83869_readext, + .writeext = dp83869_writeext +}; + +int phy_dp83869_init(void) +{ + phy_register(&DP83869_driver); + return 0; +} diff --git a/drivers/net/phy/ti_phy_init.c b/drivers/net/phy/ti_phy_init.c index 50eff77692..075b19a39f 100644 --- a/drivers/net/phy/ti_phy_init.c +++ b/drivers/net/phy/ti_phy_init.c @@ -88,6 +88,10 @@ int phy_ti_init(void) phy_dp83867_init(); #endif +#ifdef CONFIG_PHY_TI_DP83869 + phy_dp83869_init(); +#endif + #ifdef CONFIG_PHY_TI_GENERIC phy_register(&dp83822_driver); phy_register(&dp83825s_driver); diff --git a/drivers/net/phy/ti_phy_init.h b/drivers/net/phy/ti_phy_init.h index 6c7f6c640a..bcb3d320d3 100644 --- a/drivers/net/phy/ti_phy_init.h +++ b/drivers/net/phy/ti_phy_init.h @@ -11,5 +11,6 @@ #define _TI_GEN_PHY_H int phy_dp83867_init(void); +int phy_dp83869_init(void); #endif /* _TI_GEN_PHY_H */ diff --git a/include/dt-bindings/net/ti-dp83869.h b/include/dt-bindings/net/ti-dp83869.h new file mode 100644 index 0000000000..b3a5ac4a17 --- /dev/null +++ b/include/dt-bindings/net/ti-dp83869.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * TI DP83869 PHY drivers + * + */ + +#ifndef _DT_BINDINGS_TI_DP83869_H +#define _DT_BINDINGS_TI_DP83869_H + +/* PHY CTRL bits */ +#define DP83869_PHYCR_FIFO_DEPTH_3_B_NIB 0x00 +#define DP83869_PHYCR_FIFO_DEPTH_4_B_NIB 0x01 +#define DP83869_PHYCR_FIFO_DEPTH_6_B_NIB 0x02 +#define DP83869_PHYCR_FIFO_DEPTH_8_B_NIB 0x03 + +/* RGMIIDCTL internal delay for rx and tx */ +#define DP83869_RGMIIDCTL_250_PS 0x0 +#define DP83869_RGMIIDCTL_500_PS 0x1 +#define DP83869_RGMIIDCTL_750_PS 0x2 +#define DP83869_RGMIIDCTL_1_NS 0x3 +#define DP83869_RGMIIDCTL_1_25_NS 0x4 +#define DP83869_RGMIIDCTL_1_50_NS 0x5 +#define DP83869_RGMIIDCTL_1_75_NS 0x6 +#define DP83869_RGMIIDCTL_2_00_NS 0x7 +#define DP83869_RGMIIDCTL_2_25_NS 0x8 +#define DP83869_RGMIIDCTL_2_50_NS 0x9 +#define DP83869_RGMIIDCTL_2_75_NS 0xa +#define DP83869_RGMIIDCTL_3_00_NS 0xb +#define DP83869_RGMIIDCTL_3_25_NS 0xc +#define DP83869_RGMIIDCTL_3_50_NS 0xd +#define DP83869_RGMIIDCTL_3_75_NS 0xe +#define DP83869_RGMIIDCTL_4_00_NS 0xf + +/* IO_MUX_CFG - Clock output selection */ +#define DP83869_CLK_O_SEL_CHN_A_RCLK 0x0 +#define DP83869_CLK_O_SEL_CHN_B_RCLK 0x1 +#define DP83869_CLK_O_SEL_CHN_C_RCLK 0x2 +#define DP83869_CLK_O_SEL_CHN_D_RCLK 0x3 +#define DP83869_CLK_O_SEL_CHN_A_RCLK_DIV5 0x4 +#define DP83869_CLK_O_SEL_CHN_B_RCLK_DIV5 0x5 +#define DP83869_CLK_O_SEL_CHN_C_RCLK_DIV5 0x6 +#define DP83869_CLK_O_SEL_CHN_D_RCLK_DIV5 0x7 +#define DP83869_CLK_O_SEL_CHN_A_TCLK 0x8 +#define DP83869_CLK_O_SEL_CHN_B_TCLK 0x9 +#define DP83869_CLK_O_SEL_CHN_C_TCLK 0xA +#define DP83869_CLK_O_SEL_CHN_D_TCLK 0xB +#define DP83869_CLK_O_SEL_REF_CLK 0xC +/* Special flag to indicate clock should be off */ +#define DP83869_CLK_O_SEL_OFF 0xFFFFFFFF + +/* OPMODE - Operation mode */ +#define DP83869_RGMII_COPPER_ETHERNET 0x00 +#define DP83869_RGMII_1000_BASE 0x01 +#define DP83869_RGMII_100_BASE 0x02 +#define DP83869_RGMII_SGMII_BRIDGE 0x03 +#define DP83869_1000M_MEDIA_CONVERT 0x04 +#define DP83869_100M_MEDIA_CONVERT 0x05 +#define DP83869_SGMII_COPPER_ETHERNET 0x06 + +#endif