Skip to content

Commit c2468a2

Browse files
committed
drivers: mdio: Add Intel IGC MDIO driver support
Intel i226 MAC supports MDIO C22 and MDIO C45. Standard PHY registers are accessible through MDIO C22, whereas PMAPMD and PCS are accssible through MDIO C45. Additionaly, this commit adds extended MDIO shell support for retrieving real-time MDIO register values. Signed-off-by: Vijayakannan Ayyathurai <vijayakannan.ayyathurai@intel.com>
1 parent 4753e3c commit c2468a2

File tree

6 files changed

+267
-0
lines changed

6 files changed

+267
-0
lines changed

drivers/mdio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ 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_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.intel_igc"
4445

4546
config MDIO_INIT_PRIORITY
4647
int "Init priority"

drivers/mdio/Kconfig.intel_igc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2025 Intel Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
menuconfig MDIO_INTEL_IGC
5+
bool "Intel IGC MDIO driver"
6+
default y
7+
depends on DT_HAS_INTEL_IGC_MDIO_ENABLED
8+
help
9+
Enable Intel IGC MDIO driver.
10+
11+
if MDIO_INTEL_IGC
12+
13+
config MDIO_INTEL_BUSY_CHECK_TIMEOUT
14+
int "MDIO_INTEL_IGC busy wait timeout"
15+
default 10000
16+
help
17+
This timeout in microseconds, specifies the duration to wait for the
18+
completion of an MDIO read or write cycle.
19+
20+
endif # MDIO_INTEL_IGC

drivers/mdio/mdio_intel_igc.c

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* Copyright (c) 2025 Intel Corporation.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT intel_igc_mdio
8+
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/net/mdio.h>
11+
#include <zephyr/drivers/mdio.h>
12+
#include <zephyr/drivers/pcie/pcie.h>
13+
14+
#include <zephyr/logging/log.h>
15+
LOG_MODULE_REGISTER(intel_igc_mdio, CONFIG_MDIO_LOG_LEVEL);
16+
17+
#define INTEL_IGC_MDIC_OFFSET 0x00020
18+
#define INTEL_IGC_MDIC_DATA_MASK GENMASK(15, 0)
19+
#define INTEL_IGC_MDIC_REG_MASK GENMASK(20, 16)
20+
#define INTEL_IGC_MDIC_PHY_MASK GENMASK(25, 21)
21+
#define INTEL_IGC_MDIC_OP_MASK GENMASK(27, 26)
22+
#define INTEL_IGC_MDIC_READY BIT(28)
23+
#define INTEL_IGC_MMDCTRL 0xD
24+
#define INTEL_IGC_MMDCTRL_ACTYPE_MASK GENMASK(15, 14)
25+
#define INTEL_IGC_MMDCTRL_DEVAD_MASK GENMASK(4, 0)
26+
#define INTEL_IGC_MMDDATA 0xE
27+
#define INTEL_IGC_DEFAULT_DEVNUM 0
28+
29+
struct intel_igc_mdio_cfg {
30+
const struct device *const platform;
31+
};
32+
33+
struct intel_igc_mdio_data {
34+
struct k_mutex mdio_lock;
35+
mm_reg_t base;
36+
};
37+
38+
static int intel_igc_mdio(struct intel_igc_mdio_data *data, uint32_t command)
39+
{
40+
uint32_t mdic = data->base + INTEL_IGC_MDIC_OFFSET;
41+
int ret;
42+
43+
k_mutex_lock(&data->mdio_lock, K_FOREVER);
44+
45+
sys_write32(command, mdic);
46+
/* Wait for the read or write transaction to complete */
47+
if (!WAIT_FOR((sys_read32(mdic) & INTEL_IGC_MDIC_READY),
48+
CONFIG_MDIO_INTEL_BUSY_CHECK_TIMEOUT, k_usleep(1))) {
49+
LOG_ERR("MDIC operation timed out");
50+
k_mutex_unlock(&data->mdio_lock);
51+
return -ETIMEDOUT;
52+
}
53+
54+
ret = sys_read32(mdic);
55+
k_mutex_unlock(&data->mdio_lock);
56+
57+
return ret;
58+
}
59+
60+
static int intel_igc_mdio_read(const struct device *dev, uint8_t prtad,
61+
uint8_t regad, uint16_t *user_data)
62+
{
63+
struct intel_igc_mdio_data *const dev_data = dev->data;
64+
int ret;
65+
66+
uint32_t command = FIELD_PREP(INTEL_IGC_MDIC_PHY_MASK, prtad) |
67+
FIELD_PREP(INTEL_IGC_MDIC_REG_MASK, regad) |
68+
FIELD_PREP(INTEL_IGC_MDIC_OP_MASK, MDIO_OP_C22_READ);
69+
70+
ret = intel_igc_mdio(dev_data, command);
71+
if (ret < 0) {
72+
return ret;
73+
}
74+
75+
*user_data = FIELD_GET(INTEL_IGC_MDIC_DATA_MASK, ret);
76+
77+
return 0;
78+
}
79+
80+
static int intel_igc_mdio_write(const struct device *dev, uint8_t prtad,
81+
uint8_t regad, uint16_t user_data)
82+
{
83+
struct intel_igc_mdio_data *const dev_data = dev->data;
84+
int ret;
85+
86+
uint32_t command = FIELD_PREP(INTEL_IGC_MDIC_PHY_MASK, prtad) |
87+
FIELD_PREP(INTEL_IGC_MDIC_REG_MASK, regad) |
88+
FIELD_PREP(INTEL_IGC_MDIC_OP_MASK, MDIO_OP_C22_WRITE) |
89+
FIELD_PREP(INTEL_IGC_MDIC_DATA_MASK, user_data);
90+
91+
ret = intel_igc_mdio(dev_data, command);
92+
93+
return ret < 0 ? ret : 0;
94+
}
95+
96+
static int intel_igc_mdio_pre_handle_c45(const struct device *dev, uint8_t prtad,
97+
uint8_t devnum, uint16_t regad)
98+
{
99+
int ret;
100+
101+
/* Set device number using MMDCTRL */
102+
ret = intel_igc_mdio_write(dev, prtad, INTEL_IGC_MMDCTRL,
103+
(uint16_t)(FIELD_PREP(INTEL_IGC_MMDCTRL_DEVAD_MASK, devnum)));
104+
if (ret < 0) {
105+
return ret;
106+
}
107+
108+
/* Set register address using MMDDATA */
109+
ret = intel_igc_mdio_write(dev, prtad, INTEL_IGC_MMDDATA, regad);
110+
if (ret < 0) {
111+
return ret;
112+
}
113+
114+
/* Set device number and access type as data using MMDCTRL */
115+
return intel_igc_mdio_write(dev, prtad, INTEL_IGC_MMDCTRL,
116+
(uint16_t)(FIELD_PREP(INTEL_IGC_MMDCTRL_ACTYPE_MASK, 1) |
117+
FIELD_PREP(INTEL_IGC_MMDCTRL_DEVAD_MASK, devnum)));
118+
}
119+
120+
static int intel_igc_mdio_post_handle_c45(const struct device *dev, uint8_t prtad)
121+
{
122+
/* Restore default device number using MMDCTRL */
123+
return intel_igc_mdio_write(dev, prtad, INTEL_IGC_MMDCTRL, INTEL_IGC_DEFAULT_DEVNUM);
124+
}
125+
126+
static int intel_igc_mdio_read_c45(const struct device *dev, uint8_t prtad,
127+
uint8_t devnum, uint16_t regad, uint16_t *user_data)
128+
{
129+
int ret = intel_igc_mdio_pre_handle_c45(dev, prtad, devnum, regad);
130+
131+
if (ret < 0) {
132+
return ret;
133+
}
134+
135+
/* Read user data using MMDDATA */
136+
ret = intel_igc_mdio_read(dev, prtad, INTEL_IGC_MMDDATA, user_data);
137+
if (ret < 0) {
138+
return ret;
139+
}
140+
141+
return intel_igc_mdio_post_handle_c45(dev, prtad);
142+
}
143+
144+
static int intel_igc_mdio_write_c45(const struct device *dev, uint8_t prtad,
145+
uint8_t devnum, uint16_t regad, uint16_t user_data)
146+
{
147+
int ret = intel_igc_mdio_pre_handle_c45(dev, prtad, devnum, regad);
148+
149+
if (ret < 0) {
150+
return ret;
151+
}
152+
153+
/* Write the user_data using MMDDATA */
154+
ret = intel_igc_mdio_write(dev, prtad, INTEL_IGC_MMDDATA, user_data);
155+
if (ret < 0) {
156+
return ret;
157+
}
158+
159+
return intel_igc_mdio_post_handle_c45(dev, prtad);
160+
}
161+
162+
static void intel_igc_mdio_bus_enable(const struct device *dev)
163+
{
164+
ARG_UNUSED(dev);
165+
}
166+
167+
static void intel_igc_mdio_bus_disable(const struct device *dev)
168+
{
169+
ARG_UNUSED(dev);
170+
}
171+
172+
static const struct mdio_driver_api mdio_api = {
173+
.read = intel_igc_mdio_read,
174+
.write = intel_igc_mdio_write,
175+
.read_c45 = intel_igc_mdio_read_c45,
176+
.write_c45 = intel_igc_mdio_write_c45,
177+
.bus_enable = intel_igc_mdio_bus_enable,
178+
.bus_disable = intel_igc_mdio_bus_disable,
179+
};
180+
181+
static int intel_igc_mdio_init(const struct device *dev)
182+
{
183+
const struct intel_igc_mdio_cfg *cfg = dev->config;
184+
struct intel_igc_mdio_data *data = dev->data;
185+
186+
data->base = DEVICE_MMIO_GET(cfg->platform);
187+
188+
return k_mutex_init(&data->mdio_lock);
189+
}
190+
191+
#define INTEL_IGC_MDIO_INIT(n) \
192+
static struct intel_igc_mdio_data mdio_data_##n; \
193+
static struct intel_igc_mdio_cfg mdio_cfg_##n = { \
194+
.platform = DEVICE_DT_GET(DT_INST_PARENT(n)), \
195+
}; \
196+
\
197+
DEVICE_DT_INST_DEFINE(n, &intel_igc_mdio_init, NULL, &mdio_data_##n, &mdio_cfg_##n, \
198+
POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY, &mdio_api);
199+
200+
DT_INST_FOREACH_STATUS_OKAY(INTEL_IGC_MDIO_INIT)

drivers/mdio/mdio_shell.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,43 @@ static bool device_is_mdio(const struct device *dev)
1919
{
2020
return DEVICE_API_IS(mdio, dev);
2121
}
22+
#if DT_HAS_COMPAT_STATUS_OKAY(atmel_sam_mdio)
23+
#define DT_DRV_COMPAT atmel_sam_mdio
24+
#elif DT_HAS_COMPAT_STATUS_OKAY(espressif_esp32_mdio)
25+
#define DT_DRV_COMPAT espressif_esp32_mdio
26+
#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_imx_netc_emdio)
27+
#define DT_DRV_COMPAT nxp_imx_netc_emdio
28+
#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_s32_netc_emdio)
29+
#define DT_DRV_COMPAT nxp_s32_netc_emdio
30+
#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_s32_gmac_mdio)
31+
#define DT_DRV_COMPAT nxp_s32_gmac_mdio
32+
#elif DT_HAS_COMPAT_STATUS_OKAY(adi_adin2111_mdio)
33+
#define DT_DRV_COMPAT adi_adin2111_mdio
34+
#elif DT_HAS_COMPAT_STATUS_OKAY(smsc_lan91c111_mdio)
35+
#define DT_DRV_COMPAT smsc_lan91c111_mdio
36+
#elif DT_HAS_COMPAT_STATUS_OKAY(zephyr_mdio_gpio)
37+
#define DT_DRV_COMPAT zephyr_mdio_gpio
38+
#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_enet_mdio)
39+
#define DT_DRV_COMPAT nxp_enet_mdio
40+
#elif DT_HAS_COMPAT_STATUS_OKAY(infineon_xmc4xxx_mdio)
41+
#define DT_DRV_COMPAT infineon_xmc4xxx_mdio
42+
#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_enet_qos_mdio)
43+
#define DT_DRV_COMPAT nxp_enet_qos_mdio
44+
#elif DT_HAS_COMPAT_STATUS_OKAY(litex_liteeth_mdio)
45+
#define DT_DRV_COMPAT litex_liteeth_mdio
46+
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_mdio)
47+
#define DT_DRV_COMPAT st_stm32_mdio
48+
#elif DT_HAS_COMPAT_STATUS_OKAY(snps_dwcxgmac_mdio)
49+
#define DT_DRV_COMPAT snps_dwcxgmac_mdio
50+
#elif DT_HAS_COMPAT_STATUS_OKAY(sensry_sy1xx_mdio)
51+
#define DT_DRV_COMPAT sensry_sy1xx_mdio
52+
#elif DT_HAS_COMPAT_STATUS_OKAY(xlnx_axi_ethernet_1_00_a_mdio)
53+
#define DT_DRV_COMPAT xlnx_axi_ethernet_1_00_a_mdio
54+
#elif DT_HAS_COMPAT_STATUS_OKAY(intel_igc_mdio)
55+
#define DT_DRV_COMPAT intel_igc_mdio
56+
#else
57+
#error "No known devicetree compatible match for MDIO shell"
58+
#endif
2259

2360
static void device_name_get(size_t idx, struct shell_static_entry *entry)
2461
{

dts/bindings/mdio/intel,igc-mdio.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright 2025 Intel Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Intel IGC MDIO device.
5+
6+
compatible: "intel,igc-mdio"
7+
8+
include: [mdio-controller.yaml, base.yaml]

0 commit comments

Comments
 (0)