|
8 | 8 | #include "pico/time.h"
|
9 | 9 | #include "hardware/structs/usb.h"
|
10 | 10 | #include "hardware/gpio.h"
|
| 11 | +#include "hardware/structs/iobank0.h" |
| 12 | +#include "hardware/structs/padsbank0.h" |
11 | 13 | #include "pico/fix/rp2040_usb_device_enumeration.h"
|
12 | 14 |
|
13 | 15 | #define LS_SE0 0b00
|
@@ -36,7 +38,7 @@ static inline uint8_t hw_line_state(void) {
|
36 | 38 | return (usb_hw->sie_status & USB_SIE_STATUS_LINE_STATE_BITS) >> USB_SIE_STATUS_LINE_STATE_LSB;
|
37 | 39 | }
|
38 | 40 |
|
39 |
| -int64_t hw_enumeration_fix_wait_se0_callback(alarm_id_t id, void *user_data) { |
| 41 | +int64_t hw_enumeration_fix_wait_se0_callback(__unused alarm_id_t id, __unused void *user_data) { |
40 | 42 | if (hw_line_state() == LS_SE0) {
|
41 | 43 | // Come back in 1ms and check again
|
42 | 44 | return 1000;
|
@@ -69,25 +71,42 @@ static void hw_enumeration_fix_wait_se0(void) {
|
69 | 71 | hw_enumeration_fix_busy_wait_se0();
|
70 | 72 | }
|
71 | 73 |
|
72 |
| -int64_t hw_enumeration_fix_force_ls_j_done(alarm_id_t id, void *user_data) { |
| 74 | +int64_t hw_enumeration_fix_force_ls_j_done(__unused alarm_id_t id, __unused void *user_data) { |
73 | 75 | hw_enumeration_fix_finish();
|
74 | 76 | return 0;
|
75 | 77 | }
|
76 | 78 |
|
| 79 | +static uint32_t gpio_ctrl_prev = 0; |
| 80 | +static uint32_t pad_ctrl_prev = 0; |
| 81 | +static const uint dp = 15; |
| 82 | +static const uint dm = 16; |
| 83 | + |
77 | 84 | static void hw_enumeration_fix_force_ls_j(void) {
|
78 |
| - // Force LS_J |
79 |
| - const uint dp = 15; |
80 |
| - //const uint dm = 16; |
81 |
| - gpio_set_function(dp, 8); |
82 |
| - // TODO: assert dm is not funcseld to usb |
| 85 | + // DM must be 0 for this to work. This is true if it is selected |
| 86 | + // to any other function. fn 8 on this pin is only for debug so shouldn't |
| 87 | + // be selected |
| 88 | + if (gpio_get_function(dm) == 8) { |
| 89 | + panic("Not expecting DM to be function 8"); |
| 90 | + } |
| 91 | + |
| 92 | + // Before changing any pin state, take a copy of the current gpio control register |
| 93 | + gpio_ctrl_prev = iobank0_hw->io[dp].ctrl; |
| 94 | + // Also take a copy of the pads register |
| 95 | + pad_ctrl_prev = padsbank0_hw->io[dp]; |
| 96 | + |
| 97 | + // Enable bus keep and force pin to tristate, so USB DP muxing doesn't affect |
| 98 | + // pin state |
| 99 | + gpio_set_pulls(dp, true, true); |
| 100 | + gpio_set_oeover(dp, GPIO_OVERRIDE_LOW); |
| 101 | + // Select function 8 (USB debug muxing) without disturbing other controls |
| 102 | + hw_write_masked(&iobank0_hw->io[dp].ctrl, |
| 103 | + 8 << IO_BANK0_GPIO15_CTRL_FUNCSEL_LSB, IO_BANK0_GPIO15_CTRL_FUNCSEL_BITS); |
83 | 104 |
|
84 | 105 | // J state is a differential 1 for a full speed device so
|
85 | 106 | // DP = 1 and DM = 0. Don't actually need to set DM low as it
|
86 | 107 | // is already gated assuming it isn't funcseld.
|
87 | 108 | gpio_set_inover(dp, GPIO_OVERRIDE_HIGH);
|
88 | 109 |
|
89 |
| - // TODO: What to do about existing DP state here? |
90 |
| - |
91 | 110 | // Force PHY pull up to stay before switching away from the phy
|
92 | 111 | hw_set_alias(usb_hw)->phy_direct = USB_USBPHY_DIRECT_DP_PULLUP_EN_BITS;
|
93 | 112 | hw_set_alias(usb_hw)->phy_direct_override = USB_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_EN_OVERRIDE_EN_BITS;
|
@@ -118,4 +137,9 @@ static void hw_enumeration_fix_finish(void) {
|
118 | 137 |
|
119 | 138 | // Get rid of DP pullup override
|
120 | 139 | hw_clear_alias(usb_hw)->phy_direct_override = USB_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_EN_OVERRIDE_EN_BITS;
|
| 140 | + |
| 141 | + // Finally, restore the gpio ctrl value back to GPIO15 |
| 142 | + iobank0_hw->io[dp].ctrl = gpio_ctrl_prev; |
| 143 | + // Restore the pad ctrl value |
| 144 | + padsbank0_hw->io[dp] = pad_ctrl_prev; |
121 | 145 | }
|
0 commit comments