Skip to content

Commit dc05abb

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 dc05abb

File tree

5 files changed

+255
-0
lines changed

5 files changed

+255
-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: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
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+
struct k_mutex mutex;
34+
bool bus_enabled;
35+
};
36+
37+
struct mdio_xilinx_axi_ethernet_lite_config {
38+
void *reg;
39+
};
40+
41+
static inline uint32_t
42+
mdio_xilinx_axi_ethernet_lite_read_reg(const struct mdio_xilinx_axi_ethernet_lite_config *config,
43+
mem_addr_t reg)
44+
{
45+
return sys_read32((mem_addr_t)config->reg + reg);
46+
}
47+
48+
static inline void
49+
mdio_xilinx_axi_ethernet_lite_write_reg(const struct mdio_xilinx_axi_ethernet_lite_config *config,
50+
mem_addr_t reg, uint32_t value)
51+
{
52+
sys_write32(value, (mem_addr_t)config->reg + reg);
53+
}
54+
55+
static inline int
56+
mdio_xilinx_axi_ethernet_lite_check_busy(const struct mdio_xilinx_axi_ethernet_lite_config *config)
57+
{
58+
uint32_t mdio_control_reg_val = mdio_xilinx_axi_ethernet_lite_read_reg(
59+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_OFFSET);
60+
61+
return mdio_control_reg_val & MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_BUSY_MASK
62+
? -EBUSY
63+
: 0;
64+
}
65+
66+
static inline void
67+
mdio_xilinx_axi_ethernet_lite_set_addr(const struct mdio_xilinx_axi_ethernet_lite_config *config,
68+
uint8_t prtad, uint8_t regad, bool is_read)
69+
{
70+
uint32_t mdio_addr_val = is_read ? MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_OP_READ
71+
: MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_OP_WRITE;
72+
73+
/* range check done below in read/write functions */
74+
mdio_addr_val |= (uint32_t)regad
75+
<< MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_SHIFT_REGADDR;
76+
mdio_addr_val |= (uint32_t)prtad
77+
<< MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_SHIFT_PHYADDR;
78+
79+
mdio_xilinx_axi_ethernet_lite_write_reg(
80+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_ADDRESS_REG_OFFSET, mdio_addr_val);
81+
}
82+
83+
static inline void xilinx_axi_ethernet_lite_mdio_bus_enable(const struct device *dev)
84+
{
85+
const struct mdio_xilinx_axi_ethernet_lite_config *config = dev->config;
86+
struct mdio_xilinx_axi_ethernet_lite_data *data = dev->data;
87+
88+
mdio_xilinx_axi_ethernet_lite_write_reg(
89+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_OFFSET,
90+
MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_ENABLE_MASK);
91+
92+
data->bus_enabled = true;
93+
}
94+
95+
/* arbitrary but sufficient in testing */
96+
#define MDIO_MAX_WAIT_US 1000
97+
98+
static inline int xilinx_axi_ethernet_lite_mdio_complete_transaction(
99+
const struct mdio_xilinx_axi_ethernet_lite_config *config)
100+
{
101+
int waited_cycles = 0;
102+
103+
/* start transaction - everything set up */
104+
mdio_xilinx_axi_ethernet_lite_write_reg(
105+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_OFFSET,
106+
MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_ENABLE_MASK |
107+
MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_CONTROL_REG_MDIO_BUSY_MASK);
108+
109+
while (mdio_xilinx_axi_ethernet_lite_check_busy(config) &&
110+
waited_cycles < MDIO_MAX_WAIT_US) {
111+
/* no need to block the CPU */
112+
k_msleep(1);
113+
waited_cycles++;
114+
}
115+
116+
if (waited_cycles == MDIO_MAX_WAIT_US) {
117+
LOG_ERR("Timed out waiting for MDIO transaction to complete!");
118+
return -EIO;
119+
}
120+
/* busy went low - transaction complete */
121+
return 0;
122+
}
123+
124+
static int xilinx_axi_ethernet_lite_mdio_read(const struct device *dev, uint8_t prtad,
125+
uint8_t regad, uint16_t *value)
126+
{
127+
const struct mdio_xilinx_axi_ethernet_lite_config *config = dev->config;
128+
struct mdio_xilinx_axi_ethernet_lite_data *data = dev->data;
129+
130+
if (prtad >= MDIO_XILINX_AXI_ETHERNET_LITE_MAX_PHY_DEVICES) {
131+
LOG_ERR("Requested read port address %" PRIu8 " not supported - max %d", prtad,
132+
MDIO_XILINX_AXI_ETHERNET_LITE_MAX_PHY_DEVICES);
133+
return -ENOSYS;
134+
}
135+
136+
(void)k_mutex_lock(&data->mutex, K_FOREVER);
137+
138+
if (!data->bus_enabled) {
139+
LOG_ERR("MDIO bus not enabled!");
140+
(void)k_mutex_unlock(&data->mutex);
141+
return -ENOSYS;
142+
}
143+
if (mdio_xilinx_axi_ethernet_lite_check_busy(config)) {
144+
LOG_ERR("MDIO bus busy!");
145+
(void)k_mutex_unlock(&data->mutex);
146+
return -ENOSYS;
147+
}
148+
149+
mdio_xilinx_axi_ethernet_lite_set_addr(config, prtad, regad, true);
150+
151+
if (xilinx_axi_ethernet_lite_mdio_complete_transaction(config)) {
152+
(void)k_mutex_unlock(&data->mutex);
153+
return -EIO;
154+
}
155+
156+
*value = (uint16_t)mdio_xilinx_axi_ethernet_lite_read_reg(
157+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_READ_DATA_REG_OFFSET);
158+
159+
(void)k_mutex_unlock(&data->mutex);
160+
161+
return 0;
162+
}
163+
164+
static int xilinx_axi_ethernet_lite_mdio_write(const struct device *dev, uint8_t prtad,
165+
uint8_t regad, uint16_t value)
166+
{
167+
const struct mdio_xilinx_axi_ethernet_lite_config *config = dev->config;
168+
struct mdio_xilinx_axi_ethernet_lite_data *data = dev->data;
169+
170+
if (prtad >= MDIO_XILINX_AXI_ETHERNET_LITE_MAX_PHY_DEVICES) {
171+
LOG_ERR("Requested write port address %" PRIu8 " not supported - max %d", prtad,
172+
MDIO_XILINX_AXI_ETHERNET_LITE_MAX_PHY_DEVICES);
173+
return -ENOSYS;
174+
}
175+
176+
(void)k_mutex_lock(&data->mutex, K_FOREVER);
177+
178+
if (!data->bus_enabled) {
179+
LOG_ERR("MDIO bus not enabled!");
180+
(void)k_mutex_unlock(&data->mutex);
181+
return -ENOSYS;
182+
}
183+
if (mdio_xilinx_axi_ethernet_lite_check_busy(config)) {
184+
LOG_ERR("MDIO bus busy!");
185+
(void)k_mutex_unlock(&data->mutex);
186+
return -ENOSYS;
187+
}
188+
mdio_xilinx_axi_ethernet_lite_set_addr(config, prtad, regad, false);
189+
mdio_xilinx_axi_ethernet_lite_write_reg(
190+
config, MDIO_XILINX_AXI_ETHERNET_LITE_MDIO_WRITE_DATA_REG_OFFSET, value);
191+
192+
if (xilinx_axi_ethernet_lite_mdio_complete_transaction(config)) {
193+
(void)k_mutex_unlock(&data->mutex);
194+
return -EIO;
195+
}
196+
197+
(void)k_mutex_unlock(&data->mutex);
198+
199+
return 0;
200+
}
201+
202+
static int xilinx_axi_ethernet_lite_mdio_init(const struct device *dev)
203+
{
204+
struct mdio_xilinx_axi_ethernet_lite_data *data = dev->data;
205+
206+
(void)k_mutex_init(&data->mutex);
207+
208+
xilinx_axi_ethernet_lite_mdio_bus_enable(dev);
209+
210+
return 0;
211+
}
212+
213+
214+
static DEVICE_API(mdio, mdio_xilinx_axi_ethernet_lite_api) = {
215+
/* clause 45 not supported */
216+
.read = xilinx_axi_ethernet_lite_mdio_read,
217+
.write = xilinx_axi_ethernet_lite_mdio_write};
218+
219+
#define XILINX_AXI_ETHERNET_LITE_MDIO_INIT(inst) \
220+
\
221+
static const struct mdio_xilinx_axi_ethernet_lite_config \
222+
mdio_xilinx_axi_ethernet_lite_config##inst = { \
223+
.reg = (void *)(uintptr_t)DT_REG_ADDR(DT_INST_PARENT(inst)), \
224+
}; \
225+
static struct mdio_xilinx_axi_ethernet_lite_data \
226+
mdio_xilinx_axi_ethernet_lite_data##inst = {0}; \
227+
DEVICE_DT_INST_DEFINE(inst, xilinx_axi_ethernet_lite_mdio_init, NULL, \
228+
&mdio_xilinx_axi_ethernet_lite_data##inst, \
229+
&mdio_xilinx_axi_ethernet_lite_config##inst, POST_KERNEL, \
230+
CONFIG_MDIO_INIT_PRIORITY, &mdio_xilinx_axi_ethernet_lite_api);
231+
232+
#define DT_DRV_COMPAT xlnx_xps_ethernetlite_3_00_a_mdio
233+
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)