diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-22 15:58:00 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-22 15:58:00 -1000 |
| commit | 734bef548a96e8774d6eef5dfa2026c6edc4e80b (patch) | |
| tree | ce2520b2602ce3b82f9842490d21836f9838d441 /drivers/net/mdio | |
| parent | 864c07de0c7dcbca58b61625dfa2e461ae63b242 (diff) | |
| parent | 1a2c35c49cc7c6be860038b8442b52424a8f123c (diff) | |
| download | linux-next-stable.tar.gz linux-next-stable.zip | |
Merge tag 'net-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextstable
Pull networking updates from Paolo Abeni:
"This includes a significant number of low-priority fixes - especially
in the NIC drivers area - that we deferred from the last few RCs in
the previous cycle, which can't be easily outlined in the synopsis
below.
Core & protocols:
- Continue the work on removing rtnl_lock dependencies:
- per-netns ARP and ND neighbour tables, with the default settings
now configurable per netns (inheritance from init_net is
controlled by the new net.core.neigh_inherit_init_net sysctl)
- RCU-protected configuration and rtnl_lock-less fill_info() for
hsr, netkit, sit, vlan and vxlan devices
- per-netns device unregistration for ip tunnel and gre devices
- use the netdev instance lock instead of rtnl_lock when reading
speed and duplex via sysfs
- batch the flushing of uncached routes at device unregistration
time, relieving rtnl_lock contention on netns dismantle.
- Add hardware pacing offload support, admin-controlled via a new
rtnetlink attribute, with sch_fq integration (implemented in idpf).
- Re-segment BIG TCP GSO packets into GSO packets fitting the egress
device limits instead of plain MSS-sized segments, improving
throughput for mixed BIG TCP / non-BIG TCP paths by up to 70%.
- Generate RSS keys that spread flows over all the RX queues; the
previous random keys could leave some queues unused.
- Optimize the bridge VLAN forwarding paths: cache the port-VLAN in
fdb entries and consider only VLAN members when flooding, for ~10%
more Mpps in the fdb forwarding path.
- A few TCP/IP performance improvements aimed at efficient cache
usage and reducing contention.
- Add the TCP ROCCET congestion control, a CUBIC variant aimed at
avoiding bufferbloat in cellular networks.
- Continue the conversion of getsockopt callbacks to sockopt_t.
- Add an ethtool command to list the front-facing ports of an
interface, including SFP cages and modules, using the phy_port
representation.
- Enable BIG TCP support for bareudp tunnels.
- Remove the IPV6_ADDRFORM socket option and disallow buggy TCP state
transitions (listen() + shutdown() + connect() and connect() +
connect(AF_UNSPEC) + listen()).
- Remove most BUG_ON()s from core packet processing, replacing them
with error paths.
Wireless:
- Latency improvements, including lockless station get/dump and
beacon updates, plus RTNL reduction and locking cleanups.
- Infrastructure for non-station TX/RX with MLO.
- NAN (Wi-Fi Aware) Instant Communication support and other NAN
improvements.
- Control frame Integrity Protocol (CIP) and protected TWT support.
- Improvements for AP regulatory checks.
Netfilter:
- Disable br_netfilter in user namespaces and schedule it for removal
in 2027.
- Restrict the raw table in nft_compat to the specific family, hook
and priority.
- Prepare the flowtable for mixed IPv4-over-IPv6 tunneling (and vice
versa).
- Use GFP_KERNEL_ACCOUNT for most netfilter allocations, for better
integration with memcg.
- Allow discovering conntrack zone filtering support in ctnetlink
dumps, and fix nfnetlink_hook handling of large dumps.
Deletions:
- Move the LLC type 2 sockets out of tree. There are no known users
and the code attracts a never-ending stream of LLM-driven fixes.
Device drivers:
- New drivers:
- Meta Platforms NIC (mpnic), the second-generation NIC from Meta
- initial skeleton of the Nebula-matrix NICs driver (nbl)
- X-Powers AC200/AC300 Fast Ethernet PHYs
- Ethernet high-speed NICs:
- Intel:
- i40e: XDP metadata support for RX hash and VLAN tag
- ice: expose the E825 TSPLL state via the DPLL subsystem
- nVidia/Mellanox:
- move the data direct infrastructure from IB to core, in
preparation for netdev support.
- reduce the memory used per offloaded TC rule
- Meta (fbnic):
- support large io_uring zero-copy RX buffers
- Microsoft vNIC (mana):
- add BQL support
- Ethernet NICs consumer, and embedded:
- Airoha:
- LRO offload support
- Cadence (macb):
- context swapping for ringparam and MTU changes, avoiding
buffer loss under memory pressure
- Mucse (rnpgbe):
- TX/RX and link status support
- NXP (enetc):
- SR-IOV improvements and VF support for ENETC v4
- Qualcomm (ppe):
- IPQ5424 support
- Realtek PCI (r8169):
- phylink support
- RSS support for RTL8127
- Renesas (ravb):
- gPTP support for R-Car Gen4
- Synopsys (stmmac):
- XDP multi-buffer support on the TX side
- tc mqprio hw queue priority offload
- support for RK3568 SGMII, Allwinner H616 EMAC1, UltraRISC
DP1000 and Qualcomm sa8255p
- Ethernet switches:
- Mediatek (mt7530):
- fast age and change conduit support
- Microchip (ksz):
- support for KSZ8995XA/KS8995XA
- Microchip (lan966x):
- PCIe FDMA support, ~20x throughput increase in PCIe endpoint
- Microchip (sparx5):
- Time Aware Shaper (taprio) offload
- Motorcomm (yt921x):
- LED and SerDes PCS support
- Realtek (rtl8365mb):
- EEE support
- Ethernet PHYs:
- Broadcom:
- enable jumbo frames on BCM54xx
- Realtek:
- support for RTL8261CE_CG and RTL8261D_VM
- MDIO support for RTL838x and RTL839x
- WiFi drivers:
- Broadcom (brcmfmac):
- FT/OKC roaming offload
- Intel (iwlwifi):
- UHR sniffer support
- support for newer UEFI/BIOS tables
- MediaTek (mt76):
- mt7925 NAN improvements
- offchannel/scan improvements and MLO fixes
- Morse Micro (mm81x):
- monitor mode support
- Qualcomm (ath12k):
- IPQ5332 platform infrastructure
- Realtek (rtw88):
- RTL8723BS support
- SDIO TX performance improvements
- Realtek (rtw89):
- P2P_DEVICE support
- BT-coex updates for RTL8922D
- Bluetooth:
- move H:4 reassembly into the Bluetooth core
- add generic support for vendor packets
- MGMT: add security level changed event
- Bluetooth drivers:
- btusb: support Bulk Serialization Mode
- Intel: support Draco and Titan Lake, allow unsigned firmware
- Mediatek: MT7928 support
- Qualcomm: QCC2072 support
- Broadcom: Synaptics 4384 support"
* tag 'net-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (2126 commits)
Bluetooth: btusb: disable by default Qualcomm multi-subsystem
tipc: fix broadcast sender hang on last acker departure
tipc: fix use-after-free of broadcast reception link at node release
r8169: clear TX LPI on RTL8168h at link up after phylink conversion
tun: Require CAP_NET_ADMIN to attach to an ownerless device
net/sched/sch_cake: drop dead nla_nest_end() error check
net/sched/sch_cake: widen buffer_used to u64
net/sched/sch_cake: reduce parent backlog when clearing a tin
net/sched/sch_cake: serialize reconfiguration with the datapath
tg3: Add Datto SIRIS 5 1U Server to restart AER quirk table
net/mlx5e: Stop asserting for RTNL when closing channels
net: shrink the payload-only part of skb dataref to one bit
net: wwan: iosm: validate msg_head before indexing msg_ring
net: bgmac: rate limit RX error messages
selftests: net: veth: test XDP_TX to a peer with GRO enabled
veth: schedule the peer's NAPI after XDP_TX when only GRO is enabled
pppoe: delete session hash entries by pointer match
net: lan969x: don't disable hardirqs in fdma tx clear
net: sparx5/lan969x: do not clobber the PSFP cycle timer update period
net: lan969x: fix DSM calendar for empty taxis
...
Diffstat (limited to 'drivers/net/mdio')
| -rw-r--r-- | drivers/net/mdio/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/net/mdio/mdio-bcm-iproc.c | 2 | ||||
| -rw-r--r-- | drivers/net/mdio/mdio-bcm-unimac.c | 2 | ||||
| -rw-r--r-- | drivers/net/mdio/mdio-mux-meson-g12a.c | 2 | ||||
| -rw-r--r-- | drivers/net/mdio/mdio-realtek-rtl9300.c | 546 |
5 files changed, 524 insertions, 32 deletions
diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index a05229838cb4..d44278f26fab 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -172,11 +172,11 @@ config MDIO_IPQ8064 interface units of the IPQ8064 SoC config MDIO_REALTEK_RTL9300 - tristate "Realtek RTL9300 MDIO interface support" + tristate "Realtek RTL83xx/RTL93xx MDIO interface support" depends on MACH_REALTEK_RTL || COMPILE_TEST help This driver supports the MDIO interface found in the Realtek - RTL9300 family of Ethernet switches with integrated SoC. + RTL83xx/RTL93xx family of Ethernet switches with integrated SoC. config MDIO_REGMAP tristate diff --git a/drivers/net/mdio/mdio-bcm-iproc.c b/drivers/net/mdio/mdio-bcm-iproc.c index 91690b496793..22473c988e3b 100644 --- a/drivers/net/mdio/mdio-bcm-iproc.c +++ b/drivers/net/mdio/mdio-bcm-iproc.c @@ -195,7 +195,7 @@ static const struct dev_pm_ops iproc_mdio_pm_ops = { static const struct of_device_id iproc_mdio_of_match[] = { { .compatible = "brcm,iproc-mdio", }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, iproc_mdio_of_match); diff --git a/drivers/net/mdio/mdio-bcm-unimac.c b/drivers/net/mdio/mdio-bcm-unimac.c index 37e35f282d9a..31e396cc9fbf 100644 --- a/drivers/net/mdio/mdio-bcm-unimac.c +++ b/drivers/net/mdio/mdio-bcm-unimac.c @@ -345,7 +345,7 @@ static const struct of_device_id unimac_mdio_ids[] = { { .compatible = "brcm,genet-mdio-v2", }, { .compatible = "brcm,genet-mdio-v1", }, { .compatible = "brcm,unimac-mdio", }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, unimac_mdio_ids); diff --git a/drivers/net/mdio/mdio-mux-meson-g12a.c b/drivers/net/mdio/mdio-mux-meson-g12a.c index 08d6a6c93fb8..e6c9ef3641af 100644 --- a/drivers/net/mdio/mdio-mux-meson-g12a.c +++ b/drivers/net/mdio/mdio-mux-meson-g12a.c @@ -223,7 +223,7 @@ static int g12a_ephy_glue_clk_register(struct device *dev) { struct g12a_mdio_mux *priv = dev_get_drvdata(dev); const char *parent_names[PLL_MUX_NUM_PARENT]; - struct clk_init_data init; + struct clk_init_data init = {}; struct g12a_ephy_pll *pll; struct clk_mux *mux; struct clk *clk; diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index 9ce2b7807532..eb7a6238dbd1 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -35,11 +35,87 @@ * * The driver works out the mapping based on the MDIO bus described in device tree and phandles on * the ethernet-ports property. + * + * The devices have a hardware polling unit that runs in the background without any CPU load. It + * constantly scans the MDIO bus and the attached PHYs and updates the MAC status registers. + * + * How does the polling work? + * + * Each device has a SMI_POLL_CTRL register. A per-port bitmask decides if the hardware polling of + * the associated bus/address is active or not. The hardware runs a tight loop over this and for + * each set polling bit it issues a status check for the PHY. Attaching a logic analyzer to the + * MDIO bus of an RTL8380 and RTL8393 gives the following commands (in kernel notation): + * + * RTL8380 RTL8393 + * --------------------------- --------------------------- + * phy_write(phy, 31, 0x0); phy_read(phy, 0); + * phy_write(phy, 13, 0x7); phy_read(phy, 1); + * phy_write(phy, 14, 0x3c); phy_read(phy, 4); + * phy_write(phy, 13, 0x8007); phy_read(phy, 5); + * phy_read(phy, 14); phy_read(phy, 6); + * phy_write(phy, 13, 0x7); phy_read(phy, 9); + * phy_write(phy, 14, 0x3d); phy_read(phy, 10); + * phy_write(phy, 13, 0x8007); phy_read(phy, 15); + * phy_read(phy, 14); phy_write(phy, 13, 0x7); + * phy_read(phy, 9); phy_write(phy, 14, 0x3c); + * phy_read(phy, 10); phy_write(phy, 13, 0x4007); + * phy_read(phy, 15); phy_read(phy, 14); + * phy_read(phy, 0); phy_write(phy, 13, 0x7); + * phy_read(phy, 1); phy_write(phy, 14, 0x3d); + * phy_read(phy, 4); phy_write(phy, 13, 0x4007); + * phy_read(phy, 5); phy_read(phy, 14); + * phy_read(phy, 6); + * + * After one PHY status is read, the polling engine goes over to the next PHY. If one bus is fully + * scanned it switches over to the next bus. Basically the polling system is always busy and the + * MAC state is updated in real-time. + * + * This is a glimpse look at the complexity of the polling and leaves out the C45 case and the MAC + * register update logic afterwards. Important to note: + * + * - 100 MBit downshifts (broken cables) are identified and propagated to the MAC correctly + * - Access to MDIO_AN_EEE_ADV and MDIO_AN_EEE_LPABLE works via C45 over C22. + * - It is unclear if these sequences change for different PHYs. + * - Access to Realtek reserved register 26 (link speed) has not yet been seen. + * + * How does MDIO access from kernel work? + * + * When issuing MDIO accesses via an MMIO based interface the final write to the command register + * sets a "run command now" bit. Between two polling sequences for different PHYs the hardware + * checks if a user command needs to run and sends it onto the bus. Afterwards it simply continues + * its polling work. Inspecting the command sequence for a paged write on the logic analyzer gives: + * + * RTL8380 RTL8393 + * --------------------------- --------------------------- + * phy_write(phy, 31, page); phy_write(phy, 31, page); + * phy_write(phy, reg, value); phy_write(phy, reg, value); + * phy_write(phy, 31, 0); + * + * What does this mean? + * + * There are slight differences in polling and PHY access between the models but the challenge + * stays the same. On the one hand that greatly simplifies the MAC layer, on the other hand it + * has some implications for the kernel PHY subsystem. + * + * - Without the polling and a proper MAC status, some of the link handling features do not work. + * Especially an unpopulated MAC_LINK_STS register cancels operations to other MAC registers. + * - The Realtek page register 31 is magically modified in the background so that polling will + * read the right data. On the RTL838x polling simply resets it to zero. Other devices seem + * to track the page access "magically" in the background. + * - A C45 over C22 kernel access sequence is most likely to fail because chances are high that + * the polling engine overwrites registers 13/14 in between. + * - PHY firmware loading can have issues. Especially if a PHY is designed to expect a clean + * sequence of registers and values without deviation. + * - An access to one PHY will need to wait for the next free slot of the polling engine. + * + * Conclusion: The Realtek MDIO bus driver PHY access must know and handle any interference that + * arises from the above described hardware polling. */ #include <linux/bitfield.h> #include <linux/bitmap.h> #include <linux/bits.h> +#include <linux/device.h> #include <linux/find.h> #include <linux/mdio.h> #include <linux/mfd/syscon.h> @@ -47,14 +123,61 @@ #include <linux/of_mdio.h> #include <linux/phy.h> #include <linux/platform_device.h> -#include <linux/property.h> #include <linux/regmap.h> +#define RTL8380_NUM_BUSES 1 +#define RTL8380_NUM_PAGES 4096 +#define RTL8380_NUM_PORTS 28 +#define RTL8380_SMI_GLB_CTRL 0xa100 +#define RTL8380_SMI_FREQ_SEL BIT(1) +#define RTL8380_SMI_PHY_PATCH_DONE BIT(15) +#define RTL8380_SMI_ACCESS_PHY_CTRL_0 0xa1b8 +#define RTL8380_SMI_ACCESS_PHY_CTRL_1 0xa1bc +#define RTL8380_PHY_CTRL_REG_ADDR GENMASK(24, 20) +#define RTL8380_PHY_CTRL_PARK_PAGE GENMASK(19, 15) +#define RTL8380_PHY_CTRL_MAIN_PAGE GENMASK(14, 3) +#define RTL8380_PHY_CTRL_WRITE BIT(2) +#define RTL8380_PHY_CTRL_READ 0 +#define RTL8380_PHY_CTRL_TYPE_C45 BIT(1) +#define RTL8380_PHY_CTRL_TYPE_C22 0 +#define RTL8380_PHY_CTRL_FAIL 0 /* no fail indicator */ +#define RTL8380_SMI_ACCESS_PHY_CTRL_2 0xa1c0 +#define RTL8380_PHY_CTRL_INDATA GENMASK(31, 16) +#define RTL8380_PHY_CTRL_DATA GENMASK(15, 0) +#define RTL8380_SMI_ACCESS_PHY_CTRL_3 0xa1c4 +#define RTL8380_SMI_POLL_CTRL 0xa17c +#define RTL8380_SMI_PORT0_5_ADDR_CTRL 0xa1c8 + +#define RTL8390_NUM_BUSES 2 +#define RTL8390_NUM_PAGES 8192 +#define RTL8390_NUM_PORTS 52 +#define RTL8390_BCAST_PHYID_CTRL 0x03ec +#define RTL8390_PHYREG_ACCESS_CTRL 0x03dc +#define RTL8390_PHY_CTRL_REG_ADDR GENMASK(9, 5) +#define RTL8390_PHY_CTRL_MAIN_PAGE GENMASK(22, 10) +#define RTL8390_PHY_CTRL_FAIL BIT(1) +#define RTL8390_PHY_CTRL_WRITE BIT(3) +#define RTL8390_PHY_CTRL_READ 0 +#define RTL8390_PHY_CTRL_TYPE_C45 BIT(2) +#define RTL8390_PHY_CTRL_TYPE_C22 0 +#define RTL8390_PHYREG_CTRL 0x03e0 +#define RTL8390_PHY_CTRL_EXT_PAGE GENMASK(8, 0) +#define RTL8390_PHYREG_DATA_CTRL 0x03f0 +#define RTL8390_PHY_CTRL_INDATA GENMASK(31, 16) +#define RTL8390_PHY_CTRL_DATA GENMASK(15, 0) +#define RTL8390_PHYREG_MMD_CTRL 0x03f4 +#define RTL8390_PHYREG_PORT_CTRL_LOW 0x03e4 +#define RTL8390_PHYREG_PORT_CTRL_HIGH 0x03e8 +#define RTL8390_SMI_PORT_POLLING_CTRL 0x03fc +#define RTL8390_SMI_GLB_CTRL 0x03f8 +#define RTL8390_SMI_FREQ_SEL GENMASK(6, 5) + #define RTL9300_NUM_BUSES 4 #define RTL9300_NUM_PAGES 4096 #define RTL9300_NUM_PORTS 28 #define RTL9300_SMI_GLB_CTRL 0xca00 #define RTL9300_GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf)) +#define RTL9300_SMI_FREQ_SEL(intf) GENMASK((intf) * 2 + 9, (intf) * 2 + 8) #define RTL9300_SMI_PORT0_15_POLLING_SEL 0xca08 #define RTL9300_SMI_ACCESS_PHY_CTRL_0 0xcb70 #define RTL9300_SMI_ACCESS_PHY_CTRL_1 0xcb74 @@ -70,11 +193,14 @@ #define RTL9300_PHY_CTRL_INDATA GENMASK(31, 16) #define RTL9300_PHY_CTRL_DATA GENMASK(15, 0) #define RTL9300_SMI_ACCESS_PHY_CTRL_3 0xcb7c +#define RTL9300_SMI_POLL_CTRL 0xca90 #define RTL9300_SMI_PORT0_5_ADDR_CTRL 0xcb80 #define RTL9310_NUM_BUSES 4 #define RTL9310_NUM_PAGES 8192 #define RTL9310_NUM_PORTS 56 +#define RTL9310_SMI_GLB_CTRL0 0x0cc0 +#define RTL9310_SMI_FREQ_SEL(intf) GENMASK((intf) * 2 + 5, (intf) * 2 + 4) #define RTL9310_SMI_GLB_CTRL1 0x0cbc #define RTL9310_SMI_GLB_FMT_SEL_C45(intf) BIT((intf) * 2 + 1) #define RTL9310_SMI_INDRT_ACCESS_CTRL_0 0x0c00 @@ -97,12 +223,16 @@ #define RTL9310_PHY_CTRL_INDATA GENMASK(15, 0) #define RTL9310_SMI_INDRT_ACCESS_MMD_CTRL 0x0c18 #define RTL9310_SMI_PORT_ADDR_CTRL 0x0c74 +#define RTL9310_SMI_PORT_POLLING_CTRL 0x0ccc #define RTL9310_SMI_PORT_POLLING_SEL 0x0c9c #define PHY_CTRL_CMD BIT(0) #define PHY_CTRL_MMD_DEVAD GENMASK(20, 16) #define PHY_CTRL_MMD_REG GENMASK(15, 0) +#define RTL_VENDOR_ID 0x001cc800 +#define RTL_PAGE_SELECT 31 + #define MAP_ADDRS_PER_REG 6 #define MAP_BITS_PER_ADDR 5 #define MAP_BITS_PER_BUS 2 @@ -128,6 +258,7 @@ struct otto_emdio_priv { struct regmap *regmap; struct mutex lock; /* protect HW access */ DECLARE_BITMAP(valid_ports, MAX_PORTS); + u16 page[MAX_PORTS]; u8 smi_bus[MAX_PORTS]; u8 smi_addr[MAX_PORTS]; bool smi_bus_is_c45[MAX_SMI_BUSSES]; @@ -144,6 +275,8 @@ struct otto_emdio_info { u8 num_buses; u8 num_ports; u16 num_pages; + u32 poll_ctrl; + int (*set_bus_frequency)(struct mii_bus *bus, u32 freq); int (*setup_controller)(struct otto_emdio_priv *priv); int (*read_c22)(struct mii_bus *bus, int port, int regnum, u32 *value); int (*read_c45)(struct mii_bus *bus, int port, int dev_addr, int regnum, u32 *value); @@ -179,6 +312,12 @@ static struct otto_emdio_priv *otto_emdio_bus_to_priv(struct mii_bus *bus) return chan->priv; } +static int otto_emdio_set_port_polling(struct otto_emdio_priv *priv, int port, bool active) +{ + return regmap_assign_bits(priv->regmap, priv->info->poll_ctrl + (port / 32) * 4, + BIT(port % 32), active); +} + static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd, struct otto_emdio_cmd_regs *cmd_data) { @@ -187,9 +326,9 @@ static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd, u32 cmdstate; int ret; - /* Defensive pre check just in case something goes horrible wrong */ + /* Defensive pre check just in case something goes horribly wrong */ ret = regmap_read_poll_timeout(priv->regmap, info->cmd_regs.c22_data, - cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 1000); + cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 10000); if (ret) return ret; @@ -229,7 +368,7 @@ static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd, return ret; ret = regmap_read_poll_timeout(priv->regmap, info->cmd_regs.c22_data, - cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 1000); + cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 10000); if (ret) return ret; @@ -266,13 +405,123 @@ static int otto_emdio_write_cmd(struct mii_bus *bus, u32 cmd, return otto_emdio_run_cmd(bus, cmd | priv->info->cmd_write, cmd_data); } +static int otto_emdio_8380_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + struct otto_emdio_cmd_regs cmd_data = { + .c22_data = FIELD_PREP(RTL8380_PHY_CTRL_REG_ADDR, regnum) | + FIELD_PREP(RTL8380_PHY_CTRL_PARK_PAGE, 0x1f) | + FIELD_PREP(RTL8380_PHY_CTRL_MAIN_PAGE, priv->page[port]), + .io_data = FIELD_PREP(RTL8380_PHY_CTRL_INDATA, port), + }; + + return otto_emdio_read_cmd(bus, RTL8380_PHY_CTRL_TYPE_C22, &cmd_data, + RTL8380_PHY_CTRL_DATA, value); +} + +static int otto_emdio_8380_write_c22(struct mii_bus *bus, int port, int regnum, u16 value) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + struct otto_emdio_cmd_regs cmd_data = { + .c22_data = FIELD_PREP(RTL8380_PHY_CTRL_REG_ADDR, regnum) | + FIELD_PREP(RTL8380_PHY_CTRL_PARK_PAGE, 0x1f) | + FIELD_PREP(RTL8380_PHY_CTRL_MAIN_PAGE, priv->page[port]), + .io_data = FIELD_PREP(RTL8380_PHY_CTRL_INDATA, value), + .port_mask_low = BIT(port), + }; + + return otto_emdio_write_cmd(bus, RTL8380_PHY_CTRL_TYPE_C22, &cmd_data); +} + +static int otto_emdio_8380_read_c45(struct mii_bus *bus, int port, + int dev_addr, int regnum, u32 *value) +{ + struct otto_emdio_cmd_regs cmd_data = { + .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) | + FIELD_PREP(PHY_CTRL_MMD_REG, regnum), + .io_data = FIELD_PREP(RTL8380_PHY_CTRL_INDATA, port), + }; + + return otto_emdio_read_cmd(bus, RTL8380_PHY_CTRL_TYPE_C45, &cmd_data, + RTL8380_PHY_CTRL_DATA, value); +} + +static int otto_emdio_8380_write_c45(struct mii_bus *bus, int port, + int dev_addr, int regnum, u16 value) +{ + struct otto_emdio_cmd_regs cmd_data = { + .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) | + FIELD_PREP(PHY_CTRL_MMD_REG, regnum), + .io_data = FIELD_PREP(RTL8380_PHY_CTRL_INDATA, value), + .port_mask_low = BIT(port), + }; + + return otto_emdio_write_cmd(bus, RTL8380_PHY_CTRL_TYPE_C45, &cmd_data); +} + +static int otto_emdio_8390_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + struct otto_emdio_cmd_regs cmd_data = { + .c22_data = FIELD_PREP(RTL8390_PHY_CTRL_REG_ADDR, regnum) | + FIELD_PREP(RTL8390_PHY_CTRL_MAIN_PAGE, priv->page[port]), + .ext_page = FIELD_PREP(RTL8390_PHY_CTRL_EXT_PAGE, 0x1ff), + .io_data = FIELD_PREP(RTL8390_PHY_CTRL_INDATA, port), + }; + + return otto_emdio_read_cmd(bus, RTL8390_PHY_CTRL_TYPE_C22, &cmd_data, + RTL8390_PHY_CTRL_DATA, value); +} + +static int otto_emdio_8390_write_c22(struct mii_bus *bus, int port, int regnum, u16 value) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + struct otto_emdio_cmd_regs cmd_data = { + .c22_data = FIELD_PREP(RTL8390_PHY_CTRL_REG_ADDR, regnum) | + FIELD_PREP(RTL8390_PHY_CTRL_MAIN_PAGE, priv->page[port]), + .ext_page = FIELD_PREP(RTL8390_PHY_CTRL_EXT_PAGE, 0x1ff), + .io_data = FIELD_PREP(RTL8390_PHY_CTRL_INDATA, value), + .port_mask_high = (u32)(BIT_ULL(port) >> 32), + .port_mask_low = (u32)(BIT_ULL(port)), + }; + + return otto_emdio_write_cmd(bus, RTL8390_PHY_CTRL_TYPE_C22, &cmd_data); +} + +static int otto_emdio_8390_read_c45(struct mii_bus *bus, int port, + int dev_addr, int regnum, u32 *value) +{ + struct otto_emdio_cmd_regs cmd_data = { + .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) | + FIELD_PREP(PHY_CTRL_MMD_REG, regnum), + .io_data = FIELD_PREP(RTL8390_PHY_CTRL_INDATA, port), + }; + + return otto_emdio_read_cmd(bus, RTL8390_PHY_CTRL_TYPE_C45, &cmd_data, + RTL8390_PHY_CTRL_DATA, value); +} + +static int otto_emdio_8390_write_c45(struct mii_bus *bus, int port, + int dev_addr, int regnum, u16 value) +{ + struct otto_emdio_cmd_regs cmd_data = { + .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) | + FIELD_PREP(PHY_CTRL_MMD_REG, regnum), + .io_data = FIELD_PREP(RTL8390_PHY_CTRL_INDATA, value), + .port_mask_high = (u32)(BIT_ULL(port) >> 32), + .port_mask_low = (u32)(BIT_ULL(port)), + }; + + return otto_emdio_write_cmd(bus, RTL8390_PHY_CTRL_TYPE_C45, &cmd_data); +} + static int otto_emdio_9300_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value) { struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); struct otto_emdio_cmd_regs cmd_data = { .c22_data = FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) | FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) | - FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, priv->page[port]), .io_data = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, port), }; @@ -286,7 +535,7 @@ static int otto_emdio_9300_write_c22(struct mii_bus *bus, int port, int regnum, struct otto_emdio_cmd_regs cmd_data = { .c22_data = FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) | FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) | - FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, priv->page[port]), .io_data = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, value), .port_mask_low = BIT(port), }; @@ -326,7 +575,7 @@ static int otto_emdio_9310_read_c22(struct mii_bus *bus, int port, int regnum, u struct otto_emdio_cmd_regs cmd_data = { .broadcast = FIELD_PREP(RTL9310_BC_PORT_ID, port), .c22_data = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) | - FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, priv->page[port]), .ext_page = FIELD_PREP(RTL9310_SMI_INDRT_EXT_PAGE, RTL9310_SMI_INDRT_EXT_PAGE_NO_CHANGE), }; @@ -340,7 +589,7 @@ static int otto_emdio_9310_write_c22(struct mii_bus *bus, int port, int regnum, struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); struct otto_emdio_cmd_regs cmd_data = { .c22_data = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) | - FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, priv->page[port]), .ext_page = FIELD_PREP(RTL9310_SMI_INDRT_EXT_PAGE, RTL9310_SMI_INDRT_EXT_PAGE_NO_CHANGE), .io_data = FIELD_PREP(RTL9310_PHY_CTRL_INDATA, value), @@ -384,27 +633,53 @@ static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum) int ret, port; u32 value; + if (regnum == MII_MMD_CTRL || regnum == MII_MMD_DATA) { + dev_warn_once(&bus->dev, + "C45 over C22 read access broken due to polling\n"); + return -EOPNOTSUPP; + } + port = otto_emdio_phy_to_port(bus, phy_id); if (port < 0) return port; - scoped_guard(mutex, &priv->lock) + scoped_guard(mutex, &priv->lock) { + if (regnum == RTL_PAGE_SELECT) + return priv->page[port]; + ret = priv->info->read_c22(bus, port, regnum, &value); + } return ret ? ret : value; } -static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value) +static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, + u16 value) { struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); int ret, port; + if (regnum == MII_MMD_CTRL || regnum == MII_MMD_DATA) { + dev_warn_once(&bus->dev, + "C45 over C22 write access broken due to polling\n"); + return -EOPNOTSUPP; + } + port = otto_emdio_phy_to_port(bus, phy_id); if (port < 0) return port; - scoped_guard(mutex, &priv->lock) + scoped_guard(mutex, &priv->lock) { + if (regnum == RTL_PAGE_SELECT) { + if (value >= RAW_PAGE(priv)) + return -EINVAL; + + priv->page[port] = value; + return 0; + } + ret = priv->info->write_c22(bus, port, regnum, value); + } return ret; } @@ -477,6 +752,80 @@ static int otto_emdio_setup_topology(struct otto_emdio_priv *priv) return 0; } +static int otto_emdio_8380_set_bus_frequency(struct mii_bus *bus, u32 freq) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + + if (freq != 2500000 && freq != 10000000) + return -EINVAL; + + return regmap_assign_bits(priv->regmap, RTL8380_SMI_GLB_CTRL, + RTL8380_SMI_FREQ_SEL, freq == 10000000); +} + +static int otto_emdio_8380_setup_controller(struct otto_emdio_priv *priv) +{ + /* + * PHY_PATCH_DONE enables PHY control via SoC. This is required for PHY access, including + * patching and must be set before the PHYs are probed. + */ + return regmap_set_bits(priv->regmap, RTL8380_SMI_GLB_CTRL, RTL8380_SMI_PHY_PATCH_DONE); +} + +static int otto_emdio_8390_set_bus_frequency(struct mii_bus *bus, u32 freq) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + u32 mdio_bus, child_freq, val; + bool freq_set = false; + int err; + + /* + * RTL839x uses one frequency shared by both MDIO buses. Use the frequency passed by the + * caller as fallback, but prefer an explicitly requested frequency from any valid, + * available bus. Reject conflicting explicit requests. Only the first call programs the + * shared frequency. + */ + if (priv->bus[0] || priv->bus[1]) + return 0; + + for_each_available_child_of_node_scoped(bus->parent->of_node, child) { + err = of_property_read_u32(child, "reg", &mdio_bus); + if (err || mdio_bus >= priv->info->num_buses) + continue; + + if (of_property_read_u32(child, "clock-frequency", &child_freq)) + continue; + + if (freq_set && freq != child_freq) + return -EINVAL; + + freq = child_freq; + freq_set = true; + } + + if (freq != 1250000 && freq != 2500000 && freq != 5000000) + return -EINVAL; + + val = FIELD_PREP(RTL8390_SMI_FREQ_SEL, freq / 2500000); + + return regmap_update_bits(priv->regmap, RTL8390_SMI_GLB_CTRL, RTL8390_SMI_FREQ_SEL, val); +} + +static int otto_emdio_9300_set_bus_frequency(struct mii_bus *bus, u32 freq) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + struct otto_emdio_chan *chan = bus->priv; + u32 mask, val; + + if (freq != 1250000 && freq != 2500000 && freq != 5000000) + return -EINVAL; + + mask = RTL9300_SMI_FREQ_SEL(chan->mdio_bus); + val = field_prep(mask, freq / 2500000); + + return regmap_update_bits(priv->regmap, RTL9300_SMI_GLB_CTRL, mask, val); +} + static int otto_emdio_9300_setup_controller(struct otto_emdio_priv *priv) { u32 glb_ctrl_mask = 0, glb_ctrl_val = 0; @@ -497,6 +846,21 @@ static int otto_emdio_9300_setup_controller(struct otto_emdio_priv *priv) return 0; } +static int otto_emdio_9310_set_bus_frequency(struct mii_bus *bus, u32 freq) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + struct otto_emdio_chan *chan = bus->priv; + u32 mask, val; + + if (freq != 1250000 && freq != 2500000 && freq != 5000000) + return -EINVAL; + + mask = RTL9310_SMI_FREQ_SEL(chan->mdio_bus); + val = field_prep(mask, freq / 2500000); + + return regmap_update_bits(priv->regmap, RTL9310_SMI_GLB_CTRL0, mask, val); +} + static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv) { int i, err; @@ -513,15 +877,60 @@ static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv) return 0; } +static int otto_emdio_notify_phy_attach(struct phy_device *phydev) +{ + int port = otto_emdio_phy_to_port(phydev->mdio.bus, phydev->mdio.addr); + struct otto_emdio_chan *chan = phydev->mdio.bus->priv; + struct otto_emdio_priv *priv = chan->priv; + + if (port < 0) { + /* All subsequent bus operations will fail */ + phydev_err(phydev, "PHY is not mapped to a valid switch port\n"); + return port; + } + + /* "sync" page in case of previously failed attachment */ + scoped_guard(mutex, &priv->lock) + priv->page[port] = 0; + + if (!priv->smi_bus_is_c45[chan->mdio_bus] && + !phy_id_compare_vendor(phydev->phy_id, RTL_VENDOR_ID)) { + phydev_err(phydev, "Only Realtek PHYs allowed on C22 bus\n"); + return -EOPNOTSUPP; + } + + return otto_emdio_set_port_polling(priv, port, true); +} + +static void otto_emdio_notify_phy_detach(struct phy_device *phydev) +{ + struct mii_bus *bus = phydev->mdio.bus; + struct otto_emdio_priv *priv; + int port; + + priv = otto_emdio_bus_to_priv(phydev->mdio.bus); + port = otto_emdio_phy_to_port(phydev->mdio.bus, phydev->mdio.addr); + + if (port < 0) + return; + + /* "sync" page for next attachment */ + scoped_guard(mutex, &priv->lock) + priv->page[port] = 0; + + if (otto_emdio_set_port_polling(priv, port, false)) + dev_err(bus->parent, "failed to disable polling for port %d\n", port); +} + static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv, - struct fwnode_handle *node) + struct device_node *node) { struct otto_emdio_chan *chan; struct mii_bus *bus; - u32 mdio_bus; + u32 mdio_bus, freq; int err; - err = fwnode_property_read_u32(node, "reg", &mdio_bus); + err = of_property_read_u32(node, "reg", &mdio_bus); if (err) return dev_err_probe(dev, err, "undefined smi bus number\n"); @@ -529,29 +938,41 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv return dev_err_probe(dev, -EINVAL, "illegal (dangling) smi bus number %d\n", mdio_bus); + if (priv->bus[mdio_bus]) + return dev_err_probe(dev, -EINVAL, "duplicated smi bus number %d\n", mdio_bus); + bus = devm_mdiobus_alloc_size(dev, sizeof(*chan)); if (!bus) return -ENOMEM; bus->name = "Realtek Switch MDIO Bus"; - if (priv->smi_bus_is_c45[mdio_bus]) { - bus->read_c45 = otto_emdio_read_c45; - bus->write_c45 = otto_emdio_write_c45; - } else { - bus->read = otto_emdio_read_c22; - bus->write = otto_emdio_write_c22; - } + bus->read_c45 = otto_emdio_read_c45; + bus->write_c45 = otto_emdio_write_c45; + bus->read = otto_emdio_read_c22; + bus->write = otto_emdio_write_c22; bus->parent = dev; + bus->notify_phy_attach = otto_emdio_notify_phy_attach; + bus->notify_phy_detach = otto_emdio_notify_phy_detach; + chan = bus->priv; chan->mdio_bus = mdio_bus; chan->priv = priv; snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", dev_name(dev), mdio_bus); - err = devm_of_mdiobus_register(dev, bus, to_of_node(node)); + if (of_property_read_u32(node, "clock-frequency", &freq)) + freq = 2500000; + err = priv->info->set_bus_frequency(bus, freq); + if (err) + return dev_err_probe(dev, err, "cannot set frequency %u for MDIO bus %d\n", + freq, mdio_bus); + + err = devm_of_mdiobus_register(dev, bus, node); if (err) return dev_err_probe(dev, err, "cannot register MDIO bus\n"); + priv->bus[mdio_bus] = bus; + return 0; } @@ -584,8 +1005,8 @@ static int otto_emdio_map_ports(struct device *dev) ports_dn = of_get_child_by_name(parent->of_node, "ethernet-ports"); if (!ports_dn) - return dev_err_probe(dev, -EINVAL, "%pfwP missing ethernet-ports\n", - dev_fwnode(parent)); + return dev_err_probe(dev, -EINVAL, "%pOFP missing ethernet-ports\n", + parent->of_node); for_each_available_child_of_node_scoped(ports_dn, port_dn) { ctrl_dn = NULL; @@ -658,6 +1079,19 @@ put_nodes: return err; } +static int otto_emdio_init_polling(struct otto_emdio_priv *priv) +{ + int err; + + for (int port = 0; port < priv->info->num_ports; port++) { + err = otto_emdio_set_port_polling(priv, port, false); + if (err) + return err; + } + + return 0; +} + static int otto_emdio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -672,11 +1106,15 @@ static int otto_emdio_probe(struct platform_device *pdev) if (err) return err; - priv->info = device_get_match_data(dev); + priv->info = of_device_get_match_data(dev); priv->regmap = syscon_node_to_regmap(dev->parent->of_node); if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); + err = otto_emdio_init_polling(priv); + if (err) + return err; + platform_set_drvdata(pdev, priv); err = otto_emdio_map_ports(dev); @@ -693,7 +1131,7 @@ static int otto_emdio_probe(struct platform_device *pdev) return dev_err_probe(dev, err, "failed to setup MDIO bus controller\n"); } - device_for_each_child_node_scoped(dev, child) { + for_each_available_child_of_node_scoped(dev->of_node, child) { err = otto_emdio_probe_one(dev, priv, child); if (err) return err; @@ -702,6 +1140,53 @@ static int otto_emdio_probe(struct platform_device *pdev) return 0; } +static const struct otto_emdio_info otto_emdio_8380_info = { + .addr_map_base = RTL8380_SMI_PORT0_5_ADDR_CTRL, + .cmd_fail = RTL8380_PHY_CTRL_FAIL, + .cmd_read = RTL8380_PHY_CTRL_READ, + .cmd_write = RTL8380_PHY_CTRL_WRITE, + .cmd_regs = { + .c22_data = RTL8380_SMI_ACCESS_PHY_CTRL_1, + .c45_data = RTL8380_SMI_ACCESS_PHY_CTRL_3, + .io_data = RTL8380_SMI_ACCESS_PHY_CTRL_2, + .port_mask_low = RTL8380_SMI_ACCESS_PHY_CTRL_0, + }, + .num_buses = RTL8380_NUM_BUSES, + .num_pages = RTL8380_NUM_PAGES, + .num_ports = RTL8380_NUM_PORTS, + .poll_ctrl = RTL8380_SMI_POLL_CTRL, + .set_bus_frequency = otto_emdio_8380_set_bus_frequency, + .setup_controller = otto_emdio_8380_setup_controller, + .read_c22 = otto_emdio_8380_read_c22, + .read_c45 = otto_emdio_8380_read_c45, + .write_c22 = otto_emdio_8380_write_c22, + .write_c45 = otto_emdio_8380_write_c45, +}; + +static const struct otto_emdio_info otto_emdio_8390_info = { + .cmd_fail = RTL8390_PHY_CTRL_FAIL, + .cmd_read = RTL8390_PHY_CTRL_READ, + .cmd_write = RTL8390_PHY_CTRL_WRITE, + .cmd_regs = { + .broadcast = RTL8390_BCAST_PHYID_CTRL, + .c22_data = RTL8390_PHYREG_ACCESS_CTRL, + .c45_data = RTL8390_PHYREG_MMD_CTRL, + .ext_page = RTL8390_PHYREG_CTRL, + .io_data = RTL8390_PHYREG_DATA_CTRL, + .port_mask_low = RTL8390_PHYREG_PORT_CTRL_LOW, + .port_mask_high = RTL8390_PHYREG_PORT_CTRL_HIGH, + }, + .num_buses = RTL8390_NUM_BUSES, + .num_pages = RTL8390_NUM_PAGES, + .num_ports = RTL8390_NUM_PORTS, + .poll_ctrl = RTL8390_SMI_PORT_POLLING_CTRL, + .set_bus_frequency = otto_emdio_8390_set_bus_frequency, + .read_c22 = otto_emdio_8390_read_c22, + .read_c45 = otto_emdio_8390_read_c45, + .write_c22 = otto_emdio_8390_write_c22, + .write_c45 = otto_emdio_8390_write_c45, +}; + static const struct otto_emdio_info otto_emdio_9300_info = { .addr_map_base = RTL9300_SMI_PORT0_5_ADDR_CTRL, .bus_map_base = RTL9300_SMI_PORT0_15_POLLING_SEL, @@ -717,6 +1202,8 @@ static const struct otto_emdio_info otto_emdio_9300_info = { .num_buses = RTL9300_NUM_BUSES, .num_ports = RTL9300_NUM_PORTS, .num_pages = RTL9300_NUM_PAGES, + .poll_ctrl = RTL9300_SMI_POLL_CTRL, + .set_bus_frequency = otto_emdio_9300_set_bus_frequency, .setup_controller = otto_emdio_9300_setup_controller, .read_c22 = otto_emdio_9300_read_c22, .read_c45 = otto_emdio_9300_read_c45, @@ -742,6 +1229,8 @@ static const struct otto_emdio_info otto_emdio_9310_info = { .num_buses = RTL9310_NUM_BUSES, .num_pages = RTL9310_NUM_PAGES, .num_ports = RTL9310_NUM_PORTS, + .poll_ctrl = RTL9310_SMI_PORT_POLLING_CTRL, + .set_bus_frequency = otto_emdio_9310_set_bus_frequency, .setup_controller = otto_emdio_9310_setup_controller, .read_c22 = otto_emdio_9310_read_c22, .read_c45 = otto_emdio_9310_read_c45, @@ -750,6 +1239,8 @@ static const struct otto_emdio_info otto_emdio_9310_info = { }; static const struct of_device_id otto_emdio_ids[] = { + { .compatible = "realtek,rtl8380-mdio", .data = &otto_emdio_8380_info }, + { .compatible = "realtek,rtl8391-mdio", .data = &otto_emdio_8390_info }, { .compatible = "realtek,rtl9301-mdio", .data = &otto_emdio_9300_info }, { .compatible = "realtek,rtl9311-mdio", .data = &otto_emdio_9310_info }, {} @@ -761,10 +1252,11 @@ static struct platform_driver otto_emdio_driver = { .driver = { .name = "mdio-rtl9300", .of_match_table = otto_emdio_ids, + .suppress_bind_attrs = true, }, }; module_platform_driver(otto_emdio_driver); -MODULE_DESCRIPTION("RTL9300 MDIO driver"); +MODULE_DESCRIPTION("RTL83xx/RTL93xx MDIO driver"); MODULE_LICENSE("GPL"); |
