1 /* 2 * Copyright 2009-2011 Oleg Mazurov, Circuits At Home, http://www.circuitsathome.com 3 * MAX3421E USB host controller support 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the authors nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 /* MAX3421E USB host controller support */ 31 32 #include "Max3421e.h" 33 // #include "Max3421e_constants.h" 34 35 static byte vbusState; 36 37 /* Functions */ 38 39 #define INT PE6 40 #define INT_PORT PORTE 41 #define INT_DDR DDRE 42 #define INT_PIN PINE 43 44 #define RST PJ2 45 #define RST_PORT PORTJ 46 #define RST_DDR DDRJ 47 #define RST_PIN PINJ 48 49 #define GPX PJ3 50 #define GPX_PORT PORTJ 51 #define GPX_DDR DDRJ 52 #define GPX_PIN PINJ 53 54 55 void MAX3421E::setRST(uint8_t val) 56 { 57 if (val == LOW) 58 RST_PORT &= ~_BV(RST); 59 else 60 RST_PORT |= _BV(RST); 61 } 62 63 uint8_t MAX3421E::readINT(void) 64 { 65 return INT_PIN & _BV(INT) ? HIGH : LOW; 66 } 67 68 uint8_t MAX3421E::readGPX(void) 69 { 70 // return GPX_PIN & _BV(GPX) ? HIGH : LOW; 71 return LOW; 72 } 73 74 75 void MAX3421E::pinInit(void) 76 { 77 INT_DDR &= ~_BV(INT); 78 RST_DDR |= _BV(RST); 79 digitalWrite(MAX_SS,HIGH); 80 setRST(HIGH); 81 } 82 83 84 /* Constructor */ 85 MAX3421E::MAX3421E() 86 { 87 spi_init(); 88 pinInit(); 89 } 90 91 byte MAX3421E::getVbusState( void ) 92 { 93 return( vbusState ); 94 } 95 /* initialization */ 96 //void MAX3421E::init() 97 //{ 98 // /* setup pins */ 99 // pinMode( MAX_INT, INPUT); 100 // pinMode( MAX_GPX, INPUT ); 101 // pinMode( MAX_SS, OUTPUT ); 102 // //pinMode( BPNT_0, OUTPUT ); 103 // //pinMode( BPNT_1, OUTPUT ); 104 // //digitalWrite( BPNT_0, LOW ); 105 // //digitalWrite( BPNT_1, LOW ); 106 // Deselect_MAX3421E; 107 // pinMode( MAX_RESET, OUTPUT ); 108 // digitalWrite( MAX_RESET, HIGH ); //release MAX3421E from reset 109 //} 110 //byte MAX3421E::getVbusState( void ) 111 //{ 112 // return( vbusState ); 113 //} 114 //void MAX3421E::toggle( byte pin ) 115 //{ 116 // digitalWrite( pin, HIGH ); 117 // digitalWrite( pin, LOW ); 118 //} 119 /* Single host register write */ 120 void MAX3421E::regWr( byte reg, byte val) 121 { 122 digitalWrite(MAX_SS,LOW); 123 SPDR = ( reg | 0x02 ); 124 while(!( SPSR & ( 1 << SPIF ))); 125 SPDR = val; 126 while(!( SPSR & ( 1 << SPIF ))); 127 digitalWrite(MAX_SS,HIGH); 128 return; 129 } 130 /* multiple-byte write */ 131 /* returns a pointer to a memory position after last written */ 132 char * MAX3421E::bytesWr( byte reg, byte nbytes, char * data ) 133 { 134 digitalWrite(MAX_SS,LOW); 135 SPDR = ( reg | 0x02 ); 136 while( nbytes-- ) { 137 while(!( SPSR & ( 1 << SPIF ))); //check if previous byte was sent 138 SPDR = ( *data ); // send next data byte 139 data++; // advance data pointer 140 } 141 while(!( SPSR & ( 1 << SPIF ))); 142 digitalWrite(MAX_SS,HIGH); 143 return( data ); 144 } 145 /* GPIO write. GPIO byte is split between 2 registers, so two writes are needed to write one byte */ 146 /* GPOUT bits are in the low nibble. 0-3 in IOPINS1, 4-7 in IOPINS2 */ 147 /* upper 4 bits of IOPINS1, IOPINS2 are read-only, so no masking is necessary */ 148 void MAX3421E::gpioWr( byte val ) 149 { 150 regWr( rIOPINS1, val ); 151 val = val >>4; 152 regWr( rIOPINS2, val ); 153 154 return; 155 } 156 /* Single host register read */ 157 byte MAX3421E::regRd( byte reg ) 158 { 159 byte tmp; 160 digitalWrite(MAX_SS,LOW); 161 SPDR = reg; 162 while(!( SPSR & ( 1 << SPIF ))); 163 SPDR = 0; //send empty byte 164 while(!( SPSR & ( 1 << SPIF ))); 165 digitalWrite(MAX_SS,HIGH); 166 return( SPDR ); 167 } 168 /* multiple-bytes register read */ 169 /* returns a pointer to a memory position after last read */ 170 char * MAX3421E::bytesRd ( byte reg, byte nbytes, char * data ) 171 { 172 digitalWrite(MAX_SS,LOW); 173 SPDR = reg; 174 while(!( SPSR & ( 1 << SPIF ))); //wait 175 while( nbytes ) { 176 SPDR = 0; //send empty byte 177 nbytes--; 178 while(!( SPSR & ( 1 << SPIF ))); 179 *data = SPDR; 180 data++; 181 } 182 digitalWrite(MAX_SS,HIGH); 183 return( data ); 184 } 185 /* GPIO read. See gpioWr for explanation */ 186 /* GPIN pins are in high nibbles of IOPINS1, IOPINS2 */ 187 byte MAX3421E::gpioRd( void ) 188 { 189 byte tmpbyte = 0; 190 tmpbyte = regRd( rIOPINS2 ); //pins 4-7 191 tmpbyte &= 0xf0; //clean lower nibble 192 tmpbyte |= ( regRd( rIOPINS1 ) >>4 ) ; //shift low bits and OR with upper from previous operation. Upper nibble zeroes during shift, at least with this compiler 193 return( tmpbyte ); 194 } 195 /* reset MAX3421E using chip reset bit. SPI configuration is not affected */ 196 boolean MAX3421E::reset() 197 { 198 byte tmp = 0; 199 regWr( rUSBCTL, bmCHIPRES ); //Chip reset. This stops the oscillator 200 regWr( rUSBCTL, 0x00 ); //Remove the reset 201 while(!(regRd( rUSBIRQ ) & bmOSCOKIRQ )) { //wait until the PLL is stable 202 tmp++; //timeout after 256 attempts 203 if( tmp == 0 ) { 204 return( false ); 205 } 206 } 207 return( true ); 208 } 209 /* turn USB power on/off */ 210 /* does nothing, returns TRUE. Left for compatibility with old sketches */ 211 /* will be deleted eventually */ 212 ///* ON pin of VBUS switch (MAX4793 or similar) is connected to GPOUT7 */ 213 ///* OVERLOAD pin of Vbus switch is connected to GPIN7 */ 214 ///* OVERLOAD state low. NO OVERLOAD or VBUS OFF state high. */ 215 boolean MAX3421E::vbusPwr ( boolean action ) 216 { 217 // byte tmp; 218 // tmp = regRd( rIOPINS2 ); //copy of IOPINS2 219 // if( action ) { //turn on by setting GPOUT7 220 // tmp |= bmGPOUT7; 221 // } 222 // else { //turn off by clearing GPOUT7 223 // tmp &= ~bmGPOUT7; 224 // } 225 // regWr( rIOPINS2, tmp ); //send GPOUT7 226 // if( action ) { 227 // delay( 60 ); 228 // } 229 // if (( regRd( rIOPINS2 ) & bmGPIN7 ) == 0 ) { // check if overload is present. MAX4793 /FLAG ( pin 4 ) goes low if overload 230 // return( false ); 231 // } 232 return( true ); // power on/off successful 233 } 234 /* probe bus to determine device presense and speed and switch host to this speed */ 235 void MAX3421E::busprobe( void ) 236 { 237 byte bus_sample; 238 bus_sample = regRd( rHRSL ); //Get J,K status 239 bus_sample &= ( bmJSTATUS|bmKSTATUS ); //zero the rest of the byte 240 switch( bus_sample ) { //start full-speed or low-speed host 241 case( bmJSTATUS ): 242 if(( regRd( rMODE ) & bmLOWSPEED ) == 0 ) { 243 regWr( rMODE, MODE_FS_HOST ); //start full-speed host 244 vbusState = FSHOST; 245 } 246 else { 247 regWr( rMODE, MODE_LS_HOST); //start low-speed host 248 vbusState = LSHOST; 249 } 250 break; 251 case( bmKSTATUS ): 252 if(( regRd( rMODE ) & bmLOWSPEED ) == 0 ) { 253 regWr( rMODE, MODE_LS_HOST ); //start low-speed host 254 vbusState = LSHOST; 255 } 256 else { 257 regWr( rMODE, MODE_FS_HOST ); //start full-speed host 258 vbusState = FSHOST; 259 } 260 break; 261 case( bmSE1 ): //illegal state 262 vbusState = SE1; 263 break; 264 case( bmSE0 ): //disconnected state 265 regWr( rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmSEPIRQ); 266 vbusState = SE0; 267 break; 268 }//end switch( bus_sample ) 269 } 270 /* MAX3421E initialization after power-on */ 271 void MAX3421E::powerOn() 272 { 273 /* Configure full-duplex SPI, interrupt pulse */ 274 regWr( rPINCTL,( bmFDUPSPI + bmINTLEVEL + bmGPXB )); //Full-duplex SPI, level interrupt, GPX 275 if( reset() == false ) { //stop/start the oscillator 276 Serial.println("Error: OSCOKIRQ failed to assert"); 277 } 278 279 /* configure host operation */ 280 regWr( rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmSEPIRQ ); // set pull-downs, Host, Separate GPIN IRQ on GPX 281 regWr( rHIEN, bmCONDETIE|bmFRAMEIE ); //connection detection 282 /* check if device is connected */ 283 regWr( rHCTL,bmSAMPLEBUS ); // sample USB bus 284 while(!(regRd( rHCTL ) & bmSAMPLEBUS )); //wait for sample operation to finish 285 busprobe(); //check if anything is connected 286 regWr( rHIRQ, bmCONDETIRQ ); //clear connection detect interrupt 287 regWr( rCPUCTL, 0x01 ); //enable interrupt pin 288 } 289 /* MAX3421 state change task and interrupt handler */ 290 byte MAX3421E::Task( void ) 291 { 292 byte rcode = 0; 293 byte pinvalue; 294 //Serial.print("Vbus state: "); 295 //Serial.println( vbusState, HEX ); 296 pinvalue = readINT(); 297 if( pinvalue == LOW ) { 298 rcode = IntHandler(); 299 } 300 pinvalue = readGPX(); 301 if( pinvalue == LOW ) { 302 GpxHandler(); 303 } 304 // usbSM(); //USB state machine 305 return( rcode ); 306 } 307 byte MAX3421E::IntHandler() 308 { 309 byte HIRQ; 310 byte HIRQ_sendback = 0x00; 311 HIRQ = regRd( rHIRQ ); //determine interrupt source 312 //if( HIRQ & bmFRAMEIRQ ) { //->1ms SOF interrupt handler 313 // HIRQ_sendback |= bmFRAMEIRQ; 314 //}//end FRAMEIRQ handling 315 if( HIRQ & bmCONDETIRQ ) { 316 busprobe(); 317 HIRQ_sendback |= bmCONDETIRQ; 318 } 319 /* End HIRQ interrupts handling, clear serviced IRQs */ 320 regWr( rHIRQ, HIRQ_sendback ); 321 return( HIRQ_sendback ); 322 } 323 byte MAX3421E::GpxHandler() 324 { 325 byte GPINIRQ = regRd( rGPINIRQ ); //read GPIN IRQ register 326 // if( GPINIRQ & bmGPINIRQ7 ) { //vbus overload 327 // vbusPwr( OFF ); //attempt powercycle 328 // delay( 1000 ); 329 // vbusPwr( ON ); 330 // regWr( rGPINIRQ, bmGPINIRQ7 ); 331 // } 332 return( GPINIRQ ); 333 } 334 335 //void MAX3421E::usbSM( void ) //USB state machine 336 //{ 337 // 338 // 339 //} 340