|
1 | 1 | // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
2 | 2 | /* Copyright 2017-2019 NXP */ |
3 | 3 |
|
| 4 | +#include <linux/mdio.h> |
4 | 5 | #include <linux/module.h> |
5 | 6 | #include <linux/fsl/enetc_mdio.h> |
6 | 7 | #include <linux/of_mdio.h> |
@@ -833,6 +834,135 @@ static void enetc_of_put_phy(struct enetc_ndev_priv *priv) |
833 | 834 | of_node_put(priv->phy_node); |
834 | 835 | } |
835 | 836 |
|
| 837 | +static int enetc_imdio_init(struct enetc_pf *pf, bool is_c45) |
| 838 | +{ |
| 839 | + struct device *dev = &pf->si->pdev->dev; |
| 840 | + struct enetc_mdio_priv *mdio_priv; |
| 841 | + struct phy_device *pcs; |
| 842 | + struct mii_bus *bus; |
| 843 | + int err; |
| 844 | + |
| 845 | + bus = mdiobus_alloc_size(sizeof(*mdio_priv)); |
| 846 | + if (!bus) |
| 847 | + return -ENOMEM; |
| 848 | + |
| 849 | + bus->name = "Freescale ENETC internal MDIO Bus"; |
| 850 | + bus->read = enetc_mdio_read; |
| 851 | + bus->write = enetc_mdio_write; |
| 852 | + bus->parent = dev; |
| 853 | + bus->phy_mask = ~0; |
| 854 | + mdio_priv = bus->priv; |
| 855 | + mdio_priv->hw = &pf->si->hw; |
| 856 | + mdio_priv->mdio_base = ENETC_PM_IMDIO_BASE; |
| 857 | + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev)); |
| 858 | + |
| 859 | + err = mdiobus_register(bus); |
| 860 | + if (err) { |
| 861 | + dev_err(dev, "cannot register internal MDIO bus (%d)\n", err); |
| 862 | + goto free_mdio_bus; |
| 863 | + } |
| 864 | + |
| 865 | + pcs = get_phy_device(bus, 0, is_c45); |
| 866 | + if (IS_ERR(pcs)) { |
| 867 | + err = PTR_ERR(pcs); |
| 868 | + dev_err(dev, "cannot get internal PCS PHY (%d)\n", err); |
| 869 | + goto unregister_mdiobus; |
| 870 | + } |
| 871 | + |
| 872 | + pf->imdio = bus; |
| 873 | + pf->pcs = pcs; |
| 874 | + |
| 875 | + return 0; |
| 876 | + |
| 877 | +unregister_mdiobus: |
| 878 | + mdiobus_unregister(bus); |
| 879 | +free_mdio_bus: |
| 880 | + mdiobus_free(bus); |
| 881 | + return err; |
| 882 | +} |
| 883 | + |
| 884 | +static void enetc_imdio_remove(struct enetc_pf *pf) |
| 885 | +{ |
| 886 | + if (pf->pcs) |
| 887 | + put_device(&pf->pcs->mdio.dev); |
| 888 | + if (pf->imdio) { |
| 889 | + mdiobus_unregister(pf->imdio); |
| 890 | + mdiobus_free(pf->imdio); |
| 891 | + } |
| 892 | +} |
| 893 | + |
| 894 | +static void enetc_configure_sgmii(struct phy_device *pcs) |
| 895 | +{ |
| 896 | + /* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001 |
| 897 | + * for the MAC PCS in order to acknowledge the AN. |
| 898 | + */ |
| 899 | + phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII | ADVERTISE_LPACK); |
| 900 | + |
| 901 | + phy_write(pcs, ENETC_PCS_IF_MODE, |
| 902 | + ENETC_PCS_IF_MODE_SGMII_EN | |
| 903 | + ENETC_PCS_IF_MODE_USE_SGMII_AN); |
| 904 | + |
| 905 | + /* Adjust link timer for SGMII */ |
| 906 | + phy_write(pcs, ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL); |
| 907 | + phy_write(pcs, ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL); |
| 908 | + |
| 909 | + phy_write(pcs, MII_BMCR, BMCR_ANRESTART | BMCR_ANENABLE); |
| 910 | +} |
| 911 | + |
| 912 | +static void enetc_configure_2500basex(struct phy_device *pcs) |
| 913 | +{ |
| 914 | + phy_write(pcs, ENETC_PCS_IF_MODE, |
| 915 | + ENETC_PCS_IF_MODE_SGMII_EN | |
| 916 | + ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500)); |
| 917 | + |
| 918 | + phy_write(pcs, MII_BMCR, BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_RESET); |
| 919 | +} |
| 920 | + |
| 921 | +static void enetc_configure_usxgmii(struct phy_device *pcs) |
| 922 | +{ |
| 923 | + /* Configure device ability for the USXGMII Replicator */ |
| 924 | + phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE, |
| 925 | + ADVERTISE_SGMII | ADVERTISE_LPACK | |
| 926 | + MDIO_USXGMII_FULL_DUPLEX); |
| 927 | + |
| 928 | + /* Restart PCS AN */ |
| 929 | + phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_BMCR, |
| 930 | + BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART); |
| 931 | +} |
| 932 | + |
| 933 | +static int enetc_configure_serdes(struct enetc_ndev_priv *priv) |
| 934 | +{ |
| 935 | + bool is_c45 = priv->if_mode == PHY_INTERFACE_MODE_USXGMII; |
| 936 | + struct enetc_pf *pf = enetc_si_priv(priv->si); |
| 937 | + int err; |
| 938 | + |
| 939 | + if (priv->if_mode != PHY_INTERFACE_MODE_SGMII && |
| 940 | + priv->if_mode != PHY_INTERFACE_MODE_2500BASEX && |
| 941 | + priv->if_mode != PHY_INTERFACE_MODE_USXGMII) |
| 942 | + return 0; |
| 943 | + |
| 944 | + err = enetc_imdio_init(pf, is_c45); |
| 945 | + if (err) |
| 946 | + return err; |
| 947 | + |
| 948 | + switch (priv->if_mode) { |
| 949 | + case PHY_INTERFACE_MODE_SGMII: |
| 950 | + enetc_configure_sgmii(pf->pcs); |
| 951 | + break; |
| 952 | + case PHY_INTERFACE_MODE_2500BASEX: |
| 953 | + enetc_configure_2500basex(pf->pcs); |
| 954 | + break; |
| 955 | + case PHY_INTERFACE_MODE_USXGMII: |
| 956 | + enetc_configure_usxgmii(pf->pcs); |
| 957 | + break; |
| 958 | + default: |
| 959 | + dev_err(&pf->si->pdev->dev, "Unsupported link mode %s\n", |
| 960 | + phy_modes(priv->if_mode)); |
| 961 | + } |
| 962 | + |
| 963 | + return 0; |
| 964 | +} |
| 965 | + |
836 | 966 | static int enetc_pf_probe(struct pci_dev *pdev, |
837 | 967 | const struct pci_device_id *ent) |
838 | 968 | { |
@@ -897,6 +1027,10 @@ static int enetc_pf_probe(struct pci_dev *pdev, |
897 | 1027 | if (err) |
898 | 1028 | dev_warn(&pdev->dev, "Fallback to PHY-less operation\n"); |
899 | 1029 |
|
| 1030 | + err = enetc_configure_serdes(priv); |
| 1031 | + if (err) |
| 1032 | + dev_warn(&pdev->dev, "Attempted SerDes config but failed\n"); |
| 1033 | + |
900 | 1034 | err = register_netdev(ndev); |
901 | 1035 | if (err) |
902 | 1036 | goto err_reg_netdev; |
@@ -932,6 +1066,7 @@ static void enetc_pf_remove(struct pci_dev *pdev) |
932 | 1066 | priv = netdev_priv(si->ndev); |
933 | 1067 | unregister_netdev(si->ndev); |
934 | 1068 |
|
| 1069 | + enetc_imdio_remove(pf); |
935 | 1070 | enetc_mdio_remove(pf); |
936 | 1071 | enetc_of_put_phy(priv); |
937 | 1072 |
|
|
0 commit comments