// SPDX-License-Identifier: GPL-2.0-only #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EQ5_PHY_COUNT 2 #define EQ5_PHY0_GP 0x128 #define EQ5_PHY1_GP 0x12c #define EQ5_PHY0_SGMII 0x134 #define EQ5_PHY1_SGMII 0x138 #define EQ5_GP_TX_SWRST_DIS BIT(0) // Tx SW reset #define EQ5_GP_TX_M_CLKE BIT(1) // Tx M clock enable #define EQ5_GP_SYS_SWRST_DIS BIT(2) // Sys SW reset #define EQ5_GP_SYS_M_CLKE BIT(3) // Sys clock enable #define EQ5_GP_SGMII_MODE BIT(4) // SGMII mode #define EQ5_GP_RGMII_DRV GENMASK(8, 5) // RGMII drive strength #define EQ5_SGMII_PWR_EN BIT(0) #define EQ5_SGMII_RST_DIS BIT(1) #define EQ5_SGMII_PLL_EN BIT(2) #define EQ5_SGMII_SIG_DET_SW BIT(3) #define EQ5_SGMII_PWR_STATE BIT(4) #define EQ5_SGMII_PLL_ACK BIT(18) #define EQ5_SGMII_PWR_STATE_ACK GENMASK(24, 20) /* * Instead of storing a phy_interface_t, we store this enum. * * We do not deal with RGMII timings in this generic PHY driver, * it is all handled inside the net PHY. */ enum eq5_phy_submode { EQ5_PHY_SUBMODE_SGMII, EQ5_PHY_SUBMODE_RGMII, }; struct eq5_phy_inst { struct device *dev; struct phy *phy; void __iomem *gp, *sgmii; enum eq5_phy_submode submode; bool sgmii_support; }; struct eq5_phy_private { struct eq5_phy_inst phys[EQ5_PHY_COUNT]; }; static int eq5_phy_exit(struct phy *phy) { struct eq5_phy_inst *inst = phy_get_drvdata(phy); writel(0, inst->gp); writel(0, inst->sgmii); udelay(5); /* settling time */ return 0; } static int eq5_phy_init(struct phy *phy) { struct eq5_phy_inst *inst = phy_get_drvdata(phy); u32 reg; /* * Hardware stops listening to our instructions once it is started. * It must be reset to reconfigure it. */ eq5_phy_exit(phy); reg = EQ5_GP_TX_SWRST_DIS | EQ5_GP_TX_M_CLKE | EQ5_GP_SYS_SWRST_DIS | EQ5_GP_SYS_M_CLKE | FIELD_PREP(EQ5_GP_RGMII_DRV, 0x9); writel(reg, inst->gp); return 0; } static int eq5_phy_power_on(struct phy *phy) { struct eq5_phy_inst *inst = phy_get_drvdata(phy); u32 reg; if (inst->submode == EQ5_PHY_SUBMODE_SGMII) { writel(readl(inst->gp) | EQ5_GP_SGMII_MODE, inst->gp); reg = EQ5_SGMII_PWR_EN | EQ5_SGMII_RST_DIS | EQ5_SGMII_PLL_EN; writel(reg, inst->sgmii); if (readl_poll_timeout(inst->sgmii, reg, reg & EQ5_SGMII_PLL_ACK, 1, 100)) { dev_err(inst->dev, "PLL timeout\n"); return -ETIMEDOUT; } reg = readl(inst->sgmii); reg |= EQ5_SGMII_PWR_STATE | EQ5_SGMII_SIG_DET_SW; writel(reg, inst->sgmii); } else { writel(readl(inst->gp) & ~EQ5_GP_SGMII_MODE, inst->gp); writel(0, inst->sgmii); } return 0; } static int eq5_phy_power_off(struct phy *phy) { struct eq5_phy_inst *inst = phy_get_drvdata(phy); writel(readl(inst->gp) & ~EQ5_GP_SGMII_MODE, inst->gp); writel(0, inst->sgmii); return 0; } static int eq5_phy_validate(struct phy *phy, enum phy_mode mode, int submode, union phy_configure_opts *opts) { struct eq5_phy_inst *inst = phy_get_drvdata(phy); if (mode != PHY_MODE_ETHERNET) return -EINVAL; if (phy_interface_mode_is_rgmii(submode)) return 0; if (inst->sgmii_support && submode == PHY_INTERFACE_MODE_SGMII) return 0; return -EINVAL; } static int eq5_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct eq5_phy_inst *inst = phy_get_drvdata(phy); enum eq5_phy_submode target_submode; int ret; ret = eq5_phy_validate(phy, mode, submode, NULL); if (ret) return ret; if (submode == PHY_INTERFACE_MODE_SGMII) target_submode = EQ5_PHY_SUBMODE_SGMII; else target_submode = EQ5_PHY_SUBMODE_RGMII; if (target_submode == inst->submode) return 0; inst->submode = target_submode; if (phy->power_count) { eq5_phy_init(phy); return eq5_phy_power_on(phy); } return 0; } static const struct phy_ops eq5_phy_ops = { .init = eq5_phy_init, .exit = eq5_phy_exit, .power_on = eq5_phy_power_on, .power_off = eq5_phy_power_off, .set_mode = eq5_phy_set_mode, .validate = eq5_phy_validate, }; static struct phy *eq5_phy_xlate(struct device *dev, const struct of_phandle_args *args) { struct eq5_phy_private *priv = dev_get_drvdata(dev); if (args->args_count != 1 || args->args[0] >= EQ5_PHY_COUNT) return ERR_PTR(-EINVAL); return priv->phys[args->args[0]].phy; } static int eq5_phy_probe_phy(struct device *dev, struct eq5_phy_private *priv, unsigned int index, void __iomem *base, unsigned int gp, unsigned int sgmii, bool sgmii_support) { struct eq5_phy_inst *inst = &priv->phys[index]; struct phy *phy; phy = devm_phy_create(dev, dev->of_node, &eq5_phy_ops); if (IS_ERR(phy)) return dev_err_probe(dev, PTR_ERR(phy), "failed to create PHY %u\n", index); inst->dev = dev; inst->phy = phy; inst->gp = base + gp; inst->sgmii = base + sgmii; inst->sgmii_support = sgmii_support; phy_set_drvdata(phy, inst); /* * Init inst->submode based on probe hardware state, allowing * consumers to power us on without first setting the mode. */ if (sgmii_support && (readl(inst->gp) & EQ5_GP_SGMII_MODE)) inst->submode = EQ5_PHY_SUBMODE_SGMII; else inst->submode = EQ5_PHY_SUBMODE_RGMII; return 0; } static int eq5_phy_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { struct device *dev = &adev->dev; struct phy_provider *provider; struct eq5_phy_private *priv; void __iomem *base; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; dev_set_drvdata(dev, priv); base = (void __iomem *)dev_get_platdata(dev); ret = eq5_phy_probe_phy(dev, priv, 0, base, EQ5_PHY0_GP, EQ5_PHY0_SGMII, true); if (ret) return ret; ret = eq5_phy_probe_phy(dev, priv, 1, base, EQ5_PHY1_GP, EQ5_PHY1_SGMII, false); if (ret) return ret; provider = devm_of_phy_provider_register(dev, eq5_phy_xlate); if (IS_ERR(provider)) return dev_err_probe(dev, PTR_ERR(provider), "registering provider failed\n"); return 0; } static const struct auxiliary_device_id eq5_phy_id_table[] = { { .name = "clk_eyeq.phy" }, {} }; MODULE_DEVICE_TABLE(auxiliary, eq5_phy_id_table); static struct auxiliary_driver eq5_phy_driver = { .probe = eq5_phy_probe, .id_table = eq5_phy_id_table, }; module_auxiliary_driver(eq5_phy_driver); MODULE_DESCRIPTION("EyeQ5 Ethernet PHY driver"); MODULE_AUTHOR("Théo Lebrun "); MODULE_LICENSE("GPL");