Skip to content

Commit cafd721

Browse files
drivers: mdio: Add Xilinx Axi Ethernet Lite driver
This commit adds support for the Xilinx AXI Ethernet Lite device, also known as the emaclite. The emaclite is a light-weight 10/100 MII Ethernet device. It can optionally be configured to include an MDIO. The MMIO interface is controlled via MMIO registers and requires the software to busy-wait until completion. Signed-off-by: Eric Ackermann <[email protected]>
1 parent e0fcfa1 commit cafd721

File tree

5 files changed

+245
-0
lines changed

5 files changed

+245
-0
lines changed

drivers/mdio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ zephyr_library_sources_ifdef(CONFIG_MDIO_RENESAS_RA mdio_renesas_ra.c)
2020
zephyr_library_sources_ifdef(CONFIG_MDIO_LAN865X mdio_lan865x.c)
2121
zephyr_library_sources_ifdef(CONFIG_MDIO_SENSRY_SY1XX mdio_sy1xx.c)
2222
zephyr_library_sources_ifdef(CONFIG_MDIO_XILINX_AXI_ENET mdio_xilinx_axienet.c)
23+
zephyr_library_sources_ifdef(CONFIG_MDIO_XILINX_AXI_ETHERNET_LITE mdio_xilinx_axi_ethernet_lite.c)
2324
zephyr_library_sources_ifdef(CONFIG_MDIO_INTEL_IGC mdio_intel_igc.c)

drivers/mdio/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ source "drivers/mdio/Kconfig.renesas_ra"
4141
source "drivers/mdio/Kconfig.lan865x"
4242
source "drivers/mdio/Kconfig.sy1xx"
4343
source "drivers/mdio/Kconfig.xilinx_axienet"
44+
source "drivers/mdio/Kconfig.xilinx_axi_ethernet_lite"
4445
source "drivers/mdio/Kconfig.intel_igc"
4546

4647
config MDIO_INIT_PRIORITY
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright 2025 CISPA Helmholtz Center for Information Security
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config MDIO_XILINX_AXI_ETHERNET_LITE
5+
bool "Xilinx AXI Ethernet Lite MDIO driver"
6+
default y
7+
depends on DT_HAS_XLNX_XPS_ETHERNETLITE_3_00_A_MDIO_ENABLED
8+
depends on NETWORKING
9+
help
10+
Enable Xilinx AXI Ethernet Lite MDIO bus driver.
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/*
2+
* Xilinx AXI Ethernet Lite MDIO
3+
*
4+
* Copyright(c) 2025, CISPA Helmholtz Center for Information Security
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
#include <zephyr/logging/log.h>
8+
LOG_MODULE_REGISTER(eth_xilinx_axi_ethernet_lite_mdio, CONFIG_ETHERNET_LOG_LEVEL);
9+
10+
#include <zephyr/kernel.h>
11+
#include <zephyr/drivers/mdio.h>
12+
#include <zephyr/device.h>
13+
14+
#include <stdint.h>
15+
16+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MAX_PHY_DEVICES 32
17+
18+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_OFFSET 0x07e4
19+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_WRITE_DATA_REG_OFFSET 0x07e8
20+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_READ_DATA_REG_OFFSET 0x07ec
21+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_OFFSET 0x07f0
22+
23+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_ENABLE_MASK BIT(3)
24+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_BUSY_MASK BIT(0)
25+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_DISABLE_MASK 0
26+
27+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_OP_READ BIT(10)
28+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_OP_WRITE 0
29+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_SHIFT_REGADDR 0
30+
#define MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_SHIFT_PHYADDR 5
31+
32+
struct mdio_xilinx_axi_ethernet_lite_data {
33+
bool bus_enabled;
34+
};
35+
36+
struct mdio_xilinx_axi_ethernet_lite_config {
37+
void *reg;
38+
};
39+
40+
static int xilinx_axi_ethernet_lite_mdio_probe(const struct device *dev)
41+
{
42+
return 0; /* Nothing to do*/
43+
}
44+
45+
static inline uint32_t
46+
mdio_xilinx_axi_ethernet_lite_read_reg(const struct mdio_xilinx_axi_ethernet_lite_config *config,
47+
mem_addr_t reg)
48+
{
49+
return sys_read32((mem_addr_t)config->reg + reg);
50+
}
51+
52+
static inline void
53+
mdio_xilinx_axi_ethernet_lite_write_reg(const struct mdio_xilinx_axi_ethernet_lite_config *config,
54+
mem_addr_t reg, uint32_t value)
55+
{
56+
sys_write32(value, (mem_addr_t)config->reg + reg);
57+
}
58+
59+
static inline int
60+
mdio_xilinx_axi_ethernet_lite_check_busy(const struct mdio_xilinx_axi_ethernet_lite_config *config)
61+
{
62+
uint32_t mdio_control_reg_val = mdio_xilinx_axi_ethernet_lite_read_reg(
63+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_OFFSET);
64+
65+
return mdio_control_reg_val & MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_BUSY_MASK
66+
? -EBUSY
67+
: 0;
68+
}
69+
70+
static inline void
71+
mdio_xilinx_axi_ethernet_lite_set_addr(const struct mdio_xilinx_axi_ethernet_lite_config *config,
72+
uint8_t prtad, uint8_t regad, bool is_read)
73+
{
74+
uint32_t mdio_addr_val = is_read ? MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_OP_READ
75+
: MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_OP_WRITE;
76+
77+
/* range check done below in read/write functions */
78+
mdio_addr_val |= (uint32_t)regad
79+
<< MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_SHIFT_REGADDR;
80+
mdio_addr_val |= (uint32_t)prtad
81+
<< MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_SHIFT_PHYADDR;
82+
83+
mdio_xilinx_axi_ethernet_lite_write_reg(
84+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_OFFSET, mdio_addr_val);
85+
}
86+
87+
static inline void xilinx_axi_ethernet_lite_mdio_bus_enable(const struct device *dev)
88+
{
89+
const struct mdio_xilinx_axi_ethernet_lite_config *config = dev->config;
90+
struct mdio_xilinx_axi_ethernet_lite_data *data = dev->data;
91+
92+
mdio_xilinx_axi_ethernet_lite_write_reg(
93+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_OFFSET,
94+
MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_ENABLE_MASK);
95+
96+
data->bus_enabled = true;
97+
}
98+
99+
/* arbitrary but sufficient in testing */
100+
#define MDIO_MAX_WAIT_US 1000
101+
102+
static inline int xilinx_axi_ethernet_lite_mdio_complete_transaction(
103+
const struct mdio_xilinx_axi_ethernet_lite_config *config)
104+
{
105+
int waited_cycles = 0;
106+
107+
/* start transaction - everything set up */
108+
mdio_xilinx_axi_ethernet_lite_write_reg(
109+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_OFFSET,
110+
MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_ENABLE_MASK |
111+
MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_BUSY_MASK);
112+
113+
while (mdio_xilinx_axi_ethernet_lite_check_busy(config) &&
114+
waited_cycles < MDIO_MAX_WAIT_US) {
115+
/* no need to block the CPU */
116+
k_msleep(1);
117+
waited_cycles++;
118+
}
119+
120+
if (waited_cycles == MDIO_MAX_WAIT_US) {
121+
LOG_ERR("Timed out waiting for MDIO transaction to complete!");
122+
return -EIO;
123+
}
124+
/* busy went low - transaction complete */
125+
return 0;
126+
}
127+
128+
static int xilinx_axi_ethernet_lite_mdio_read(const struct device *dev, uint8_t prtad,
129+
uint8_t regad, uint16_t *value)
130+
{
131+
const struct mdio_xilinx_axi_ethernet_lite_config *config = dev->config;
132+
struct mdio_xilinx_axi_ethernet_lite_data *data = dev->data;
133+
134+
if (prtad >= MDIO_XILINX_AXI_ETHERNET_LITE_MAX_PHY_DEVICES) {
135+
LOG_ERR("Requested read port address %" PRIu8 " not supported - max %d", prtad,
136+
MDIO_XILINX_AXI_ETHERNET_LITE_MAX_PHY_DEVICES);
137+
return -ENOSYS;
138+
}
139+
if (!data->bus_enabled) {
140+
LOG_ERR("MDIO bus not enabled!");
141+
return -ENOSYS;
142+
}
143+
if (mdio_xilinx_axi_ethernet_lite_check_busy(config)) {
144+
LOG_ERR("MDIO bus busy!");
145+
return -ENOSYS;
146+
}
147+
148+
mdio_xilinx_axi_ethernet_lite_set_addr(config, prtad, regad, true);
149+
150+
if (xilinx_axi_ethernet_lite_mdio_complete_transaction(config)) {
151+
return -EIO;
152+
}
153+
154+
*value = (uint16_t)mdio_xilinx_axi_ethernet_lite_read_reg(
155+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_READ_DATA_REG_OFFSET);
156+
157+
return 0;
158+
}
159+
160+
static int xilinx_axi_ethernet_lite_mdio_write(const struct device *dev, uint8_t prtad,
161+
uint8_t regad, uint16_t value)
162+
{
163+
const struct mdio_xilinx_axi_ethernet_lite_config *config = dev->config;
164+
struct mdio_xilinx_axi_ethernet_lite_data *data = dev->data;
165+
166+
if (prtad >= MDIO_XILINX_AXI_ETHERNET_LITE_MAX_PHY_DEVICES) {
167+
LOG_ERR("Requested write port address %" PRIu8 " not supported - max %d", prtad,
168+
MDIO_XILINX_AXI_ETHERNET_LITE_MAX_PHY_DEVICES);
169+
return -ENOSYS;
170+
}
171+
if (!data->bus_enabled) {
172+
LOG_ERR("MDIO bus not enabled!");
173+
return -ENOSYS;
174+
}
175+
if (mdio_xilinx_axi_ethernet_lite_check_busy(config)) {
176+
LOG_ERR("MDIO bus busy!");
177+
return -ENOSYS;
178+
}
179+
mdio_xilinx_axi_ethernet_lite_set_addr(config, prtad, regad, false);
180+
mdio_xilinx_axi_ethernet_lite_write_reg(
181+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_WRITE_DATA_REG_OFFSET, value);
182+
183+
if (xilinx_axi_ethernet_lite_mdio_complete_transaction(config)) {
184+
return -EIO;
185+
}
186+
187+
return 0;
188+
}
189+
190+
static inline void xilinx_axi_ethernet_lite_mdio_bus_disable(const struct device *dev)
191+
{
192+
const struct mdio_xilinx_axi_ethernet_lite_config *config = dev->config;
193+
struct mdio_xilinx_axi_ethernet_lite_data *data = dev->data;
194+
195+
mdio_xilinx_axi_ethernet_lite_write_reg(
196+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_OFFSET,
197+
MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_DISABLE_MASK);
198+
199+
data->bus_enabled = false;
200+
}
201+
202+
static DEVICE_API(mdio, mdio_xilinx_axi_ethernet_lite_api) = {
203+
/* clause 45 not supported */
204+
.bus_enable = xilinx_axi_ethernet_lite_mdio_bus_enable,
205+
.bus_disable = xilinx_axi_ethernet_lite_mdio_bus_disable,
206+
.read = xilinx_axi_ethernet_lite_mdio_read,
207+
.write = xilinx_axi_ethernet_lite_mdio_write};
208+
209+
#define XILINX_AXI_ETHERNET_LITE_MDIO_INIT(inst) \
210+
\
211+
static const struct mdio_xilinx_axi_ethernet_lite_config \
212+
mdio_xilinx_axi_ethernet_lite_config##inst = { \
213+
.reg = (void *)(uintptr_t)DT_REG_ADDR(DT_INST_PARENT(inst)), \
214+
}; \
215+
static struct mdio_xilinx_axi_ethernet_lite_data \
216+
mdio_xilinx_axi_ethernet_lite_data##inst = {0}; \
217+
DEVICE_DT_INST_DEFINE(inst, xilinx_axi_ethernet_lite_mdio_probe, NULL, \
218+
&mdio_xilinx_axi_ethernet_lite_data##inst, \
219+
&mdio_xilinx_axi_ethernet_lite_config##inst, POST_KERNEL, \
220+
CONFIG_MDIO_INIT_PRIORITY, &mdio_xilinx_axi_ethernet_lite_api);
221+
222+
#define DT_DRV_COMPAT xlnx_xps_ethernetlite_3_00_a_mdio
223+
DT_INST_FOREACH_STATUS_OKAY(XILINX_AXI_ETHERNET_LITE_MDIO_INIT)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2025 CISPA Helmholtz Center for Information Security
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Common fields for MDIO controllers
5+
6+
include: mdio-controller.yaml
7+
8+
compatible: xlnx,xps-ethernetlite-3.00.a-mdio
9+
10+
description: MDIO interface of Xilinx AXI Ethernet Lite.

0 commit comments

Comments
 (0)