Skip to content

Commit 824d97c

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. - PHY link interrupt service handler. - 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 a837a5c commit 824d97c

File tree

5 files changed

+290
-0
lines changed

5 files changed

+290
-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
@@ -123,4 +123,12 @@ config PHY_VERIFY_DEVICE_IDENTIFICATION
123123
Verify the organizationally unique identifier that is reported
124124
by the phy chip.
125125

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

drivers/ethernet/phy/phy_intel_igc.c

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/*
2+
* Copyright (c) 2025 Intel Corporation.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <errno.h>
8+
#include <zephyr/device.h>
9+
#include <zephyr/drivers/mdio.h>
10+
#include <zephyr/init.h>
11+
#include <zephyr/kernel.h>
12+
#include <zephyr/net/phy.h>
13+
#include <zephyr/net/mii.h>
14+
#include <zephyr/sys/util.h>
15+
#include <zephyr/drivers/ethernet/eth_intel_plat.h>
16+
17+
#define DT_DRV_COMPAT intel_igc_phy
18+
19+
#include <zephyr/logging/log.h>
20+
LOG_MODULE_REGISTER(intel_igc_phy, CONFIG_PHY_LOG_LEVEL);
21+
22+
#include "phy_mii.h"
23+
24+
#define PHY_REV_MASK GENMASK(31, 4)
25+
#define MII_INVALID_PHY_ID UINT32_MAX
26+
#define PHY_ID_SHIFT 16
27+
#define PHY_IGC_READ_DELAY 100
28+
29+
struct phy_intel_igc_cfg {
30+
const struct device *mdio;
31+
enum phy_link_speed default_speeds;
32+
uint8_t phy_addr;
33+
};
34+
35+
struct phy_intel_igc_data {
36+
const struct device *phy;
37+
struct phy_link_state state;
38+
struct k_work_delayable link_work;
39+
phy_callback_t cb;
40+
void *cb_data;
41+
};
42+
43+
static int phy_intel_igc_read(const struct device *dev, uint16_t reg_addr, uint32_t *value)
44+
{
45+
const struct phy_intel_igc_cfg *const cfg = dev->config;
46+
47+
return mdio_read(cfg->mdio, cfg->phy_addr, reg_addr, (uint16_t *)value);
48+
}
49+
50+
static int phy_intel_igc_write(const struct device *dev, uint16_t reg_addr, uint32_t value)
51+
{
52+
const struct phy_intel_igc_cfg *const cfg = dev->config;
53+
54+
return mdio_write(cfg->mdio, cfg->phy_addr, reg_addr, (uint16_t)value);
55+
}
56+
57+
static int phy_intel_igc_id_read(const struct device *dev)
58+
{
59+
uint32_t phy_id, value = 0;
60+
int ret;
61+
62+
ret = phy_intel_igc_read(dev, MII_PHYID1R, &value);
63+
if (ret < 0) {
64+
return ret;
65+
}
66+
67+
phy_id = value << PHY_ID_SHIFT;
68+
69+
ret = phy_intel_igc_read(dev, MII_PHYID2R, &value);
70+
if (ret < 0) {
71+
return ret;
72+
}
73+
74+
phy_id |= (value & PHY_REV_MASK);
75+
76+
if (phy_id == MII_INVALID_PHY_ID) {
77+
return -EINVAL;
78+
}
79+
80+
LOG_INF("PHY ID = 0x%x", phy_id);
81+
82+
return 0;
83+
}
84+
85+
static int phy_intel_igc_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds,
86+
enum phy_cfg_link_flag flags)
87+
{
88+
if ((flags & PHY_FLAG_AUTO_NEGOTIATION_DISABLED) != 0) {
89+
LOG_ERR("Disabling auto-negotiation is not supported by this driver");
90+
return -ENOTSUP;
91+
}
92+
93+
return phy_mii_cfg_link_autoneg(dev, adv_speeds, true);
94+
}
95+
96+
static int phy_intel_igc_get_link_state(const struct device *dev, struct phy_link_state *state)
97+
{
98+
uint32_t anlpar_val = 0;
99+
uint32_t anar_val = 0;
100+
uint32_t c1kt_val = 0;
101+
uint32_t s1kt_val = 0;
102+
uint32_t bmsr_val = 0;
103+
int ret;
104+
105+
ret = phy_intel_igc_read(dev, MII_BMSR, &bmsr_val);
106+
if (ret < 0) {
107+
return ret;
108+
}
109+
110+
if ((bmsr_val & MII_BMSR_LINK_STATUS) == 0U) {
111+
state->is_up = false;
112+
return 0;
113+
}
114+
state->is_up = true;
115+
116+
ret = phy_intel_igc_read(dev, MII_ANAR, &anar_val);
117+
if (ret < 0) {
118+
return ret;
119+
}
120+
121+
ret = phy_intel_igc_read(dev, MII_ANLPAR, &anlpar_val);
122+
if (ret < 0) {
123+
return ret;
124+
}
125+
126+
ret = phy_intel_igc_read(dev, MII_1KTCR, &c1kt_val);
127+
if (ret < 0) {
128+
return ret;
129+
}
130+
131+
ret = phy_intel_igc_read(dev, MII_1KSTSR, &s1kt_val);
132+
if (ret < 0) {
133+
return ret;
134+
}
135+
136+
s1kt_val = (s1kt_val >> 2) & 0xFFFF;
137+
if ((c1kt_val & s1kt_val & MII_ADVERTISE_1000_FULL) != 0U) {
138+
state->speed = LINK_FULL_1000BASE;
139+
} else if ((anar_val & anlpar_val & MII_ADVERTISE_100_FULL) != 0U) {
140+
state->speed = LINK_FULL_100BASE;
141+
} else if ((anar_val & anlpar_val & MII_ADVERTISE_100_HALF) != 0U) {
142+
state->speed = LINK_HALF_100BASE;
143+
} else if ((anar_val & anlpar_val & MII_ADVERTISE_10_FULL) != 0U) {
144+
state->speed = LINK_FULL_10BASE;
145+
} else {
146+
state->speed = LINK_HALF_10BASE;
147+
}
148+
149+
return 0;
150+
}
151+
152+
static void invoke_link_cb(const struct device *dev)
153+
{
154+
struct phy_intel_igc_data *const data = dev->data;
155+
156+
if (data->cb == NULL) {
157+
return;
158+
}
159+
160+
data->cb(data->phy, &data->state, data->cb_data);
161+
}
162+
163+
static int phy_intel_igc_link_cb_set(const struct device *dev, phy_callback_t cb, void *user_data)
164+
{
165+
struct phy_intel_igc_data *data = dev->data;
166+
167+
data->cb = cb;
168+
data->cb_data = user_data;
169+
170+
invoke_link_cb(dev);
171+
172+
return 0;
173+
}
174+
175+
static void update_link_state(struct k_work *work)
176+
{
177+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
178+
struct phy_intel_igc_data *data = CONTAINER_OF(dwork, struct phy_intel_igc_data, link_work);
179+
const struct device *dev = data->phy;
180+
struct phy_link_state new_state = {0};
181+
182+
if (phy_intel_igc_get_link_state(dev, &new_state) < 0) {
183+
LOG_DBG("Failed to read PHY status registers");
184+
}
185+
186+
if (new_state.is_up != data->state.is_up || new_state.speed != data->state.speed) {
187+
data->state.is_up = new_state.is_up;
188+
data->state.speed = new_state.speed;
189+
190+
invoke_link_cb(dev);
191+
192+
if (data->state.is_up) {
193+
LOG_INF("PHY Link is Up with Speed %s Mbps & %s duplex",
194+
PHY_LINK_IS_SPEED_1000M(new_state.speed)
195+
? "1000"
196+
: (PHY_LINK_IS_SPEED_100M(new_state.speed) ? "100" : "10"),
197+
PHY_LINK_IS_FULL_DUPLEX(new_state.speed) ? "Full" : "Half");
198+
} else {
199+
LOG_INF("PHY Link is Down");
200+
}
201+
}
202+
}
203+
204+
void eth_intel_igc_misc_phy_link_isr(const struct device *dev)
205+
{
206+
struct phy_intel_igc_data *data = dev->data;
207+
208+
k_work_reschedule(&data->link_work, K_NO_WAIT);
209+
}
210+
211+
static int phy_intel_igc_init(const struct device *dev)
212+
{
213+
const struct phy_intel_igc_cfg *const cfg = dev->config;
214+
int ret;
215+
216+
ret = phy_intel_igc_id_read(dev);
217+
if (ret < 0) {
218+
LOG_ERR("Failed to read PHY ID");
219+
return ret;
220+
}
221+
222+
return phy_intel_igc_cfg_link(dev, cfg->default_speeds, 0);
223+
}
224+
225+
static DEVICE_API(ethphy, phy_api) = {
226+
.get_link = phy_intel_igc_get_link_state,
227+
.cfg_link = phy_intel_igc_cfg_link,
228+
.link_cb_set = phy_intel_igc_link_cb_set,
229+
.read = phy_intel_igc_read,
230+
.write = phy_intel_igc_write,
231+
};
232+
233+
#define IGC_PHY_GENERATE_DEFAULT_SPEEDS(n) \
234+
((DT_INST_ENUM_HAS_VALUE(n, default_speeds, 10base_half_duplex) ? LINK_HALF_10BASE : 0) | \
235+
(DT_INST_ENUM_HAS_VALUE(n, default_speeds, 10base_full_duplex) ? LINK_FULL_10BASE : 0) | \
236+
(DT_INST_ENUM_HAS_VALUE(n, default_speeds, 100base_half_duplex) ? LINK_HALF_100BASE \
237+
: 0) | \
238+
(DT_INST_ENUM_HAS_VALUE(n, default_speeds, 100base_full_duplex) ? LINK_FULL_100BASE \
239+
: 0) | \
240+
(DT_INST_ENUM_HAS_VALUE(n, default_speeds, 1000base_full_duplex) ? LINK_FULL_1000BASE \
241+
: 0))
242+
243+
#define INTEL_IGC_PHY_CONFIG(n) \
244+
BUILD_ASSERT(IGC_PHY_GENERATE_DEFAULT_SPEEDS(n) != 0, \
245+
"At least one valid speed must be configured for this driver"); \
246+
static const struct phy_intel_igc_cfg phy_cfg_##n = { \
247+
.mdio = DEVICE_DT_GET(DT_INST_PARENT(n)), \
248+
.phy_addr = DT_INST_REG_ADDR(n), \
249+
.default_speeds = IGC_PHY_GENERATE_DEFAULT_SPEEDS(n)};
250+
251+
#define INTEL_IGC_PHY_INIT(n) \
252+
INTEL_IGC_PHY_CONFIG(n); \
253+
static struct phy_intel_igc_data phy_data_##n = { \
254+
.link_work = Z_WORK_DELAYABLE_INITIALIZER(update_link_state), \
255+
.phy = DEVICE_DT_INST_GET(n), \
256+
}; \
257+
DEVICE_DT_INST_DEFINE(n, &phy_intel_igc_init, NULL, &phy_data_##n, &phy_cfg_##n, \
258+
POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, &phy_api);
259+
260+
DT_INST_FOREACH_STATUS_OKAY(INTEL_IGC_PHY_INIT)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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: [ethernet-phy.yaml]
9+
10+
properties:
11+
reg:
12+
required: true
13+
description: PHY address.

include/zephyr/drivers/ethernet/eth_intel_plat.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@ extern "C" {
2525
*/
2626
extern uint32_t eth_intel_get_pcie_bdf(const struct device *dev);
2727

28+
/**
29+
* @brief This ISR will be called if there is any link change in the PHY.
30+
* This interrupt is part of the Miscellaneous interrupt vector.
31+
*
32+
* @param dev Pointer to the device structure.
33+
*/
34+
extern void eth_intel_igc_misc_phy_link_isr(const struct device *dev);
35+
2836
#endif /* ZEPHYR_INCLUDE_DRIVERS_ETH_INTEL_PLAT_H__ */

0 commit comments

Comments
 (0)