Skip to content

Commit 62237cf

Browse files
meowthinksuda-morris
authored andcommitted
Add Microchip KSZ8041 PHY support.
Merges espressif/esp-idf#5452
1 parent 49c8d07 commit 62237cf

File tree

3 files changed

+333
-0
lines changed

3 files changed

+333
-0
lines changed

components/esp_eth/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ if(CONFIG_ETH_ENABLED)
2222
list(APPEND srcs "src/esp_eth_mac_esp32.c"
2323
"src/esp_eth_phy_dp83848.c"
2424
"src/esp_eth_phy_ip101.c"
25+
"src/esp_eth_phy_ksz8041.c"
2526
"src/esp_eth_phy_lan8720.c"
2627
"src/esp_eth_phy_rtl8201.c")
2728
endif()

components/esp_eth/include/esp_eth_phy.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,17 @@ esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config);
251251
* - NULL: create PHY instance failed because some error occurred
252252
*/
253253
esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config);
254+
255+
/**
256+
* @brief Create a PHY instance of KSZ8041
257+
*
258+
* @param[in] config: configuration of PHY
259+
*
260+
* @return
261+
* - instance: create PHY instance successfully
262+
* - NULL: create PHY instance failed because some error occurred
263+
*/
264+
esp_eth_phy_t *esp_eth_phy_new_ksz8041(const eth_phy_config_t *config);
254265
#endif
255266
#ifdef __cplusplus
256267
}
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
#include <string.h>
15+
#include <stdlib.h>
16+
#include <sys/cdefs.h>
17+
#include "esp_log.h"
18+
#include "esp_eth.h"
19+
#include "eth_phy_regs_struct.h"
20+
#include "freertos/FreeRTOS.h"
21+
#include "freertos/task.h"
22+
#include "driver/gpio.h"
23+
24+
static const char *TAG = "ksz8041";
25+
#define PHY_CHECK(a, str, goto_tag, ...) \
26+
do \
27+
{ \
28+
if (!(a)) \
29+
{ \
30+
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
31+
goto goto_tag; \
32+
} \
33+
} while (0)
34+
35+
/***************Vendor Specific Register***************/
36+
/**
37+
* @brief PC2R(PHY Control 2 Register)
38+
*
39+
*/
40+
typedef union {
41+
struct {
42+
uint32_t hp_mdix : 1; /* HP Auto MDI/MDI-X Mode */
43+
uint32_t mdi_x_select : 1; /* MDI/MDI-X Select */
44+
uint32_t pairswap_dis : 1; /* Disable Auto MDI/MDI-X */
45+
uint32_t energy_det : 1; /* Presence of Signal on RX+/- Wire Pair */
46+
uint32_t force_link : 1; /* Force Link Pass */
47+
uint32_t power_saving : 1; /* Enable Powering Saving */
48+
uint32_t irq_level : 1; /* Interrupt Pin Active Level */
49+
uint32_t jabber : 1; /* Enable Jabber Counter */
50+
uint32_t auto_nego_comp : 1; /* Auto Negotiation Complete */
51+
uint32_t flow_ctl_cap : 1; /* Flow Control Capable */
52+
uint32_t phy_iso : 1; /* PHY in Isolate Mode */
53+
uint32_t op_mode_ind : 3; /* Operation Mode Indication */
54+
uint32_t en_sqe_test : 1; /* Enable SQE Test */
55+
uint32_t dis_data_scr: 1; /* Disable Scrambler */
56+
};
57+
uint32_t val;
58+
} pc2r_reg_t;
59+
#define ETH_PHY_PC2R_REG_ADDR (0x1F)
60+
61+
typedef struct {
62+
esp_eth_phy_t parent;
63+
esp_eth_mediator_t *eth;
64+
uint32_t addr;
65+
uint32_t reset_timeout_ms;
66+
uint32_t autonego_timeout_ms;
67+
eth_link_t link_status;
68+
int reset_gpio_num;
69+
} phy_ksz8041_t;
70+
71+
static esp_err_t ksz8041_update_link_duplex_speed(phy_ksz8041_t *ksz8041)
72+
{
73+
esp_eth_mediator_t *eth = ksz8041->eth;
74+
eth_speed_t speed = ETH_SPEED_10M;
75+
eth_duplex_t duplex = ETH_DUPLEX_HALF;
76+
bmsr_reg_t bmsr;
77+
pc2r_reg_t pc2r;
78+
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
79+
"read BMSR failed", err);
80+
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
81+
/* check if link status changed */
82+
if (ksz8041->link_status != link) {
83+
/* when link up, read negotiation result */
84+
if (link == ETH_LINK_UP) {
85+
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_PC2R_REG_ADDR, &(pc2r.val)) == ESP_OK,
86+
"read PC2R failed", err);
87+
switch (pc2r.op_mode_ind) {
88+
case 1: //10Base-T half-duplex
89+
speed = ETH_SPEED_10M;
90+
duplex = ETH_DUPLEX_HALF;
91+
break;
92+
case 2: //100Base-TX half-duplex
93+
speed = ETH_SPEED_100M;
94+
duplex = ETH_DUPLEX_HALF;
95+
break;
96+
case 5: //10Base-T full-duplex
97+
speed = ETH_SPEED_10M;
98+
duplex = ETH_DUPLEX_FULL;
99+
break;
100+
case 6: //100Base-TX full-duplex
101+
speed = ETH_SPEED_100M;
102+
duplex = ETH_DUPLEX_FULL;
103+
break;
104+
default:
105+
break;
106+
}
107+
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK,
108+
"change speed failed", err);
109+
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
110+
"change duplex failed", err);
111+
}
112+
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
113+
"change link failed", err);
114+
ksz8041->link_status = link;
115+
}
116+
return ESP_OK;
117+
err:
118+
return ESP_FAIL;
119+
}
120+
121+
static esp_err_t ksz8041_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
122+
{
123+
PHY_CHECK(eth, "can't set mediator to null", err);
124+
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
125+
ksz8041->eth = eth;
126+
return ESP_OK;
127+
err:
128+
return ESP_ERR_INVALID_ARG;
129+
}
130+
131+
static esp_err_t ksz8041_get_link(esp_eth_phy_t *phy)
132+
{
133+
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
134+
/* Update information about link, speed, duplex */
135+
PHY_CHECK(ksz8041_update_link_duplex_speed(ksz8041) == ESP_OK, "update link duplex speed failed", err);
136+
return ESP_OK;
137+
err:
138+
return ESP_FAIL;
139+
}
140+
141+
static esp_err_t ksz8041_reset(esp_eth_phy_t *phy)
142+
{
143+
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
144+
ksz8041->link_status = ETH_LINK_DOWN;
145+
esp_eth_mediator_t *eth = ksz8041->eth;
146+
bmcr_reg_t bmcr = {.reset = 1};
147+
PHY_CHECK(eth->phy_reg_write(eth, ksz8041->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
148+
"write BMCR failed", err);
149+
/* wait for reset complete */
150+
uint32_t to = 0;
151+
for (to = 0; to < ksz8041->reset_timeout_ms / 10; to++) {
152+
vTaskDelay(pdMS_TO_TICKS(10));
153+
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
154+
"read BMCR failed", err);
155+
if (!bmcr.reset) {
156+
break;
157+
}
158+
}
159+
PHY_CHECK(to < ksz8041->reset_timeout_ms / 10, "reset timeout", err);
160+
return ESP_OK;
161+
err:
162+
return ESP_FAIL;
163+
}
164+
165+
static esp_err_t ksz8041_reset_hw(esp_eth_phy_t *phy)
166+
{
167+
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
168+
if (ksz8041->reset_gpio_num >= 0) {
169+
gpio_pad_select_gpio(ksz8041->reset_gpio_num);
170+
gpio_set_direction(ksz8041->reset_gpio_num, GPIO_MODE_OUTPUT);
171+
gpio_set_level(ksz8041->reset_gpio_num, 0);
172+
gpio_set_level(ksz8041->reset_gpio_num, 1);
173+
}
174+
return ESP_OK;
175+
}
176+
177+
static esp_err_t ksz8041_negotiate(esp_eth_phy_t *phy)
178+
{
179+
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
180+
esp_eth_mediator_t *eth = ksz8041->eth;
181+
/* Restart auto negotiation */
182+
bmcr_reg_t bmcr = {
183+
.speed_select = 1, /* 100Mbps */
184+
.duplex_mode = 1, /* Full Duplex */
185+
.en_auto_nego = 1, /* Auto Negotiation */
186+
.restart_auto_nego = 1 /* Restart Auto Negotiation */
187+
};
188+
PHY_CHECK(eth->phy_reg_write(eth, ksz8041->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
189+
/* Wait for auto negotiation complete */
190+
bmsr_reg_t bmsr;
191+
pc2r_reg_t pc2r;
192+
int32_t to = 0;
193+
for (to = 0; to < ksz8041->autonego_timeout_ms / 10; to++) {
194+
vTaskDelay(pdMS_TO_TICKS(10));
195+
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
196+
"read BMSR failed", err);
197+
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_PC2R_REG_ADDR, &(pc2r.val)) == ESP_OK,
198+
"read PC2R failed", err);
199+
if (bmsr.auto_nego_complete && pc2r.auto_nego_comp) {
200+
break;
201+
}
202+
}
203+
/* Auto negotiation failed, maybe no network cable plugged in, so output a warning */
204+
if (to >= ksz8041->autonego_timeout_ms / 10) {
205+
ESP_LOGW(TAG, "auto negotiation timeout");
206+
}
207+
/* Updata information about link, speed, duplex */
208+
PHY_CHECK(ksz8041_update_link_duplex_speed(ksz8041) == ESP_OK, "update link duplex speed failed", err);
209+
return ESP_OK;
210+
err:
211+
return ESP_FAIL;
212+
}
213+
214+
static esp_err_t ksz8041_pwrctl(esp_eth_phy_t *phy, bool enable)
215+
{
216+
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
217+
esp_eth_mediator_t *eth = ksz8041->eth;
218+
bmcr_reg_t bmcr;
219+
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
220+
"read BMCR failed", err);
221+
if (!enable) {
222+
/* General Power Down Mode */
223+
bmcr.power_down = 1;
224+
} else {
225+
/* Normal operation Mode */
226+
bmcr.power_down = 0;
227+
}
228+
PHY_CHECK(eth->phy_reg_write(eth, ksz8041->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
229+
"write BMCR failed", err);
230+
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
231+
"read BMCR failed", err);
232+
if (!enable) {
233+
PHY_CHECK(bmcr.power_down == 1, "power down failed", err);
234+
} else {
235+
PHY_CHECK(bmcr.power_down == 0, "power up failed", err);
236+
}
237+
return ESP_OK;
238+
err:
239+
return ESP_FAIL;
240+
}
241+
242+
static esp_err_t ksz8041_set_addr(esp_eth_phy_t *phy, uint32_t addr)
243+
{
244+
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
245+
ksz8041->addr = addr;
246+
return ESP_OK;
247+
}
248+
249+
static esp_err_t ksz8041_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
250+
{
251+
PHY_CHECK(addr, "addr can't be null", err);
252+
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
253+
*addr = ksz8041->addr;
254+
return ESP_OK;
255+
err:
256+
return ESP_ERR_INVALID_ARG;
257+
}
258+
259+
static esp_err_t ksz8041_del(esp_eth_phy_t *phy)
260+
{
261+
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
262+
free(ksz8041);
263+
return ESP_OK;
264+
}
265+
266+
static esp_err_t ksz8041_init(esp_eth_phy_t *phy)
267+
{
268+
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
269+
esp_eth_mediator_t *eth = ksz8041->eth;
270+
/* Power on Ethernet PHY */
271+
PHY_CHECK(ksz8041_pwrctl(phy, true) == ESP_OK, "power control failed", err);
272+
/* Reset Ethernet PHY */
273+
PHY_CHECK(ksz8041_reset(phy) == ESP_OK, "reset failed", err);
274+
/* Check PHY ID */
275+
phyidr1_reg_t id1;
276+
phyidr2_reg_t id2;
277+
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK,
278+
"read ID1 failed", err);
279+
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK,
280+
"read ID2 failed", err);
281+
PHY_CHECK(id1.oui_msb == 0x22 && id2.oui_lsb == 0x5 && id2.vendor_model == 0x11, "wrong chip ID", err);
282+
return ESP_OK;
283+
err:
284+
return ESP_FAIL;
285+
}
286+
287+
static esp_err_t ksz8041_deinit(esp_eth_phy_t *phy)
288+
{
289+
/* Power off Ethernet PHY */
290+
PHY_CHECK(ksz8041_pwrctl(phy, false) == ESP_OK, "power control failed", err);
291+
return ESP_OK;
292+
err:
293+
return ESP_FAIL;
294+
}
295+
296+
esp_eth_phy_t *esp_eth_phy_new_ksz8041(const eth_phy_config_t *config)
297+
{
298+
PHY_CHECK(config, "can't set phy config to null", err);
299+
phy_ksz8041_t *ksz8041 = calloc(1, sizeof(phy_ksz8041_t));
300+
PHY_CHECK(ksz8041, "calloc ksz8041 failed", err);
301+
ksz8041->addr = config->phy_addr;
302+
ksz8041->reset_gpio_num = config->reset_gpio_num;
303+
ksz8041->reset_timeout_ms = config->reset_timeout_ms;
304+
ksz8041->link_status = ETH_LINK_DOWN;
305+
ksz8041->autonego_timeout_ms = config->autonego_timeout_ms;
306+
ksz8041->parent.reset = ksz8041_reset;
307+
ksz8041->parent.reset_hw = ksz8041_reset_hw;
308+
ksz8041->parent.init = ksz8041_init;
309+
ksz8041->parent.deinit = ksz8041_deinit;
310+
ksz8041->parent.set_mediator = ksz8041_set_mediator;
311+
ksz8041->parent.negotiate = ksz8041_negotiate;
312+
ksz8041->parent.get_link = ksz8041_get_link;
313+
ksz8041->parent.pwrctl = ksz8041_pwrctl;
314+
ksz8041->parent.get_addr = ksz8041_get_addr;
315+
ksz8041->parent.set_addr = ksz8041_set_addr;
316+
ksz8041->parent.del = ksz8041_del;
317+
318+
return &(ksz8041->parent);
319+
err:
320+
return NULL;
321+
}

0 commit comments

Comments
 (0)