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