diff options
Diffstat (limited to 'Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp')
-rw-r--r-- | Marlin/src/sd/usb_flashdrive/lib-uhs2/usbhost.cpp | 207 |
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 |