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
This commit is contained in:
Lephe 2023-01-29 19:37:19 +01:00
parent cf2b86deaa
commit 18e0db3886
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
7 changed files with 177 additions and 150 deletions

View File

@ -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;

View File

@ -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;

View File

@ -11,6 +11,19 @@ extern "C" {
#include <gint/defs/types.h>
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;

View File

@ -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;

View File

@ -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;

View File

@ -2,6 +2,7 @@
#include <gint/mpu/usb.h>
#include <gint/mpu/power.h>
#include <gint/mpu/cpg.h>
#include <gint/mpu/pfc.h>
#include <gint/drivers.h>
#include <gint/drivers/states.h>
#include <gint/clock.h>
@ -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,

View File

@ -6,7 +6,7 @@
#define GINT_USB_USB_PRIVATE
#include <gint/defs/attributes.h>
#include <gint/timer.h>
#include <gint/defs/timeout.h>
#include <gint/gint.h>
#include <gint/usb.h>
@ -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 */