Skip to content

Commit 98a4c70

Browse files
linkineoespressif-bot
authored andcommitted
Added support for Ethernet PHY KSZ8081
Merges #6578
1 parent 9d7ba4c commit 98a4c70

File tree

5 files changed

+381
-0
lines changed

5 files changed

+381
-0
lines changed

components/esp_eth/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ if(CONFIG_ETH_ENABLED)
2323
"src/esp_eth_phy_dp83848.c"
2424
"src/esp_eth_phy_ip101.c"
2525
"src/esp_eth_phy_ksz8041.c"
26+
"src/esp_eth_phy_ksz8081.c"
2627
"src/esp_eth_phy_lan8720.c"
2728
"src/esp_eth_phy_rtl8201.c")
2829
endif()

components/esp_eth/include/esp_eth_phy.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,17 @@ esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config);
264264
*/
265265
esp_eth_phy_t *esp_eth_phy_new_ksz8041(const eth_phy_config_t *config);
266266

267+
/**
268+
* @brief Create a PHY instance of KSZ8081
269+
*
270+
* @param[in] config: configuration of PHY
271+
*
272+
* @return
273+
* - instance: create PHY instance successfully
274+
* - NULL: create PHY instance failed because some error occurred
275+
*/
276+
esp_eth_phy_t *esp_eth_phy_new_ksz8081(const eth_phy_config_t *config);
277+
267278
#if CONFIG_ETH_SPI_ETHERNET_DM9051
268279
/**
269280
* @brief Create a PHY instance of DM9051
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
// Copyright 2021 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+
#include "esp_rom_gpio.h"
24+
#include "esp_rom_sys.h"
25+
26+
static const char *TAG = "ksz8081";
27+
#define PHY_CHECK(a, str, goto_tag, ...) \
28+
do { \
29+
if (!(a)) \
30+
{ \
31+
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
32+
goto goto_tag; \
33+
} \
34+
} while (0)
35+
36+
/***************Vendor Specific Register***************/
37+
/**
38+
* @brief PC1R(PHY Control 1 Register)
39+
*
40+
*/
41+
typedef union {
42+
struct {
43+
uint32_t op_mode : 3; /* Operation Mode Indication */
44+
uint32_t phy_iso : 1; /* PHY in Isolate Mode */
45+
uint32_t energy_det: 1; /* Signal presence on RX pair */
46+
uint32_t mdix_state : 1; /* MDI/MDI-X state */
47+
uint32_t reserved_6 : 1; /* Reserved */
48+
uint32_t polarity_status : 1; /* Polarity status */
49+
uint32_t link_status : 1; /* Link status */
50+
uint32_t en_flow_ctrl : 1; /* Flow control */
51+
uint32_t reserved_15_10 : 6; /* Reserved */
52+
};
53+
uint32_t val;
54+
} pc1r_reg_t;
55+
#define ETH_PHY_PC1R_REG_ADDR (0x1E)
56+
57+
typedef struct {
58+
esp_eth_phy_t parent;
59+
esp_eth_mediator_t *eth;
60+
int addr;
61+
uint32_t reset_timeout_ms;
62+
uint32_t autonego_timeout_ms;
63+
eth_link_t link_status;
64+
int reset_gpio_num;
65+
} phy_ksz8081_t;
66+
67+
static esp_err_t ksz8081_update_link_duplex_speed(phy_ksz8081_t *ksz8081)
68+
{
69+
esp_eth_mediator_t *eth = ksz8081->eth;
70+
eth_speed_t speed = ETH_SPEED_10M;
71+
eth_duplex_t duplex = ETH_DUPLEX_HALF;
72+
uint32_t peer_pause_ability = false;
73+
anlpar_reg_t anlpar;
74+
bmsr_reg_t bmsr;
75+
pc1r_reg_t pc1r;
76+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)) == ESP_OK,
77+
"read ANLPAR failed", err);
78+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->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 (ksz8081->link_status != link) {
83+
/* when link up, read negotiation result */
84+
if (link == ETH_LINK_UP) {
85+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->addr, ETH_PHY_PC1R_REG_ADDR, &(pc1r.val)) == ESP_OK,
86+
"read PC1R failed", err);
87+
switch (pc1r.op_mode) {
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+
/* if we're in duplex mode, and peer has the flow control ability */
112+
if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) {
113+
peer_pause_ability = 1;
114+
} else {
115+
peer_pause_ability = 0;
116+
}
117+
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability) == ESP_OK,
118+
"change pause ability failed", err);
119+
}
120+
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
121+
"change link failed", err);
122+
ksz8081->link_status = link;
123+
}
124+
return ESP_OK;
125+
err:
126+
return ESP_FAIL;
127+
}
128+
129+
static esp_err_t ksz8081_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
130+
{
131+
PHY_CHECK(eth, "can't set mediator to null", err);
132+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
133+
ksz8081->eth = eth;
134+
return ESP_OK;
135+
err:
136+
return ESP_ERR_INVALID_ARG;
137+
}
138+
139+
static esp_err_t ksz8081_get_link(esp_eth_phy_t *phy)
140+
{
141+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
142+
/* Update information about link, speed, duplex */
143+
PHY_CHECK(ksz8081_update_link_duplex_speed(ksz8081) == ESP_OK, "update link duplex speed failed", err);
144+
return ESP_OK;
145+
err:
146+
return ESP_FAIL;
147+
}
148+
149+
static esp_err_t ksz8081_reset(esp_eth_phy_t *phy)
150+
{
151+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
152+
ksz8081->link_status = ETH_LINK_DOWN;
153+
esp_eth_mediator_t *eth = ksz8081->eth;
154+
bmcr_reg_t bmcr = {.reset = 1};
155+
PHY_CHECK(eth->phy_reg_write(eth, ksz8081->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
156+
"write BMCR failed", err);
157+
/* wait for reset complete */
158+
uint32_t to = 0;
159+
for (to = 0; to < ksz8081->reset_timeout_ms / 10; to++) {
160+
vTaskDelay(pdMS_TO_TICKS(10));
161+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
162+
"read BMCR failed", err);
163+
if (!bmcr.reset) {
164+
break;
165+
}
166+
}
167+
PHY_CHECK(to < ksz8081->reset_timeout_ms / 10, "reset timeout", err);
168+
return ESP_OK;
169+
err:
170+
return ESP_FAIL;
171+
}
172+
173+
static esp_err_t ksz8081_reset_hw(esp_eth_phy_t *phy)
174+
{
175+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
176+
if (ksz8081->reset_gpio_num >= 0) {
177+
esp_rom_gpio_pad_select_gpio(ksz8081->reset_gpio_num);
178+
gpio_set_direction(ksz8081->reset_gpio_num, GPIO_MODE_OUTPUT);
179+
gpio_set_level(ksz8081->reset_gpio_num, 0);
180+
esp_rom_delay_us(100); // insert min input assert time
181+
gpio_set_level(ksz8081->reset_gpio_num, 1);
182+
}
183+
return ESP_OK;
184+
}
185+
186+
static esp_err_t ksz8081_negotiate(esp_eth_phy_t *phy)
187+
{
188+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
189+
esp_eth_mediator_t *eth = ksz8081->eth;
190+
/* Restart auto negotiation */
191+
bmcr_reg_t bmcr = {
192+
.speed_select = 1, /* 100Mbps */
193+
.duplex_mode = 1, /* Full Duplex */
194+
.en_auto_nego = 1, /* Auto Negotiation */
195+
.restart_auto_nego = 1 /* Restart Auto Negotiation */
196+
};
197+
PHY_CHECK(eth->phy_reg_write(eth, ksz8081->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
198+
/* Wait for auto negotiation complete */
199+
bmsr_reg_t bmsr;
200+
uint32_t to = 0;
201+
for (to = 0; to < ksz8081->autonego_timeout_ms / 10; to++) {
202+
vTaskDelay(pdMS_TO_TICKS(10));
203+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
204+
"read BMSR failed", err);
205+
if (bmsr.auto_nego_complete) {
206+
break;
207+
}
208+
}
209+
/* Auto negotiation failed, maybe no network cable plugged in, so output a warning */
210+
if (to >= ksz8081->autonego_timeout_ms / 10) {
211+
ESP_LOGW(TAG, "auto negotiation timeout");
212+
}
213+
/* Updata information about link, speed, duplex */
214+
PHY_CHECK(ksz8081_update_link_duplex_speed(ksz8081) == ESP_OK, "update link duplex speed failed", err);
215+
return ESP_OK;
216+
err:
217+
return ESP_FAIL;
218+
}
219+
220+
static esp_err_t ksz8081_pwrctl(esp_eth_phy_t *phy, bool enable)
221+
{
222+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
223+
esp_eth_mediator_t *eth = ksz8081->eth;
224+
bmcr_reg_t bmcr;
225+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
226+
"read BMCR failed", err);
227+
if (!enable) {
228+
/* General Power Down Mode */
229+
bmcr.power_down = 1;
230+
} else {
231+
/* Normal operation Mode */
232+
bmcr.power_down = 0;
233+
}
234+
PHY_CHECK(eth->phy_reg_write(eth, ksz8081->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
235+
"write BMCR failed", err);
236+
if (!enable) {
237+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
238+
"read BMCR failed", err);
239+
PHY_CHECK(bmcr.power_down == 1, "power down failed", err);
240+
} else {
241+
/* wait for power up complete */
242+
uint32_t to = 0;
243+
for (to = 0; to < ksz8081->reset_timeout_ms / 10; to++) {
244+
vTaskDelay(pdMS_TO_TICKS(10));
245+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
246+
"read BMCR failed", err);
247+
if (bmcr.power_down == 0) {
248+
break;
249+
}
250+
}
251+
PHY_CHECK(to < ksz8081->reset_timeout_ms / 10, "power up timeout", err);
252+
}
253+
254+
return ESP_OK;
255+
err:
256+
return ESP_FAIL;
257+
}
258+
259+
static esp_err_t ksz8081_set_addr(esp_eth_phy_t *phy, uint32_t addr)
260+
{
261+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
262+
ksz8081->addr = addr;
263+
return ESP_OK;
264+
}
265+
266+
static esp_err_t ksz8081_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
267+
{
268+
PHY_CHECK(addr, "addr can't be null", err);
269+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
270+
*addr = ksz8081->addr;
271+
return ESP_OK;
272+
err:
273+
return ESP_ERR_INVALID_ARG;
274+
}
275+
276+
static esp_err_t ksz8081_del(esp_eth_phy_t *phy)
277+
{
278+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
279+
free(ksz8081);
280+
return ESP_OK;
281+
}
282+
283+
static esp_err_t ksz8081_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
284+
{
285+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
286+
esp_eth_mediator_t *eth = ksz8081->eth;
287+
/* Set PAUSE function ability */
288+
anar_reg_t anar;
289+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)) == ESP_OK,
290+
"read ANAR failed", err);
291+
if (ability) {
292+
anar.asymmetric_pause = 1;
293+
anar.symmetric_pause = 1;
294+
} else {
295+
anar.asymmetric_pause = 0;
296+
anar.symmetric_pause = 0;
297+
}
298+
PHY_CHECK(eth->phy_reg_write(eth, ksz8081->addr, ETH_PHY_ANAR_REG_ADDR, anar.val) == ESP_OK,
299+
"write ANAR failed", err);
300+
return ESP_OK;
301+
err:
302+
return ESP_FAIL;
303+
}
304+
305+
static esp_err_t ksz8081_init(esp_eth_phy_t *phy)
306+
{
307+
phy_ksz8081_t *ksz8081 = __containerof(phy, phy_ksz8081_t, parent);
308+
esp_eth_mediator_t *eth = ksz8081->eth;
309+
/* Power on Ethernet PHY */
310+
PHY_CHECK(ksz8081_pwrctl(phy, true) == ESP_OK, "power control failed", err);
311+
/* Reset Ethernet PHY */
312+
PHY_CHECK(ksz8081_reset(phy) == ESP_OK, "reset failed", err);
313+
/* Check PHY ID */
314+
phyidr1_reg_t id1;
315+
phyidr2_reg_t id2;
316+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK,
317+
"read ID1 failed", err);
318+
PHY_CHECK(eth->phy_reg_read(eth, ksz8081->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK,
319+
"read ID2 failed", err);
320+
PHY_CHECK(id1.oui_msb == 0x22 && id2.oui_lsb == 0x5 && id2.vendor_model == 0x16, "wrong chip ID", err);
321+
return ESP_OK;
322+
err:
323+
return ESP_FAIL;
324+
}
325+
326+
static esp_err_t ksz8081_deinit(esp_eth_phy_t *phy)
327+
{
328+
/* Power off Ethernet PHY */
329+
PHY_CHECK(ksz8081_pwrctl(phy, false) == ESP_OK, "power control failed", err);
330+
return ESP_OK;
331+
err:
332+
return ESP_FAIL;
333+
}
334+
335+
esp_eth_phy_t *esp_eth_phy_new_ksz8081(const eth_phy_config_t *config)
336+
{
337+
PHY_CHECK(config, "can't set phy config to null", err);
338+
phy_ksz8081_t *ksz8081 = calloc(1, sizeof(phy_ksz8081_t));
339+
PHY_CHECK(ksz8081, "calloc ksz8081 failed", err);
340+
ksz8081->addr = config->phy_addr;
341+
ksz8081->reset_gpio_num = config->reset_gpio_num;
342+
ksz8081->reset_timeout_ms = config->reset_timeout_ms;
343+
ksz8081->link_status = ETH_LINK_DOWN;
344+
ksz8081->autonego_timeout_ms = config->autonego_timeout_ms;
345+
ksz8081->parent.reset = ksz8081_reset;
346+
ksz8081->parent.reset_hw = ksz8081_reset_hw;
347+
ksz8081->parent.init = ksz8081_init;
348+
ksz8081->parent.deinit = ksz8081_deinit;
349+
ksz8081->parent.set_mediator = ksz8081_set_mediator;
350+
ksz8081->parent.negotiate = ksz8081_negotiate;
351+
ksz8081->parent.get_link = ksz8081_get_link;
352+
ksz8081->parent.pwrctl = ksz8081_pwrctl;
353+
ksz8081->parent.get_addr = ksz8081_get_addr;
354+
ksz8081->parent.set_addr = ksz8081_set_addr;
355+
ksz8081->parent.advertise_pause_ability = ksz8081_advertise_pause_ability;
356+
ksz8081->parent.del = ksz8081_del;
357+
358+
return &(ksz8081->parent);
359+
err:
360+
return NULL;
361+
}

0 commit comments

Comments
 (0)