Files
super6502/sw/usb/MAX3421E.c
Byron Lathi 5d8c4aab44 Add USB code
Adds the usb code that we got in ECE 385. It will not work now, and
parts that involve the timer are disabled. It does compile though, with
a few warnings.

The goal will be to add USB MSD support, which is not actually given to
us so I will have to do myself or find some other code to base it off
of.
2022-03-10 16:15:08 -06:00

251 lines
8.5 KiB
C

//Fill in your low-level SPI functions here, as per your host platform
#define _MAX3421E_C_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "project_config.h"
#include <spi.h>
#include <gpio.h>
#include <unistd.h>
//variables and data structures
//External variables
extern BYTE usb_task_state;
uint8_t* usb_gpio = USB_GPIO;
/* Functions */
void SPI_init(BYTE sync_mode, BYTE bus_mode, BYTE smp_phase) {
//Don't need to initialize SPI port, already ready to go with BSP
(void) sync_mode;
(void) bus_mode;
(void) smp_phase;
}
//writes single byte to MAX3421E via SPI, simultanously reads status register and returns it
BYTE SPI_wr(BYTE data) {
(void) data;
//This function is never used by the code, so you do not need to fill it in
return -1;
}
//writes register to MAX3421E via SPI
void MAXreg_wr(BYTE reg, BYTE val) {
//psuedocode:
//select MAX3421E (may not be necessary if you are using SPI peripheral)
//write reg + 2 via SPI
//write val via SPI
//read return code from SPI peripheral (see Intel documentation)
//if return code < 0 print an error
//deselect MAX3421E (may not be necessary if you are using SPI peripheral)
BYTE regdir = reg + 2; //reg bitshift plus direction bit.
spi_byte(regdir);
spi_byte(val);
spi_deselect();
}
//multiple-byte write
//returns a pointer to a memory position after last written
BYTE* MAXbytes_wr(BYTE reg, BYTE nbytes, BYTE* data) {
//psuedocode:
//select MAX3421E (may not be necessary if you are using SPI peripheral)
//write reg + 2 via SPI
//write data[n] via SPI, where n goes from 0 to nbytes-1
//read return code from SPI peripheral (see Intel documentation)
//if return code < 0 print an error
//deselect MAX3421E (may not be necessary if you are using SPI peripheral)
//return (data + nbytes);
int i;
BYTE regdir = reg + 2;
spi_byte(regdir);
for (i = 0; i < nbytes; i++){
spi_byte(data[i]);
}
spi_deselect();
return data + nbytes;
}
//reads register from MAX3421E via SPI
BYTE MAXreg_rd(BYTE reg) {
//psuedocode:
//select MAX3421E (may not be necessary if you are using SPI peripheral)
//write reg via SPI
//read val via SPI
//read return code from SPI peripheral (see Intel documentation)
//if return code < 0 print an error
//deselect MAX3421E (may not be necessary if you are using SPI peripheral)
//return val
BYTE regdir = reg;
BYTE rxdata;
spi_byte(regdir);
rxdata = spi_byte(0);
spi_deselect();
return rxdata;
}
//multiple-byte write
//returns a pointer to a memory position after last written
BYTE* MAXbytes_rd(BYTE reg, BYTE nbytes, BYTE* data) {
//psuedocode:
//select MAX3421E (may not be necessary if you are using SPI peripheral)
//write reg via SPI
//read data[n] from SPI, where n goes from 0 to nbytes-1
//read return code from SPI peripheral (see Intel documentation)
//if return code < 0 print an error
//deselect MAX3421E (may not be necessary if you are using SPI peripheral)
//return (data + nbytes);
int i;
BYTE regdir = reg;
spi_byte(regdir);
for (i = 0; i < nbytes; i++){
data[i] = spi_byte(0);
}
spi_deselect();
return data + nbytes;
}
/* reset MAX3421E using chip reset bit. SPI configuration is not affected */
void MAX3421E_reset(void) {
//hardware reset, then software reset
BYTE tmp;
*usb_gpio &= ~USB_GPIO_RST;
//TODO
//sleep(1);
*usb_gpio |= USB_GPIO_RST;
tmp = 0;
MAXreg_wr( rUSBCTL, bmCHIPRES); //Chip reset. This stops the oscillator
MAXreg_wr( rUSBCTL, 0x00); //Remove the reset
while (!(MAXreg_rd( rUSBIRQ) & bmOSCOKIRQ)) { //wait until the PLL stabilizes
tmp++; //timeout after 256 attempts
if (tmp == 0) {
//printf("reset timeout!");
}
}
}
/* turn USB power on/off */
/* 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. */
BOOL Vbus_power(BOOL action) {
// power on/off successful
return (1);
}
/* probe bus to determine device presense and speed */
void MAX_busprobe(void) {
BYTE bus_sample;
// MAXreg_wr(rHCTL,bmSAMPLEBUS);
bus_sample = MAXreg_rd( 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):
/*kludgy*/
if (usb_task_state != USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE) { //bus reset causes connection detect interrupt
if (!(MAXreg_rd( rMODE) & bmLOWSPEED)) {
MAXreg_wr( rMODE, MODE_FS_HOST); //start full-speed host
//printf("Starting in full speed\n");
} else {
MAXreg_wr( rMODE, MODE_LS_HOST); //start low-speed host
//printf("Starting in low speed\n");
}
usb_task_state = ( USB_STATE_ATTACHED); //signal usb state machine to start attachment sequence
}
break;
case ( bmKSTATUS):
if (usb_task_state != USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE) { //bus reset causes connection detect interrupt
if (!(MAXreg_rd( rMODE) & bmLOWSPEED)) {
MAXreg_wr( rMODE, MODE_LS_HOST); //start low-speed host
//printf("Starting in low speed\n");
} else {
MAXreg_wr( rMODE, MODE_FS_HOST); //start full-speed host
//printf("Starting in full speed\n");
}
usb_task_state = ( USB_STATE_ATTACHED); //signal usb state machine to start attachment sequence
}
break;
case ( bmSE1): //illegal state
usb_task_state = ( USB_DETACHED_SUBSTATE_ILLEGAL);
break;
case ( bmSE0): //disconnected state
if (!((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED)) //if we came here from other than detached state
usb_task_state = ( USB_DETACHED_SUBSTATE_INITIALIZE); //clear device data structures
else {
MAXreg_wr( rMODE, MODE_FS_HOST); //start full-speed host
usb_task_state = ( USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE);
}
break;
} //end switch( bus_sample )
}
/* MAX3421E initialization after power-on */
void MAX3421E_init(void) {
/* Configure full-duplex SPI, interrupt pulse */
MAXreg_wr( rPINCTL, (bmFDUPSPI + bmINTLEVEL + bmGPXB)); //Full-duplex SPI, level interrupt, GPX
MAX3421E_reset(); //stop/start the oscillator
/* configure power switch */
Vbus_power( OFF); //turn Vbus power off
MAXreg_wr( rGPINIEN, bmGPINIEN7); //enable interrupt on GPIN7 (power switch overload flag)
Vbus_power( ON);
/* configure host operation */
MAXreg_wr( rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ); // set pull-downs, SOF, Host, Separate GPIN IRQ on GPX
//MAXreg_wr( rHIEN, bmFRAMEIE|bmCONDETIE|bmBUSEVENTIE ); // enable SOF, connection detection, bus event IRQs
MAXreg_wr( rHIEN, bmCONDETIE); //connection detection
/* HXFRDNIRQ is checked in Dispatch packet function */
MAXreg_wr(rHCTL, bmSAMPLEBUS); // update the JSTATUS and KSTATUS bits
MAX_busprobe(); //check if anything is connected
MAXreg_wr( rHIRQ, bmCONDETIRQ); //clear connection detect interrupt
MAXreg_wr( rCPUCTL, 0x01); //enable interrupt pin
}
/* MAX3421 state change task and interrupt handler */
void MAX3421E_Task(void) {
if ((*usb_gpio & USB_GPIO_IRQ) == 0) {
//printf("MAX interrupt\n\r");
MaxIntHandler();
}
if ((*usb_gpio & USB_GPIO_GPX) != 0) {
//printf("GPX interrupt\n\r");
MaxGpxHandler();
}
}
void MaxIntHandler(void) {
BYTE HIRQ;
BYTE HIRQ_sendback = 0x00;
HIRQ = MAXreg_rd( rHIRQ); //determine interrupt source
//printf("IRQ: %x\n", HIRQ);
if (HIRQ & bmFRAMEIRQ) { //->1ms SOF interrupt handler
HIRQ_sendback |= bmFRAMEIRQ;
} //end FRAMEIRQ handling
if (HIRQ & bmCONDETIRQ) {
MAX_busprobe();
HIRQ_sendback |= bmCONDETIRQ; //set sendback to 1 to clear register
}
if (HIRQ & bmSNDBAVIRQ) //if the send buffer is clear (previous transfer completed without issue)
{
MAXreg_wr(rSNDBC, 0x00);//clear the send buffer (not really necessary, but clears interrupt)
}
if (HIRQ & bmBUSEVENTIRQ) { //bus event is either reset or suspend
usb_task_state++; //advance USB task state machine
HIRQ_sendback |= bmBUSEVENTIRQ;
}
/* End HIRQ interrupts handling, clear serviced IRQs */
MAXreg_wr( rHIRQ, HIRQ_sendback); //write '1' to CONDETIRQ to ack bus state change
}
void MaxGpxHandler(void) {
BYTE GPINIRQ;
GPINIRQ = MAXreg_rd( rGPINIRQ); //read both IRQ registers
}