1 /*
2 * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <esp_types.h>
8 #include <string.h>
9 #include "freertos/FreeRTOS.h"
10 #include "esp_log.h"
11 #include "esp_check.h"
12 #include "driver/periph_ctrl.h"
13 #include "esp_private/usb_phy.h"
14 #include "soc/usb_phy_periph.h"
15 #include "hal/usb_phy_hal.h"
16 #include "hal/usb_phy_ll.h"
17 #include "esp_rom_gpio.h"
18 #include "driver/gpio.h"
19 #include "hal/gpio_ll.h"
20 #include "soc/usb_pins.h"
21
22 static const char *USBPHY_TAG = "usb_phy";
23
24 #define USBPHY_NOT_INIT_ERR_STR "USB_PHY is not initialized"
25
26 typedef struct phy_context_t phy_context_t;
27
28 struct phy_context_t {
29 usb_phy_target_t target; /**< PHY target */
30 usb_phy_controller_t controller; /**< PHY controller */
31 usb_phy_status_t status; /**< PHY status */
32 usb_otg_mode_t otg_mode; /**< USB OTG mode */
33 usb_phy_speed_t otg_speed; /**< USB speed */
34 usb_phy_gpio_conf_t *iopins; /**< external PHY I/O pins */
35 usb_phy_hal_context_t hal_context; /**< USB_PHY hal context */
36 };
37
38 typedef struct {
39 phy_context_t *internal_phy; /**< internal PHY context */
40 phy_context_t *external_phy; /**< external PHY context */
41 uint32_t ref_count; /**< reference count used to protect p_phy_ctrl_obj */
42 } phy_ctrl_obj_t;
43
44 /**
45 * @brief A pin descriptor for initialize external PHY I/O pins
46 */
47 typedef struct {
48 int pin; /**< GPIO pin num */
49 const int func; /**< GPIO matrix signal */
50 const bool is_output; /**< input/output signal */
51 } usb_iopin_dsc_t;
52
53 static phy_ctrl_obj_t *p_phy_ctrl_obj = NULL;
54 static portMUX_TYPE phy_spinlock = portMUX_INITIALIZER_UNLOCKED;
55
phy_external_iopins_configure(usb_phy_gpio_conf_t * gpio_conf)56 static esp_err_t phy_external_iopins_configure(usb_phy_gpio_conf_t *gpio_conf)
57 {
58 const usb_iopin_dsc_t usb_periph_iopins[] = {
59 {gpio_conf->vp_io_num, usb_phy_periph_signal.extphy_vp_in, 0},
60 {gpio_conf->vm_io_num, usb_phy_periph_signal.extphy_vm_in, 0},
61 {gpio_conf->rcv_io_num, usb_phy_periph_signal.extphy_rcv_in, 0},
62 {gpio_conf->oen_io_num, usb_phy_periph_signal.extphy_oen_out, 1},
63 {gpio_conf->vpo_io_num, usb_phy_periph_signal.extphy_vpo_out, 1},
64 {gpio_conf->vmo_io_num, usb_phy_periph_signal.extphy_vmo_out, 1},
65 };
66
67 for (int i = 0; i < sizeof(usb_periph_iopins)/sizeof(usb_iopin_dsc_t); i++) {
68 const usb_iopin_dsc_t iopin = usb_periph_iopins[i];
69 if (iopin.pin != -1) {
70 ESP_RETURN_ON_FALSE((iopin.is_output && GPIO_IS_VALID_OUTPUT_GPIO(iopin.pin)) ||
71 (!iopin.is_output && GPIO_IS_VALID_GPIO(iopin.pin)),
72 ESP_ERR_INVALID_ARG, USBPHY_TAG, "io_num argument is invalid");
73 esp_rom_gpio_pad_select_gpio(iopin.pin);
74 if (iopin.is_output) {
75 esp_rom_gpio_connect_out_signal(iopin.pin, iopin.func, false, false);
76 } else {
77 esp_rom_gpio_connect_in_signal(iopin.pin, iopin.func, false);
78 gpio_ll_input_enable(&GPIO, iopin.pin);
79 }
80 esp_rom_gpio_pad_unhold(iopin.pin);
81 }
82 }
83 return ESP_OK;
84 }
85
usb_phy_otg_set_mode(usb_phy_handle_t handle,usb_otg_mode_t mode)86 esp_err_t usb_phy_otg_set_mode(usb_phy_handle_t handle, usb_otg_mode_t mode)
87 {
88 ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, USBPHY_TAG, "handle argument is invalid");
89 ESP_RETURN_ON_FALSE(mode < USB_OTG_MODE_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "mode argument is invalid");
90 ESP_RETURN_ON_FALSE(handle->controller == USB_PHY_CTRL_OTG, ESP_FAIL, USBPHY_TAG, "phy source is not USB_OTG");
91
92 handle->otg_mode = mode;
93 if (mode == USB_OTG_MODE_HOST) {
94 esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_IDDIG_IN_IDX, false); //connected connector is A side
95 esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_SRP_BVALID_IN_IDX, false);
96 esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, false); //receiving a valid Vbus from host
97 esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_AVALID_IN_IDX, false); //HIGH to force USB host mode
98 if (handle->target == USB_PHY_TARGET_INT) {
99 usb_phy_hal_int_load_conf_host(&(handle->hal_context));
100 }
101 } else if (mode == USB_OTG_MODE_DEVICE) {
102 esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_IDDIG_IN_IDX, false); //connected connector is mini-B side
103 esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_SRP_BVALID_IN_IDX, false); //HIGH to force USB device mode
104 esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, false); //receiving a valid Vbus from device
105 esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_AVALID_IN_IDX, false);
106 }
107
108 return ESP_OK;
109 }
110
usb_phy_otg_dev_set_speed(usb_phy_handle_t handle,usb_phy_speed_t speed)111 esp_err_t usb_phy_otg_dev_set_speed(usb_phy_handle_t handle, usb_phy_speed_t speed)
112 {
113 ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, USBPHY_TAG, "handle argument is invalid");
114 ESP_RETURN_ON_FALSE(speed < USB_PHY_SPEED_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "speed argument is invalid");
115 ESP_RETURN_ON_FALSE(handle->controller == USB_PHY_CTRL_OTG, ESP_FAIL, USBPHY_TAG, "phy source is not USB_OTG");
116 ESP_RETURN_ON_FALSE((handle->target == USB_PHY_TARGET_INT && handle->otg_mode == USB_OTG_MODE_DEVICE), ESP_FAIL,
117 USBPHY_TAG, "set speed not supported");
118
119 handle->otg_speed = speed;
120 usb_priv_speed_t hal_speed = 0;
121 if (speed == USB_PHY_SPEED_LOW) {
122 hal_speed = USB_PRIV_SPEED_LOW;
123 } else if (speed == USB_PHY_SPEED_FULL) {
124 hal_speed = USB_PRIV_SPEED_FULL;
125 }
126 usb_phy_hal_int_load_conf_dev(&(handle->hal_context), hal_speed);
127 return ESP_OK;
128 }
129
usb_phy_action(usb_phy_handle_t handle,usb_phy_action_t action)130 esp_err_t usb_phy_action(usb_phy_handle_t handle, usb_phy_action_t action)
131 {
132 ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, USBPHY_TAG, "handle argument is invalid");
133 ESP_RETURN_ON_FALSE(action < USB_PHY_ACTION_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "action argument is invalid");
134 ESP_RETURN_ON_FALSE((action == USB_PHY_ACTION_HOST_ALLOW_CONN && handle->controller == USB_PHY_CTRL_OTG) ||
135 (action == USB_PHY_ACTION_HOST_FORCE_DISCONN && handle->controller == USB_PHY_CTRL_OTG),
136 ESP_ERR_INVALID_ARG, USBPHY_TAG, "wrong target for the action");
137
138 esp_err_t ret = ESP_OK;
139 switch (action) {
140 case USB_PHY_ACTION_HOST_ALLOW_CONN:
141 if (handle->target == USB_PHY_TARGET_INT) {
142 usb_phy_hal_int_mimick_disconn(&(handle->hal_context), false);
143 } else {
144 if (!handle->iopins) {
145 ret = ESP_FAIL;
146 ESP_LOGE(USBPHY_TAG, "no I/O pins provided for connection");
147 break;
148 }
149 /*
150 Allow for connections on the external PHY by connecting the VP and VM signals to the external PHY.
151 */
152 esp_rom_gpio_connect_in_signal(handle->iopins->vp_io_num, USB_EXTPHY_VP_IDX, false);
153 esp_rom_gpio_connect_in_signal(handle->iopins->vm_io_num, USB_EXTPHY_VM_IDX, false);
154 }
155 break;
156
157 case USB_PHY_ACTION_HOST_FORCE_DISCONN:
158 if (handle->target == USB_PHY_TARGET_INT) {
159 usb_phy_hal_int_mimick_disconn(&(handle->hal_context), true);
160 } else {
161 /*
162 Disable connections on the external PHY by connecting the VP and VM signals to the constant LOW signal.
163 */
164 esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_EXTPHY_VP_IDX, false);
165 esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_EXTPHY_VM_IDX, false);
166 }
167 break;
168
169 default:
170 break;
171 }
172
173 return ret;
174 }
175
usb_phy_install(void)176 static esp_err_t usb_phy_install(void)
177 {
178 portENTER_CRITICAL(&phy_spinlock);
179 if (p_phy_ctrl_obj) {
180 // p_phy_ctrl_obj already installed, return immediately
181 portEXIT_CRITICAL(&phy_spinlock);
182 return ESP_OK;
183 }
184 portEXIT_CRITICAL(&phy_spinlock);
185
186 esp_err_t ret = ESP_OK;
187 phy_ctrl_obj_t *phy_ctrl_obj = (phy_ctrl_obj_t *) calloc(1, sizeof(phy_ctrl_obj_t));
188 ESP_GOTO_ON_FALSE(phy_ctrl_obj, ESP_ERR_NO_MEM, cleanup, USBPHY_TAG, "no mem for USB_PHY driver");
189
190 portENTER_CRITICAL(&phy_spinlock);
191 if (!p_phy_ctrl_obj) {
192 p_phy_ctrl_obj = phy_ctrl_obj;
193 p_phy_ctrl_obj->ref_count = 0;
194 } else {
195 // p_phy_ctrl_obj already installed, need to free resource
196 portEXIT_CRITICAL(&phy_spinlock);
197 goto cleanup;
198 }
199 portEXIT_CRITICAL(&phy_spinlock);
200 periph_module_enable(usb_phy_periph_signal.module);
201 periph_module_reset(usb_phy_periph_signal.module);
202 return ESP_OK;
203
204 cleanup:
205 free(phy_ctrl_obj);
206 return ret;
207 }
208
usb_new_phy(const usb_phy_config_t * config,usb_phy_handle_t * handle_ret)209 esp_err_t usb_new_phy(const usb_phy_config_t *config, usb_phy_handle_t *handle_ret)
210 {
211 ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, USBPHY_TAG, "config argument is invalid");
212 ESP_RETURN_ON_FALSE(config->target < USB_PHY_TARGET_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "specified PHY argument is invalid");
213 ESP_RETURN_ON_FALSE(config->controller < USB_PHY_CTRL_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "specified source argument is invalid");
214
215 ESP_RETURN_ON_ERROR(usb_phy_install(), USBPHY_TAG, "usb_phy driver installation failed");
216 esp_err_t ret = ESP_OK;
217 bool new_phy = false;
218 phy_context_t *phy_context = (phy_context_t *) calloc(1, sizeof(phy_context_t));
219 ESP_GOTO_ON_FALSE(phy_context, ESP_ERR_NO_MEM, cleanup, USBPHY_TAG, "no mem for phy context");
220
221 portENTER_CRITICAL(&phy_spinlock);
222 usb_phy_get_phy_status(config->target, &phy_context->status);
223 if (phy_context->status == USB_PHY_STATUS_FREE) {
224 new_phy = true;
225 p_phy_ctrl_obj->ref_count++;
226 if (config->target == USB_PHY_TARGET_EXT) {
227 p_phy_ctrl_obj->external_phy = phy_context;
228 } else {
229 p_phy_ctrl_obj->internal_phy = phy_context;
230 }
231 }
232 portEXIT_CRITICAL(&phy_spinlock);
233 ESP_GOTO_ON_FALSE(new_phy, ESP_ERR_INVALID_STATE, cleanup, USBPHY_TAG, "selected PHY is in use");
234
235 phy_context->target = config->target;
236 phy_context->controller = config->controller;
237 phy_context->status = USB_PHY_STATUS_IN_USE;
238
239 usb_phy_hal_init(&(phy_context->hal_context));
240 if (config->controller == USB_PHY_CTRL_OTG) {
241 usb_phy_hal_otg_conf(&(phy_context->hal_context), config->target == USB_PHY_TARGET_EXT);
242 }
243 #if SOC_USB_SERIAL_JTAG_SUPPORTED
244 else if (config->controller == USB_PHY_CTRL_SERIAL_JTAG) {
245 usb_phy_hal_jtag_conf(&(phy_context->hal_context), config->target == USB_PHY_TARGET_EXT);
246 phy_context->otg_mode = USB_OTG_MODE_DEVICE;
247 phy_context->otg_speed = USB_PHY_SPEED_FULL;
248 }
249 #endif
250
251 if (config->target == USB_PHY_TARGET_INT) {
252 gpio_set_drive_capability(USBPHY_DM_NUM, GPIO_DRIVE_CAP_3);
253 gpio_set_drive_capability(USBPHY_DP_NUM, GPIO_DRIVE_CAP_3);
254 }
255
256 *handle_ret = (usb_phy_handle_t) phy_context;
257 if (config->gpio_conf && config->target == USB_PHY_TARGET_EXT) {
258 phy_context->iopins = (usb_phy_gpio_conf_t *) calloc(1, sizeof(usb_phy_gpio_conf_t));
259 ESP_GOTO_ON_FALSE(phy_context->iopins, ESP_ERR_NO_MEM, cleanup, USBPHY_TAG, "no mem for storing I/O pins");
260 memcpy(phy_context->iopins, config->gpio_conf, sizeof(usb_phy_gpio_conf_t));
261 ESP_ERROR_CHECK(phy_external_iopins_configure(phy_context->iopins));
262 }
263 if (config->otg_mode != USB_PHY_MODE_DEFAULT) {
264 ESP_ERROR_CHECK(usb_phy_otg_set_mode(*handle_ret, config->otg_mode));
265 }
266 if (config->otg_speed != USB_PHY_SPEED_UNDEFINED) {
267 ESP_ERROR_CHECK(usb_phy_otg_dev_set_speed(*handle_ret, config->otg_speed));
268 }
269 return ESP_OK;
270
271 cleanup:
272 free(phy_context->iopins);
273 free(phy_context);
274 if (p_phy_ctrl_obj->ref_count == 0) {
275 free(p_phy_ctrl_obj);
276 p_phy_ctrl_obj = NULL;
277 }
278 return ret;
279 }
280
phy_uninstall(void)281 static void phy_uninstall(void)
282 {
283 phy_ctrl_obj_t *p_phy_ctrl_obj_free = NULL;
284 portENTER_CRITICAL(&phy_spinlock);
285 if (p_phy_ctrl_obj->ref_count == 0) {
286 p_phy_ctrl_obj_free = p_phy_ctrl_obj;
287 p_phy_ctrl_obj = NULL;
288 // Disable USB peripheral
289 periph_module_disable(usb_phy_periph_signal.module);
290 }
291 portEXIT_CRITICAL(&phy_spinlock);
292 free(p_phy_ctrl_obj_free);
293 }
294
usb_del_phy(usb_phy_handle_t handle)295 esp_err_t usb_del_phy(usb_phy_handle_t handle)
296 {
297 ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, USBPHY_TAG, "handle argument is invalid");
298
299 portENTER_CRITICAL(&phy_spinlock);
300 p_phy_ctrl_obj->ref_count--;
301 if (handle->target == USB_PHY_TARGET_EXT) {
302 p_phy_ctrl_obj->external_phy = NULL;
303 } else {
304 // Clear pullup and pulldown loads on D+ / D-
305 usb_phy_ll_int_load_conf(handle->hal_context.wrap_dev, false, false, false, false);
306 p_phy_ctrl_obj->internal_phy = NULL;
307 }
308 portEXIT_CRITICAL(&phy_spinlock);
309 free(handle->iopins);
310 free(handle);
311 phy_uninstall();
312 return ESP_OK;
313 }
314
usb_phy_get_phy_status(usb_phy_target_t target,usb_phy_status_t * status)315 esp_err_t usb_phy_get_phy_status(usb_phy_target_t target, usb_phy_status_t *status)
316 {
317 ESP_RETURN_ON_FALSE(target < USB_PHY_TARGET_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "argument is invalid");
318 ESP_RETURN_ON_FALSE(p_phy_ctrl_obj, ESP_ERR_INVALID_STATE, USBPHY_TAG, USBPHY_NOT_INIT_ERR_STR);
319
320 if (target == USB_PHY_TARGET_EXT && p_phy_ctrl_obj->external_phy) {
321 *status = p_phy_ctrl_obj->external_phy->status;
322 } else if (target == USB_PHY_TARGET_INT && p_phy_ctrl_obj->internal_phy) {
323 *status = p_phy_ctrl_obj->internal_phy->status;
324 } else {
325 *status = USB_PHY_STATUS_FREE;
326 }
327 return ESP_OK;
328 }
329