1*0feaf415SCiprian Regus // SPDX-License-Identifier: GPL-2.0+
2*0feaf415SCiprian Regus /*
3*0feaf415SCiprian Regus * Driver for Analog Devices, Inc. ADIN1140 10BASE-T1S PHY
4*0feaf415SCiprian Regus *
5*0feaf415SCiprian Regus * Copyright 2026 Analog Devices Inc.
6*0feaf415SCiprian Regus */
7*0feaf415SCiprian Regus
8*0feaf415SCiprian Regus #include <linux/kernel.h>
9*0feaf415SCiprian Regus #include <linux/module.h>
10*0feaf415SCiprian Regus #include <linux/phy.h>
11*0feaf415SCiprian Regus
12*0feaf415SCiprian Regus #define ADIN1140_PHY_ID 0x0283be00
13*0feaf415SCiprian Regus
14*0feaf415SCiprian Regus #define ADIN1140_PCS_CTRL 0x08f3
15*0feaf415SCiprian Regus #define ADIN1140_PCS_CTRL_LOOPBACK BIT(14)
16*0feaf415SCiprian Regus
adin1140_config_aneg(struct phy_device * phydev)17*0feaf415SCiprian Regus static int adin1140_config_aneg(struct phy_device *phydev)
18*0feaf415SCiprian Regus {
19*0feaf415SCiprian Regus /* phylib tries to clear BIT(12) in MDIO_CTRL1, since AN is disabled.
20*0feaf415SCiprian Regus * However, on the ADIN1140, that field is non-standard, being used
21*0feaf415SCiprian Regus * to control the reset status of the PHY (thus it needs to remain set).
22*0feaf415SCiprian Regus */
23*0feaf415SCiprian Regus return 0;
24*0feaf415SCiprian Regus }
25*0feaf415SCiprian Regus
adin1140_loopback(struct phy_device * phydev,bool enable,int speed)26*0feaf415SCiprian Regus static int adin1140_loopback(struct phy_device *phydev, bool enable, int speed)
27*0feaf415SCiprian Regus {
28*0feaf415SCiprian Regus if (enable && speed)
29*0feaf415SCiprian Regus return -EOPNOTSUPP;
30*0feaf415SCiprian Regus
31*0feaf415SCiprian Regus return phy_modify_mmd(phydev, MDIO_MMD_PCS, ADIN1140_PCS_CTRL,
32*0feaf415SCiprian Regus ADIN1140_PCS_CTRL_LOOPBACK,
33*0feaf415SCiprian Regus enable ? ADIN1140_PCS_CTRL_LOOPBACK : 0);
34*0feaf415SCiprian Regus }
35*0feaf415SCiprian Regus
adin1140_read_status(struct phy_device * phydev)36*0feaf415SCiprian Regus static int adin1140_read_status(struct phy_device *phydev)
37*0feaf415SCiprian Regus {
38*0feaf415SCiprian Regus phydev->link = 1;
39*0feaf415SCiprian Regus phydev->duplex = DUPLEX_HALF;
40*0feaf415SCiprian Regus phydev->speed = SPEED_10;
41*0feaf415SCiprian Regus phydev->autoneg = AUTONEG_DISABLE;
42*0feaf415SCiprian Regus
43*0feaf415SCiprian Regus return 0;
44*0feaf415SCiprian Regus }
45*0feaf415SCiprian Regus
46*0feaf415SCiprian Regus static struct phy_driver adin1140_driver[] = {
47*0feaf415SCiprian Regus {
48*0feaf415SCiprian Regus PHY_ID_MATCH_EXACT(ADIN1140_PHY_ID),
49*0feaf415SCiprian Regus .name = "ADIN1140_PHY",
50*0feaf415SCiprian Regus .features = PHY_BASIC_T1S_P2MP_FEATURES,
51*0feaf415SCiprian Regus .read_status = adin1140_read_status,
52*0feaf415SCiprian Regus .config_aneg = adin1140_config_aneg,
53*0feaf415SCiprian Regus .set_loopback = adin1140_loopback,
54*0feaf415SCiprian Regus .read_mmd = genphy_read_mmd_c45,
55*0feaf415SCiprian Regus .write_mmd = genphy_write_mmd_c45,
56*0feaf415SCiprian Regus .get_plca_cfg = genphy_c45_plca_get_cfg,
57*0feaf415SCiprian Regus .set_plca_cfg = genphy_c45_plca_set_cfg,
58*0feaf415SCiprian Regus .get_plca_status = genphy_c45_plca_get_status,
59*0feaf415SCiprian Regus },
60*0feaf415SCiprian Regus };
61*0feaf415SCiprian Regus module_phy_driver(adin1140_driver);
62*0feaf415SCiprian Regus
63*0feaf415SCiprian Regus static const struct mdio_device_id __maybe_unused adin1140_tbl[] = {
64*0feaf415SCiprian Regus { PHY_ID_MATCH_EXACT(ADIN1140_PHY_ID) },
65*0feaf415SCiprian Regus { }
66*0feaf415SCiprian Regus };
67*0feaf415SCiprian Regus
68*0feaf415SCiprian Regus MODULE_DEVICE_TABLE(mdio, adin1140_tbl);
69*0feaf415SCiprian Regus
70*0feaf415SCiprian Regus MODULE_DESCRIPTION("Analog Devices, Inc. ADIN1140 10BASE-T1S PHY");
71*0feaf415SCiprian Regus MODULE_AUTHOR("Ciprian Regus <ciprian.regus@analog.com>");
72*0feaf415SCiprian Regus MODULE_LICENSE("GPL");
73