aboutsummaryrefslogtreecommitdiff
path: root/Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp')
-rw-r--r--Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp207
1 files changed, 207 insertions, 0 deletions
diff --git a/Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp b/Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp
new file mode 100644
index 0000000..190a0f1
--- /dev/null
+++ b/Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp
@@ -0,0 +1,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