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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
/****************
* usb_host.cpp *
****************/
/****************************************************************************
* Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <https://www.gnu.org/licenses/>. *
****************************************************************************/
/* What follows is a modified version of the MAX3421e originally defined in
* lib/usbhost.c". This has been rewritten to use SPI routines from the
* Marlin HAL */
#include "../../../inc/MarlinConfigPre.h"
#if ENABLED(USB_FLASH_DRIVE_SUPPORT) && DISABLED(USE_UHS3_USB)
#include "Usb.h"
#include "usbhost.h"
uint8_t MAX3421e::vbusState = 0;
// constructor
void MAX3421e::cs() {
WRITE(USB_CS_PIN,0);
}
void MAX3421e::ncs() {
WRITE(USB_CS_PIN,1);
}
// write single byte into MAX3421 register
void MAX3421e::regWr(uint8_t reg, uint8_t data) {
cs();
spiSend(reg | 0x02);
spiSend(data);
ncs();
};
// multiple-byte write
// return a pointer to memory position after last written
uint8_t* MAX3421e::bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p) {
cs();
spiSend(reg | 0x02);
while (nbytes--) spiSend(*data_p++);
ncs();
return data_p;
}
// GPIO write
// GPIO byte is split between 2 registers, so two writes are needed to write one byte
// GPOUT bits are in the low nybble. 0-3 in IOPINS1, 4-7 in IOPINS2
void MAX3421e::gpioWr(uint8_t data) {
regWr(rIOPINS1, data);
regWr(rIOPINS2, data >> 4);
}
// single host register read
uint8_t MAX3421e::regRd(uint8_t reg) {
cs();
spiSend(reg);
uint8_t rv = spiRec();
ncs();
return rv;
}
// multiple-byte register read
// return a pointer to a memory position after last read
uint8_t* MAX3421e::bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p) {
cs();
spiSend(reg);
while (nbytes--) *data_p++ = spiRec();
ncs();
return data_p;
}
// GPIO read. See gpioWr for explanation
// GPIN pins are in high nybbles of IOPINS1, IOPINS2
uint8_t MAX3421e::gpioRd() {
return (regRd(rIOPINS2) & 0xF0) | // pins 4-7, clean lower nybble
(regRd(rIOPINS1) >> 4); // shift low bits and OR with upper from previous operation.
}
// reset MAX3421e. Returns false if PLL failed to stabilize 1 second after reset
bool MAX3421e::reset() {
regWr(rUSBCTL, bmCHIPRES);
regWr(rUSBCTL, 0x00);
for (uint8_t i = 100; i--;) {
if (regRd(rUSBIRQ) & bmOSCOKIRQ) return true;
delay(10);
}
return false;
}
// initialize MAX3421e. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not
bool MAX3421e::start() {
// Initialize pins and SPI bus
SET_OUTPUT(USB_CS_PIN);
SET_INPUT_PULLUP(USB_INTR_PIN);
ncs();
spiBegin();
spiInit(SD_SPI_SPEED);
// MAX3421e - full-duplex, level interrupt, vbus off.
regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | GPX_VBDET));
const uint8_t revision = regRd(rREVISION);
if (revision == 0x00 || revision == 0xFF) {
SERIAL_ECHOLNPAIR("Revision register appears incorrect on MAX3421e initialization. Got ", revision);
return false;
}
if (!reset()) {
SERIAL_ECHOLNPGM("OSCOKIRQ hasn't asserted in time");
return false;
}
// Delay a minimum of 1 second to ensure any capacitors are drained.
// 1 second is required to make sure we do not smoke a Microdrive!
delay(1000);
regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host
regWr(rHIEN, bmCONDETIE | bmFRAMEIE); // connection detection
// check if device is connected
regWr(rHCTL, bmSAMPLEBUS); // sample USB bus
while (!(regRd(rHCTL) & bmSAMPLEBUS)) delay(10); // wait for sample operation to finish
busprobe(); // check if anything is connected
regWr(rHIRQ, bmCONDETIRQ); // clear connection detect interrupt
regWr(rCPUCTL, 0x01); // enable interrupt pin
// GPX pin on. This is done here so that busprobe will fail if we have a switch connected.
regWr(rPINCTL, bmFDUPSPI | bmINTLEVEL);
return true;
}
// Probe bus to determine device presence and speed. Switch host to this speed.
void MAX3421e::busprobe() {
// Switch on just the J & K bits
switch (regRd(rHRSL) & (bmJSTATUS | bmKSTATUS)) {
case bmJSTATUS:
if ((regRd(rMODE) & bmLOWSPEED) == 0) {
regWr(rMODE, MODE_FS_HOST); // start full-speed host
vbusState = FSHOST;
}
else {
regWr(rMODE, MODE_LS_HOST); // start low-speed host
vbusState = LSHOST;
}
break;
case bmKSTATUS:
if ((regRd(rMODE) & bmLOWSPEED) == 0) {
regWr(rMODE, MODE_LS_HOST); // start low-speed host
vbusState = LSHOST;
}
else {
regWr(rMODE, MODE_FS_HOST); // start full-speed host
vbusState = FSHOST;
}
break;
case bmSE1: // illegal state
vbusState = SE1;
break;
case bmSE0: // disconnected state
regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ);
vbusState = SE0;
break;
}
}
// MAX3421 state change task and interrupt handler
uint8_t MAX3421e::Task() {
return READ(USB_INTR_PIN) ? 0 : IntHandler();
}
uint8_t MAX3421e::IntHandler() {
uint8_t HIRQ = regRd(rHIRQ), // determine interrupt source
HIRQ_sendback = 0x00;
if (HIRQ & bmCONDETIRQ) {
busprobe();
HIRQ_sendback |= bmCONDETIRQ;
}
// End HIRQ interrupts handling, clear serviced IRQs
regWr(rHIRQ, HIRQ_sendback);
return HIRQ_sendback;
}
#endif // USB_FLASH_DRIVE_SUPPORT
|