17d901a1eSXu Liang // SPDX-License-Identifier: GPL-2.0+
27d901a1eSXu Liang /* Copyright (C) 2021 Maxlinear Corporation
37d901a1eSXu Liang * Copyright (C) 2020 Intel Corporation
47d901a1eSXu Liang *
57d901a1eSXu Liang * Drivers for Maxlinear Ethernet GPY
67d901a1eSXu Liang *
77d901a1eSXu Liang */
87d901a1eSXu Liang
97d901a1eSXu Liang #include <linux/module.h>
107d901a1eSXu Liang #include <linux/bitfield.h>
1109ce6b20SMichael Walle #include <linux/hwmon.h>
125f4d487dSMichael Walle #include <linux/mutex.h>
137d901a1eSXu Liang #include <linux/phy.h>
1409ce6b20SMichael Walle #include <linux/polynomial.h>
1597a89ed1SMichael Walle #include <linux/property.h>
167d901a1eSXu Liang #include <linux/netdevice.h>
177d901a1eSXu Liang
187d901a1eSXu Liang /* PHY ID */
197d901a1eSXu Liang #define PHY_ID_GPYx15B_MASK 0xFFFFFFFC
207d901a1eSXu Liang #define PHY_ID_GPY21xB_MASK 0xFFFFFFF9
217d901a1eSXu Liang #define PHY_ID_GPY2xx 0x67C9DC00
227d901a1eSXu Liang #define PHY_ID_GPY115B 0x67C9DF00
237d901a1eSXu Liang #define PHY_ID_GPY115C 0x67C9DF10
247d901a1eSXu Liang #define PHY_ID_GPY211B 0x67C9DE08
257d901a1eSXu Liang #define PHY_ID_GPY211C 0x67C9DE10
267d901a1eSXu Liang #define PHY_ID_GPY212B 0x67C9DE09
277d901a1eSXu Liang #define PHY_ID_GPY212C 0x67C9DE20
287d901a1eSXu Liang #define PHY_ID_GPY215B 0x67C9DF04
297d901a1eSXu Liang #define PHY_ID_GPY215C 0x67C9DF20
307d901a1eSXu Liang #define PHY_ID_GPY241B 0x67C9DE40
317d901a1eSXu Liang #define PHY_ID_GPY241BM 0x67C9DE80
327d901a1eSXu Liang #define PHY_ID_GPY245B 0x67C9DEC0
337d901a1eSXu Liang
34fd8825cdSRaju Lakkaraju #define PHY_CTL1 0x13
35fd8825cdSRaju Lakkaraju #define PHY_CTL1_MDICD BIT(3)
36fd8825cdSRaju Lakkaraju #define PHY_CTL1_MDIAB BIT(2)
37fd8825cdSRaju Lakkaraju #define PHY_CTL1_AMDIX BIT(0)
387d901a1eSXu Liang #define PHY_MIISTAT 0x18 /* MII state */
397d901a1eSXu Liang #define PHY_IMASK 0x19 /* interrupt mask */
407d901a1eSXu Liang #define PHY_ISTAT 0x1A /* interrupt status */
417d901a1eSXu Liang #define PHY_FWV 0x1E /* firmware version */
427d901a1eSXu Liang
437d901a1eSXu Liang #define PHY_MIISTAT_SPD_MASK GENMASK(2, 0)
447d901a1eSXu Liang #define PHY_MIISTAT_DPX BIT(3)
457d901a1eSXu Liang #define PHY_MIISTAT_LS BIT(10)
467d901a1eSXu Liang
477d901a1eSXu Liang #define PHY_MIISTAT_SPD_10 0
487d901a1eSXu Liang #define PHY_MIISTAT_SPD_100 1
497d901a1eSXu Liang #define PHY_MIISTAT_SPD_1000 2
507d901a1eSXu Liang #define PHY_MIISTAT_SPD_2500 4
517d901a1eSXu Liang
527d901a1eSXu Liang #define PHY_IMASK_WOL BIT(15) /* Wake-on-LAN */
537d901a1eSXu Liang #define PHY_IMASK_ANC BIT(10) /* Auto-Neg complete */
547d901a1eSXu Liang #define PHY_IMASK_ADSC BIT(5) /* Link auto-downspeed detect */
557d901a1eSXu Liang #define PHY_IMASK_DXMC BIT(2) /* Duplex mode change */
567d901a1eSXu Liang #define PHY_IMASK_LSPC BIT(1) /* Link speed change */
577d901a1eSXu Liang #define PHY_IMASK_LSTC BIT(0) /* Link state change */
587d901a1eSXu Liang #define PHY_IMASK_MASK (PHY_IMASK_LSTC | \
597d901a1eSXu Liang PHY_IMASK_LSPC | \
607d901a1eSXu Liang PHY_IMASK_DXMC | \
617d901a1eSXu Liang PHY_IMASK_ADSC | \
627d901a1eSXu Liang PHY_IMASK_ANC)
637d901a1eSXu Liang
647d901a1eSXu Liang #define PHY_FWV_REL_MASK BIT(15)
651e9aa7baSMichael Walle #define PHY_FWV_MAJOR_MASK GENMASK(11, 8)
667d901a1eSXu Liang #define PHY_FWV_MINOR_MASK GENMASK(7, 0)
677d901a1eSXu Liang
68fd8825cdSRaju Lakkaraju #define PHY_PMA_MGBT_POLARITY 0x82
69fd8825cdSRaju Lakkaraju #define PHY_MDI_MDI_X_MASK GENMASK(1, 0)
70fd8825cdSRaju Lakkaraju #define PHY_MDI_MDI_X_NORMAL 0x3
71fd8825cdSRaju Lakkaraju #define PHY_MDI_MDI_X_AB 0x2
72fd8825cdSRaju Lakkaraju #define PHY_MDI_MDI_X_CD 0x1
73fd8825cdSRaju Lakkaraju #define PHY_MDI_MDI_X_CROSS 0x0
74fd8825cdSRaju Lakkaraju
757d901a1eSXu Liang /* SGMII */
767d901a1eSXu Liang #define VSPEC1_SGMII_CTRL 0x08
777d901a1eSXu Liang #define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */
787d901a1eSXu Liang #define VSPEC1_SGMII_CTRL_ANRS BIT(9) /* Restart Aneg */
797d901a1eSXu Liang #define VSPEC1_SGMII_ANEN_ANRS (VSPEC1_SGMII_CTRL_ANEN | \
807d901a1eSXu Liang VSPEC1_SGMII_CTRL_ANRS)
817d901a1eSXu Liang
8209ce6b20SMichael Walle /* Temperature sensor */
83343a5d35SMichael Walle #define VSPEC1_TEMP_STA 0x0E
84343a5d35SMichael Walle #define VSPEC1_TEMP_STA_DATA GENMASK(9, 0)
8509ce6b20SMichael Walle
865f4d487dSMichael Walle /* Mailbox */
875f4d487dSMichael Walle #define VSPEC1_MBOX_DATA 0x5
885f4d487dSMichael Walle #define VSPEC1_MBOX_ADDRLO 0x6
895f4d487dSMichael Walle #define VSPEC1_MBOX_CMD 0x7
905f4d487dSMichael Walle #define VSPEC1_MBOX_CMD_ADDRHI GENMASK(7, 0)
915f4d487dSMichael Walle #define VSPEC1_MBOX_CMD_RD (0 << 8)
925f4d487dSMichael Walle #define VSPEC1_MBOX_CMD_READY BIT(15)
935f4d487dSMichael Walle
947d901a1eSXu Liang /* WoL */
957d901a1eSXu Liang #define VPSPEC2_WOL_CTL 0x0E06
967d901a1eSXu Liang #define VPSPEC2_WOL_AD01 0x0E08
977d901a1eSXu Liang #define VPSPEC2_WOL_AD23 0x0E09
987d901a1eSXu Liang #define VPSPEC2_WOL_AD45 0x0E0A
997d901a1eSXu Liang #define WOL_EN BIT(0)
1007d901a1eSXu Liang
1015f4d487dSMichael Walle /* Internal registers, access via mbox */
1025f4d487dSMichael Walle #define REG_GPIO0_OUT 0xd3ce00
1035f4d487dSMichael Walle
1041db85870SMichael Walle struct gpy_priv {
1055f4d487dSMichael Walle /* serialize mailbox acesses */
1065f4d487dSMichael Walle struct mutex mbox_lock;
1075f4d487dSMichael Walle
1081e9aa7baSMichael Walle u8 fw_major;
1091db85870SMichael Walle u8 fw_minor;
110*123eaaf6SRaju Lakkaraju u32 wolopts;
1110ba13995SXu Liang
1120ba13995SXu Liang /* It takes 3 seconds to fully switch out of loopback mode before
1130ba13995SXu Liang * it can safely re-enter loopback mode. Record the time when
1140ba13995SXu Liang * loopback is disabled. Check and wait if necessary before loopback
1150ba13995SXu Liang * is enabled.
1160ba13995SXu Liang */
1170ba13995SXu Liang u64 lb_dis_to;
1181db85870SMichael Walle };
1191db85870SMichael Walle
1207d901a1eSXu Liang static const struct {
1211e9aa7baSMichael Walle int major;
1227d901a1eSXu Liang int minor;
1237d901a1eSXu Liang } ver_need_sgmii_reaneg[] = {
1247d901a1eSXu Liang {7, 0x6D},
1257d901a1eSXu Liang {8, 0x6D},
1267d901a1eSXu Liang {9, 0x73},
1277d901a1eSXu Liang };
1287d901a1eSXu Liang
12909ce6b20SMichael Walle #if IS_ENABLED(CONFIG_HWMON)
13009ce6b20SMichael Walle /* The original translation formulae of the temperature (in degrees of Celsius)
13109ce6b20SMichael Walle * are as follows:
13209ce6b20SMichael Walle *
13309ce6b20SMichael Walle * T = -2.5761e-11*(N^4) + 9.7332e-8*(N^3) + -1.9165e-4*(N^2) +
13409ce6b20SMichael Walle * 3.0762e-1*(N^1) + -5.2156e1
13509ce6b20SMichael Walle *
13609ce6b20SMichael Walle * where [-52.156, 137.961]C and N = [0, 1023].
13709ce6b20SMichael Walle *
13809ce6b20SMichael Walle * They must be accordingly altered to be suitable for the integer arithmetics.
13909ce6b20SMichael Walle * The technique is called 'factor redistribution', which just makes sure the
14009ce6b20SMichael Walle * multiplications and divisions are made so to have a result of the operations
14109ce6b20SMichael Walle * within the integer numbers limit. In addition we need to translate the
14209ce6b20SMichael Walle * formulae to accept millidegrees of Celsius. Here what it looks like after
14309ce6b20SMichael Walle * the alterations:
14409ce6b20SMichael Walle *
14509ce6b20SMichael Walle * T = -25761e-12*(N^4) + 97332e-9*(N^3) + -191650e-6*(N^2) +
14609ce6b20SMichael Walle * 307620e-3*(N^1) + -52156
14709ce6b20SMichael Walle *
14809ce6b20SMichael Walle * where T = [-52156, 137961]mC and N = [0, 1023].
14909ce6b20SMichael Walle */
15009ce6b20SMichael Walle static const struct polynomial poly_N_to_temp = {
15109ce6b20SMichael Walle .terms = {
15209ce6b20SMichael Walle {4, -25761, 1000, 1},
15309ce6b20SMichael Walle {3, 97332, 1000, 1},
15409ce6b20SMichael Walle {2, -191650, 1000, 1},
15509ce6b20SMichael Walle {1, 307620, 1000, 1},
15609ce6b20SMichael Walle {0, -52156, 1, 1}
15709ce6b20SMichael Walle }
15809ce6b20SMichael Walle };
15909ce6b20SMichael Walle
gpy_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * value)16009ce6b20SMichael Walle static int gpy_hwmon_read(struct device *dev,
16109ce6b20SMichael Walle enum hwmon_sensor_types type,
16209ce6b20SMichael Walle u32 attr, int channel, long *value)
16309ce6b20SMichael Walle {
16409ce6b20SMichael Walle struct phy_device *phydev = dev_get_drvdata(dev);
16509ce6b20SMichael Walle int ret;
16609ce6b20SMichael Walle
167343a5d35SMichael Walle ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_TEMP_STA);
16809ce6b20SMichael Walle if (ret < 0)
16909ce6b20SMichael Walle return ret;
17009ce6b20SMichael Walle if (!ret)
17109ce6b20SMichael Walle return -ENODATA;
17209ce6b20SMichael Walle
17309ce6b20SMichael Walle *value = polynomial_calc(&poly_N_to_temp,
174343a5d35SMichael Walle FIELD_GET(VSPEC1_TEMP_STA_DATA, ret));
17509ce6b20SMichael Walle
17609ce6b20SMichael Walle return 0;
17709ce6b20SMichael Walle }
17809ce6b20SMichael Walle
gpy_hwmon_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)17909ce6b20SMichael Walle static umode_t gpy_hwmon_is_visible(const void *data,
18009ce6b20SMichael Walle enum hwmon_sensor_types type,
18109ce6b20SMichael Walle u32 attr, int channel)
18209ce6b20SMichael Walle {
18309ce6b20SMichael Walle return 0444;
18409ce6b20SMichael Walle }
18509ce6b20SMichael Walle
1862ed84c0cSKrzysztof Kozlowski static const struct hwmon_channel_info * const gpy_hwmon_info[] = {
18709ce6b20SMichael Walle HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
18809ce6b20SMichael Walle NULL
18909ce6b20SMichael Walle };
19009ce6b20SMichael Walle
19109ce6b20SMichael Walle static const struct hwmon_ops gpy_hwmon_hwmon_ops = {
19209ce6b20SMichael Walle .is_visible = gpy_hwmon_is_visible,
19309ce6b20SMichael Walle .read = gpy_hwmon_read,
19409ce6b20SMichael Walle };
19509ce6b20SMichael Walle
19609ce6b20SMichael Walle static const struct hwmon_chip_info gpy_hwmon_chip_info = {
19709ce6b20SMichael Walle .ops = &gpy_hwmon_hwmon_ops,
19809ce6b20SMichael Walle .info = gpy_hwmon_info,
19909ce6b20SMichael Walle };
20009ce6b20SMichael Walle
gpy_hwmon_register(struct phy_device * phydev)20109ce6b20SMichael Walle static int gpy_hwmon_register(struct phy_device *phydev)
20209ce6b20SMichael Walle {
20309ce6b20SMichael Walle struct device *dev = &phydev->mdio.dev;
20409ce6b20SMichael Walle struct device *hwmon_dev;
20509ce6b20SMichael Walle char *hwmon_name;
20609ce6b20SMichael Walle
20709ce6b20SMichael Walle hwmon_name = devm_hwmon_sanitize_name(dev, dev_name(dev));
20809ce6b20SMichael Walle if (IS_ERR(hwmon_name))
20909ce6b20SMichael Walle return PTR_ERR(hwmon_name);
21009ce6b20SMichael Walle
21109ce6b20SMichael Walle hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name,
21209ce6b20SMichael Walle phydev,
21309ce6b20SMichael Walle &gpy_hwmon_chip_info,
21409ce6b20SMichael Walle NULL);
21509ce6b20SMichael Walle
21609ce6b20SMichael Walle return PTR_ERR_OR_ZERO(hwmon_dev);
21709ce6b20SMichael Walle }
21809ce6b20SMichael Walle #else
gpy_hwmon_register(struct phy_device * phydev)21909ce6b20SMichael Walle static int gpy_hwmon_register(struct phy_device *phydev)
22009ce6b20SMichael Walle {
22109ce6b20SMichael Walle return 0;
22209ce6b20SMichael Walle }
22309ce6b20SMichael Walle #endif
22409ce6b20SMichael Walle
gpy_ack_interrupt(struct phy_device * phydev)225*123eaaf6SRaju Lakkaraju static int gpy_ack_interrupt(struct phy_device *phydev)
226*123eaaf6SRaju Lakkaraju {
227*123eaaf6SRaju Lakkaraju int ret;
228*123eaaf6SRaju Lakkaraju
229*123eaaf6SRaju Lakkaraju /* Clear all pending interrupts */
230*123eaaf6SRaju Lakkaraju ret = phy_read(phydev, PHY_ISTAT);
231*123eaaf6SRaju Lakkaraju return ret < 0 ? ret : 0;
232*123eaaf6SRaju Lakkaraju }
233*123eaaf6SRaju Lakkaraju
gpy_mbox_read(struct phy_device * phydev,u32 addr)2345f4d487dSMichael Walle static int gpy_mbox_read(struct phy_device *phydev, u32 addr)
2355f4d487dSMichael Walle {
2365f4d487dSMichael Walle struct gpy_priv *priv = phydev->priv;
2375f4d487dSMichael Walle int val, ret;
2385f4d487dSMichael Walle u16 cmd;
2395f4d487dSMichael Walle
2405f4d487dSMichael Walle mutex_lock(&priv->mbox_lock);
2415f4d487dSMichael Walle
2425f4d487dSMichael Walle ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_MBOX_ADDRLO,
2435f4d487dSMichael Walle addr);
2445f4d487dSMichael Walle if (ret)
2455f4d487dSMichael Walle goto out;
2465f4d487dSMichael Walle
2475f4d487dSMichael Walle cmd = VSPEC1_MBOX_CMD_RD;
2485f4d487dSMichael Walle cmd |= FIELD_PREP(VSPEC1_MBOX_CMD_ADDRHI, addr >> 16);
2495f4d487dSMichael Walle
2505f4d487dSMichael Walle ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_MBOX_CMD, cmd);
2515f4d487dSMichael Walle if (ret)
2525f4d487dSMichael Walle goto out;
2535f4d487dSMichael Walle
2545f4d487dSMichael Walle /* The mbox read is used in the interrupt workaround. It was observed
2555f4d487dSMichael Walle * that a read might take up to 2.5ms. This is also the time for which
2565f4d487dSMichael Walle * the interrupt line is stuck low. To be on the safe side, poll the
2575f4d487dSMichael Walle * ready bit for 10ms.
2585f4d487dSMichael Walle */
2595f4d487dSMichael Walle ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
2605f4d487dSMichael Walle VSPEC1_MBOX_CMD, val,
2615f4d487dSMichael Walle (val & VSPEC1_MBOX_CMD_READY),
2625f4d487dSMichael Walle 500, 10000, false);
2635f4d487dSMichael Walle if (ret)
2645f4d487dSMichael Walle goto out;
2655f4d487dSMichael Walle
2665f4d487dSMichael Walle ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_MBOX_DATA);
2675f4d487dSMichael Walle
2685f4d487dSMichael Walle out:
2695f4d487dSMichael Walle mutex_unlock(&priv->mbox_lock);
2705f4d487dSMichael Walle return ret;
2715f4d487dSMichael Walle }
2725f4d487dSMichael Walle
gpy_config_init(struct phy_device * phydev)2737d901a1eSXu Liang static int gpy_config_init(struct phy_device *phydev)
2747d901a1eSXu Liang {
275*123eaaf6SRaju Lakkaraju /* Nothing to configure. Configuration Requirement Placeholder */
276*123eaaf6SRaju Lakkaraju return 0;
2777d901a1eSXu Liang }
2787d901a1eSXu Liang
gpy_probe(struct phy_device * phydev)2797d901a1eSXu Liang static int gpy_probe(struct phy_device *phydev)
2807d901a1eSXu Liang {
2811db85870SMichael Walle struct device *dev = &phydev->mdio.dev;
2821db85870SMichael Walle struct gpy_priv *priv;
283fc3dd036SMichael Walle int fw_version;
2847d901a1eSXu Liang int ret;
2857d901a1eSXu Liang
2867d901a1eSXu Liang if (!phydev->is_c45) {
2877d901a1eSXu Liang ret = phy_get_c45_ids(phydev);
2887d901a1eSXu Liang if (ret < 0)
2897d901a1eSXu Liang return ret;
2907d901a1eSXu Liang }
2917d901a1eSXu Liang
2921db85870SMichael Walle priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
2931db85870SMichael Walle if (!priv)
2941db85870SMichael Walle return -ENOMEM;
2951db85870SMichael Walle phydev->priv = priv;
2965f4d487dSMichael Walle mutex_init(&priv->mbox_lock);
2971db85870SMichael Walle
298519d6487SXu Liang if (!device_property_present(dev, "maxlinear,use-broken-interrupts"))
29997a89ed1SMichael Walle phydev->dev_flags |= PHY_F_NO_IRQ;
30097a89ed1SMichael Walle
301fc3dd036SMichael Walle fw_version = phy_read(phydev, PHY_FWV);
302fc3dd036SMichael Walle if (fw_version < 0)
303fc3dd036SMichael Walle return fw_version;
3041e9aa7baSMichael Walle priv->fw_major = FIELD_GET(PHY_FWV_MAJOR_MASK, fw_version);
3051db85870SMichael Walle priv->fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_version);
3067d901a1eSXu Liang
30709ce6b20SMichael Walle ret = gpy_hwmon_register(phydev);
30809ce6b20SMichael Walle if (ret)
30909ce6b20SMichael Walle return ret;
31009ce6b20SMichael Walle
3111db85870SMichael Walle /* Show GPY PHY FW version in dmesg */
312d523f2ebSMichael Walle phydev_info(phydev, "Firmware Version: %d.%d (0x%04X%s)\n",
313d523f2ebSMichael Walle priv->fw_major, priv->fw_minor, fw_version,
314d523f2ebSMichael Walle fw_version & PHY_FWV_REL_MASK ? "" : " test version");
3157d901a1eSXu Liang
3167d901a1eSXu Liang return 0;
3177d901a1eSXu Liang }
3187d901a1eSXu Liang
gpy_sgmii_need_reaneg(struct phy_device * phydev)3197d901a1eSXu Liang static bool gpy_sgmii_need_reaneg(struct phy_device *phydev)
3207d901a1eSXu Liang {
3211db85870SMichael Walle struct gpy_priv *priv = phydev->priv;
3227d901a1eSXu Liang size_t i;
3237d901a1eSXu Liang
3247d901a1eSXu Liang for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) {
3251e9aa7baSMichael Walle if (priv->fw_major != ver_need_sgmii_reaneg[i].major)
3267d901a1eSXu Liang continue;
3271db85870SMichael Walle if (priv->fw_minor < ver_need_sgmii_reaneg[i].minor)
3287d901a1eSXu Liang return true;
3297d901a1eSXu Liang break;
3307d901a1eSXu Liang }
3317d901a1eSXu Liang
3327d901a1eSXu Liang return false;
3337d901a1eSXu Liang }
3347d901a1eSXu Liang
gpy_2500basex_chk(struct phy_device * phydev)3357d901a1eSXu Liang static bool gpy_2500basex_chk(struct phy_device *phydev)
3367d901a1eSXu Liang {
3377d901a1eSXu Liang int ret;
3387d901a1eSXu Liang
3397d901a1eSXu Liang ret = phy_read(phydev, PHY_MIISTAT);
3407d901a1eSXu Liang if (ret < 0) {
3417d901a1eSXu Liang phydev_err(phydev, "Error: MDIO register access failed: %d\n",
3427d901a1eSXu Liang ret);
3437d901a1eSXu Liang return false;
3447d901a1eSXu Liang }
3457d901a1eSXu Liang
3467d901a1eSXu Liang if (!(ret & PHY_MIISTAT_LS) ||
3477d901a1eSXu Liang FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500)
3487d901a1eSXu Liang return false;
3497d901a1eSXu Liang
3507d901a1eSXu Liang phydev->speed = SPEED_2500;
3517d901a1eSXu Liang phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
3527d901a1eSXu Liang phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
3537d901a1eSXu Liang VSPEC1_SGMII_CTRL_ANEN, 0);
3547d901a1eSXu Liang return true;
3557d901a1eSXu Liang }
3567d901a1eSXu Liang
gpy_sgmii_aneg_en(struct phy_device * phydev)3577d901a1eSXu Liang static bool gpy_sgmii_aneg_en(struct phy_device *phydev)
3587d901a1eSXu Liang {
3597d901a1eSXu Liang int ret;
3607d901a1eSXu Liang
3617d901a1eSXu Liang ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL);
3627d901a1eSXu Liang if (ret < 0) {
3637d901a1eSXu Liang phydev_err(phydev, "Error: MMD register access failed: %d\n",
3647d901a1eSXu Liang ret);
3657d901a1eSXu Liang return true;
3667d901a1eSXu Liang }
3677d901a1eSXu Liang
3687d901a1eSXu Liang return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false;
3697d901a1eSXu Liang }
3707d901a1eSXu Liang
gpy_config_mdix(struct phy_device * phydev,u8 ctrl)371fd8825cdSRaju Lakkaraju static int gpy_config_mdix(struct phy_device *phydev, u8 ctrl)
372fd8825cdSRaju Lakkaraju {
373fd8825cdSRaju Lakkaraju int ret;
374fd8825cdSRaju Lakkaraju u16 val;
375fd8825cdSRaju Lakkaraju
376fd8825cdSRaju Lakkaraju switch (ctrl) {
377fd8825cdSRaju Lakkaraju case ETH_TP_MDI_AUTO:
378fd8825cdSRaju Lakkaraju val = PHY_CTL1_AMDIX;
379fd8825cdSRaju Lakkaraju break;
380fd8825cdSRaju Lakkaraju case ETH_TP_MDI_X:
381fd8825cdSRaju Lakkaraju val = (PHY_CTL1_MDIAB | PHY_CTL1_MDICD);
382fd8825cdSRaju Lakkaraju break;
383fd8825cdSRaju Lakkaraju case ETH_TP_MDI:
384fd8825cdSRaju Lakkaraju val = 0;
385fd8825cdSRaju Lakkaraju break;
386fd8825cdSRaju Lakkaraju default:
387fd8825cdSRaju Lakkaraju return 0;
388fd8825cdSRaju Lakkaraju }
389fd8825cdSRaju Lakkaraju
390fd8825cdSRaju Lakkaraju ret = phy_modify(phydev, PHY_CTL1, PHY_CTL1_AMDIX | PHY_CTL1_MDIAB |
391fd8825cdSRaju Lakkaraju PHY_CTL1_MDICD, val);
392fd8825cdSRaju Lakkaraju if (ret < 0)
393fd8825cdSRaju Lakkaraju return ret;
394fd8825cdSRaju Lakkaraju
395fd8825cdSRaju Lakkaraju return genphy_c45_restart_aneg(phydev);
396fd8825cdSRaju Lakkaraju }
397fd8825cdSRaju Lakkaraju
gpy_config_aneg(struct phy_device * phydev)3987d901a1eSXu Liang static int gpy_config_aneg(struct phy_device *phydev)
3997d901a1eSXu Liang {
4007d901a1eSXu Liang bool changed = false;
4017d901a1eSXu Liang u32 adv;
4027d901a1eSXu Liang int ret;
4037d901a1eSXu Liang
4047d901a1eSXu Liang if (phydev->autoneg == AUTONEG_DISABLE) {
4057d901a1eSXu Liang /* Configure half duplex with genphy_setup_forced,
4067d901a1eSXu Liang * because genphy_c45_pma_setup_forced does not support.
4077d901a1eSXu Liang */
4087d901a1eSXu Liang return phydev->duplex != DUPLEX_FULL
4097d901a1eSXu Liang ? genphy_setup_forced(phydev)
4107d901a1eSXu Liang : genphy_c45_pma_setup_forced(phydev);
4117d901a1eSXu Liang }
4127d901a1eSXu Liang
413fd8825cdSRaju Lakkaraju ret = gpy_config_mdix(phydev, phydev->mdix_ctrl);
414fd8825cdSRaju Lakkaraju if (ret < 0)
415fd8825cdSRaju Lakkaraju return ret;
416fd8825cdSRaju Lakkaraju
4177d901a1eSXu Liang ret = genphy_c45_an_config_aneg(phydev);
4187d901a1eSXu Liang if (ret < 0)
4197d901a1eSXu Liang return ret;
4207d901a1eSXu Liang if (ret > 0)
4217d901a1eSXu Liang changed = true;
4227d901a1eSXu Liang
4237d901a1eSXu Liang adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
4247d901a1eSXu Liang ret = phy_modify_changed(phydev, MII_CTRL1000,
4257d901a1eSXu Liang ADVERTISE_1000FULL | ADVERTISE_1000HALF,
4267d901a1eSXu Liang adv);
4277d901a1eSXu Liang if (ret < 0)
4287d901a1eSXu Liang return ret;
4297d901a1eSXu Liang if (ret > 0)
4307d901a1eSXu Liang changed = true;
4317d901a1eSXu Liang
4327d901a1eSXu Liang ret = genphy_c45_check_and_restart_aneg(phydev, changed);
4337d901a1eSXu Liang if (ret < 0)
4347d901a1eSXu Liang return ret;
4357d901a1eSXu Liang
4367d901a1eSXu Liang if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
4377d901a1eSXu Liang phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
4387d901a1eSXu Liang return 0;
4397d901a1eSXu Liang
4407d901a1eSXu Liang /* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is
4417d901a1eSXu Liang * disabled.
4427d901a1eSXu Liang */
4437d901a1eSXu Liang if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) ||
4447d901a1eSXu Liang !gpy_sgmii_aneg_en(phydev))
4457d901a1eSXu Liang return 0;
4467d901a1eSXu Liang
4477d901a1eSXu Liang /* There is a design constraint in GPY2xx device where SGMII AN is
4487d901a1eSXu Liang * only triggered when there is change of speed. If, PHY link
4497d901a1eSXu Liang * partner`s speed is still same even after PHY TPI is down and up
4507d901a1eSXu Liang * again, SGMII AN is not triggered and hence no new in-band message
4517d901a1eSXu Liang * from GPY to MAC side SGMII.
4527d901a1eSXu Liang * This could cause an issue during power up, when PHY is up prior to
4537d901a1eSXu Liang * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII
4547d901a1eSXu Liang * wouldn`t receive new in-band message from GPY with correct link
4557d901a1eSXu Liang * status, speed and duplex info.
4567d901a1eSXu Liang *
4577d901a1eSXu Liang * 1) If PHY is already up and TPI link status is still down (such as
4587d901a1eSXu Liang * hard reboot), TPI link status is polled for 4 seconds before
4597d901a1eSXu Liang * retriggerring SGMII AN.
4607d901a1eSXu Liang * 2) If PHY is already up and TPI link status is also up (such as soft
4617d901a1eSXu Liang * reboot), polling of TPI link status is not needed and SGMII AN is
4627d901a1eSXu Liang * immediately retriggered.
4637d901a1eSXu Liang * 3) Other conditions such as PHY is down, speed change etc, skip
4647d901a1eSXu Liang * retriggering SGMII AN. Note: in case of speed change, GPY FW will
4657d901a1eSXu Liang * initiate SGMII AN.
4667d901a1eSXu Liang */
4677d901a1eSXu Liang
4687d901a1eSXu Liang if (phydev->state != PHY_UP)
4697d901a1eSXu Liang return 0;
4707d901a1eSXu Liang
4717d901a1eSXu Liang ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS,
4727d901a1eSXu Liang 20000, 4000000, false);
4737d901a1eSXu Liang if (ret == -ETIMEDOUT)
4747d901a1eSXu Liang return 0;
4757d901a1eSXu Liang else if (ret < 0)
4767d901a1eSXu Liang return ret;
4777d901a1eSXu Liang
4787d901a1eSXu Liang /* Trigger SGMII AN. */
4797d901a1eSXu Liang return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
4807d901a1eSXu Liang VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS);
4817d901a1eSXu Liang }
4827d901a1eSXu Liang
gpy_update_mdix(struct phy_device * phydev)483fd8825cdSRaju Lakkaraju static int gpy_update_mdix(struct phy_device *phydev)
484fd8825cdSRaju Lakkaraju {
485fd8825cdSRaju Lakkaraju int ret;
486fd8825cdSRaju Lakkaraju
487fd8825cdSRaju Lakkaraju ret = phy_read(phydev, PHY_CTL1);
488fd8825cdSRaju Lakkaraju if (ret < 0)
489fd8825cdSRaju Lakkaraju return ret;
490fd8825cdSRaju Lakkaraju
491fd8825cdSRaju Lakkaraju if (ret & PHY_CTL1_AMDIX)
492fd8825cdSRaju Lakkaraju phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
493fd8825cdSRaju Lakkaraju else
494fd8825cdSRaju Lakkaraju if (ret & PHY_CTL1_MDICD || ret & PHY_CTL1_MDIAB)
495fd8825cdSRaju Lakkaraju phydev->mdix_ctrl = ETH_TP_MDI_X;
496fd8825cdSRaju Lakkaraju else
497fd8825cdSRaju Lakkaraju phydev->mdix_ctrl = ETH_TP_MDI;
498fd8825cdSRaju Lakkaraju
499fd8825cdSRaju Lakkaraju ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PHY_PMA_MGBT_POLARITY);
500fd8825cdSRaju Lakkaraju if (ret < 0)
501fd8825cdSRaju Lakkaraju return ret;
502fd8825cdSRaju Lakkaraju
503fd8825cdSRaju Lakkaraju if ((ret & PHY_MDI_MDI_X_MASK) < PHY_MDI_MDI_X_NORMAL)
504fd8825cdSRaju Lakkaraju phydev->mdix = ETH_TP_MDI_X;
505fd8825cdSRaju Lakkaraju else
506fd8825cdSRaju Lakkaraju phydev->mdix = ETH_TP_MDI;
507fd8825cdSRaju Lakkaraju
508fd8825cdSRaju Lakkaraju return 0;
509fd8825cdSRaju Lakkaraju }
510fd8825cdSRaju Lakkaraju
gpy_update_interface(struct phy_device * phydev)5117a495ddeSRaju Lakkaraju static int gpy_update_interface(struct phy_device *phydev)
5127d901a1eSXu Liang {
5137d901a1eSXu Liang int ret;
5147d901a1eSXu Liang
5157d901a1eSXu Liang /* Interface mode is fixed for USXGMII and integrated PHY */
5167d901a1eSXu Liang if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
5177d901a1eSXu Liang phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
5187a495ddeSRaju Lakkaraju return -EINVAL;
5197d901a1eSXu Liang
5207d901a1eSXu Liang /* Automatically switch SERDES interface between SGMII and 2500-BaseX
5217d901a1eSXu Liang * according to speed. Disable ANEG in 2500-BaseX mode.
5227d901a1eSXu Liang */
5237d901a1eSXu Liang switch (phydev->speed) {
5247d901a1eSXu Liang case SPEED_2500:
5257d901a1eSXu Liang phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
5267d901a1eSXu Liang ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
5277d901a1eSXu Liang VSPEC1_SGMII_CTRL_ANEN, 0);
5287a495ddeSRaju Lakkaraju if (ret < 0) {
5297d901a1eSXu Liang phydev_err(phydev,
5307d901a1eSXu Liang "Error: Disable of SGMII ANEG failed: %d\n",
5317d901a1eSXu Liang ret);
5327a495ddeSRaju Lakkaraju return ret;
5337a495ddeSRaju Lakkaraju }
5347d901a1eSXu Liang break;
5357d901a1eSXu Liang case SPEED_1000:
5367d901a1eSXu Liang case SPEED_100:
5377d901a1eSXu Liang case SPEED_10:
5387d901a1eSXu Liang phydev->interface = PHY_INTERFACE_MODE_SGMII;
5397d901a1eSXu Liang if (gpy_sgmii_aneg_en(phydev))
5407d901a1eSXu Liang break;
5417d901a1eSXu Liang /* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed
5427d901a1eSXu Liang * if ANEG is disabled (in 2500-BaseX mode).
5437d901a1eSXu Liang */
5447d901a1eSXu Liang ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
5457d901a1eSXu Liang VSPEC1_SGMII_ANEN_ANRS,
5467d901a1eSXu Liang VSPEC1_SGMII_ANEN_ANRS);
5477a495ddeSRaju Lakkaraju if (ret < 0) {
5487d901a1eSXu Liang phydev_err(phydev,
5497d901a1eSXu Liang "Error: Enable of SGMII ANEG failed: %d\n",
5507d901a1eSXu Liang ret);
5517a495ddeSRaju Lakkaraju return ret;
5527a495ddeSRaju Lakkaraju }
5537d901a1eSXu Liang break;
5547d901a1eSXu Liang }
555311abcddSRaju Lakkaraju
5567a495ddeSRaju Lakkaraju if (phydev->speed == SPEED_2500 || phydev->speed == SPEED_1000) {
5577a495ddeSRaju Lakkaraju ret = genphy_read_master_slave(phydev);
5587a495ddeSRaju Lakkaraju if (ret < 0)
5597a495ddeSRaju Lakkaraju return ret;
5607a495ddeSRaju Lakkaraju }
5617a495ddeSRaju Lakkaraju
562fd8825cdSRaju Lakkaraju return gpy_update_mdix(phydev);
5637d901a1eSXu Liang }
5647d901a1eSXu Liang
gpy_read_status(struct phy_device * phydev)5657d901a1eSXu Liang static int gpy_read_status(struct phy_device *phydev)
5667d901a1eSXu Liang {
5677d901a1eSXu Liang int ret;
5687d901a1eSXu Liang
5697d901a1eSXu Liang ret = genphy_update_link(phydev);
5707d901a1eSXu Liang if (ret)
5717d901a1eSXu Liang return ret;
5727d901a1eSXu Liang
5737d901a1eSXu Liang phydev->speed = SPEED_UNKNOWN;
5747d901a1eSXu Liang phydev->duplex = DUPLEX_UNKNOWN;
5757d901a1eSXu Liang phydev->pause = 0;
5767d901a1eSXu Liang phydev->asym_pause = 0;
5777d901a1eSXu Liang
5787d901a1eSXu Liang if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
5797d901a1eSXu Liang ret = genphy_c45_read_lpa(phydev);
5807d901a1eSXu Liang if (ret < 0)
5817d901a1eSXu Liang return ret;
5827d901a1eSXu Liang
5837d901a1eSXu Liang /* Read the link partner's 1G advertisement */
5847d901a1eSXu Liang ret = phy_read(phydev, MII_STAT1000);
5857d901a1eSXu Liang if (ret < 0)
5867d901a1eSXu Liang return ret;
5877d901a1eSXu Liang mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
5887d901a1eSXu Liang } else if (phydev->autoneg == AUTONEG_DISABLE) {
5897d901a1eSXu Liang linkmode_zero(phydev->lp_advertising);
5907d901a1eSXu Liang }
5917d901a1eSXu Liang
5927d901a1eSXu Liang ret = phy_read(phydev, PHY_MIISTAT);
5937d901a1eSXu Liang if (ret < 0)
5947d901a1eSXu Liang return ret;
5957d901a1eSXu Liang
5967d901a1eSXu Liang phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0;
5977d901a1eSXu Liang phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
5987d901a1eSXu Liang switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) {
5997d901a1eSXu Liang case PHY_MIISTAT_SPD_10:
6007d901a1eSXu Liang phydev->speed = SPEED_10;
6017d901a1eSXu Liang break;
6027d901a1eSXu Liang case PHY_MIISTAT_SPD_100:
6037d901a1eSXu Liang phydev->speed = SPEED_100;
6047d901a1eSXu Liang break;
6057d901a1eSXu Liang case PHY_MIISTAT_SPD_1000:
6067d901a1eSXu Liang phydev->speed = SPEED_1000;
6077d901a1eSXu Liang break;
6087d901a1eSXu Liang case PHY_MIISTAT_SPD_2500:
6097d901a1eSXu Liang phydev->speed = SPEED_2500;
6107d901a1eSXu Liang break;
6117d901a1eSXu Liang }
6127d901a1eSXu Liang
6137a495ddeSRaju Lakkaraju if (phydev->link) {
6147a495ddeSRaju Lakkaraju ret = gpy_update_interface(phydev);
6157a495ddeSRaju Lakkaraju if (ret < 0)
6167a495ddeSRaju Lakkaraju return ret;
6177a495ddeSRaju Lakkaraju }
6187d901a1eSXu Liang
6197d901a1eSXu Liang return 0;
6207d901a1eSXu Liang }
6217d901a1eSXu Liang
gpy_config_intr(struct phy_device * phydev)6227d901a1eSXu Liang static int gpy_config_intr(struct phy_device *phydev)
6237d901a1eSXu Liang {
624*123eaaf6SRaju Lakkaraju struct gpy_priv *priv = phydev->priv;
6257d901a1eSXu Liang u16 mask = 0;
626*123eaaf6SRaju Lakkaraju int ret;
627*123eaaf6SRaju Lakkaraju
628*123eaaf6SRaju Lakkaraju ret = gpy_ack_interrupt(phydev);
629*123eaaf6SRaju Lakkaraju if (ret)
630*123eaaf6SRaju Lakkaraju return ret;
6317d901a1eSXu Liang
6327d901a1eSXu Liang if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
6337d901a1eSXu Liang mask = PHY_IMASK_MASK;
6347d901a1eSXu Liang
635*123eaaf6SRaju Lakkaraju if (priv->wolopts & WAKE_MAGIC)
636*123eaaf6SRaju Lakkaraju mask |= PHY_IMASK_WOL;
637*123eaaf6SRaju Lakkaraju
638*123eaaf6SRaju Lakkaraju if (priv->wolopts & WAKE_PHY)
639*123eaaf6SRaju Lakkaraju mask |= PHY_IMASK_LSTC;
640*123eaaf6SRaju Lakkaraju
6417d901a1eSXu Liang return phy_write(phydev, PHY_IMASK, mask);
6427d901a1eSXu Liang }
6437d901a1eSXu Liang
gpy_handle_interrupt(struct phy_device * phydev)6447d901a1eSXu Liang static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
6457d901a1eSXu Liang {
6467d901a1eSXu Liang int reg;
6477d901a1eSXu Liang
6487d901a1eSXu Liang reg = phy_read(phydev, PHY_ISTAT);
6497d901a1eSXu Liang if (reg < 0) {
6507d901a1eSXu Liang phy_error(phydev);
6517d901a1eSXu Liang return IRQ_NONE;
6527d901a1eSXu Liang }
6537d901a1eSXu Liang
6547d901a1eSXu Liang if (!(reg & PHY_IMASK_MASK))
6557d901a1eSXu Liang return IRQ_NONE;
6567d901a1eSXu Liang
6575f4d487dSMichael Walle /* The PHY might leave the interrupt line asserted even after PHY_ISTAT
6585f4d487dSMichael Walle * is read. To avoid interrupt storms, delay the interrupt handling as
6595f4d487dSMichael Walle * long as the PHY drives the interrupt line. An internal bus read will
6605f4d487dSMichael Walle * stall as long as the interrupt line is asserted, thus just read a
6615f4d487dSMichael Walle * random register here.
6625f4d487dSMichael Walle * Because we cannot access the internal bus at all while the interrupt
6635f4d487dSMichael Walle * is driven by the PHY, there is no way to make the interrupt line
6645f4d487dSMichael Walle * unstuck (e.g. by changing the pinmux to GPIO input) during that time
6655f4d487dSMichael Walle * frame. Therefore, polling is the best we can do and won't do any more
6665f4d487dSMichael Walle * harm.
6675f4d487dSMichael Walle * It was observed that this bug happens on link state and link speed
668519d6487SXu Liang * changes independent of the firmware version.
6695f4d487dSMichael Walle */
670519d6487SXu Liang if (reg & (PHY_IMASK_LSTC | PHY_IMASK_LSPC)) {
6715f4d487dSMichael Walle reg = gpy_mbox_read(phydev, REG_GPIO0_OUT);
6725f4d487dSMichael Walle if (reg < 0) {
6735f4d487dSMichael Walle phy_error(phydev);
6745f4d487dSMichael Walle return IRQ_NONE;
6755f4d487dSMichael Walle }
6765f4d487dSMichael Walle }
6775f4d487dSMichael Walle
6787d901a1eSXu Liang phy_trigger_machine(phydev);
6797d901a1eSXu Liang
6807d901a1eSXu Liang return IRQ_HANDLED;
6817d901a1eSXu Liang }
6827d901a1eSXu Liang
gpy_set_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)6837d901a1eSXu Liang static int gpy_set_wol(struct phy_device *phydev,
6847d901a1eSXu Liang struct ethtool_wolinfo *wol)
6857d901a1eSXu Liang {
6867d901a1eSXu Liang struct net_device *attach_dev = phydev->attached_dev;
687*123eaaf6SRaju Lakkaraju struct gpy_priv *priv = phydev->priv;
6887d901a1eSXu Liang int ret;
6897d901a1eSXu Liang
6907d901a1eSXu Liang if (wol->wolopts & WAKE_MAGIC) {
6917d901a1eSXu Liang /* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5
6927d901a1eSXu Liang * VPSPEC2_WOL_AD45 = Byte0:Byte1
6937d901a1eSXu Liang * VPSPEC2_WOL_AD23 = Byte2:Byte3
6947d901a1eSXu Liang * VPSPEC2_WOL_AD01 = Byte4:Byte5
6957d901a1eSXu Liang */
6967d901a1eSXu Liang ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
6977d901a1eSXu Liang VPSPEC2_WOL_AD45,
6987d901a1eSXu Liang ((attach_dev->dev_addr[0] << 8) |
6997d901a1eSXu Liang attach_dev->dev_addr[1]));
7007d901a1eSXu Liang if (ret < 0)
7017d901a1eSXu Liang return ret;
7027d901a1eSXu Liang
7037d901a1eSXu Liang ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
7047d901a1eSXu Liang VPSPEC2_WOL_AD23,
7057d901a1eSXu Liang ((attach_dev->dev_addr[2] << 8) |
7067d901a1eSXu Liang attach_dev->dev_addr[3]));
7077d901a1eSXu Liang if (ret < 0)
7087d901a1eSXu Liang return ret;
7097d901a1eSXu Liang
7107d901a1eSXu Liang ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
7117d901a1eSXu Liang VPSPEC2_WOL_AD01,
7127d901a1eSXu Liang ((attach_dev->dev_addr[4] << 8) |
7137d901a1eSXu Liang attach_dev->dev_addr[5]));
7147d901a1eSXu Liang if (ret < 0)
7157d901a1eSXu Liang return ret;
7167d901a1eSXu Liang
7177d901a1eSXu Liang /* Enable the WOL interrupt */
7187d901a1eSXu Liang ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL);
7197d901a1eSXu Liang if (ret < 0)
7207d901a1eSXu Liang return ret;
7217d901a1eSXu Liang
7227d901a1eSXu Liang /* Enable magic packet matching */
7237d901a1eSXu Liang ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
7247d901a1eSXu Liang VPSPEC2_WOL_CTL,
7257d901a1eSXu Liang WOL_EN);
7267d901a1eSXu Liang if (ret < 0)
7277d901a1eSXu Liang return ret;
7287d901a1eSXu Liang
7297d901a1eSXu Liang /* Clear the interrupt status register.
7307d901a1eSXu Liang * Only WoL is enabled so clear all.
7317d901a1eSXu Liang */
7327d901a1eSXu Liang ret = phy_read(phydev, PHY_ISTAT);
7337d901a1eSXu Liang if (ret < 0)
7347d901a1eSXu Liang return ret;
735*123eaaf6SRaju Lakkaraju
736*123eaaf6SRaju Lakkaraju priv->wolopts |= WAKE_MAGIC;
7377d901a1eSXu Liang } else {
7387d901a1eSXu Liang /* Disable magic packet matching */
7397d901a1eSXu Liang ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
7407d901a1eSXu Liang VPSPEC2_WOL_CTL,
7417d901a1eSXu Liang WOL_EN);
7427d901a1eSXu Liang if (ret < 0)
7437d901a1eSXu Liang return ret;
744*123eaaf6SRaju Lakkaraju
745*123eaaf6SRaju Lakkaraju /* Disable the WOL interrupt */
746*123eaaf6SRaju Lakkaraju ret = phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_WOL);
747*123eaaf6SRaju Lakkaraju if (ret < 0)
748*123eaaf6SRaju Lakkaraju return ret;
749*123eaaf6SRaju Lakkaraju
750*123eaaf6SRaju Lakkaraju priv->wolopts &= ~WAKE_MAGIC;
7517d901a1eSXu Liang }
7527d901a1eSXu Liang
7537d901a1eSXu Liang if (wol->wolopts & WAKE_PHY) {
7547d901a1eSXu Liang /* Enable the link state change interrupt */
7557d901a1eSXu Liang ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
7567d901a1eSXu Liang if (ret < 0)
7577d901a1eSXu Liang return ret;
7587d901a1eSXu Liang
7597d901a1eSXu Liang /* Clear the interrupt status register */
7607d901a1eSXu Liang ret = phy_read(phydev, PHY_ISTAT);
7617d901a1eSXu Liang if (ret < 0)
7627d901a1eSXu Liang return ret;
7637d901a1eSXu Liang
7647d901a1eSXu Liang if (ret & (PHY_IMASK_MASK & ~PHY_IMASK_LSTC))
7657d901a1eSXu Liang phy_trigger_machine(phydev);
7667d901a1eSXu Liang
767*123eaaf6SRaju Lakkaraju priv->wolopts |= WAKE_PHY;
7687d901a1eSXu Liang return 0;
7697d901a1eSXu Liang }
7707d901a1eSXu Liang
771*123eaaf6SRaju Lakkaraju priv->wolopts &= ~WAKE_PHY;
7727d901a1eSXu Liang /* Disable the link state change interrupt */
7737d901a1eSXu Liang return phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
7747d901a1eSXu Liang }
7757d901a1eSXu Liang
gpy_get_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)7767d901a1eSXu Liang static void gpy_get_wol(struct phy_device *phydev,
7777d901a1eSXu Liang struct ethtool_wolinfo *wol)
7787d901a1eSXu Liang {
779*123eaaf6SRaju Lakkaraju struct gpy_priv *priv = phydev->priv;
7807d901a1eSXu Liang
7817d901a1eSXu Liang wol->supported = WAKE_MAGIC | WAKE_PHY;
782*123eaaf6SRaju Lakkaraju wol->wolopts = priv->wolopts;
7837d901a1eSXu Liang }
7847d901a1eSXu Liang
gpy_loopback(struct phy_device * phydev,bool enable)7857d901a1eSXu Liang static int gpy_loopback(struct phy_device *phydev, bool enable)
7867d901a1eSXu Liang {
7870ba13995SXu Liang struct gpy_priv *priv = phydev->priv;
7880ba13995SXu Liang u16 set = 0;
7897d901a1eSXu Liang int ret;
7907d901a1eSXu Liang
7910ba13995SXu Liang if (enable) {
7920ba13995SXu Liang u64 now = get_jiffies_64();
7930ba13995SXu Liang
7940ba13995SXu Liang /* wait until 3 seconds from last disable */
7950ba13995SXu Liang if (time_before64(now, priv->lb_dis_to))
7960ba13995SXu Liang msleep(jiffies64_to_msecs(priv->lb_dis_to - now));
7970ba13995SXu Liang
7980ba13995SXu Liang set = BMCR_LOOPBACK;
7997d901a1eSXu Liang }
8007d901a1eSXu Liang
8010ba13995SXu Liang ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, set);
8020ba13995SXu Liang if (ret <= 0)
8037d901a1eSXu Liang return ret;
8040ba13995SXu Liang
8050ba13995SXu Liang if (enable) {
8060ba13995SXu Liang /* It takes some time for PHY device to switch into
8070ba13995SXu Liang * loopback mode.
8080ba13995SXu Liang */
8090ba13995SXu Liang msleep(100);
8100ba13995SXu Liang } else {
8110ba13995SXu Liang priv->lb_dis_to = get_jiffies_64() + HZ * 3;
8120ba13995SXu Liang }
8130ba13995SXu Liang
8140ba13995SXu Liang return 0;
8157d901a1eSXu Liang }
8167d901a1eSXu Liang
gpy115_loopback(struct phy_device * phydev,bool enable)8173b1b6e82SXu Liang static int gpy115_loopback(struct phy_device *phydev, bool enable)
8183b1b6e82SXu Liang {
8191db85870SMichael Walle struct gpy_priv *priv = phydev->priv;
8203b1b6e82SXu Liang
8213b1b6e82SXu Liang if (enable)
8223b1b6e82SXu Liang return gpy_loopback(phydev, enable);
8233b1b6e82SXu Liang
8241db85870SMichael Walle if (priv->fw_minor > 0x76)
8253b1b6e82SXu Liang return gpy_loopback(phydev, 0);
8263b1b6e82SXu Liang
8273b1b6e82SXu Liang return genphy_soft_reset(phydev);
8283b1b6e82SXu Liang }
8293b1b6e82SXu Liang
8307d901a1eSXu Liang static struct phy_driver gpy_drivers[] = {
8317d901a1eSXu Liang {
8327d901a1eSXu Liang PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
8337d901a1eSXu Liang .name = "Maxlinear Ethernet GPY2xx",
8347d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
8357d901a1eSXu Liang .config_init = gpy_config_init,
8367d901a1eSXu Liang .probe = gpy_probe,
8377d901a1eSXu Liang .suspend = genphy_suspend,
8387d901a1eSXu Liang .resume = genphy_resume,
8397d901a1eSXu Liang .config_aneg = gpy_config_aneg,
8407d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
8417d901a1eSXu Liang .read_status = gpy_read_status,
8427d901a1eSXu Liang .config_intr = gpy_config_intr,
8437d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
8447d901a1eSXu Liang .set_wol = gpy_set_wol,
8457d901a1eSXu Liang .get_wol = gpy_get_wol,
8467d901a1eSXu Liang .set_loopback = gpy_loopback,
8477d901a1eSXu Liang },
8487d901a1eSXu Liang {
8497d901a1eSXu Liang .phy_id = PHY_ID_GPY115B,
8507d901a1eSXu Liang .phy_id_mask = PHY_ID_GPYx15B_MASK,
8517d901a1eSXu Liang .name = "Maxlinear Ethernet GPY115B",
8527d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
8537d901a1eSXu Liang .config_init = gpy_config_init,
8547d901a1eSXu Liang .probe = gpy_probe,
8557d901a1eSXu Liang .suspend = genphy_suspend,
8567d901a1eSXu Liang .resume = genphy_resume,
8577d901a1eSXu Liang .config_aneg = gpy_config_aneg,
8587d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
8597d901a1eSXu Liang .read_status = gpy_read_status,
8607d901a1eSXu Liang .config_intr = gpy_config_intr,
8617d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
8627d901a1eSXu Liang .set_wol = gpy_set_wol,
8637d901a1eSXu Liang .get_wol = gpy_get_wol,
8643b1b6e82SXu Liang .set_loopback = gpy115_loopback,
8657d901a1eSXu Liang },
8667d901a1eSXu Liang {
8677d901a1eSXu Liang PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
8687d901a1eSXu Liang .name = "Maxlinear Ethernet GPY115C",
8697d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
8707d901a1eSXu Liang .config_init = gpy_config_init,
8717d901a1eSXu Liang .probe = gpy_probe,
8727d901a1eSXu Liang .suspend = genphy_suspend,
8737d901a1eSXu Liang .resume = genphy_resume,
8747d901a1eSXu Liang .config_aneg = gpy_config_aneg,
8757d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
8767d901a1eSXu Liang .read_status = gpy_read_status,
8777d901a1eSXu Liang .config_intr = gpy_config_intr,
8787d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
8797d901a1eSXu Liang .set_wol = gpy_set_wol,
8807d901a1eSXu Liang .get_wol = gpy_get_wol,
8813b1b6e82SXu Liang .set_loopback = gpy115_loopback,
8827d901a1eSXu Liang },
8837d901a1eSXu Liang {
8847d901a1eSXu Liang .phy_id = PHY_ID_GPY211B,
8857d901a1eSXu Liang .phy_id_mask = PHY_ID_GPY21xB_MASK,
8867d901a1eSXu Liang .name = "Maxlinear Ethernet GPY211B",
8877d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
8887d901a1eSXu Liang .config_init = gpy_config_init,
8897d901a1eSXu Liang .probe = gpy_probe,
8907d901a1eSXu Liang .suspend = genphy_suspend,
8917d901a1eSXu Liang .resume = genphy_resume,
8927d901a1eSXu Liang .config_aneg = gpy_config_aneg,
8937d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
8947d901a1eSXu Liang .read_status = gpy_read_status,
8957d901a1eSXu Liang .config_intr = gpy_config_intr,
8967d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
8977d901a1eSXu Liang .set_wol = gpy_set_wol,
8987d901a1eSXu Liang .get_wol = gpy_get_wol,
8997d901a1eSXu Liang .set_loopback = gpy_loopback,
9007d901a1eSXu Liang },
9017d901a1eSXu Liang {
9027d901a1eSXu Liang PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
9037d901a1eSXu Liang .name = "Maxlinear Ethernet GPY211C",
9047d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
9057d901a1eSXu Liang .config_init = gpy_config_init,
9067d901a1eSXu Liang .probe = gpy_probe,
9077d901a1eSXu Liang .suspend = genphy_suspend,
9087d901a1eSXu Liang .resume = genphy_resume,
9097d901a1eSXu Liang .config_aneg = gpy_config_aneg,
9107d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
9117d901a1eSXu Liang .read_status = gpy_read_status,
9127d901a1eSXu Liang .config_intr = gpy_config_intr,
9137d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
9147d901a1eSXu Liang .set_wol = gpy_set_wol,
9157d901a1eSXu Liang .get_wol = gpy_get_wol,
9167d901a1eSXu Liang .set_loopback = gpy_loopback,
9177d901a1eSXu Liang },
9187d901a1eSXu Liang {
9197d901a1eSXu Liang .phy_id = PHY_ID_GPY212B,
9207d901a1eSXu Liang .phy_id_mask = PHY_ID_GPY21xB_MASK,
9217d901a1eSXu Liang .name = "Maxlinear Ethernet GPY212B",
9227d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
9237d901a1eSXu Liang .config_init = gpy_config_init,
9247d901a1eSXu Liang .probe = gpy_probe,
9257d901a1eSXu Liang .suspend = genphy_suspend,
9267d901a1eSXu Liang .resume = genphy_resume,
9277d901a1eSXu Liang .config_aneg = gpy_config_aneg,
9287d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
9297d901a1eSXu Liang .read_status = gpy_read_status,
9307d901a1eSXu Liang .config_intr = gpy_config_intr,
9317d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
9327d901a1eSXu Liang .set_wol = gpy_set_wol,
9337d901a1eSXu Liang .get_wol = gpy_get_wol,
9347d901a1eSXu Liang .set_loopback = gpy_loopback,
9357d901a1eSXu Liang },
9367d901a1eSXu Liang {
9377d901a1eSXu Liang PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
9387d901a1eSXu Liang .name = "Maxlinear Ethernet GPY212C",
9397d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
9407d901a1eSXu Liang .config_init = gpy_config_init,
9417d901a1eSXu Liang .probe = gpy_probe,
9427d901a1eSXu Liang .suspend = genphy_suspend,
9437d901a1eSXu Liang .resume = genphy_resume,
9447d901a1eSXu Liang .config_aneg = gpy_config_aneg,
9457d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
9467d901a1eSXu Liang .read_status = gpy_read_status,
9477d901a1eSXu Liang .config_intr = gpy_config_intr,
9487d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
9497d901a1eSXu Liang .set_wol = gpy_set_wol,
9507d901a1eSXu Liang .get_wol = gpy_get_wol,
9517d901a1eSXu Liang .set_loopback = gpy_loopback,
9527d901a1eSXu Liang },
9537d901a1eSXu Liang {
9547d901a1eSXu Liang .phy_id = PHY_ID_GPY215B,
9557d901a1eSXu Liang .phy_id_mask = PHY_ID_GPYx15B_MASK,
9567d901a1eSXu Liang .name = "Maxlinear Ethernet GPY215B",
9577d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
9587d901a1eSXu Liang .config_init = gpy_config_init,
9597d901a1eSXu Liang .probe = gpy_probe,
9607d901a1eSXu Liang .suspend = genphy_suspend,
9617d901a1eSXu Liang .resume = genphy_resume,
9627d901a1eSXu Liang .config_aneg = gpy_config_aneg,
9637d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
9647d901a1eSXu Liang .read_status = gpy_read_status,
9657d901a1eSXu Liang .config_intr = gpy_config_intr,
9667d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
9677d901a1eSXu Liang .set_wol = gpy_set_wol,
9687d901a1eSXu Liang .get_wol = gpy_get_wol,
9697d901a1eSXu Liang .set_loopback = gpy_loopback,
9707d901a1eSXu Liang },
9717d901a1eSXu Liang {
9727d901a1eSXu Liang PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
9737d901a1eSXu Liang .name = "Maxlinear Ethernet GPY215C",
9747d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
9757d901a1eSXu Liang .config_init = gpy_config_init,
9767d901a1eSXu Liang .probe = gpy_probe,
9777d901a1eSXu Liang .suspend = genphy_suspend,
9787d901a1eSXu Liang .resume = genphy_resume,
9797d901a1eSXu Liang .config_aneg = gpy_config_aneg,
9807d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
9817d901a1eSXu Liang .read_status = gpy_read_status,
9827d901a1eSXu Liang .config_intr = gpy_config_intr,
9837d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
9847d901a1eSXu Liang .set_wol = gpy_set_wol,
9857d901a1eSXu Liang .get_wol = gpy_get_wol,
9867d901a1eSXu Liang .set_loopback = gpy_loopback,
9877d901a1eSXu Liang },
9887d901a1eSXu Liang {
9897d901a1eSXu Liang PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
9907d901a1eSXu Liang .name = "Maxlinear Ethernet GPY241B",
9917d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
9927d901a1eSXu Liang .config_init = gpy_config_init,
9937d901a1eSXu Liang .probe = gpy_probe,
9947d901a1eSXu Liang .suspend = genphy_suspend,
9957d901a1eSXu Liang .resume = genphy_resume,
9967d901a1eSXu Liang .config_aneg = gpy_config_aneg,
9977d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
9987d901a1eSXu Liang .read_status = gpy_read_status,
9997d901a1eSXu Liang .config_intr = gpy_config_intr,
10007d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
10017d901a1eSXu Liang .set_wol = gpy_set_wol,
10027d901a1eSXu Liang .get_wol = gpy_get_wol,
10037d901a1eSXu Liang .set_loopback = gpy_loopback,
10047d901a1eSXu Liang },
10057d901a1eSXu Liang {
10067d901a1eSXu Liang PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
10077d901a1eSXu Liang .name = "Maxlinear Ethernet GPY241BM",
10087d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
10097d901a1eSXu Liang .config_init = gpy_config_init,
10107d901a1eSXu Liang .probe = gpy_probe,
10117d901a1eSXu Liang .suspend = genphy_suspend,
10127d901a1eSXu Liang .resume = genphy_resume,
10137d901a1eSXu Liang .config_aneg = gpy_config_aneg,
10147d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
10157d901a1eSXu Liang .read_status = gpy_read_status,
10167d901a1eSXu Liang .config_intr = gpy_config_intr,
10177d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
10187d901a1eSXu Liang .set_wol = gpy_set_wol,
10197d901a1eSXu Liang .get_wol = gpy_get_wol,
10207d901a1eSXu Liang .set_loopback = gpy_loopback,
10217d901a1eSXu Liang },
10227d901a1eSXu Liang {
10237d901a1eSXu Liang PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
10247d901a1eSXu Liang .name = "Maxlinear Ethernet GPY245B",
10257d901a1eSXu Liang .get_features = genphy_c45_pma_read_abilities,
10267d901a1eSXu Liang .config_init = gpy_config_init,
10277d901a1eSXu Liang .probe = gpy_probe,
10287d901a1eSXu Liang .suspend = genphy_suspend,
10297d901a1eSXu Liang .resume = genphy_resume,
10307d901a1eSXu Liang .config_aneg = gpy_config_aneg,
10317d901a1eSXu Liang .aneg_done = genphy_c45_aneg_done,
10327d901a1eSXu Liang .read_status = gpy_read_status,
10337d901a1eSXu Liang .config_intr = gpy_config_intr,
10347d901a1eSXu Liang .handle_interrupt = gpy_handle_interrupt,
10357d901a1eSXu Liang .set_wol = gpy_set_wol,
10367d901a1eSXu Liang .get_wol = gpy_get_wol,
10377d901a1eSXu Liang .set_loopback = gpy_loopback,
10387d901a1eSXu Liang },
10397d901a1eSXu Liang };
10407d901a1eSXu Liang module_phy_driver(gpy_drivers);
10417d901a1eSXu Liang
10427d901a1eSXu Liang static struct mdio_device_id __maybe_unused gpy_tbl[] = {
10437d901a1eSXu Liang {PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)},
10447d901a1eSXu Liang {PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK},
10457d901a1eSXu Liang {PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)},
10467d901a1eSXu Liang {PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK},
10477d901a1eSXu Liang {PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)},
10487d901a1eSXu Liang {PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK},
10497d901a1eSXu Liang {PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)},
10507d901a1eSXu Liang {PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK},
10517d901a1eSXu Liang {PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)},
10527d901a1eSXu Liang {PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
10537d901a1eSXu Liang {PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
10547d901a1eSXu Liang {PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
10557d901a1eSXu Liang { }
10567d901a1eSXu Liang };
10577d901a1eSXu Liang MODULE_DEVICE_TABLE(mdio, gpy_tbl);
10587d901a1eSXu Liang
10597d901a1eSXu Liang MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
10607d901a1eSXu Liang MODULE_AUTHOR("Xu Liang");
10617d901a1eSXu Liang MODULE_LICENSE("GPL");
1062