usb.cpp
5.25 KB
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include <ion/usb.h>
#include "usb.h"
#include <ion/display.h>
#include "device.h"
#include "display.h"
#include "regs/regs.h"
#include <stdlib.h>
namespace Ion {
namespace USB {
bool isPlugged() {
return Device::VbusPin.group().IDR()->get(Device::VbusPin.pin());
}
bool isEnumerated() {
/* Note: This implementation is not perfect. One would assume isEnumerated to
* return true for as long as the device is enumerated. But the GINTSTS
* register will be cleared in the poll() routine. */
return OTG.GINTSTS()->getENUMDNE();
}
void clearEnumerationInterrupt() {
OTG.GINTSTS()->setENUMDNE(true);
}
void enable() {
// Get out of soft-disconnected state
OTG.DCTL()->setSDIS(false);
}
void disable() {
// Get into soft-disconnected state
OTG.DCTL()->setSDIS(true);
}
}
}
namespace Ion {
namespace USB {
namespace Device {
void init() {
initGPIO();
initOTG();
}
void shutdown() {
shutdownOTG();
shutdownGPIO();
}
static inline void DEBUGTOGGLE() {
bool state = GPIOC.ODR()->get(11);
GPIOC.ODR()->set(11, !state);
}
#include <stdlib.h>
void initGPIO() {
// DEBUG GPIO pin
GPIOC.MODER()->setMode(11, GPIO::MODER::Mode::Output);
GPIOC.ODR()->set(11, false);
/* Configure the GPIO
* The VBUS pin is connected to the USB VBUS port. To read if the USB is
* plugged, the pin must be pulled down. */
// FIXME: Understand how the Vbus pin really works!
#if 0
VbusPin.group().MODER()->setMode(VbusPin.pin(), GPIO::MODER::Mode::Input);
VbusPin.group().PUPDR()->setPull(VbusPin.pin(), GPIO::PUPDR::Pull::Down);
#else
VbusPin.group().MODER()->setMode(VbusPin.pin(), GPIO::MODER::Mode::AlternateFunction);
VbusPin.group().AFR()->setAlternateFunction(VbusPin.pin(), GPIO::AFR::AlternateFunction::AF10);
#endif
DmPin.group().MODER()->setMode(DmPin.pin(), GPIO::MODER::Mode::AlternateFunction);
DmPin.group().AFR()->setAlternateFunction(DmPin.pin(), GPIO::AFR::AlternateFunction::AF10);
DpPin.group().MODER()->setMode(DpPin.pin(), GPIO::MODER::Mode::AlternateFunction);
DpPin.group().AFR()->setAlternateFunction(DpPin.pin(), GPIO::AFR::AlternateFunction::AF10);
}
void shutdownGPIO() {
constexpr static GPIOPin USBPins[] = {DpPin, DmPin, VbusPin};
for (const GPIOPin & g : USBPins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::Analog);
g.group().PUPDR()->setPull(g.pin(), GPIO::PUPDR::Pull::None);
}
}
void initOTG() {
// Wait for AHB idle
while (!OTG.GRSTCTL()->getAHBIDL()) {
}
/* Core soft reset: Clears the interrupts and many of the CSR register bits,
* resets state machines, flushes the FIFOs and terminates USB transactions.*/
OTG.GRSTCTL()->setCSRST(true);
while (OTG.GRSTCTL()->getCSRST()) {
}
/* Enable the transceiver module of the PHY. It must be done to allow any USB
* operation */
OTG.GCCFG()->setPWRDWN(true);
/* Enable VBUS sensing comparators to detect valid levels for USB operation.
* This is used for instance to end the session if the host is switched off.*/
OTG.GCCFG()->setVBDEN(true);
// Force peripheral only mode
OTG.GUSBCFG()->setFDMOD(true);
// Configure the USB turnaround time, depending on the AHB clock speed (96MHz)
OTG.GUSBCFG()->setTRDT(0x6);
// Clear the interrupts
OTG.GINTSTS()->set(0);
// Full speed device
OTG.DCFG()->setDSPD(OTG::DCFG::DSPD::FullSpeed);
/* RxFIFO size. The value is in terms of 32-bit words.
* According to the reference manual, it should be, at minimum:
* (4 * number of control endpoints + 6)
* To receive SETUP packets on control endpoint
* + ((largest USB packet used / 4) + 1)
* To receive 1 USB packet + 1 packet status
* + (2 * number of OUT endpoints)
* Transfer complete status information
* + 1 for Global NAK
* So, for the calculator: (4*1+6) + (64/4 + 1) + (2*1) + 1 = 30
* As the RAM size is 1.25kB, the size should be at most 320, minus the space
* for the Tx FIFOs.
* However, we tested and found that only values between 40 and 255 actually
* work. We arbitrarily chose 128. */
OTG.GRXFSIZ()->setRXFD(128);
// Unmask the interrupt line assertions
OTG.GAHBCFG()->setGINTMSK(true);
// Restart the PHY clock
OTG.PCGCCTL()->setSTPPCLK(false);
// Pick which interrupts we're interested in
class OTG::GINTMSK intMask(0); // Reset value
intMask.setENUMDNEM(true); // Speed enumeration done
intMask.setRXFLVLM(true); // Receive FIFO non empty
intMask.setIEPINT(true); // IN endpoint interrupt
OTG.GINTMSK()->set(intMask);
// Unmask IN interrupts for endpoint 0 only
OTG.DAINTMSK()->setIEPM(1);
/* Unmask the IN transfer completed interrupt for all endpoints. This
* interrupt warns that a IN transaction happened on the endpoint. */
OTG.DIEPMSK()->setXFRCM(true);
/* To communicate with a USB host, the device still needs to get out of soft-
* disconnected state (SDIS in the DCTL register). We do this when we detect
* that the USB cable is plugged. */
}
void shutdownOTG() {
// Core soft reset
OTG.GRSTCTL()->setCSRST(true);
while (OTG.GRSTCTL()->getCSRST()) {
}
// Get into soft-disconnected state
OTG.DCTL()->setSDIS(true);
// Stop the PHY clock
OTG.PCGCCTL()->setSTPPCLK(true);
// Stop VBUS sensing
OTG.GCCFG()->setVBDEN(false);
// Disable the transceiver module of the PHY
OTG.GCCFG()->setPWRDWN(false);
}
}
}
}