| /* | |
| * Copyright 2009-2011 Oleg Mazurov, Circuits At Home, http://www.circuitsathome.com | |
| * MAX3421E USB host controller support | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * 3. Neither the name of the authors nor the names of its contributors | |
| * may be used to endorse or promote products derived from this software | |
| * without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| * SUCH DAMAGE. | |
| */ | |
| /* MAX3421E USB host controller support */ | |
| #include "Max3421e.h" | |
| // #include "Max3421e_constants.h" | |
| static byte vbusState; | |
| /* Functions */ | |
| #define INT PE6 | |
| #define INT_PORT PORTE | |
| #define INT_DDR DDRE | |
| #define INT_PIN PINE | |
| #define RST PJ2 | |
| #define RST_PORT PORTJ | |
| #define RST_DDR DDRJ | |
| #define RST_PIN PINJ | |
| #define GPX PJ3 | |
| #define GPX_PORT PORTJ | |
| #define GPX_DDR DDRJ | |
| #define GPX_PIN PINJ | |
| void MAX3421E::setRST(uint8_t val) | |
| { | |
| if (val == LOW) | |
| RST_PORT &= ~_BV(RST); | |
| else | |
| RST_PORT |= _BV(RST); | |
| } | |
| uint8_t MAX3421E::readINT(void) | |
| { | |
| return INT_PIN & _BV(INT) ? HIGH : LOW; | |
| } | |
| uint8_t MAX3421E::readGPX(void) | |
| { | |
| // return GPX_PIN & _BV(GPX) ? HIGH : LOW; | |
| return LOW; | |
| } | |
| void MAX3421E::pinInit(void) | |
| { | |
| INT_DDR &= ~_BV(INT); | |
| RST_DDR |= _BV(RST); | |
| digitalWrite(MAX_SS,HIGH); | |
| setRST(HIGH); | |
| } | |
| /* Constructor */ | |
| MAX3421E::MAX3421E() | |
| { | |
| spi_init(); | |
| pinInit(); | |
| } | |
| byte MAX3421E::getVbusState( void ) | |
| { | |
| return( vbusState ); | |
| } | |
| /* initialization */ | |
| //void MAX3421E::init() | |
| //{ | |
| // /* setup pins */ | |
| // pinMode( MAX_INT, INPUT); | |
| // pinMode( MAX_GPX, INPUT ); | |
| // pinMode( MAX_SS, OUTPUT ); | |
| // //pinMode( BPNT_0, OUTPUT ); | |
| // //pinMode( BPNT_1, OUTPUT ); | |
| // //digitalWrite( BPNT_0, LOW ); | |
| // //digitalWrite( BPNT_1, LOW ); | |
| // Deselect_MAX3421E; | |
| // pinMode( MAX_RESET, OUTPUT ); | |
| // digitalWrite( MAX_RESET, HIGH ); //release MAX3421E from reset | |
| //} | |
| //byte MAX3421E::getVbusState( void ) | |
| //{ | |
| // return( vbusState ); | |
| //} | |
| //void MAX3421E::toggle( byte pin ) | |
| //{ | |
| // digitalWrite( pin, HIGH ); | |
| // digitalWrite( pin, LOW ); | |
| //} | |
| /* Single host register write */ | |
| void MAX3421E::regWr( byte reg, byte val) | |
| { | |
| digitalWrite(MAX_SS,LOW); | |
| SPDR = ( reg | 0x02 ); | |
| while(!( SPSR & ( 1 << SPIF ))); | |
| SPDR = val; | |
| while(!( SPSR & ( 1 << SPIF ))); | |
| digitalWrite(MAX_SS,HIGH); | |
| return; | |
| } | |
| /* multiple-byte write */ | |
| /* returns a pointer to a memory position after last written */ | |
| char * MAX3421E::bytesWr( byte reg, byte nbytes, char * data ) | |
| { | |
| digitalWrite(MAX_SS,LOW); | |
| SPDR = ( reg | 0x02 ); | |
| while( nbytes-- ) { | |
| while(!( SPSR & ( 1 << SPIF ))); //check if previous byte was sent | |
| SPDR = ( *data ); // send next data byte | |
| data++; // advance data pointer | |
| } | |
| while(!( SPSR & ( 1 << SPIF ))); | |
| digitalWrite(MAX_SS,HIGH); | |
| return( data ); | |
| } | |
| /* GPIO write. GPIO byte is split between 2 registers, so two writes are needed to write one byte */ | |
| /* GPOUT bits are in the low nibble. 0-3 in IOPINS1, 4-7 in IOPINS2 */ | |
| /* upper 4 bits of IOPINS1, IOPINS2 are read-only, so no masking is necessary */ | |
| void MAX3421E::gpioWr( byte val ) | |
| { | |
| regWr( rIOPINS1, val ); | |
| val = val >>4; | |
| regWr( rIOPINS2, val ); | |
| return; | |
| } | |
| /* Single host register read */ | |
| byte MAX3421E::regRd( byte reg ) | |
| { | |
| byte tmp; | |
| digitalWrite(MAX_SS,LOW); | |
| SPDR = reg; | |
| while(!( SPSR & ( 1 << SPIF ))); | |
| SPDR = 0; //send empty byte | |
| while(!( SPSR & ( 1 << SPIF ))); | |
| digitalWrite(MAX_SS,HIGH); | |
| return( SPDR ); | |
| } | |
| /* multiple-bytes register read */ | |
| /* returns a pointer to a memory position after last read */ | |
| char * MAX3421E::bytesRd ( byte reg, byte nbytes, char * data ) | |
| { | |
| digitalWrite(MAX_SS,LOW); | |
| SPDR = reg; | |
| while(!( SPSR & ( 1 << SPIF ))); //wait | |
| while( nbytes ) { | |
| SPDR = 0; //send empty byte | |
| nbytes--; | |
| while(!( SPSR & ( 1 << SPIF ))); | |
| *data = SPDR; | |
| data++; | |
| } | |
| digitalWrite(MAX_SS,HIGH); | |
| return( data ); | |
| } | |
| /* GPIO read. See gpioWr for explanation */ | |
| /* GPIN pins are in high nibbles of IOPINS1, IOPINS2 */ | |
| byte MAX3421E::gpioRd( void ) | |
| { | |
| byte tmpbyte = 0; | |
| tmpbyte = regRd( rIOPINS2 ); //pins 4-7 | |
| tmpbyte &= 0xf0; //clean lower nibble | |
| tmpbyte |= ( regRd( rIOPINS1 ) >>4 ) ; //shift low bits and OR with upper from previous operation. Upper nibble zeroes during shift, at least with this compiler | |
| return( tmpbyte ); | |
| } | |
| /* reset MAX3421E using chip reset bit. SPI configuration is not affected */ | |
| boolean MAX3421E::reset() | |
| { | |
| byte tmp = 0; | |
| regWr( rUSBCTL, bmCHIPRES ); //Chip reset. This stops the oscillator | |
| regWr( rUSBCTL, 0x00 ); //Remove the reset | |
| while(!(regRd( rUSBIRQ ) & bmOSCOKIRQ )) { //wait until the PLL is stable | |
| tmp++; //timeout after 256 attempts | |
| if( tmp == 0 ) { | |
| return( false ); | |
| } | |
| } | |
| return( true ); | |
| } | |
| /* turn USB power on/off */ | |
| /* does nothing, returns TRUE. Left for compatibility with old sketches */ | |
| /* will be deleted eventually */ | |
| ///* ON pin of VBUS switch (MAX4793 or similar) is connected to GPOUT7 */ | |
| ///* OVERLOAD pin of Vbus switch is connected to GPIN7 */ | |
| ///* OVERLOAD state low. NO OVERLOAD or VBUS OFF state high. */ | |
| boolean MAX3421E::vbusPwr ( boolean action ) | |
| { | |
| // byte tmp; | |
| // tmp = regRd( rIOPINS2 ); //copy of IOPINS2 | |
| // if( action ) { //turn on by setting GPOUT7 | |
| // tmp |= bmGPOUT7; | |
| // } | |
| // else { //turn off by clearing GPOUT7 | |
| // tmp &= ~bmGPOUT7; | |
| // } | |
| // regWr( rIOPINS2, tmp ); //send GPOUT7 | |
| // if( action ) { | |
| // delay( 60 ); | |
| // } | |
| // if (( regRd( rIOPINS2 ) & bmGPIN7 ) == 0 ) { // check if overload is present. MAX4793 /FLAG ( pin 4 ) goes low if overload | |
| // return( false ); | |
| // } | |
| return( true ); // power on/off successful | |
| } | |
| /* probe bus to determine device presense and speed and switch host to this speed */ | |
| void MAX3421E::busprobe( void ) | |
| { | |
| byte bus_sample; | |
| bus_sample = regRd( rHRSL ); //Get J,K status | |
| bus_sample &= ( bmJSTATUS|bmKSTATUS ); //zero the rest of the byte | |
| switch( bus_sample ) { //start full-speed or low-speed host | |
| 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; | |
| }//end switch( bus_sample ) | |
| } | |
| /* MAX3421E initialization after power-on */ | |
| void MAX3421E::powerOn() | |
| { | |
| /* Configure full-duplex SPI, interrupt pulse */ | |
| regWr( rPINCTL,( bmFDUPSPI + bmINTLEVEL + bmGPXB )); //Full-duplex SPI, level interrupt, GPX | |
| if( reset() == false ) { //stop/start the oscillator | |
| Serial.println("Error: OSCOKIRQ failed to assert"); | |
| } | |
| /* configure host operation */ | |
| regWr( rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmSEPIRQ ); // set pull-downs, Host, Separate GPIN IRQ on GPX | |
| regWr( rHIEN, bmCONDETIE|bmFRAMEIE ); //connection detection | |
| /* check if device is connected */ | |
| regWr( rHCTL,bmSAMPLEBUS ); // sample USB bus | |
| while(!(regRd( rHCTL ) & bmSAMPLEBUS )); //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 | |
| } | |
| /* MAX3421 state change task and interrupt handler */ | |
| byte MAX3421E::Task( void ) | |
| { | |
| byte rcode = 0; | |
| byte pinvalue; | |
| //Serial.print("Vbus state: "); | |
| //Serial.println( vbusState, HEX ); | |
| pinvalue = readINT(); | |
| if( pinvalue == LOW ) { | |
| rcode = IntHandler(); | |
| } | |
| pinvalue = readGPX(); | |
| if( pinvalue == LOW ) { | |
| GpxHandler(); | |
| } | |
| // usbSM(); //USB state machine | |
| return( rcode ); | |
| } | |
| byte MAX3421E::IntHandler() | |
| { | |
| byte HIRQ; | |
| byte HIRQ_sendback = 0x00; | |
| HIRQ = regRd( rHIRQ ); //determine interrupt source | |
| //if( HIRQ & bmFRAMEIRQ ) { //->1ms SOF interrupt handler | |
| // HIRQ_sendback |= bmFRAMEIRQ; | |
| //}//end FRAMEIRQ handling | |
| if( HIRQ & bmCONDETIRQ ) { | |
| busprobe(); | |
| HIRQ_sendback |= bmCONDETIRQ; | |
| } | |
| /* End HIRQ interrupts handling, clear serviced IRQs */ | |
| regWr( rHIRQ, HIRQ_sendback ); | |
| return( HIRQ_sendback ); | |
| } | |
| byte MAX3421E::GpxHandler() | |
| { | |
| byte GPINIRQ = regRd( rGPINIRQ ); //read GPIN IRQ register | |
| // if( GPINIRQ & bmGPINIRQ7 ) { //vbus overload | |
| // vbusPwr( OFF ); //attempt powercycle | |
| // delay( 1000 ); | |
| // vbusPwr( ON ); | |
| // regWr( rGPINIRQ, bmGPINIRQ7 ); | |
| // } | |
| return( GPINIRQ ); | |
| } | |
| //void MAX3421E::usbSM( void ) //USB state machine | |
| //{ | |
| // | |
| // | |
| //} |