123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- /****************
- * 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)
-
- #if !PINS_EXIST(USB_CS, USB_INTR)
- #error "USB_FLASH_DRIVE_SUPPORT requires USB_CS_PIN and USB_INTR_PIN to be defined."
- #endif
-
- #include "Usb.h"
- #include "usbhost.h"
-
- uint8_t MAX3421e::vbusState = 0;
-
- // constructor
- void MAX3421e::cs() { WRITE(USB_CS_PIN, LOW); }
- void MAX3421e::ncs() { WRITE(USB_CS_PIN, HIGH); }
-
- // 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_ECHOLNPGM("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
|