*
*/
#include <common.h>
+#include <log.h>
#include <phy.h>
+#include <dm/devres.h>
+#include <linux/bitops.h>
#include <linux/compat.h>
#include <malloc.h>
#include <dm.h>
#include <dt-bindings/net/ti-dp83867.h>
+#include "ti_phy_init.h"
/* TI DP83867 */
#define DP83867_DEVADDR 0x1f
#define DP83867_CFG4 0x0031
#define DP83867_RGMIICTL 0x0032
#define DP83867_STRAP_STS1 0x006E
+#define DP83867_STRAP_STS2 0x006f
#define DP83867_RGMIIDCTL 0x0086
#define DP83867_IO_MUX_CFG 0x0170
+#define DP83867_SGMIICTL 0x00D3
#define DP83867_SW_RESET BIT(15)
#define DP83867_SW_RESTART BIT(14)
/* 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)
+
/* PHY CTRL bits */
#define DP83867_PHYCR_FIFO_DEPTH_SHIFT 14
+#define DP83867_PHYCR_FIFO_DEPTH_MASK GENMASK(15, 14)
#define DP83867_PHYCR_RESERVED_MASK BIT(11)
+#define DP83867_PHYCR_FORCE_LINK_GOOD BIT(10)
#define DP83867_MDI_CROSSOVER 5
-#define DP83867_MDI_CROSSOVER_AUTO 2
#define DP83867_MDI_CROSSOVER_MDIX 2
#define DP83867_PHYCTRL_SGMIIEN 0x0800
#define DP83867_PHYCTRL_RXFIFO_SHIFT 12
#define DP83867_PHYCTRL_TXFIFO_SHIFT 14
/* RGMIIDCTL bits */
+#define DP83867_RGMII_TX_CLK_DELAY_MAX 0xf
#define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4
+#define DP83867_RGMII_RX_CLK_DELAY_MAX 0xf
/* CFG2 bits */
#define MII_DP83867_CFG2_SPEEDOPT_10EN 0x0040
#define MII_DP83867_CFG2_MASK 0x003F
/* User setting - can be taken from DTS */
-#define DEFAULT_RX_ID_DELAY DP83867_RGMIIDCTL_2_25_NS
-#define DEFAULT_TX_ID_DELAY DP83867_RGMIIDCTL_2_75_NS
#define DEFAULT_FIFO_DEPTH DP83867_PHYCR_FIFO_DEPTH_4_B_NIB
/* IO_MUX_CFG bits */
#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_SHIFT 8
#define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK \
GENMASK(0x1f, DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT)
/* CFG4 bits */
#define DP83867_CFG4_PORT_MIRROR_EN BIT(0)
+/* SGMIICTL bits */
+#define DP83867_SGMII_TYPE BIT(14)
+
enum {
DP83867_PORT_MIRRORING_KEEP,
DP83867_PORT_MIRRORING_EN,
};
struct dp83867_private {
- int rx_id_delay;
- int tx_id_delay;
+ u32 rx_id_delay;
+ u32 tx_id_delay;
int fifo_depth;
int io_impedance;
bool rxctrl_strap_quirk;
int port_mirroring;
+ bool set_clk_output;
unsigned int clk_output_sel;
+ bool sgmii_ref_clk_en;
};
static int dp83867_config_port_mirroring(struct phy_device *phydev)
{
struct dp83867_private *dp83867 = phydev->priv;
ofnode node;
- u16 val;
+ int ret;
node = phy_get_ofnode(phydev);
if (!ofnode_valid(node))
return -EINVAL;
- /* Keep the default value if ti,clk-output-sel is not set */
- dp83867->clk_output_sel =
- ofnode_read_u32_default(node, "ti,clk-output-sel",
- DP83867_CLK_O_SEL_REF_CLK);
+ /* Optional configuration */
+ ret = ofnode_read_u32(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) {
+ pr_debug("ti,clk-output-sel value %u out of range\n",
+ dp83867->clk_output_sel);
+ return -EINVAL;
+ }
+ }
if (ofnode_read_bool(node, "ti,max-output-impedance"))
dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX;
if (ofnode_read_bool(node, "ti,dp83867-rxctrl-strap-quirk"))
dp83867->rxctrl_strap_quirk = true;
- dp83867->rx_id_delay = ofnode_read_u32_default(node,
- "ti,rx-internal-delay",
- DEFAULT_RX_ID_DELAY);
- dp83867->tx_id_delay = ofnode_read_u32_default(node,
- "ti,tx-internal-delay",
- DEFAULT_TX_ID_DELAY);
+ /* 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) {
+ u16 val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_STRAP_STS2);
+ u16 txskew = (val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK) >>
+ DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT;
+ 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)
+ pr_warn("PHY has delays via pin strapping, but phy-mode = 'rgmii'\n"
+ "Should be 'rgmii-id' to use internal delays\n");
+ }
+
+ /* 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) {
+ ret = ofnode_read_u32(node, "ti,rx-internal-delay",
+ &dp83867->rx_id_delay);
+ if (ret) {
+ pr_debug("ti,rx-internal-delay must be specified\n");
+ return ret;
+ }
+ if (dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
+ pr_debug("ti,rx-internal-delay value of %u out of range\n",
+ dp83867->rx_id_delay);
+ return -EINVAL;
+ }
+ }
+
+ /* 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) {
+ ret = ofnode_read_u32(node, "ti,tx-internal-delay",
+ &dp83867->tx_id_delay);
+ if (ret) {
+ debug("ti,tx-internal-delay must be specified\n");
+ return ret;
+ }
+ if (dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
+ pr_debug("ti,tx-internal-delay value of %u out of range\n",
+ dp83867->tx_id_delay);
+ return -EINVAL;
+ }
+ }
dp83867->fifo_depth = ofnode_read_u32_default(node, "ti,fifo-depth",
DEFAULT_FIFO_DEPTH);
if (ofnode_read_bool(node, "enet-phy-lane-no-swap"))
dp83867->port_mirroring = DP83867_PORT_MIRRORING_DIS;
-
- /* Clock output selection if muxing property is set */
- if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK) {
- val = phy_read_mmd(phydev, DP83867_DEVADDR,
- DP83867_IO_MUX_CFG);
- val &= ~DP83867_IO_MUX_CFG_CLK_O_SEL_MASK;
- val |= (dp83867->clk_output_sel <<
- DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT);
- phy_write_mmd(phydev, DP83867_DEVADDR,
- DP83867_IO_MUX_CFG, val);
- }
+ if (ofnode_read_bool(node, "ti,sgmii-ref-clock-output-enable"))
+ dp83867->sgmii_ref_clk_en = true;
return 0;
}
{
struct dp83867_private *dp83867 = phydev->priv;
- dp83867->rx_id_delay = DEFAULT_RX_ID_DELAY;
- dp83867->tx_id_delay = DEFAULT_TX_ID_DELAY;
+ dp83867->rx_id_delay = DP83867_RGMIIDCTL_2_25_NS;
+ dp83867->tx_id_delay = DP83867_RGMIIDCTL_2_75_NS;
dp83867->fifo_depth = DEFAULT_FIFO_DEPTH;
dp83867->io_impedance = -EINVAL;
}
if (phy_interface_is_rgmii(phydev)) {
- ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL,
- (DP83867_MDI_CROSSOVER_AUTO << DP83867_MDI_CROSSOVER) |
- (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT));
- if (ret)
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL);
+ if (val < 0)
goto err_out;
+ val &= ~DP83867_PHYCR_FIFO_DEPTH_MASK;
+ val |= (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT);
+
+ /* Do not force link good */
+ val &= ~DP83867_PHYCR_FORCE_LINK_GOOD;
/* The code below checks if "port mirroring" N/A MODE4 has been
* enabled during power on bootstrap.
* register's bit 11 (marked as RESERVED).
*/
- bs = phy_read_mmd(phydev, DP83867_DEVADDR,
- DP83867_STRAP_STS1);
- val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL);
- if (bs & DP83867_STRAP_STS1_RESERVED) {
+ bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1);
+ if (bs & DP83867_STRAP_STS1_RESERVED)
val &= ~DP83867_PHYCR_RESERVED_MASK;
- phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL,
- val);
- }
- } else if (phy_interface_is_sgmii(phydev)) {
+ ret = phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_DP83867_PHYCTRL, val);
+
+ 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;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ val |= DP83867_RGMII_RX_CLK_DELAY_EN;
+
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val);
+
+ delay = (dp83867->rx_id_delay |
+ (dp83867->tx_id_delay <<
+ DP83867_RGMII_TX_CLK_DELAY_SHIFT));
+
+ phy_write_mmd(phydev, DP83867_DEVADDR,
+ DP83867_RGMIIDCTL, delay);
+ }
+
+ if (phy_interface_is_sgmii(phydev)) {
+ if (dp83867->sgmii_ref_clk_en)
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL,
+ DP83867_SGMII_TYPE);
+
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR,
(BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000));
phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_BISCR, 0x0);
}
- if (phy_interface_is_rgmii(phydev)) {
- val = phy_read_mmd(phydev, DP83867_DEVADDR,
- DP83867_RGMIICTL);
-
- 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;
-
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
- val |= DP83867_RGMII_RX_CLK_DELAY_EN;
-
+ if (dp83867->io_impedance >= 0) {
+ val = phy_read_mmd(phydev,
+ DP83867_DEVADDR,
+ DP83867_IO_MUX_CFG);
+ 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_RGMIICTL, val);
+ DP83867_IO_MUX_CFG, val);
+ }
- delay = (dp83867->rx_id_delay |
- (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT));
+ if (dp83867->port_mirroring != DP83867_PORT_MIRRORING_KEEP)
+ dp83867_config_port_mirroring(phydev);
- phy_write_mmd(phydev, DP83867_DEVADDR,
- DP83867_RGMIIDCTL, delay);
+ /* Clock output selection if muxing property is set */
+ if (dp83867->set_clk_output) {
+ val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_IO_MUX_CFG);
- if (dp83867->io_impedance >= 0) {
- val = phy_read_mmd(phydev,
- DP83867_DEVADDR,
- DP83867_IO_MUX_CFG);
- 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_IO_MUX_CFG, val);
+ if (dp83867->clk_output_sel == DP83867_CLK_O_SEL_OFF) {
+ val |= DP83867_IO_MUX_CFG_CLK_O_DISABLE;
+ } else {
+ val &= ~(DP83867_IO_MUX_CFG_CLK_O_SEL_MASK |
+ DP83867_IO_MUX_CFG_CLK_O_DISABLE);
+ val |= dp83867->clk_output_sel <<
+ DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT;
}
+ phy_write_mmd(phydev, DP83867_DEVADDR,
+ DP83867_IO_MUX_CFG, val);
}
- if (dp83867->port_mirroring != DP83867_PORT_MIRRORING_KEEP)
- dp83867_config_port_mirroring(phydev);
-
genphy_config_aneg(phydev);
return 0;
.shutdown = &genphy_shutdown,
};
-int phy_ti_init(void)
+int phy_dp83867_init(void)
{
phy_register(&DP83867_driver);
return 0;