From 18e0db388606f968a2eb8c55113914d5916f4cbd Mon Sep 17 00:00:00 2001 From: Lephe Date: Sun, 29 Jan 2023 19:37:19 +0100 Subject: [PATCH] usb: improve driver with updated knowledge and prepare reading * Finish updating the register list * Use RTC-based timeouts to not involve more interrupts * Be a lot more conservative about PID=BUF * Start setting up parameters and checking invariants for future bidirectional communications --- include/gint/drivers/keydev.h | 6 +- include/gint/mpu/pfc.h | 4 +- include/gint/mpu/usb.h | 76 +++++++++--------------- src/usb/pipes.c | 109 +++++++++++++++++++++++----------- src/usb/string.c | 13 +--- src/usb/usb.c | 84 +++++++++++++------------- src/usb/usb_private.h | 35 +++++++---- 7 files changed, 177 insertions(+), 150 deletions(-) diff --git a/include/gint/drivers/keydev.h b/include/gint/drivers/keydev.h index 40defec..f5495cc 100644 --- a/include/gint/drivers/keydev.h +++ b/include/gint/drivers/keydev.h @@ -125,7 +125,7 @@ typedef struct { that the precision of the delay is limited by the speed at which the keyboard is scanned, which is nowhere near microsecond-level. By default (128 Hz) the precision is about 7.8 ms. */ - keydev_repeat_profile_t repeater; + keydev_repeat_profile_t repeater; } GPACKEDENUM keydev_transform_t; @@ -204,8 +204,8 @@ typedef struct { /* Event queue (circular buffer) */ key_event_t queue[KEYBOARD_QUEUE_SIZE]; - /* Parameters for the standard repeat function */ - int rep_standard_first, rep_standard_next; + /* Parameters for the standard repeat function */ + int rep_standard_first, rep_standard_next; } keydev_t; diff --git a/include/gint/mpu/pfc.h b/include/gint/mpu/pfc.h index 7b5376f..5f3cbce 100644 --- a/include/gint/mpu/pfc.h +++ b/include/gint/mpu/pfc.h @@ -190,7 +190,9 @@ typedef volatile struct /* Module function selection registers. WARNING: These are the SH7724 bits, not necessarily the SH7305! */ word_union(MSELCRA, - uint16_t :16; + uint16_t :8; + uint16_t UNKNOWN_USB :2; + uint16_t :6; ); word_union(MSELCRB, uint16_t XTAL_USB :2; diff --git a/include/gint/mpu/usb.h b/include/gint/mpu/usb.h index c4206a4..4253a4a 100644 --- a/include/gint/mpu/usb.h +++ b/include/gint/mpu/usb.h @@ -11,6 +11,19 @@ extern "C" { #include +typedef struct +{ + word_union(TRE, + uint16_t :6; + uint16_t TRENB :1; /* Transaction Counter Enable */ + uint16_t TRCLR :1; /* Transaction Counter Clear */ + uint16_t :8; + ); + word_union(TRN, + uint16_t TRCNT :16; /* Transaction Counter */ + ); +} sh7305_usb_pipetr; + typedef volatile struct { /* System Configuration Control Register */ @@ -192,7 +205,19 @@ typedef volatile struct uint16_t VALID :1; /* USB Request Reception */ uint16_t CTSQ :3; /* Control Transfer Stage */ ); - pad(4); + word_union(INTSTS1, + uint16_t _1 :1; /* Unknown role */ + uint16_t BCHG :1; /* Bus Change */ + uint16_t :1; + uint16_t DTCH :1; /* Disconnection Detection */ + uint16_t ATTCH :1; /* Connection Detection */ + uint16_t :4; + uint16_t EOFERR :1; /* EOF Error Detection */ + uint16_t SIGN :1; /* Setup Transaction Error */ + uint16_t SACK :1; /* Setup Transaction Normal Response */ + uint16_t :4; + ); + pad(2); /* BRDY Interrupt Status Register */ uint16_t BRDYSTS; @@ -333,53 +358,8 @@ typedef volatile struct ); pad(14); - /* PIPEn Transaction Counter Enable Registers and */ - /* PIPEn Transaction Counter Registers */ - word_union(PIPE1TRE, - uint16_t :6; - uint16_t TRENB :1; /* Transaction Counter Enable */ - uint16_t TRCLR :1; /* Transaction Counter Clear */ - uint16_t :8; - ); - word_union(PIPE1TRN, - uint16_t TRCNT :16; /* Transaction Counter */ - ); - word_union(PIPE2TRE, - uint16_t :6; - uint16_t TRENB :1; /* Transaction Counter Enable */ - uint16_t TRCLR :1; /* Transaction Counter Clear */ - uint16_t :8; - ); - word_union(PIPE2TRN, - uint16_t TRCNT :16; /* Transaction Counter */ - ); - word_union(PIPE3TRE, - uint16_t :6; - uint16_t TRENB :1; /* Transaction Counter Enable */ - uint16_t TRCLR :1; /* Transaction Counter Clear */ - uint16_t :8; - ); - word_union(PIPE3TRN, - uint16_t TRCNT :16; /* Transaction Counter */ - ); - word_union(PIPE4TRE, - uint16_t :6; - uint16_t TRENB :1; /* Transaction Counter Enable */ - uint16_t TRCLR :1; /* Transaction Counter Clear */ - uint16_t :8; - ); - word_union(PIPE4TRN, - uint16_t TRCNT :16; /* Transaction Counter */ - ); - word_union(PIPE5TRE, - uint16_t :6; - uint16_t TRENB :1; /* Transaction Counter Enable */ - uint16_t TRCLR :1; /* Transaction Counter Clear */ - uint16_t :8; - ); - word_union(PIPE5TRN, - uint16_t TRCNT :16; /* Transaction Counter */ - ); + /* Transaction Counter Registers (PIPE1..PIPE5 only) */ + sh7305_usb_pipetr PIPETR[5]; pad(0x1e); uint16_t REG_C2; diff --git a/src/usb/pipes.c b/src/usb/pipes.c index e46c03f..1836ad1 100644 --- a/src/usb/pipes.c +++ b/src/usb/pipes.c @@ -18,21 +18,42 @@ void usb_pipe_configure(int address, endpoint_t const *ep) { /* Maps USB 2.0 transfer types to SH7305 USB module transfer types */ - static uint8_t type_map[4] = { 0, 3, 1, 2 }; - USB.PIPESEL.PIPESEL = ep->pipe; + static uint8_t type_map[4] = { + 0, TYPE_ISOCHRONOUS, TYPE_BULK, TYPE_INTERRUPT }; - USB.PIPECFG.TYPE = type_map[ep->dc->bmAttributes & 0x03]; - USB.PIPECFG.BFRE = 0; + if(ep->pipe > 0) { + /* Set PID to NAK so we can modify the pipe's configuration */ + USB.PIPECTR[ep->pipe-1].PID = PID_NAK; + usb_while(USB.PIPECTR[ep->pipe-1].PBUSY); + } + + int type = type_map[ep->dc->bmAttributes & 0x03]; + bool dir_transmitting = (address & 0x80) != 0; + bool dir_receiving = !dir_transmitting; + + USB.PIPESEL.PIPESEL = ep->pipe; + USB.PIPECFG.TYPE = type; + USB.PIPECFG.BFRE = dir_receiving; + /* Enable continuous mode on all bulk transfer pipes + TODO: Also make it double mode*/ USB.PIPECFG.DBLB = 0; - USB.PIPECFG.CNTMD = 1; - USB.PIPECFG.SHTNAK = 0; - USB.PIPECFG.DIR = (address & 0x80) != 0; + USB.PIPECFG.CNTMD = (type == TYPE_BULK); + USB.PIPECFG.SHTNAK = 1; + USB.PIPECFG.DIR = dir_transmitting; USB.PIPECFG.EPNUM = (address & 0x0f); USB.PIPEBUF.BUFSIZE = ep->bufsize - 1; USB.PIPEBUF.BUFNMB = ep->bufnmb; USB.PIPEMAXP.MXPS = le16toh(ep->dc->wMaxPacketSize); + + USB.PIPEPERI.IFIS = 0; + USB.PIPEPERI.IITV = 0; + + if(ep->pipe >= 1 && ep->pipe <= 5) { + USB.PIPETR[ep->pipe-1].TRE.TRCLR = 1; + USB.PIPETR[ep->pipe-1].TRE.TRENB = 0; + } } /* usb_pipe_clear(): Clear all data in the pipe */ @@ -41,16 +62,14 @@ void usb_pipe_clear(int pipe) if(pipe <= 0 || pipe > 9) return; /* Set PID=NAK then use ACLRM to clear the pipe */ - USB.PIPECTR[pipe-1].PID = 0; + USB.PIPECTR[pipe-1].PID = PID_NAK; usb_while(USB.PIPECTR[pipe-1].PBUSY); USB.PIPECTR[pipe-1].ACLRM = 1; USB.PIPECTR[pipe-1].ACLRM = 0; - usb_while(!USB.PIPECTR[pipe-1].BSTS); - - USB.PIPECTR[pipe-1].PID = 0; - usb_while(USB.PIPECTR[pipe-1].CSSTS || USB.PIPECTR[pipe-1].PBUSY); + /* Clear the sequence bit (important after a world switch since we restore + hardware registers but the host connection is starting from scratch!) */ USB.PIPECTR[pipe-1].SQCLR = 1; usb_while(USB.PIPECTR[pipe-1].SQMON != 0); } @@ -65,22 +84,22 @@ typedef enum { CF, /* Used for the Default Control Pipe */ D0F, /* FIFO Controller 0 */ D1F, /* FIFO Controller 1 */ -} fifo_t; +} GPACKEDENUM fifo_t; enum { FIFO_READ = 0, /* Read mode */ FIFO_WRITE = 1, /* Write mode */ }; -/* fifo_access(): Get a FIFO controller for a pipe */ -static fifo_t fifo_access(int pipe) +/* fifo_find_available_controller(): Get a FIFO controller for a pipe */ +static fifo_t fifo_find_available_controller(int pipe) { - /* TODO: USB: fifo_access(): Possibly use CFIFO for all pipes? */ if(pipe == 0) return CF; - /* Find a free controller */ + if(USB.D0FIFOSEL.CURPIPE == 0) return D0F; - USB_LOG("Wait D0 is unavailable\n"); + USB_LOG("D0 is unavailable!\n"); if(USB.D1FIFOSEL.CURPIPE == 0) return D1F; + USB_LOG("D1 is unavailable!\n"); return NOF; } @@ -90,38 +109,58 @@ static void fifo_bind(fifo_t ct, int pipe, int mode, int size) { size = (size - (size == 4) - 1) & 3; - if(pipe == 0) - { - if(USB.CFIFOSEL.ISEL == 1 && USB.DCPCTR.PID == 1) return; + if(pipe == 0) { + if(USB.CFIFOSEL.ISEL == 1 && USB.DCPCTR.PID == 1) + return; - if(mode == FIFO_WRITE) USB.DCPCTR.PID = 1; + if(mode == FIFO_WRITE) + USB.DCPCTR.PID = PID_BUF; /* RCNT=0 REW=0 MBW=size BIGEND=1 ISEL=mode CURPIPE=0 */ USB.CFIFOSEL.word = 0x0100 | (mode << 5) | (size << 10); usb_while(!USB.CFIFOCTR.FRDY || USB.CFIFOSEL.ISEL != mode); return; } - /* Set PID to BUF */ - USB.PIPECTR[pipe-1].PID = 1; + __typeof__(USB.D0FIFOSEL) sel; + sel.RCNT = 0; + sel.REW = 0; + sel.DCLRM = (mode == FIFO_READ); + sel.DREQE = 0; + sel.MBW = size; + sel.BIGEND = 1; + sel.CURPIPE = pipe; - /* RCNT=0 REW=0 DCLRM=0 DREQE=0 MBW=size BIGEND=1 */ - if(ct == D0F) USB.D0FIFOSEL.word = 0x0100 | (size << 10) | pipe; - if(ct == D1F) USB.D1FIFOSEL.word = 0x0100 | (size << 10) | pipe; + if(ct == D0F) { + USB.D0FIFOSEL.word = sel.word; + usb_while(!USB.D0FIFOCTR.FRDY || USB.PIPECFG.DIR != mode); + } + if(ct == D1F) { + USB.D1FIFOSEL.word = sel.word; + usb_while(!USB.D1FIFOCTR.FRDY || USB.PIPECFG.DIR != mode); + } - if(ct == D0F) usb_while(!USB.D0FIFOCTR.FRDY || USB.PIPECFG.DIR!=mode); - if(ct == D1F) usb_while(!USB.D1FIFOCTR.FRDY || USB.PIPECFG.DIR!=mode); + /* Enable USB comunication! */ + USB.PIPECTR[pipe-1].PID = PID_BUF; } -/* fifo_unbind(): Free a FIFO */ +/* fifo_unbind(): Unbind a FIFO */ static void fifo_unbind(fifo_t ct) { - if(ct == D0F) - { + int pipe = -1; + if(ct == D0F) pipe = USB.D0FIFOSEL.CURPIPE; + if(ct == D1F) pipe = USB.D1FIFOSEL.CURPIPE; + if(pipe <= 0) + return; + + /* Disbable communication on the pipe */ + USB.PIPECTR[pipe-1].PID = PID_NAK; + usb_while(USB.PIPECTR[pipe-1].PBUSY); + + if(ct == D0F) { USB.D0FIFOSEL.word = 0x0000; usb_while(USB.D0FIFOSEL.CURPIPE != 0); } - if(ct == D1F) - { + if(ct == D1F) { USB.D1FIFOSEL.word = 0x0000; usb_while(USB.D1FIFOSEL.CURPIPE != 0); } @@ -327,7 +366,7 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size, otherwise try to get a new free one */ /* TODO: usb_write_async(): TOC/TOU race on controller being free */ fifo_t ct = t->ct; - if(ct == NOF) ct = fifo_access(pipe); + if(ct == NOF) ct = fifo_find_available_controller(pipe); if(ct == NOF) return USB_WRITE_NOFIFO; t->data = data; diff --git a/src/usb/string.c b/src/usb/string.c index d7e97a9..a7ac1e3 100644 --- a/src/usb/string.c +++ b/src/usb/string.c @@ -30,21 +30,14 @@ uint16_t usb_dc_string(uint16_t const *literal, size_t len) dc->bDescriptorType = USB_DC_STRING; for(size_t i = 0; i < len; i++) dc->data[i] = htole16(literal[i]); - /* Try to make room in the array; if realloc() fails, try to allocate - again in another arena */ + /* Try to make room in the array */ size_t new_size = (array_size + 1) * sizeof(*array); usb_dc_string_t **new_array = realloc(array, new_size); if(!new_array) { - new_array = malloc(new_size); - if(!new_array) - { - free(dc); - return 0; - } - memcpy(new_array, array, array_size * sizeof(*array)); - free(array); + free(dc); + return 0; } array = new_array; diff --git a/src/usb/usb.c b/src/usb/usb.c index c0b1699..29235a5 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -76,11 +77,8 @@ static void hpoweron(void) { if(hpowered()) return; - /* TODO: USB: Proper handling of MSELCRA and MSELCRB */ - uint16_t volatile *MSELCRA = (void *)0xa4050180; - uint16_t volatile *MSELCRB = (void *)0xa4050182; - *MSELCRA &= 0xff3f; - *MSELCRB &= 0x3fff; + SH7305_PFC.MSELCRA.UNKNOWN_USB = 0; + SH7305_PFC.MSELCRB.XTAL_USB = 0; /* Leave some delay for the clock to settle. The OS leaves 100 ms, but it just never seems necessary. */ @@ -106,9 +104,6 @@ static void hpoweron_write(void) static void hpoweroff(void) { - uint16_t volatile *MSELCRA = (void *)0xa4050180; - uint16_t volatile *MSELCRB = (void *)0xa4050182; - SH7305_USB_UPONCR.word = 0x0000; /* This delay is crucial and omitting it has caused constant freezes in @@ -120,12 +115,15 @@ static void hpoweroff(void) sleep_us_spin(1000); /* The values used by the OS (a PFC driver could do better) */ - *MSELCRB = (*MSELCRB & 0x3fff) | 0xc000; - *MSELCRA = (*MSELCRA & 0xff3f) | 0x0040; + SH7305_PFC.MSELCRB.XTAL_USB = 3; + SH7305_PFC.MSELCRA.UNKNOWN_USB = 1; } int usb_open(usb_interface_t const **interfaces, gint_call_t callback) { + /* TODO: Check whether the calculator can host devices (probably no) */ + bool host = false; + USB_LOG("---- usb_open ----\n"); int rc = usb_configure_solve(interfaces); @@ -143,38 +141,37 @@ int usb_open(usb_interface_t const **interfaces, gint_call_t callback) USB.REG_C2 = 0x0020; - // TODO: Configuration sequence - // - DPRPU = 0 (disconnect if previously a function) - // - DPRD = 0 (required for setting DCFM) - // - DCFM = 0/1 (select host/function) - // - USBE = 0 (clears registers based on DCFM) - // Then for function: - // - HSE = 1 (use high-speed) - // - USBE = 1 - // - ... - // - DPRPU = 1 (notify host of connection) - // - Read LNST - // And for host: - // - HSE = 0 (allow slow devices) - // - USBE = 1 - // - DRPD = 1 (host setting) - // - Read LNST - - /* Turn on the module now that SCKE=1 */ - // TODO: Set to 0 now, set to 1 after enabling DPRPU/DPRD - USB.SYSCFG.USBE = 1; - USB.SYSCFG.HSE = 1; - - /* Disable pin pull-down and pull-up in order to change DCFM */ - USB.SYSCFG.DRPD = 0; + /* Disconnect (DPRPU=0) if we were previously connected as a function. Also + drive down DRPR, since both are required for setting DCFM. */ USB.SYSCFG.DPRPU = 0; - /* Select function controller */ - USB.SYSCFG.DCFM = 0; + USB.SYSCFG.DRPD = 0; - /* Pull D+ up to 3.3V, notifying connection when possible. Read - SYSSTS.LNST as required after modifying DPRPU */ - USB.SYSCFG.DPRPU = 1; - GUNUSED volatile int LNST = USB.SYSSTS.LNST; + if(host) { + /* Select the host controller */ + USB.SYSCFG.DCFM = 1; + /* Clear registers to prepare for host operation */ + USB.SYSCFG.USBE = 0; + /* Do not require high-speed operation; also accept full-speed */ + USB.SYSCFG.HSE = 0; + + /* Pull DPRD and eliminate chattering */ + USB.SYSCFG.DRPD = 1; + GUNUSED volatile int LNST = USB.SYSSTS.LNST; + + /* Enable the module */ + USB.SYSCFG.USBE = 1; + } + else { + /* Select the function controller */ + USB.SYSCFG.DCFM = 0; + /* Clear registers to prepare for function operation */ + USB.SYSCFG.USBE = 0; + /* Use high-speed only */ + USB.SYSCFG.HSE = 1; + + /* Enable the module */ + USB.SYSCFG.USBE = 1; + } /* Prepare the default control pipe. */ USB.DCPCFG.DIR = 0; @@ -206,6 +203,11 @@ int usb_open(usb_interface_t const **interfaces, gint_call_t callback) intc_handler_function(0xa20, GINT_CALL(usb_interrupt_handler)); intc_priority(INTC_USB, 8); + /* Pull D+ up to 3.3V, notifying connection when possible. Read + SYSSTS.LNST as required after modifying DPRPU */ + USB.SYSCFG.DPRPU = 1; + GUNUSED volatile int LNST = USB.SYSSTS.LNST; + return 0; } @@ -356,7 +358,7 @@ static void hrestore(usb_state_t const *s) gint_driver_t drv_usb = { .name = "USB", - /* TODO: Wait for remaining transfers in unbind() */ + /* TODO: usb: Wait for remaining transfers in unbind() */ .hpowered = hpowered, .hpoweron = hpoweron, .hpoweroff = hpoweroff, diff --git a/src/usb/usb_private.h b/src/usb/usb_private.h index 2057409..bbca9bb 100644 --- a/src/usb/usb_private.h +++ b/src/usb/usb_private.h @@ -6,7 +6,7 @@ #define GINT_USB_USB_PRIVATE #include -#include +#include #include #include @@ -133,20 +133,15 @@ void usb_pipe_write_bemp(int pipe); void usb_pipe_init_transfers(void); //--- -// Timout waits +// Timeout waits //--- /* usb_while(): A while loop with a timeout */ -#define usb_while(condition) ({ \ - volatile int __f = 0; \ - int __t = timer_configure(TIMER_ANY, 100000 /*µs*/, \ - GINT_CALL_SET_STOP(&__f)); \ - if(__t >= 0) timer_start(__t); \ - while((condition) && __f == 0) {} \ - if(__f) USB_LOG("%s: %d: (" #condition ") holds\n", \ - __FUNCTION__, __LINE__); \ - if(__t >= 0) timer_stop(__t); \ - __f != 0; \ +#define usb_while(COND) ({ \ + timeout_t __t = timeout_make_ms(100); \ + bool __b = false; \ + while((COND) && !(__b = timeout_elapsed(&__t))) {} \ + if(__b) USB_LOG("%s:%d: inf. while(" #COND ")\n", __FUNCTION__, __LINE__); \ }) //--- @@ -175,4 +170,20 @@ enum { USBLENG registers, along with the DCP FIFO. */ void usb_req_setup(void); +//--- +// Enumerations and stuff +//--- + +enum { + PID_NAK = 0, + PID_BUF = 1, + PID_STALL2 = 2, + PID_STALL3 = 3, +}; +enum { + TYPE_BULK = 1, + TYPE_INTERRUPT = 2, + TYPE_ISOCHRONOUS = 3, +}; + #endif /* GINT_USB_USB_PRIVATE */