forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmdio_esp32.c
147 lines (115 loc) · 3.54 KB
/
mdio_esp32.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
* Copyright (c) 2022 Grant Ramsay <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT espressif_esp32_mdio
#include <soc.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/mdio.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <esp_mac.h>
#include <hal/emac_hal.h>
#include <hal/emac_ll.h>
LOG_MODULE_REGISTER(mdio_esp32, CONFIG_MDIO_LOG_LEVEL);
#define PHY_OPERATION_TIMEOUT_US 1000
struct mdio_esp32_dev_data {
struct k_sem sem;
emac_hal_context_t hal;
};
struct mdio_esp32_dev_config {
const struct pinctrl_dev_config *pcfg;
};
static int mdio_transfer(const struct device *dev, uint8_t prtad, uint8_t regad,
bool write, uint16_t data_in, uint16_t *data_out)
{
struct mdio_esp32_dev_data *const dev_data = dev->data;
k_sem_take(&dev_data->sem, K_FOREVER);
if (emac_ll_is_mii_busy(dev_data->hal.mac_regs)) {
LOG_ERR("phy busy");
k_sem_give(&dev_data->sem);
return -EBUSY;
}
if (write) {
emac_ll_set_phy_data(dev_data->hal.mac_regs, data_in);
}
emac_hal_set_phy_cmd(&dev_data->hal, prtad, regad, write);
/* Poll until operation complete */
bool success = false;
for (uint32_t t_us = 0; t_us < PHY_OPERATION_TIMEOUT_US; t_us += 100) {
k_sleep(K_USEC(100));
if (!emac_ll_is_mii_busy(dev_data->hal.mac_regs)) {
success = true;
break;
}
}
if (!success) {
LOG_ERR("phy timeout");
k_sem_give(&dev_data->sem);
return -ETIMEDOUT;
}
if (!write && data_out != NULL) {
*data_out = emac_ll_get_phy_data(dev_data->hal.mac_regs);
}
k_sem_give(&dev_data->sem);
return 0;
}
static int mdio_esp32_read(const struct device *dev, uint8_t prtad, uint8_t regad,
uint16_t *data)
{
return mdio_transfer(dev, prtad, regad, false, 0, data);
}
static int mdio_esp32_write(const struct device *dev, uint8_t prtad,
uint8_t regad, uint16_t data)
{
return mdio_transfer(dev, prtad, regad, true, data, NULL);
}
static int mdio_esp32_initialize(const struct device *dev)
{
const struct mdio_esp32_dev_config *const cfg = dev->config;
struct mdio_esp32_dev_data *const dev_data = dev->data;
int res;
k_sem_init(&dev_data->sem, 1, 1);
res = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (res != 0) {
goto err;
}
const struct device *clock_dev =
DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_NODELABEL(mdio)));
clock_control_subsys_t clock_subsys =
(clock_control_subsys_t)DT_CLOCKS_CELL(DT_NODELABEL(mdio), offset);
/* clock is shared, so do not bail out if already enabled */
res = clock_control_on(clock_dev, clock_subsys);
if (res < 0 && res != -EALREADY) {
goto err;
}
/* Only the mac registers are required for MDIO */
dev_data->hal.mac_regs = &EMAC_MAC;
/* Init MDIO clock */
emac_hal_set_csr_clock_range(&dev_data->hal, esp_clk_apb_freq());
return 0;
err:
return res;
}
static const struct mdio_driver_api mdio_esp32_driver_api = {
.read = mdio_esp32_read,
.write = mdio_esp32_write,
};
#define MDIO_ESP32_CONFIG(n) \
static const struct mdio_esp32_dev_config mdio_esp32_dev_config_##n = { \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
};
#define MDIO_ESP32_DEVICE(n) \
PINCTRL_DT_INST_DEFINE(n); \
MDIO_ESP32_CONFIG(n); \
static struct mdio_esp32_dev_data mdio_esp32_dev_data##n; \
DEVICE_DT_INST_DEFINE(n, \
&mdio_esp32_initialize, \
NULL, \
&mdio_esp32_dev_data##n, \
&mdio_esp32_dev_config_##n, POST_KERNEL, \
CONFIG_MDIO_INIT_PRIORITY, \
&mdio_esp32_driver_api);
DT_INST_FOREACH_STATUS_OKAY(MDIO_ESP32_DEVICE)