Skip to content

Commit 34fc3da

Browse files
committed
drivers: ethernet: phy: Add Intel IGC PHY driver
Intel IGC(aka Foxville) is a fully integrated MAC and PHY. This commit adds support for the Intel IGC PHY driver with the following features: - Support 10/100/1000 BASE-T. - Auto Negotiation for 10/100/1000 Mbps speeds. - Integration with the MDIO interface for PHY register access. - Device tree support for PHY configuration and initialization. It uses the MDIO interface to communicate with the PHY and supports modular configuration via the device tree. Signed-off-by: Vijayakannan Ayyathurai <vijayakannan.ayyathurai@intel.com> Signed-off-by: Ling Pei Lee <pei.lee.ling@intel.com>
1 parent 0f8cf9f commit 34fc3da

File tree

4 files changed

+371
-0
lines changed

4 files changed

+371
-0
lines changed

drivers/ethernet/phy/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ zephyr_library_sources_ifdef(CONFIG_PHY_GENERIC_MII phy_mii.c)
55
# zephyr-keep-sorted-start
66
zephyr_library_sources_ifdef(CONFIG_PHY_ADIN2111 phy_adin2111.c)
77
zephyr_library_sources_ifdef(CONFIG_PHY_DM8806 phy_dm8806.c)
8+
zephyr_library_sources_ifdef(CONFIG_PHY_INTEL_IGC phy_intel_igc.c)
89
zephyr_library_sources_ifdef(CONFIG_PHY_MICROCHIP_KSZ8081 phy_microchip_ksz8081.c)
910
zephyr_library_sources_ifdef(CONFIG_PHY_MICROCHIP_T1S phy_microchip_t1s.c)
1011
zephyr_library_sources_ifdef(CONFIG_PHY_MICROCHIP_VSC8541 phy_microchip_vsc8541.c)

drivers/ethernet/phy/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,12 @@ config PHY_VERIFY_DEVICE_IDENTIFICATION
122122
Verify the organizationally unique identifier that is reported
123123
by the phy chip.
124124

125+
config PHY_INTEL_IGC
126+
bool "Intel IGC PHY driver"
127+
default y
128+
depends on DT_HAS_INTEL_IGC_PHY_ENABLED
129+
depends on MDIO_INTEL_IGC
130+
help
131+
Enable Intel IGC PHY driver.
132+
125133
endif # "Ethernet PHY Drivers"

drivers/ethernet/phy/phy_intel_igc.c

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
/*
2+
* Copyright (c) 2025 Intel Corporation.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT intel_igc_phy
8+
9+
#include <errno.h>
10+
#include <zephyr/device.h>
11+
#include <zephyr/drivers/mdio.h>
12+
#include <zephyr/init.h>
13+
#include <zephyr/kernel.h>
14+
#include <zephyr/net/phy.h>
15+
#include <zephyr/net/mii.h>
16+
#include <zephyr/drivers/ethernet/eth_intel_plat.h>
17+
18+
#include <zephyr/logging/log.h>
19+
LOG_MODULE_REGISTER(intel_igc_phy, CONFIG_PHY_LOG_LEVEL);
20+
21+
/* MULTI GBT AN Control Register */
22+
#define ANEG_MGBT_AN_CTRL_REG 0x20
23+
#define ADVERTISE_2P5G BIT(7)
24+
25+
#define ANEG_MMD_DEV_NUM 7
26+
#define PHY_REV_MASK GENMASK(31, 4)
27+
#define MII_INVALID_PHY_ID UINT32_MAX
28+
#define PHY_ID_SHIFT 16
29+
30+
struct phy_intel_igc_cfg {
31+
const struct device *const mdio;
32+
uint8_t phy_addr;
33+
bool multi_gb_supported;
34+
};
35+
36+
struct phy_intel_igc_data {
37+
const struct device *dev;
38+
struct phy_link_state state;
39+
uint32_t phy_id;
40+
};
41+
42+
static int phy_intel_igc_read(const struct device *dev, uint16_t reg_addr, uint32_t *value)
43+
{
44+
const struct phy_intel_igc_cfg *const cfg = dev->config;
45+
46+
return mdio_read(cfg->mdio, cfg->phy_addr, reg_addr, (uint16_t *)value);
47+
}
48+
49+
static int phy_intel_igc_write(const struct device *dev, uint16_t reg_addr, uint32_t value)
50+
{
51+
const struct phy_intel_igc_cfg *const cfg = dev->config;
52+
53+
return mdio_write(cfg->mdio, cfg->phy_addr, reg_addr, (uint16_t)value);
54+
}
55+
56+
static int phy_intel_igc_await_phy_status(const struct device *dev, uint16_t reg_addr,
57+
uint16_t mask, uint32_t *value, uint16_t delay)
58+
{
59+
uint32_t timeout;
60+
61+
for (timeout = 0U; timeout < CONFIG_PHY_AUTONEG_TIMEOUT_MS; timeout += delay) {
62+
if (phy_intel_igc_read(dev, reg_addr, value) >= 0) {
63+
if ((*value & mask) == mask) {
64+
return 0;
65+
}
66+
}
67+
k_msleep(delay);
68+
}
69+
70+
return -ETIMEDOUT;
71+
}
72+
73+
static int phy_intel_igc_get_link_state(const struct device *dev,
74+
struct phy_link_state *state)
75+
{
76+
const struct phy_intel_igc_cfg *const cfg = dev->config;
77+
uint32_t value = 0;
78+
int ret;
79+
80+
ret = phy_intel_igc_await_phy_status(dev, MII_BMSR,
81+
MII_BMSR_LINK_STATUS, &value, 10);
82+
if (ret < 0) {
83+
LOG_INF("Get PHY (%d) Status Timed Out", cfg->phy_addr);
84+
state->is_up = false;
85+
} else {
86+
state->is_up = (value & MII_BMSR_LINK_STATUS) ? true : false;
87+
}
88+
89+
LOG_INF("PHY (%d) Link %s", cfg->phy_addr, state->is_up ? "Up" : "Down");
90+
91+
return ret;
92+
}
93+
94+
static int phy_intel_igc_restart_aneg(const struct device *dev)
95+
{
96+
uint32_t value = 0;
97+
int ret;
98+
99+
ret = phy_intel_igc_read(dev, MII_BMCR, &value);
100+
if (ret < 0) {
101+
return ret;
102+
}
103+
104+
/* Enable Autoneg and Autoneg-restart */
105+
value |= MII_BMCR_AUTONEG_ENABLE | MII_BMCR_AUTONEG_RESTART;
106+
ret = phy_intel_igc_write(dev, MII_BMCR, value);
107+
if (ret < 0) {
108+
return ret;
109+
}
110+
111+
ret = phy_intel_igc_await_phy_status(dev, MII_BMSR,
112+
MII_BMSR_AUTONEG_COMPLETE, &value, 100);
113+
if (ret < 0) {
114+
return ret;
115+
}
116+
117+
return 0;
118+
}
119+
120+
static int phy_intel_igc_cfg_link(const struct device *dev, enum phy_link_speed speed)
121+
{
122+
uint32_t mii_1ktcr_val = 0;
123+
uint32_t mii_anar_val = 0;
124+
int ret = 0;
125+
126+
if ((phy_intel_igc_read(dev, MII_ANAR, &mii_anar_val) < 0) ||
127+
(phy_intel_igc_read(dev, MII_1KTCR, &mii_1ktcr_val) < 0)) {
128+
return -EIO;
129+
}
130+
131+
if (speed & LINK_HALF_10BASE_T) {
132+
mii_anar_val |= MII_ADVERTISE_10_HALF;
133+
} else {
134+
mii_anar_val &= ~MII_ADVERTISE_10_HALF;
135+
}
136+
if (speed & LINK_FULL_10BASE_T) {
137+
mii_anar_val |= MII_ADVERTISE_10_FULL;
138+
} else {
139+
mii_anar_val &= ~MII_ADVERTISE_10_FULL;
140+
}
141+
if (speed & LINK_HALF_100BASE_T) {
142+
mii_anar_val |= MII_ADVERTISE_100_HALF;
143+
} else {
144+
mii_anar_val &= ~MII_ADVERTISE_100_HALF;
145+
}
146+
if (speed & LINK_FULL_100BASE_T) {
147+
mii_anar_val |= MII_ADVERTISE_100_FULL;
148+
} else {
149+
mii_anar_val &= ~MII_ADVERTISE_100_FULL;
150+
}
151+
152+
ret = phy_intel_igc_write(dev, MII_ANAR, mii_anar_val);
153+
if (ret < 0) {
154+
return ret;
155+
}
156+
157+
if (speed & LINK_FULL_1000BASE_T) {
158+
mii_1ktcr_val |= MII_ADVERTISE_1000_FULL;
159+
} else {
160+
mii_1ktcr_val &= ~MII_ADVERTISE_1000_FULL;
161+
}
162+
163+
ret = phy_intel_igc_write(dev, MII_1KTCR, mii_1ktcr_val);
164+
if (ret < 0) {
165+
return ret;
166+
}
167+
168+
ret = phy_intel_igc_restart_aneg(dev);
169+
if (ret < 0) {
170+
return ret;
171+
}
172+
173+
return ret;
174+
}
175+
176+
static int phy_intel_igc_aneg_10_100mbps(const struct device *dev)
177+
{
178+
uint32_t value = 0;
179+
int ret = 0;
180+
181+
ret = phy_intel_igc_read(dev, MII_ANAR, &value);
182+
if (ret < 0) {
183+
return ret;
184+
}
185+
186+
/* Configure 10/100 Mbps advertise */
187+
value |= MII_ADVERTISE_10_HALF | MII_ADVERTISE_100_HALF |
188+
MII_ADVERTISE_10_FULL | MII_ADVERTISE_100_FULL;
189+
190+
/* Disable Flow control (Rx & Tx) */
191+
value &= ~(MII_ADVERTISE_PAUSE | MII_ADVERTISE_ASYM_PAUSE);
192+
193+
ret = phy_intel_igc_write(dev, MII_ANAR, value);
194+
if (ret < 0) {
195+
return ret;
196+
}
197+
198+
return ret;
199+
}
200+
201+
static int phy_intel_igc_aneg_1gbps(const struct device *dev)
202+
{
203+
uint32_t value = 0;
204+
int ret = 0;
205+
206+
ret = phy_intel_igc_read(dev, MII_1KTCR, &value);
207+
if (ret < 0) {
208+
return ret;
209+
}
210+
211+
/* Configure 1000 Mbps advertise */
212+
ret = phy_intel_igc_read(dev, MII_1KTCR, &value);
213+
if (ret < 0) {
214+
return ret;
215+
}
216+
217+
value &= ~MII_ADVERTISE_1000_HALF;
218+
value |= MII_ADVERTISE_1000_FULL;
219+
ret = phy_intel_igc_write(dev, MII_1KTCR, value);
220+
if (ret < 0) {
221+
return ret;
222+
}
223+
224+
return ret;
225+
}
226+
227+
static int phy_intel_igc_aneg_multigb(const struct device *dev)
228+
{
229+
const struct phy_intel_igc_cfg *cfg = dev->config;
230+
uint16_t value = 0;
231+
int ret = 0;
232+
233+
/* Configure Multi Gigabit advertise */
234+
ret = mdio_read_c45(cfg->mdio, cfg->phy_addr, ANEG_MMD_DEV_NUM,
235+
ANEG_MGBT_AN_CTRL_REG, &value);
236+
if (ret < 0) {
237+
return ret;
238+
}
239+
240+
if (cfg->multi_gb_supported) {
241+
value |= ADVERTISE_2P5G;
242+
} else {
243+
value &= ~ADVERTISE_2P5G;
244+
}
245+
246+
ret = mdio_write_c45(cfg->mdio, cfg->phy_addr, ANEG_MMD_DEV_NUM,
247+
ANEG_MGBT_AN_CTRL_REG, value);
248+
if (ret < 0) {
249+
return ret;
250+
}
251+
252+
return ret;
253+
}
254+
255+
static int phy_intel_igc_id_read(const struct device *dev)
256+
{
257+
const struct phy_intel_igc_cfg *cfg = dev->config;
258+
struct phy_intel_igc_data *data = dev->data;
259+
uint32_t value = 0;
260+
int ret = 0;
261+
262+
ret = phy_intel_igc_read(dev, MII_PHYID1R, &value);
263+
if (ret < 0) {
264+
return ret;
265+
}
266+
267+
data->phy_id = value << PHY_ID_SHIFT;
268+
269+
ret = phy_intel_igc_read(dev, MII_PHYID2R, &value);
270+
if (ret < 0) {
271+
return ret;
272+
}
273+
274+
data->phy_id |= (value & PHY_REV_MASK);
275+
276+
if (data->phy_id == MII_INVALID_PHY_ID) {
277+
LOG_ERR("Invalid PHY ID (%d) found at address (%d)",
278+
data->phy_id, cfg->phy_addr);
279+
return -EINVAL;
280+
}
281+
282+
LOG_INF("PHY ID = 0x%x", data->phy_id);
283+
284+
return 0;
285+
}
286+
287+
static int phy_intel_igc_init(const struct device *dev)
288+
{
289+
int ret;
290+
291+
ret = phy_intel_igc_id_read(dev);
292+
if (ret < 0) {
293+
LOG_ERR("Failed to read PHY ID");
294+
return ret;
295+
}
296+
297+
ret = phy_intel_igc_aneg_10_100mbps(dev);
298+
if (ret < 0) {
299+
LOG_ERR("Failed to config Autoneg 10/100 Mbps");
300+
return ret;
301+
}
302+
303+
ret = phy_intel_igc_aneg_1gbps(dev);
304+
if (ret < 0) {
305+
LOG_ERR("Failed to config Autoneg 1Gbps");
306+
return ret;
307+
}
308+
309+
ret = phy_intel_igc_aneg_multigb(dev);
310+
if (ret < 0) {
311+
LOG_ERR("Failed to config Autoneg Multi-Gigabit");
312+
return ret;
313+
}
314+
315+
ret = phy_intel_igc_restart_aneg(dev);
316+
if (ret < 0) {
317+
LOG_ERR("Failed to restart Autonegotiation");
318+
}
319+
320+
return ret;
321+
}
322+
323+
static const struct ethphy_driver_api phy_api = {
324+
.get_link = phy_intel_igc_get_link_state,
325+
.cfg_link = phy_intel_igc_cfg_link,
326+
.read = phy_intel_igc_read,
327+
.write = phy_intel_igc_write,
328+
};
329+
330+
#define INTEL_IGC_PHY_CONFIG(n) \
331+
static const struct phy_intel_igc_cfg phy_cfg_##n = { \
332+
.mdio = DEVICE_DT_GET(DT_INST_PARENT(n)), \
333+
.phy_addr = DT_INST_REG_ADDR(n), \
334+
.multi_gb_supported = DT_INST_PROP(n, multi_gigabit_support), \
335+
};
336+
337+
#define INTEL_IGC_PHY_INIT(n) \
338+
INTEL_IGC_PHY_CONFIG(n); \
339+
static struct phy_intel_igc_data phy_data_##n; \
340+
\
341+
DEVICE_DT_INST_DEFINE(n, &phy_intel_igc_init, NULL, \
342+
&phy_data_##n, &phy_cfg_##n, \
343+
POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, &phy_api);
344+
345+
DT_INST_FOREACH_STATUS_OKAY(INTEL_IGC_PHY_INIT)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) 2025 Intel Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Intel IGC PHY documentation.
5+
6+
compatible: "intel,igc-phy"
7+
8+
include: [phy.yaml, ethernet-controller.yaml]
9+
10+
properties:
11+
reg:
12+
required: true
13+
description: PHY address.
14+
15+
multi-gigabit-support:
16+
type: boolean
17+
description: It Enable 2.5Gbps multi-gigabit mode.

0 commit comments

Comments
 (0)