Skip to content

Commit 19e74f1

Browse files
robi251kartben
authored andcommitted
drivers: dm8806: add new driver for davicom dm8806 phy mac
New driver for Davicom DM8806 PHY. Driver is using standar mdio API to manage the DM8806 switch controller. Register access needs the PHY addres or switch address to be one of five possible values, since DM8806 has built-in five PHY's. These values should be defined in the application .dts file. One DM8806 ethernet port must corresponds with one ethernet PHY node with two properties for ethernet port: one for PHY address and one for switch address - <reg> for register access from Internal PHY Register area and <reg-switch> for register access from Switch Per-Port Registers area. Device tree example below: example device-tree: dm8806_phy: ethernet-phy@0 { reg = <2>; reg-switch = <8>; compatible = "davicom,dm8806-phy"; status = "okay"; davicom,interface-type = "rmii"; reset-gpio = <&gpiod 2 GPIO_ACTIVE_LOW>; interrupt-gpio = <&gpioc 1 GPIO_ACTIVE_HIGH>; }; Signed-off-by: Robert Slawinski <robert.slawinski1@gmail.com>
1 parent 3e0c08c commit 19e74f1

File tree

5 files changed

+417
-0
lines changed

5 files changed

+417
-0
lines changed

drivers/ethernet/phy/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
zephyr_library_sources_ifdef(CONFIG_PHY_GENERIC_MII phy_mii.c)
44
zephyr_library_sources_ifdef(CONFIG_PHY_ADIN2111 phy_adin2111.c)
5+
zephyr_library_sources_ifdef(CONFIG_PHY_DM8806 phy_dm8806.c)
56
zephyr_library_sources_ifdef(CONFIG_PHY_TJA1103 phy_tja1103.c)
67
zephyr_library_sources_ifdef(CONFIG_PHY_MICROCHIP_KSZ8081 phy_microchip_ksz8081.c)
78
zephyr_library_sources_ifdef(CONFIG_PHY_TI_DP83825 phy_ti_dp83825.c)

drivers/ethernet/phy/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ config PHY_ADIN2111
4040
help
4141
Enable ADIN2111 PHY driver.
4242

43+
config PHY_DM8806
44+
bool "DM8806 PHY driver"
45+
default y
46+
depends on DT_HAS_DAVICOM_DM8806_PHY_ENABLED
47+
help
48+
Enable DM8806 PHY driver.
49+
4350
config PHY_MICROCHIP_KSZ8081
4451
bool "Microchip KSZ8081 PHY Driver"
4552
default y

drivers/ethernet/phy/phy_dm8806.c

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
/*
2+
* Copyright (c) 2024 Robert Slawinski <robert.slawinski1@gmail.com>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT davicom_dm8806_phy
8+
9+
#include <zephyr/logging/log.h>
10+
LOG_MODULE_REGISTER(eth_dm8806_phy, CONFIG_ETHERNET_LOG_LEVEL);
11+
12+
#include <stdio.h>
13+
#include <sys/types.h>
14+
#include <zephyr/kernel.h>
15+
#include <zephyr/net/phy.h>
16+
#include <zephyr/drivers/mdio.h>
17+
#include <zephyr/drivers/gpio.h>
18+
19+
#include "phy_dm8806_priv.h"
20+
21+
struct phy_dm8806_config {
22+
const struct device *mdio;
23+
uint8_t phy_addr;
24+
uint8_t switch_addr;
25+
struct gpio_dt_spec gpio_rst;
26+
struct gpio_dt_spec gpio_int;
27+
bool mii;
28+
};
29+
30+
struct phy_dm8806_data {
31+
const struct device *dev;
32+
struct phy_link_state state;
33+
struct k_sem sem;
34+
struct k_work_delayable monitor_work;
35+
phy_callback_t cb;
36+
void *cb_data;
37+
};
38+
39+
static int phy_dm8806_init(const struct device *dev)
40+
{
41+
int ret;
42+
uint16_t val;
43+
const struct phy_dm8806_config *cfg = dev->config;
44+
45+
ret = mdio_read(cfg->mdio, PHY_ADDRESS_18H, PORT5_MAC_CONTROL, &val);
46+
if (ret) {
47+
LOG_ERR("Failed to read PORT5_MAC_CONTROL: %i", ret);
48+
return ret;
49+
}
50+
51+
/* Activate default working mode*/
52+
val |= (P5_50M_INT_CLK_SOURCE | P5_50M_CLK_OUT_ENABLE | P5_EN_FORCE);
53+
val &= (P5_SPEED_100M | P5_FULL_DUPLEX | P5_FORCE_LINK_ON);
54+
55+
ret = mdio_write(cfg->mdio, PHY_ADDRESS_18H, PORT5_MAC_CONTROL, val);
56+
if (ret) {
57+
LOG_ERR("Failed to write PORT5_MAC_CONTROL, %i", ret);
58+
return ret;
59+
}
60+
61+
ret = mdio_read(cfg->mdio, PHY_ADDRESS_18H, IRQ_LED_CONTROL, &val);
62+
if (ret) {
63+
LOG_ERR("Failed to read IRQ_LED_CONTROL, %i", ret);
64+
return ret;
65+
}
66+
67+
/* Activate LED blinking mode indicator mode 0. */
68+
val &= LED_MODE_0;
69+
ret = mdio_write(cfg->mdio, PHY_ADDRESS_18H, IRQ_LED_CONTROL, val);
70+
if (ret) {
71+
LOG_ERR("Failed to write IRQ_LED_CONTROL, %i", ret);
72+
return ret;
73+
}
74+
return 0;
75+
}
76+
77+
static int phy_dm8806_get_link_state(const struct device *dev, struct phy_link_state *state)
78+
{
79+
int ret;
80+
uint16_t status;
81+
uint16_t data;
82+
const struct phy_dm8806_config *cfg = dev->config;
83+
84+
/* Read data from Switch Per-Port Register. */
85+
ret = mdio_read(cfg->mdio, cfg->switch_addr, PORTX_SWITCH_STATUS, &data);
86+
if (ret) {
87+
LOG_ERR("Failes to read data drom DM8806 Switch Per-Port Registers area");
88+
return ret;
89+
}
90+
/* Extract speed and duplex status from Switch Per-Port Register: Per Port
91+
* Status Data Register
92+
*/
93+
status = data;
94+
status >>= SPEED_AND_DUPLEX_OFFSET;
95+
switch (status & SPEED_AND_DUPLEX_MASK) {
96+
case SPEED_10MBPS_HALF_DUPLEX:
97+
state->speed = LINK_HALF_10BASE_T;
98+
break;
99+
case SPEED_10MBPS_FULL_DUPLEX:
100+
state->speed = LINK_FULL_10BASE_T;
101+
break;
102+
case SPEED_100MBPS_HALF_DUPLEX:
103+
state->speed = LINK_HALF_100BASE_T;
104+
break;
105+
case SPEED_100MBPS_FULL_DUPLEX:
106+
state->speed = LINK_FULL_100BASE_T;
107+
break;
108+
}
109+
/* Extract link status from Switch Per-Port Register: Per Port Status Data
110+
* Register
111+
*/
112+
status = data;
113+
if (status & LINK_STATUS_MASK) {
114+
state->is_up = true;
115+
} else {
116+
state->is_up = false;
117+
}
118+
return ret;
119+
}
120+
121+
static int phy_dm8806_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds)
122+
{
123+
uint8_t ret;
124+
uint16_t data;
125+
uint16_t req_speed;
126+
const struct phy_dm8806_config *cfg = dev->config;
127+
128+
req_speed = adv_speeds;
129+
switch (req_speed) {
130+
case LINK_HALF_10BASE_T:
131+
req_speed = MODE_10_BASET_HALF_DUPLEX;
132+
break;
133+
134+
case LINK_FULL_10BASE_T:
135+
req_speed = MODE_10_BASET_FULL_DUPLEX;
136+
break;
137+
138+
case LINK_HALF_100BASE_T:
139+
req_speed = MODE_100_BASET_HALF_DUPLEX;
140+
break;
141+
142+
case LINK_FULL_100BASE_T:
143+
req_speed = MODE_100_BASET_FULL_DUPLEX;
144+
break;
145+
}
146+
147+
/* Power down */
148+
ret = mdio_read(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, &data);
149+
if (ret) {
150+
LOG_ERR("Failes to read data drom DM8806");
151+
return ret;
152+
}
153+
data |= POWER_DOWN;
154+
ret = mdio_write(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, data);
155+
if (ret) {
156+
LOG_ERR("Failed to write data to DM8806");
157+
return ret;
158+
}
159+
160+
/* Turn off the auto-negotiation process. */
161+
ret = mdio_read(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, &data);
162+
if (ret) {
163+
LOG_ERR("Failed to write data to DM8806");
164+
return ret;
165+
}
166+
data &= ~(AUTO_NEGOTIATION);
167+
ret = mdio_write(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, data);
168+
if (ret) {
169+
LOG_ERR("Failed to write data to DM8806");
170+
return ret;
171+
}
172+
173+
/* Change the link speed. */
174+
ret = mdio_read(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, &data);
175+
if (ret) {
176+
LOG_ERR("Failed to read data from DM8806");
177+
return ret;
178+
}
179+
data &= ~(LINK_SPEED | DUPLEX_MODE);
180+
data |= req_speed;
181+
ret = mdio_write(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, data);
182+
if (ret) {
183+
LOG_ERR("Failed to write data to DM8806");
184+
return ret;
185+
}
186+
187+
/* Power up ethernet port*/
188+
ret = mdio_read(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, &data);
189+
if (ret) {
190+
LOG_ERR("Failes to read data drom DM8806");
191+
return ret;
192+
}
193+
data &= ~(POWER_DOWN);
194+
ret = mdio_write(cfg->mdio, cfg->phy_addr, PORTX_PHY_CONTROL_REGISTER, data);
195+
if (ret) {
196+
LOG_ERR("Failed to write data to DM8806");
197+
return ret;
198+
}
199+
return -ENOTSUP;
200+
}
201+
202+
static int phy_dm8806_link_cb_set(const struct device *dev, phy_callback_t cb, void *user_data)
203+
{
204+
ARG_UNUSED(dev);
205+
ARG_UNUSED(cb);
206+
ARG_UNUSED(user_data);
207+
208+
return -ENOTSUP;
209+
}
210+
211+
static int phy_dm8806_reg_read(const struct device *dev, uint16_t phy_addr, uint16_t reg_addr,
212+
uint32_t *data)
213+
{
214+
int res;
215+
const struct phy_dm8806_config *cfg = dev->config;
216+
217+
res = mdio_read(cfg->mdio, phy_addr, reg_addr, (uint16_t *)data);
218+
if (res) {
219+
LOG_ERR("Failed to read data from DM8806");
220+
return res;
221+
}
222+
return res;
223+
}
224+
225+
static int phy_dm8806_reg_write(const struct device *dev, uint16_t phy_addr, uint16_t reg_addr,
226+
uint32_t data)
227+
{
228+
int res;
229+
const struct phy_dm8806_config *cfg = dev->config;
230+
231+
res = mdio_write(cfg->mdio, phy_addr, reg_addr, data);
232+
if (res) {
233+
LOG_ERR("Failed to write data to DM8806");
234+
return res;
235+
}
236+
return res;
237+
}
238+
239+
static const struct ethphy_driver_api phy_dm8806_api = {
240+
.get_link = phy_dm8806_get_link_state,
241+
.cfg_link = phy_dm8806_cfg_link,
242+
.link_cb_set = phy_dm8806_link_cb_set,
243+
.read = phy_dm8806_reg_read,
244+
.write = phy_dm8806_reg_write,
245+
};
246+
247+
#define DM8806_PHY_DEFINE_CONFIG(n) \
248+
static const struct phy_dm8806_config phy_dm8806_config_##n = { \
249+
.mdio = DEVICE_DT_GET(DT_INST_BUS(n)), \
250+
.phy_addr = DT_INST_REG_ADDR(n), \
251+
.switch_addr = DT_PROP(DT_NODELABEL(dm8806_phy##n), reg_switch), \
252+
.gpio_int = GPIO_DT_SPEC_INST_GET(n, interrupt_gpio), \
253+
.gpio_rst = GPIO_DT_SPEC_INST_GET(n, reset_gpio), \
254+
}
255+
256+
#define DM8806_PHY_INITIALIZE(n) \
257+
DM8806_PHY_DEFINE_CONFIG(n); \
258+
static struct phy_dm8806_data phy_dm8806_data_##n = { \
259+
.sem = Z_SEM_INITIALIZER(phy_dm8806_data_##n.sem, 1, 1), \
260+
}; \
261+
DEVICE_DT_INST_DEFINE(n, &phy_dm8806_init, NULL, &phy_dm8806_data_##n, \
262+
&phy_dm8806_config_##n, POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, \
263+
&phy_dm8806_api);
264+
265+
DT_INST_FOREACH_STATUS_OKAY(DM8806_PHY_INITIALIZE)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) 2024 Robert Slawinski <robert.slawinski1@gmail.com>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/* Port 0~4 PHY Control Register. */
8+
#define PORTX_PHY_CONTROL_REGISTER 0x0u
9+
/* 10 Mbit/s transfer with half duplex mask. */
10+
#define MODE_10_BASET_HALF_DUPLEX 0x0u
11+
/* 10 Mbit/s transfer with full duplex mask. */
12+
#define MODE_10_BASET_FULL_DUPLEX 0x100u
13+
/* 100 Mbit/s transfer with half duplex mask. */
14+
#define MODE_100_BASET_HALF_DUPLEX 0x2000u
15+
/* 100 Mbit/s transfer with full duplex mask. */
16+
#define MODE_100_BASET_FULL_DUPLEX 0x2100u
17+
/* Duplex mode ability offset. */
18+
#define DUPLEX_MODE (1 << 8)
19+
/* Power down mode offset. */
20+
#define POWER_DOWN (1 << 11)
21+
/* Auto negotiation mode offset. */
22+
#define AUTO_NEGOTIATION (1 << 12)
23+
/* Link speed selection offset. */
24+
#define LINK_SPEED (1 << 13)
25+
26+
/* Port 0~4 Status Data Register. */
27+
#define PORTX_SWITCH_STATUS 0x10u
28+
/* 10 Mbit/s transfer speed with half duplex. */
29+
#define SPEED_10MBPS_HALF_DUPLEX 0x00u
30+
/* 10 Mbit/s transfer speed with full duplex. */
31+
#define SPEED_10MBPS_FULL_DUPLEX 0x01u
32+
/* 100 Mbit/s transfer speed with half duplex. */
33+
#define SPEED_100MBPS_HALF_DUPLEX 0x02u
34+
/* 100 Mbit/s transfer speed with full duplex. */
35+
#define SPEED_100MBPS_FULL_DUPLEX 0x03u
36+
/* Speed and duplex mode status offset. */
37+
#define SPEED_AND_DUPLEX_OFFSET 0x01u
38+
/* Speed and duplex mode staus mask. */
39+
#define SPEED_AND_DUPLEX_MASK 0x07u
40+
/* Link status mask. */
41+
#define LINK_STATUS_MASK 0x1u
42+
43+
/* PHY address 0x18h */
44+
#define PHY_ADDRESS_18H 0x18u
45+
46+
#define PORT5_MAC_CONTROL 0x15u
47+
/* Port 5 Force Speed control bit */
48+
#define P5_SPEED_100M ~BIT(0)
49+
/* Port 5 Force Duplex control bit */
50+
#define P5_FULL_DUPLEX ~BIT(1)
51+
/* Port 5 Force Link control bit. Only available in force mode. */
52+
#define P5_FORCE_LINK_ON ~BIT(2)
53+
/* Port 5 Force Mode Enable control bit. Only available for
54+
* MII/RevMII/RMII
55+
*/
56+
#define P5_EN_FORCE BIT(3)
57+
/* Bit 4 is reserved and should not be use */
58+
/* Port 5 50MHz Clock Output Enable control bit. Only available when Port 5
59+
* be configured as RMII
60+
*/
61+
#define P5_50M_CLK_OUT_ENABLE BIT(5)
62+
/* Port 5 Clock Source Selection control bit. Only available when Port 5
63+
* is configured as RMII
64+
*/
65+
#define P5_50M_INT_CLK_SOURCE BIT(6)
66+
/* Port 5 Output Pin Slew Rate. */
67+
#define P5_NORMAL_SLEW_RATE ~BIT(7)
68+
/* IRQ and LED Control Register. */
69+
#define IRQ_LED_CONTROL 0x17u
70+
/* LED mode 0:
71+
* LNK_LED:
72+
* 100M link fail - LED off
73+
* 100M link ok and no TX/RX activity - LED on
74+
* 100M link ok and TX/RX activity - LED blinking
75+
* SPD_LED:
76+
* No colision: - LED off
77+
* Colision: - LED blinking
78+
* FDX_LED:
79+
* 10M link fail - LED off
80+
* 10M link ok and no TX/RX activity - LED on
81+
* 10M link ok and TX/RX activity - LED blinking
82+
*/
83+
#define LED_MODE_0 ~(BIT(0) | BIT(1))

0 commit comments

Comments
 (0)