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