Other Parts Discussed in Thread: DP83TG720S-Q1
Hi,
Can TI provide us with the driver for the DP83TC812S? Our SDK version is ti-processor-sdk-linux-adas-j784s4-evm-09_00_01_03
Thanks!
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Hi,
Can TI provide us with the driver for the DP83TC812S? Our SDK version is ti-processor-sdk-linux-adas-j784s4-evm-09_00_01_03
Thanks!
Hello Yao,
https://www.ti.com/tool/ETHERNET-SW#downloads
Here you can find our Ethernet PHY Linux drivers & tools including the driver for the DP83TC812.
Regards,
Avtar
I use file dp83tc812.c in https://git.ti.com/gitweb?p=ti-analog-linux-kernel/analog-linux.git;a=blob;f=drivers/net/phy/dp83tc812.c;h=0c594375f188d40299603628a0636947cfea35bf;hb=f973f3deb81b1f44aa4c33b275a342e28fc982fd
It shows build error:
make[1]: Leaving directory '/home/share/test/ethfw_vision/TDA4_J784S4_SDK/linux/board-support/linux-6.1.46+gitAUTOINC+f8110d9ce8-gf8110d9ce8' make -j 16 -C /home/share/test/ethfw_vision/TDA4_J784S4_SDK/linux/board-support/linux-6.1.46+gitAUTOINC+f8110d9ce8-gf8110d9ce8 ARCH=arm64 CROSS_COMPILE=/home/share/test/ethfw_vision/TDA4_J784S4_SDK/linux/external-toolchain-dir/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu- Image make[1]: Entering directory '/home/share/test/ethfw_vision/TDA4_J784S4_SDK/linux/board-support/linux-6.1.46+gitAUTOINC+f8110d9ce8-gf8110d9ce8' CALL scripts/checksyscalls.sh CC drivers/net/phy/dp83tc812.o drivers/net/phy/dp83tc812.c:524:18: error: ‘struct phy_driver’ has no member named ‘ack_interrupt’; did you mean ‘handle_interrupt’? 524 | .ack_interrupt = dp83812_ack_interrupt, \ | ^~~~~~~~~~~~~ drivers/net/phy/dp83tc812.c:534:9: note: in expansion of macro ‘DP83812_PHY_DRIVER’ 534 | DP83812_PHY_DRIVER(DP83TC812_CS1_0_PHY_ID, "TI DP83TC812CS1.0"), | ^~~~~~~~~~~~~~~~~~ drivers/net/phy/dp83tc812.c:524:18: error: ‘struct phy_driver’ has no member named ‘ack_interrupt’; did you mean ‘handle_interrupt’? 524 | .ack_interrupt = dp83812_ack_interrupt, \ | ^~~~~~~~~~~~~ drivers/net/phy/dp83tc812.c:535:9: note: in expansion of macro ‘DP83812_PHY_DRIVER’ 535 | DP83812_PHY_DRIVER(DP83TC812_CS2_0_PHY_ID, "TI DP83TC812CS2.0"), | ^~~~~~~~~~~~~~~~~~ drivers/net/phy/dp83tc812.c:524:18: error: ‘struct phy_driver’ has no member named ‘ack_interrupt’; did you mean ‘handle_interrupt’? 524 | .ack_interrupt = dp83812_ack_interrupt, \ | ^~~~~~~~~~~~~ drivers/net/phy/dp83tc812.c:536:9: note: in expansion of macro ‘DP83812_PHY_DRIVER’ 536 | DP83812_PHY_DRIVER(DP83TC813_CS2_0_PHY_ID, "TI DP83TC813CS2.0"), | ^~~~~~~~~~~~~~~~~~ drivers/net/phy/dp83tc812.c:524:18: error: ‘struct phy_driver’ has no member named ‘ack_interrupt’; did you mean ‘handle_interrupt’? 524 | .ack_interrupt = dp83812_ack_interrupt, \ | ^~~~~~~~~~~~~ drivers/net/phy/dp83tc812.c:537:9: note: in expansion of macro ‘DP83812_PHY_DRIVER’ 537 | DP83812_PHY_DRIVER(DP83TC814_CS2_0_PHY_ID, "TI DP83TC814CS2.0"), | ^~~~~~~~~~~~~~~~~~ make[5]: *** [scripts/Makefile.build:250: drivers/net/phy/dp83tc812.o] Error 1 make[4]: *** [scripts/Makefile.build:502: drivers/net/phy] Error 2 make[3]: *** [scripts/Makefile.build:502: drivers/net] Error 2 make[3]: *** Waiting for unfinished jobs.... make[2]: *** [scripts/Makefile.build:502: drivers] Error 2 make[1]: *** [Makefile:2012: .] Error 2 make[1]: Leaving directory '/home/share/test/ethfw_vision/TDA4_J784S4_SDK/linux/board-support/linux-6.1.46+gitAUTOINC+f8110d9ce8-gf8110d9ce8' make: *** [makerules/Makefile_linux:7: linux] Error 2
This driver has an obvious error, lack of 'phydev->priv = dp83720;':
static int dp83812_probe(struct phy_device *phydev) { struct dp83812_private *dp83812; int ret; dp83812 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83812), GFP_KERNEL); if (!dp83812) return -ENOMEM; phydev->priv = dp83720; //lack of this, it will case coredump. ret = dp83812_read_straps(phydev); if (ret) return ret; switch (phydev->phy_id) { case DP83TC812_CS1_0_PHY_ID: dp83812->chip = DP83812_CS1; break; case DP83TC812_CS2_0_PHY_ID: dp83812->chip = DP83812_CS2; break; case DP83TC813_CS2_0_PHY_ID: dp83812->chip = DP83813_CS2; break; case DP83TC814_CS2_0_PHY_ID: dp83812->chip = DP83814_CS2; break; default: return -EINVAL; }; /* vikram : above code added to switch between different phy ids */ return dp83812_config_init(phydev); }
Hello,
I see the problem, here is a new driver that we have tested to work on kernel 5.10. Please try and let me know if the same errors are being seen.
// SPDX-License-Identifier: GPL-2.0 /* * Driver for the Texas Instruments DP83TC812 PHY * * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ * */ #include <linux/ethtool.h> #include <linux/etherdevice.h> #include <linux/kernel.h> #include <linux/mii.h> #include <linux/module.h> #include <linux/of.h> #include <linux/phy.h> #include <linux/netdevice.h> #define DP83TC812_CS1_0_PHY_ID 0x2000a270 #define DP83TC812_CS2_0_PHY_ID 0x2000a271 #define DP83TC813_CS2_0_PHY_ID 0x2000a211 #define DP83TC814_CS2_0_PHY_ID 0x2000a261 #define DP83812_DEVADDR 0x1f #define DP83812_DEVADDR_MMD1 0x1 #define DP83812_STRAP 0x45d #define MII_DP83812_SGMII_CTRL 0x608 #define SGMII_CONFIG_VAL 0x027B #define MII_DP83812_RGMII_CTRL 0x600 #define MII_DP83812_INT_STAT1 0x12 #define MII_DP83812_INT_STAT2 0x13 #define MII_DP83812_INT_STAT3 0x18 #define MII_DP83812_RESET_CTRL 0x1f #define DP83812_HW_RESET BIT(15) #define DP83812_SW_RESET BIT(14) /* INT_STAT1 bits */ #define DP83812_RX_ERR_CNT_HALF_FULL_INT_EN BIT(0) #define DP83812_TX_ERR_CNT_HALF_FULL_INT_EN BIT(1) #define DP83812_MS_TRAIN_DONE_INT_EN BIT(2) #define DP83812_ESD_EVENT_INT_EN BIT(3) #define DP83812_LINK_STAT_INT_EN BIT(5) #define DP83812_ENERGY_DET_INT_EN BIT(6) #define DP83812_LINK_QUAL_INT_EN BIT(7) /* INT_STAT2 bits */ #define DP83812_JABBER_INT_EN BIT(0) #define DP83812_POL_INT_EN BIT(1) #define DP83812_SLEEP_MODE_INT_EN BIT(2) #define DP83812_OVERTEMP_INT_EN BIT(3) #define DP83812_FIFO_INT_EN BIT(4) #define DP83812_PAGE_RXD_INT_EN BIT(5) #define DP83812_OVERVOLTAGE_INT_EN BIT(6) #define DP83812_UNDERVOLTAGE_INT_EN BIT(7) /* INT_STAT3 bits */ #define DP83812_LPS_INT_EN BIT(0) #define DP83812_WUP_INT_EN BIT(1) #define DP83812_WAKE_REQ_INT_EN BIT(2) #define DP83811_NO_FRAME_INT_EN BIT(3) #define DP83811_POR_DONE_INT_EN BIT(4) #define DP83812_SLEEP_FAIL_INT_EN BIT(5) /* RGMII_CTRL bits */ #define DP83812_RGMII_EN BIT(3) /* SGMII CTRL bits */ #define DP83812_SGMII_AUTO_NEG_EN BIT(0) #define DP83812_SGMII_EN BIT(9) /* Strap bits */ #define DP83812_MASTER_MODE BIT(9) #define DP83812_RGMII_IS_EN BIT(7) /* RGMII ID CTRL */ #define DP83812_RGMII_ID_CTRL 0x602 #define DP83812_RX_CLK_SHIFT BIT(1) #define DP83812_TX_CLK_SHIFT BIT(0) enum dp83812_chip_type { DP83812_CS1 = 0, DP83812_CS2, DP83813_CS2, DP83814_CS2, }; struct dp83812_init_reg { int reg; int val; }; static const struct dp83812_init_reg dp83812_master_cs1_0_init[] = { {0x523, 0x0001}, {0x800, 0xf864}, {0x803, 0x1552}, {0x804, 0x1a66}, {0x805, 0x1f7b}, {0x81f, 0x2a88}, {0x825, 0x40e5}, {0x82b, 0x7f3f}, {0x830, 0x0543}, {0x836, 0x5008}, {0x83a, 0x08e0}, {0x83b, 0x0845}, {0x83e, 0x0445}, {0x855, 0x9b9a}, {0x85f, 0x2010}, {0x860, 0x6040}, {0x86c, 0x1333}, {0x86b, 0x3e10}, {0x872, 0x88c0}, {0x873, 0x0003}, {0x879, 0x000f}, {0x87b, 0x0070}, {0x87c, 0x003f}, {0x89e, 0x00aa}, {0x523, 0x0000}, }; static const struct dp83812_init_reg dp83812_master_cs2_0_init[] = { {0x523, 0x0001}, {0x81C, 0x0fe2}, {0x872, 0x0300}, {0x879, 0x0f00}, {0x806, 0x2952}, {0x807, 0x3361}, {0x808, 0x3D7B}, {0x83E, 0x045F}, {0x834, 0x8000}, {0x862, 0x00E8}, {0x896, 0x32CB}, {0x03E, 0x0009}, {0x01f, 0x4000}, {0x523, 0x0000}, }; static const struct dp83812_init_reg dp83812_slave_cs1_0_init[] = { {0x523, 0x0001}, {0x803, 0x1b52}, {0x804, 0x216c}, {0x805, 0x277b}, {0x827, 0x3000}, {0x830, 0x0543}, {0x83a, 0x0020}, {0x83c, 0x0001}, {0x855, 0x9b9a}, {0x85f, 0x2010}, {0x860, 0x6040}, {0x86c, 0x0333}, {0x872, 0x88c0}, {0x873, 0x0021}, {0x879, 0x000f}, {0x87b, 0x0070}, {0x87c, 0x0002}, {0x897, 0x003f}, {0x89e, 0x00a2}, {0x510, 0x000f}, {0x523, 0x0000}, }; static const struct dp83812_init_reg dp83812_slave_cs2_0_init[] = { {0x523, 0x0001}, {0x873, 0x0821}, {0x896, 0x22ff}, {0x89E, 0x0000}, {0x01f, 0x4000}, {0x523, 0x0000}, }; struct dp83812_private { int chip; bool is_master; bool is_rgmii; bool is_sgmii; }; static int dp83812_read_straps(struct phy_device *phydev) { struct dp83812_private *dp83812 = phydev->priv; int strap; strap = phy_read_mmd(phydev, DP83812_DEVADDR, DP83812_STRAP); if (strap < 0) return strap; printk("%s: Strap is 0x%X\n", __func__, strap); if (strap & DP83812_MASTER_MODE) dp83812->is_master = true; if (strap & DP83812_RGMII_IS_EN) dp83812->is_rgmii = true; return 0; }; static int dp83812_reset(struct phy_device *phydev, bool hw_reset) { int ret; if (hw_reset) ret = phy_write_mmd(phydev, DP83812_DEVADDR, MII_DP83812_RESET_CTRL, DP83812_HW_RESET); else ret = phy_write_mmd(phydev, DP83812_DEVADDR, MII_DP83812_RESET_CTRL, DP83812_SW_RESET); if (ret) return ret; mdelay(100); return 0; } static int dp83812_phy_reset(struct phy_device *phydev) { int err; int ret; err = phy_write_mmd(phydev, DP83812_DEVADDR, MII_DP83812_RESET_CTRL, DP83812_HW_RESET); if (err < 0) return err; ret = dp83812_read_straps(phydev); if (ret) return ret; return 0; } static int dp83812_write_seq(struct phy_device *phydev, const struct dp83812_init_reg *init_data, int size) { int ret; int i; for (i = 0; i < size; i++) { ret = phy_write_mmd(phydev, DP83812_DEVADDR, init_data[i].reg, init_data[i].val); if (ret) return ret; } return 0; } static int dp83812_chip_init(struct phy_device *phydev) { struct dp83812_private *dp83812 = phydev->priv; int ret; ret = dp83812_reset(phydev, true); if (ret) return ret; phydev->autoneg = AUTONEG_DISABLE; phydev->speed = SPEED_100; phydev->duplex = DUPLEX_FULL; linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported); if (dp83812->is_master) ret = phy_write_mmd(phydev, DP83812_DEVADDR_MMD1, 0x0834, 0xc001); // ret = phy_write_mmd(phydev, DP83812_DEVADDR, 0x0834, 0xc001); else ret = phy_write_mmd(phydev, DP83812_DEVADDR_MMD1, 0x0834, 0x8001); // ret = phy_write_mmd(phydev, DP83812_DEVADDR, 0x0834, 0x8001); switch (dp83812->chip) { case DP83812_CS1: if (dp83812->is_master) ret = dp83812_write_seq(phydev, dp83812_master_cs1_0_init, ARRAY_SIZE(dp83812_master_cs1_0_init)); else ret = dp83812_write_seq(phydev, dp83812_slave_cs1_0_init, ARRAY_SIZE(dp83812_slave_cs1_0_init)); break; case DP83812_CS2: if (dp83812->is_master) ret = dp83812_write_seq(phydev, dp83812_master_cs2_0_init, ARRAY_SIZE(dp83812_master_cs2_0_init)); else ret = dp83812_write_seq(phydev, dp83812_slave_cs2_0_init, ARRAY_SIZE(dp83812_slave_cs2_0_init)); break; case DP83813_CS2: if (dp83812->is_master) ret = dp83812_write_seq(phydev, dp83812_master_cs2_0_init, ARRAY_SIZE(dp83812_master_cs2_0_init)); else ret = dp83812_write_seq(phydev, dp83812_slave_cs2_0_init, ARRAY_SIZE(dp83812_slave_cs2_0_init)); break; case DP83814_CS2: if (dp83812->is_master) ret = dp83812_write_seq(phydev, dp83812_master_cs2_0_init, ARRAY_SIZE(dp83812_master_cs2_0_init)); else ret = dp83812_write_seq(phydev, dp83812_slave_cs2_0_init, ARRAY_SIZE(dp83812_slave_cs2_0_init)); break; default: return -EINVAL; }; if (ret) return ret; mdelay(10); // phy_write_mmd(phydev, DP83812_DEVADDR, 0x523, 0x00); /* Do a soft reset to restart the PHY with updated values */ return dp83812_reset(phydev, false); } static int dp83812_config_init(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; s32 rx_int_delay; s32 tx_int_delay; int rgmii_delay; int value, ret; ret = dp83812_chip_init(phydev); if (ret) return ret; if (phy_interface_is_rgmii(phydev)) { rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, true); if (rx_int_delay <= 0) rgmii_delay = 0; else rgmii_delay = DP83812_RX_CLK_SHIFT; tx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, false); if (tx_int_delay <= 0) rgmii_delay &= ~DP83812_TX_CLK_SHIFT; else rgmii_delay |= DP83812_TX_CLK_SHIFT; if (rgmii_delay) { ret = phy_set_bits_mmd(phydev, DP83812_DEVADDR_MMD1, DP83812_RGMII_ID_CTRL, rgmii_delay); if (ret) return ret; } } if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { value = phy_read(phydev, MII_DP83812_SGMII_CTRL); ret = phy_write_mmd(phydev, DP83812_DEVADDR, MII_DP83812_SGMII_CTRL, SGMII_CONFIG_VAL); if (ret < 0) return ret; } return 0; } static int dp83812_ack_interrupt(struct phy_device *phydev) { int err; err = phy_read(phydev, MII_DP83812_INT_STAT1); if (err < 0) return err; err = phy_read(phydev, MII_DP83812_INT_STAT2); if (err < 0) return err; err = phy_read(phydev, MII_DP83812_INT_STAT3); if (err < 0) return err; return 0; } static int dp83812_config_intr(struct phy_device *phydev) { int misr_status, err; if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { misr_status = phy_read(phydev, MII_DP83812_INT_STAT1); if (misr_status < 0) return misr_status; misr_status |= (DP83812_ESD_EVENT_INT_EN | DP83812_LINK_STAT_INT_EN | DP83812_ENERGY_DET_INT_EN | DP83812_LINK_QUAL_INT_EN); err = phy_write(phydev, MII_DP83812_INT_STAT1, misr_status); if (err < 0) return err; misr_status = phy_read(phydev, MII_DP83812_INT_STAT2); if (misr_status < 0) return misr_status; misr_status |= (DP83812_SLEEP_MODE_INT_EN | DP83812_OVERTEMP_INT_EN | DP83812_OVERVOLTAGE_INT_EN | DP83812_UNDERVOLTAGE_INT_EN); err = phy_write(phydev, MII_DP83812_INT_STAT2, misr_status); if (err < 0) return err; misr_status = phy_read(phydev, MII_DP83812_INT_STAT3); if (misr_status < 0) return misr_status; misr_status |= (DP83812_LPS_INT_EN | DP83812_WAKE_REQ_INT_EN | DP83811_NO_FRAME_INT_EN | DP83811_POR_DONE_INT_EN); err = phy_write(phydev, MII_DP83812_INT_STAT3, misr_status); } else { err = phy_write(phydev, MII_DP83812_INT_STAT1, 0); if (err < 0) return err; err = phy_write(phydev, MII_DP83812_INT_STAT2, 0); if (err < 0) return err; err = phy_write(phydev, MII_DP83812_INT_STAT3, 0); } return err; } #if 0 static irqreturn_t dp83812_handle_interrupt(struct phy_device *phydev) { bool trigger_machine = false; int irq_status; /* The INT_STAT registers 1, 2 and 3 are holding the interrupt status * in the upper half (15:8), while the lower half (7:0) is used for * controlling the interrupt enable state of those individual interrupt * sources. To determine the possible interrupt sources, just read the * INT_STAT* register and use it directly to know which interrupts have * been enabled previously or not. */ irq_status = phy_read(phydev, MII_DP83812_INT_STAT1); if (irq_status < 0) { phy_error(phydev); return IRQ_NONE; } if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) trigger_machine = true; irq_status = phy_read(phydev, MII_DP83812_INT_STAT2); if (irq_status < 0) { phy_error(phydev); return IRQ_NONE; } if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) trigger_machine = true; irq_status = phy_read(phydev, MII_DP83812_INT_STAT3); if (irq_status < 0) { phy_error(phydev); return IRQ_NONE; } if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) trigger_machine = true; if (!trigger_machine) return IRQ_NONE; phy_trigger_machine(phydev); return IRQ_HANDLED; } #endif static int dp83812_config_aneg(struct phy_device *phydev) { int value, ret; if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { value = phy_read(phydev, MII_DP83812_SGMII_CTRL); ret = phy_write_mmd(phydev, DP83812_DEVADDR, MII_DP83812_SGMII_CTRL, SGMII_CONFIG_VAL); if (ret < 0) return ret; } return genphy_config_aneg(phydev); } static int dp83812_probe(struct phy_device *phydev) { struct dp83812_private *dp83812; int ret; dp83812 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83812), GFP_KERNEL); if (!dp83812) return -ENOMEM; phydev->priv = dp83812; ret = dp83812_read_straps(phydev); if (ret) return ret; switch (phydev->phy_id) { case DP83TC812_CS1_0_PHY_ID: dp83812->chip = DP83812_CS1; break; case DP83TC812_CS2_0_PHY_ID: dp83812->chip = DP83812_CS2; break; case DP83TC813_CS2_0_PHY_ID: dp83812->chip = DP83813_CS2; break; case DP83TC814_CS2_0_PHY_ID: dp83812->chip = DP83814_CS2; break; default: return -EINVAL; }; /* vikram : above code added to switch between different phy ids */ return dp83812_config_init(phydev); } #define DP83812_PHY_DRIVER(_id, _name) \ { \ PHY_ID_MATCH_EXACT(_id), \ .name = (_name), \ .probe = dp83812_probe, \ /* PHY_BASIC_FEATURES */ \ .soft_reset = dp83812_phy_reset, \ .config_init = dp83812_config_init, \ .config_aneg = dp83812_config_aneg, \ .ack_interrupt = dp83812_ack_interrupt, \ /*if 0 \ .handle_interrupt = dp83812_handle_interrupt, \ #endif */ \ .config_intr = dp83812_config_intr, \ .suspend = genphy_suspend, \ .resume = genphy_resume, \ } static struct phy_driver dp83812_driver[] = { DP83812_PHY_DRIVER(DP83TC812_CS1_0_PHY_ID, "TI DP83TC812CS1.0"), DP83812_PHY_DRIVER(DP83TC812_CS2_0_PHY_ID, "TI DP83TC812CS2.0"), DP83812_PHY_DRIVER(DP83TC813_CS2_0_PHY_ID, "TI DP83TC813CS2.0"), DP83812_PHY_DRIVER(DP83TC814_CS2_0_PHY_ID, "TI DP83TC814CS2.0"), }; module_phy_driver(dp83812_driver); static struct mdio_device_id __maybe_unused dp83812_tbl[] = { { PHY_ID_MATCH_EXACT(DP83TC812_CS1_0_PHY_ID) }, { PHY_ID_MATCH_EXACT(DP83TC812_CS2_0_PHY_ID) }, { PHY_ID_MATCH_EXACT(DP83TC813_CS2_0_PHY_ID) }, { PHY_ID_MATCH_EXACT(DP83TC814_CS2_0_PHY_ID) }, { }, }; MODULE_DEVICE_TABLE(mdio, dp83812_tbl); MODULE_DESCRIPTION("Texas Instruments DP83TC812 PHY driver"); MODULE_AUTHOR("Hari Nagalla <hnagalla@ti.com"); MODULE_LICENSE("GPL");
Regards,
Avtar
1. In my sdk, 'ack_interrupt' does not exsit, and build fail, so I delete it, open the 'handle_interrupt = dp83812_handle_interrupt'. Can I do so?
2.With you latest driver, phy works well, and can you supply 'DP83TG720S-Q1' driver, it has the same issue.
I see the problem, here is a new driver that we have tested to work on kernel 5.10.
Thank you so much to support us.
Hello Zheng,
1. Yes you can do so.
2.
// SPDX-License-Identifier: GPL-2.0-only /* Driver for the Texas Instruments DP83TG720 PHY * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ */ #include <linux/ethtool.h> #include <linux/etherdevice.h> #include <linux/kernel.h> #include <linux/mii.h> #include <linux/module.h> #include <linux/phy.h> #include <linux/netdevice.h> #define DP83TG720ES1_PHY_ID 0x2000a280 #define DP83TG720ES2_PHY_ID 0x2000a281 #define DP83TG720CS_1_0_PHY_ID 0x2000a283 #define DP83TG720CS_1_1_PHY_ID 0x2000a284 #define DP83TG721CS_1_0_PHY_ID 0x2000a290 #define DP83720_DEVADDR 0x1f #define DP83720_DEVADDR_MMD1 0x1 #define MII_DP83720_INT_STAT1 0x12 #define MII_DP83720_INT_STAT2 0x13 #define MII_DP83720_INT_STAT3 0x18 #define MII_DP83720_RESET_CTRL 0x1f #define DP83720_HW_RESET BIT(15) #define DP83720_SW_RESET BIT(14) #define DP83720_STRAP 0x45d #define DP83720_SGMII_CTRL 0x608 #define SGMII_CONFIG_VAL 0x027B /* INT_STAT1 bits */ #define DP83720_ANEG_COMPLETE_INT_EN BIT(2) #define DP83720_ESD_EVENT_INT_EN BIT(3) #define DP83720_LINK_STAT_INT_EN BIT(5) #define DP83720_ENERGY_DET_INT_EN BIT(6) #define DP83720_LINK_QUAL_INT_EN BIT(7) /* INT_STAT2 bits */ #define DP83720_SLEEP_MODE_INT_EN BIT(2) #define DP83720_OVERTEMP_INT_EN BIT(3) #define DP83720_OVERVOLTAGE_INT_EN BIT(6) #define DP83720_UNDERVOLTAGE_INT_EN BIT(7) /* INT_STAT3 bits */ #define DP83720_LPS_INT_EN BIT(0) #define DP83720_WAKE_REQ_EN BIT(1) #define DP83720_NO_FRAME_INT_EN BIT(2) #define DP83720_POR_DONE_INT_EN BIT(3) /* SGMII CTRL bits */ #define DP83720_SGMII_AUTO_NEG_EN BIT(0) #define DP83720_SGMII_EN BIT(9) /* Strap bits */ #define DP83720_MASTER_MODE BIT(5) #define DP83720_RGMII_IS_EN BIT(12) #define DP83720_SGMII_IS_EN BIT(13) #define DP83720_RX_SHIFT_EN BIT(14) #define DP83720_TX_SHIFT_EN BIT(15) /* RGMII ID CTRL */ #define DP83720_RGMII_ID_CTRL 0x602 #define DP83720_RX_CLK_SHIFT BIT(1) #define DP83720_TX_CLK_SHIFT BIT(0) enum dp83720_chip_type { DP83720_ES1, DP83720_ES2, DP83720_CS1, DP83720_CS1_1, DP83721_CS1, }; struct dp83720_init_reg { int reg; int val; }; static const struct dp83720_init_reg dp83720_es1_init[] = { {0x182, 0x3000}, {0x56a, 0xfc5}, {0x510, 0x2d51}, {0x408, 0x400}, {0x409, 0x2b}, {0x509, 0x4c04}, {0x8a1, 0xbff}, {0x802, 0x422}, {0x853, 0x632}, {0x824, 0x15e0}, {0x86a, 0x106}, {0x852, 0x3261}, {0x851, 0x5141}, {0x852, 0x327a}, {0x851, 0x6652}, {0x405, 0x1a0}, {0x423, 0x2}, {0x422, 0x0}, {0x420, 0x5510}, {0x421, 0x4077}, {0x412, 0x10}, {0x40f, 0x10}, {0x85d, 0x6405}, {0x894, 0x5557}, {0x892, 0x1b0}, {0x877, 0x55}, {0x80b, 0x16}, {0x864, 0x1fd0}, {0x865, 0xa}, }; static const struct dp83720_init_reg dp83720_es2_master_init[] = { {0x408, 0x580}, {0x409, 0x2a}, {0x8a1, 0xbff}, {0x802, 0x422}, {0x840, 0x4120}, {0x841, 0x6151}, {0x8a3, 0x24e9}, {0x800, 0x2090}, {0x864, 0x1fd0}, {0x865, 0x2}, {0x405, 0x6800}, {0x420, 0x3310}, {0x412, 0x10}, {0x40f, 0xe4ce}, {0x844, 0x3f10}, {0x8a0, 0x1e7}, {0x843, 0x327a}, {0x842, 0x6652}, {0x50b, 0x7e7c}, {0x56a, 0x7f41}, {0x56b, 0xffb4}, {0x813, 0x3fa0}, {0x88d, 0x3fa0}, {0x899, 0x3fa0}, }; static const struct dp83720_init_reg dp83720_es2_slave_init[] = { {0x408, 0x580}, {0x409, 0x2a}, {0x8a1, 0xbff}, {0x802, 0x422}, {0x853, 0x632}, {0x824, 0x15e0}, {0x86a, 0x106}, {0x852, 0x327a}, {0x851, 0x6652}, {0x405, 0x6800}, {0x420, 0x3310}, {0x412, 0x10}, {0x40f, 0x10}, {0x85d, 0x6405}, {0x894, 0x5057}, {0x892, 0x1b0}, {0x877, 0x55}, {0x80b, 0x16}, {0x864, 0x1fd0}, {0x865, 0x2}, {0x50b, 0x7e7c}, {0x56a, 0x7f41}, {0x56c, 0xffb4}, {0x813, 0x3fa0}, {0x88d, 0x3fa0}, {0x899, 0x3fa0}, }; static const struct dp83720_init_reg dp83720_cs1_master_init[] = { {0x408, 0x580}, {0x409, 0x2a}, {0x8a1, 0xbff}, {0x802, 0x422}, {0x864, 0x1fd0}, {0x865, 0x2}, {0x8a3, 0x24e9}, {0x800, 0x2090}, {0x840, 0x4120}, {0x841, 0x6151}, {0x8a0, 0x01e7}, {0x879, 0xe4ce}, {0x89f, 0x1}, {0x844, 0x3f10}, {0x843, 0x327a}, {0x842, 0x6652}, {0x8a8, 0xe080}, {0x8a9, 0x3f0}, {0x88d, 0x3fa0}, {0x889, 0x3fa0}, {0x50b, 0x7e7c}, {0x56a, 0x5f41}, {0x56b, 0xffb4}, {0x56c, 0xffb4}, {0x573, 0x1}, }; static const struct dp83720_init_reg dp83720_cs1_slave_init[] = { {0x408, 0x580}, {0x409, 0x2a}, {0x8a1, 0xbff}, {0x802, 0x422}, {0x864, 0x1fd0}, {0x865, 0x2}, {0x853, 0x632}, {0x824, 0x15e0}, {0x86a, 0x106}, {0x894, 0x5057}, {0x85d, 0x6405}, {0x892, 0x1b0}, {0x852, 0x327a}, {0x851, 0x6652}, {0x877, 0x55}, {0x80b, 0x16}, {0x8a8, 0xe080}, {0x8a9, 0x3f0}, {0x88d, 0x3fa0}, {0x899, 0x3fa0}, {0x1f, 0x4000}, {0x56a, 0x5f41}, {0x56b, 0xffb4}, {0x56c, 0xffb4}, {0x573, 0x1}, }; static const struct dp83720_init_reg dp83720_cs1_1_master_init[] = { {0x405, 0x5800}, {0x8ad, 0x3c51}, {0x894, 0x5df7}, {0x8a0, 0x9e7}, {0x8c0, 0x4000}, {0x814, 0x4800}, {0x80d, 0x2ebf}, {0x8c1, 0xb00}, {0x87d, 0x001}, {0x82e, 0x000}, {0x837, 0x0f4}, {0x8be, 0x200}, {0x8c5, 0x4000}, {0x8c7, 0x2000}, {0x8b3, 0x05a}, {0x8b4, 0x05a}, {0x8b0, 0x202}, {0x8b5, 0x0ea}, {0x8ba, 0x2828}, {0x8bb, 0x6828}, {0x8bc, 0x028}, {0x8bf, 0x000}, {0x8b1, 0x014}, {0x8b2, 0x008}, {0x8ec, 0x000}, {0x8c8, 0x003}, {0x8be, 0x201}, {0x18c, 0x001}, }; static const struct dp83720_init_reg dp83720_cs1_1_slave_init[] = { {0x894, 0x5df7}, {0x56a, 0x5f40}, {0x405, 0x5800}, {0x8ad, 0x3c51}, {0x894, 0x5df7}, {0x8a0, 0x9e7}, {0x8c0, 0x4000}, {0x814, 0x4800}, {0x80d, 0x2ebf}, {0x8c1, 0xb00}, {0x87d, 0x001}, {0x82e, 0x000}, {0x837, 0x0f4}, {0x8be, 0x200}, {0x8c5, 0x4000}, {0x8c7, 0x2000}, {0x8b3, 0x05a}, {0x8b4, 0x05a}, {0x8b0, 0x202}, {0x8b5, 0x0ea}, {0x8ba, 0x2828}, {0x8bb, 0x6828}, {0x8bc, 0x028}, {0x8bf, 0x000}, {0x8b1, 0x014}, {0x8b2, 0x008}, {0x8ec, 0x000}, {0x8c8, 0x003}, {0x8be, 0x201}, {0x56a, 0x5f40}, {0x18c, 0x001}, }; static const struct dp83720_init_reg dp83721_cs1_master_init[] = { {0x001F,0x8000}, {0x0573,0x0801}, {0x0834,0xC001}, {0x0405,0x6C00}, {0x08AD,0x3C51}, {0x0894,0x5DF7}, {0x08A0,0x09E7}, {0x08C0,0x4000}, {0x0814,0x4800}, {0x080D,0x2EBF}, {0x08C1,0x0B00}, {0x087D,0x0001}, {0x082E,0x0000}, {0x0837,0x00F8}, {0x08BE,0x0200}, {0x08C5,0x4000}, {0x08C7,0x2000}, {0x08B3,0x005A}, {0x08B4,0x005A}, {0x08B0,0x0202}, {0x08B5,0x00EA}, {0x08BA,0x2828}, {0x08BB,0x6828}, {0x08BC,0x0028}, {0x08BF,0x0000}, {0x08B1,0x0014}, {0x08B2,0x0008}, {0x08EC,0x0000}, {0x08FC,0x0091}, {0x08BE,0x0201}, {0x0335,0x0010}, {0x0336,0x0009}, {0x0337,0x0208}, {0x0338,0x0208}, {0x0339,0x02CB}, {0x033A,0x0208}, {0x033B,0x0109}, {0x0418,0x0380}, {0x0420,0xFF10}, {0x0421,0x4033}, {0x0422,0x0800}, {0x0423,0x0002}, {0x0484,0x0003}, {0x055D,0x0008}, {0x042B,0x0018}, {0x087C,0x0080}, {0x08C1,0x0900}, {0x08fc,0x4091}, {0x0881,0x5146}, {0x08be,0x02a1}, {0x0867,0x9999}, {0x0869,0x9666}, {0x086a,0x0009}, {0x0822,0x11e1}, {0x08f9,0x1f11}, {0x08a3,0x24e8}, {0x018C,0x0001}, {0x001F,0x4000}, {0x0573,0x0001}, {0x056A,0x5F41}, }; static const struct dp83720_init_reg dp83721_cs1_slave_init[] = { {0x405, 0x6C00}, {0x8ad, 0x3c51}, {0x894, 0x5df7}, {0x8a0, 0x9e7}, {0x8c0, 0x4000}, {0x814, 0x4800}, {0x80d, 0x2ebf}, {0x8c1, 0xb00}, {0x87d, 0x001}, {0x82e, 0x000}, {0x837, 0x0f4}, {0x8be, 0x200}, {0x8c5, 0x4000}, {0x8c7, 0x2000}, {0x8b3, 0x05a}, {0x8b4, 0x05a}, {0x8b0, 0x202}, {0x8b5, 0x0ea}, {0x8ba, 0x2828}, {0x8bb, 0x6828}, {0x8bc, 0x028}, {0x8bf, 0x000}, {0x8b1, 0x014}, {0x8b2, 0x008}, {0x8ec, 0x000}, {0x8c8, 0x003}, {0x8be, 0x201}, {0x56a, 0x5f40}, {0x18c, 0x001}, }; struct dp83720_private { int chip; bool is_master; bool is_rgmii; bool is_sgmii; bool rx_shift; bool tx_shift; }; #if 0 static irqreturn_t dp83720_handle_interrupt(struct phy_device *phydev) { int irq_status; irq_status = phy_read(phydev, MII_DP83720_INT_STAT1); if (irq_status < 0) { phy_error(phydev); return IRQ_NONE; } if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) goto trigger_machine; irq_status = phy_read(phydev, MII_DP83720_INT_STAT2); if (irq_status < 0) { phy_error(phydev); return IRQ_NONE; } if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) goto trigger_machine; irq_status = phy_read(phydev, MII_DP83720_INT_STAT3); if (irq_status < 0) { phy_error(phydev); return IRQ_NONE; } if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) goto trigger_machine; return IRQ_NONE; trigger_machine: phy_trigger_machine(phydev); return IRQ_HANDLED; } #endif static int dp83720_config_intr(struct phy_device *phydev) { int misr_status, ret; if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { misr_status = phy_read(phydev, MII_DP83720_INT_STAT1); if (misr_status < 0) return misr_status; misr_status |= (DP83720_ANEG_COMPLETE_INT_EN | DP83720_ESD_EVENT_INT_EN | DP83720_LINK_STAT_INT_EN | DP83720_ENERGY_DET_INT_EN | DP83720_LINK_QUAL_INT_EN); ret = phy_write(phydev, MII_DP83720_INT_STAT1, misr_status); if (ret < 0) return ret; misr_status = phy_read(phydev, MII_DP83720_INT_STAT2); if (misr_status < 0) return misr_status; misr_status |= (DP83720_SLEEP_MODE_INT_EN | DP83720_OVERTEMP_INT_EN | DP83720_OVERVOLTAGE_INT_EN | DP83720_UNDERVOLTAGE_INT_EN); ret = phy_write(phydev, MII_DP83720_INT_STAT2, misr_status); if (ret < 0) return ret; misr_status = phy_read(phydev, MII_DP83720_INT_STAT3); if (misr_status < 0) return misr_status; misr_status |= (DP83720_LPS_INT_EN | DP83720_WAKE_REQ_EN | DP83720_NO_FRAME_INT_EN | DP83720_POR_DONE_INT_EN); ret = phy_write(phydev, MII_DP83720_INT_STAT3, misr_status); } else { ret = phy_write(phydev, MII_DP83720_INT_STAT1, 0); if (ret < 0) return ret; ret = phy_write(phydev, MII_DP83720_INT_STAT2, 0); if (ret < 0) return ret; ret = phy_write(phydev, MII_DP83720_INT_STAT3, 0); if (ret < 0) return ret; ret = phy_read(phydev, MII_DP83720_INT_STAT1); if (ret < 0) return ret; ret = phy_read(phydev, MII_DP83720_INT_STAT2); if (ret < 0) return ret; ret = phy_read(phydev, MII_DP83720_INT_STAT3); if (ret < 0) return ret; ret = 0; } return ret; } static int dp83720_config_aneg(struct phy_device *phydev) { int value, ret; if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { value = phy_read(phydev, DP83720_SGMII_CTRL); ret = phy_write_mmd(phydev, DP83720_DEVADDR, DP83720_SGMII_CTRL, SGMII_CONFIG_VAL); if (ret < 0) return ret; } return genphy_config_aneg(phydev); } static int dp83720_read_straps(struct phy_device *phydev) { struct dp83720_private *dp83720 = phydev->priv; int strap; strap = phy_read_mmd(phydev, DP83720_DEVADDR, DP83720_STRAP); if (strap < 0) return strap; if (strap & DP83720_MASTER_MODE) dp83720->is_master = true; if (strap & DP83720_RGMII_IS_EN) dp83720->is_rgmii = true; if (strap & DP83720_SGMII_IS_EN) dp83720->is_sgmii = true; if (strap & DP83720_RX_SHIFT_EN) dp83720->rx_shift = true; if (strap & DP83720_TX_SHIFT_EN) dp83720->tx_shift = true; return 0; }; static int dp83720_reset(struct phy_device *phydev, bool hw_reset) { int ret; if (hw_reset) ret = phy_write_mmd(phydev, DP83720_DEVADDR, MII_DP83720_RESET_CTRL, DP83720_HW_RESET); else ret = phy_write_mmd(phydev, DP83720_DEVADDR, MII_DP83720_RESET_CTRL, DP83720_SW_RESET); if (ret) return ret; mdelay(100); return 0; } static int dp83720_phy_reset(struct phy_device *phydev) { int ret; ret = dp83720_reset(phydev, false); if (ret) return ret; ret = dp83720_read_straps(phydev); if (ret) return ret; return 0; } static int dp83720_write_seq(struct phy_device *phydev, const struct dp83720_init_reg *init_data, int size) { int ret; int i; for (i = 0; i < size; i++) { ret = phy_write_mmd(phydev, DP83720_DEVADDR, init_data[i].reg, init_data[i].val); if (ret) return ret; } return 0; } static int dp83720_chip_init(struct phy_device *phydev) { struct dp83720_private *dp83720 = phydev->priv; int ret; ret = dp83720_reset(phydev, true); if (ret) return ret; phydev->autoneg = AUTONEG_DISABLE; phydev->speed = SPEED_1000; phydev->duplex = DUPLEX_FULL; linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev->supported); if (dp83720->chip == DP83720_CS1 && dp83720->is_master) { ret = phy_write_mmd(phydev, DP83720_DEVADDR, MII_BMSR, 0x940); if (ret) return ret; ret = phy_write_mmd(phydev, DP83720_DEVADDR, MII_BMSR, 0x140); if (ret) return ret; } if (dp83720->is_master) ret = phy_write_mmd(phydev, DP83720_DEVADDR_MMD1, 0x0834, 0xc001); else ret = phy_write_mmd(phydev, DP83720_DEVADDR_MMD1, 0x0834, 0x8001); if (ret) return ret; switch (dp83720->chip) { case DP83720_ES1: ret = dp83720_write_seq(phydev, dp83720_es1_init, ARRAY_SIZE(dp83720_es1_init)); break; case DP83720_ES2: if (dp83720->is_master) ret = dp83720_write_seq(phydev, dp83720_es2_master_init, ARRAY_SIZE(dp83720_es2_master_init)); else ret = dp83720_write_seq(phydev, dp83720_es2_slave_init, ARRAY_SIZE(dp83720_es2_slave_init)); break; case DP83720_CS1: ret = phy_write_mmd(phydev, DP83720_DEVADDR, 0x573, 0x101); if (ret) return ret; if (dp83720->is_master) ret = dp83720_write_seq(phydev, dp83720_cs1_master_init, ARRAY_SIZE(dp83720_cs1_master_init)); else ret = dp83720_write_seq(phydev, dp83720_cs1_slave_init, ARRAY_SIZE(dp83720_cs1_slave_init)); break; case DP83720_CS1_1: ret = phy_write_mmd(phydev, DP83720_DEVADDR, 0x573, 0x101); if (ret) return ret; if (dp83720->is_master) ret = dp83720_write_seq(phydev, dp83720_cs1_1_master_init, ARRAY_SIZE(dp83720_cs1_1_master_init)); else ret = dp83720_write_seq(phydev, dp83720_cs1_1_slave_init, ARRAY_SIZE(dp83720_cs1_1_slave_init)); ret = dp83720_reset(phydev, false); ret = phy_write_mmd(phydev, DP83720_DEVADDR, 0x573, 0x001); if (ret) return ret; return phy_write_mmd(phydev, DP83720_DEVADDR, 0x56a, 0x5f41); case DP83721_CS1: ret = phy_write_mmd(phydev, DP83720_DEVADDR, 0x573, 0x101); if (ret) return ret; if (dp83720->is_master) ret = dp83720_write_seq(phydev, dp83721_cs1_master_init, ARRAY_SIZE(dp83721_cs1_master_init)); else ret = dp83720_write_seq(phydev, dp83721_cs1_slave_init, ARRAY_SIZE(dp83721_cs1_slave_init)); ret = dp83720_reset(phydev, false); ret = phy_write_mmd(phydev, DP83720_DEVADDR, 0x573, 0x001); if (ret) return ret; return phy_write_mmd(phydev, DP83720_DEVADDR, 0x56a, 0x5f41); default: return -EINVAL; }; if (ret) return ret; /* Enable the PHY */ ret = phy_write_mmd(phydev, DP83720_DEVADDR, 0x18c, 0x1); if (ret) return ret; mdelay(10); /* Do a software reset to restart the PHY with the updated values */ return dp83720_reset(phydev, false); } static int dp83720_config_init(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; s32 rx_int_delay; s32 tx_int_delay; int rgmii_delay; int value, ret; ret = dp83720_chip_init(phydev); if (ret) return ret; if (phy_interface_is_rgmii(phydev)) { rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, true); if (rx_int_delay <= 0) rgmii_delay = 0; else rgmii_delay = DP83720_RX_CLK_SHIFT; tx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, false); if (tx_int_delay <= 0) rgmii_delay &= ~DP83720_TX_CLK_SHIFT; else rgmii_delay |= DP83720_TX_CLK_SHIFT; if (rgmii_delay) { ret = phy_set_bits_mmd(phydev, DP83720_DEVADDR_MMD1, DP83720_RGMII_ID_CTRL, rgmii_delay); if (ret) return ret; } } value = phy_read_mmd(phydev, DP83720_DEVADDR, DP83720_SGMII_CTRL); if (value < 0) return value; if (phydev->interface == PHY_INTERFACE_MODE_SGMII) value |= DP83720_SGMII_EN; else value &= ~DP83720_SGMII_EN; ret = phy_write_mmd(phydev, DP83720_DEVADDR, DP83720_SGMII_CTRL, value); if (ret < 0) return ret; return 0; } static int dp83720_probe(struct phy_device *phydev) { struct dp83720_private *dp83720; int ret; dp83720 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83720), GFP_KERNEL); if (!dp83720) return -ENOMEM; phydev->priv = dp83720; ret = dp83720_read_straps(phydev); if (ret) return ret; switch (phydev->phy_id) { case DP83TG720ES1_PHY_ID: dp83720->chip = DP83720_ES1; break; case DP83TG720ES2_PHY_ID: dp83720->chip = DP83720_ES2; break; case DP83TG720CS_1_0_PHY_ID: dp83720->chip = DP83720_CS1; break; case DP83TG720CS_1_1_PHY_ID: dp83720->chip = DP83720_CS1_1; break; case DP83TG721CS_1_0_PHY_ID: dp83720->chip = DP83721_CS1; break; default: return -EINVAL; }; return dp83720_config_init(phydev); } #define DP83720_PHY_DRIVER(_id, _name) \ { \ PHY_ID_MATCH_EXACT(_id), \ .name = (_name), \ .probe = dp83720_probe, \ /* PHY_GBIT_FEATURES */ \ .soft_reset = dp83720_phy_reset, \ .config_init = dp83720_config_init, \ .config_aneg = dp83720_config_aneg, \ /*if 0 \ .handle_interrupt = dp83720_handle_interrupt, \ #endif */ \ .config_intr = dp83720_config_intr, \ .suspend = genphy_suspend, \ .resume = genphy_resume, \ } static struct phy_driver dp83720_driver[] = { DP83720_PHY_DRIVER(DP83TG720ES1_PHY_ID, "TI DP83TG720ES1"), DP83720_PHY_DRIVER(DP83TG720ES2_PHY_ID, "TI DP83TG720ES2"), DP83720_PHY_DRIVER(DP83TG720CS_1_0_PHY_ID, "TI DP83TG720CS1.0"), DP83720_PHY_DRIVER(DP83TG720CS_1_1_PHY_ID, "TI DP83TG720CS1.1"), DP83720_PHY_DRIVER(DP83TG721CS_1_0_PHY_ID, "TI DP83TG721CS1.0"), }; module_phy_driver(dp83720_driver); static struct mdio_device_id __maybe_unused dp83720_tbl[] = { { PHY_ID_MATCH_EXACT(DP83TG720ES1_PHY_ID) }, { PHY_ID_MATCH_EXACT(DP83TG720ES2_PHY_ID) }, { PHY_ID_MATCH_EXACT(DP83TG720CS_1_0_PHY_ID) }, { PHY_ID_MATCH_EXACT(DP83TG720CS_1_1_PHY_ID) }, { PHY_ID_MATCH_EXACT(DP83TG721CS_1_0_PHY_ID) }, { }, }; MODULE_DEVICE_TABLE(mdio, dp83720_tbl); MODULE_DESCRIPTION("Texas Instruments DP83TG720 PHY driver"); MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); MODULE_LICENSE("GPL");
Here is the DP83TG720S-Q1 driver