| Line 1... |
Line 1... |
| 1 |
//
|
1 |
#include "global.h"
|
| 2 |
//
|
2 |
#include "synopsysotg.h"
|
| 3 |
// Copyright 2010 TheSeven
|
3 |
#include "usb.h"
|
| 4 |
//
|
4 |
#include "timer.h"
|
| 5 |
//
|
5 |
#include "util.h"
|
| 6 |
// This file is part of emCORE.
|
6 |
|
| 7 |
//
|
7 |
#ifndef SYNOPSYSOTG_AHB_BURST_LEN
|
| 8 |
// emCORE is free software: you can redistribute it and/or
|
8 |
#define SYNOPSYSOTG_AHB_BURST_LEN 5
|
| 9 |
// modify it under the terms of the GNU General Public License as
|
9 |
#endif
|
| 10 |
// published by the Free Software Foundation, either version 2 of the
|
10 |
#ifndef SYNOPSYSOTG_AHB_THRESHOLD
|
| 11 |
// License, or (at your option) any later version.
|
11 |
#define SYNOPSYSOTG_AHB_THRESHOLD 8
|
| 12 |
//
|
12 |
#endif
|
| 13 |
// emCORE is distributed in the hope that it will be useful,
|
13 |
#ifndef SYNOPSYSOTG_TURNAROUND
|
| 14 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
#define SYNOPSYSOTG_TURNAROUND 3
|
| 15 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
15 |
#endif
|
| 16 |
// See the GNU General Public License for more details.
|
16 |
|
| 17 |
//
|
17 |
static void synopsysotg_flush_out_endpoint(const struct usb_instance* instance, int ep)
|
| 18 |
// You should have received a copy of the GNU General Public License along
|
18 |
{
|
| 19 |
// with emCORE. If not, see <http://www.gnu.org/licenses/>.
|
19 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 20 |
//
|
20 |
if (data->core->outep_regs[ep].doepctl.b.epena)
|
| 21 |
//
|
21 |
{
|
| 22 |
|
22 |
// We are waiting for an OUT packet on this endpoint, which might arrive any moment.
|
| 23 |
|
23 |
// Assert a global output NAK to avoid race conditions while shutting down the endpoint.
|
| 24 |
#include "global.h"
|
24 |
synopsysotg_target_disable_irq(instance);
|
| 25 |
#include "mmu.h"
|
25 |
data->core->dregs.dctl.b.sgoutnak = 1;
|
| 26 |
#include "panic.h"
|
26 |
while (!(data->core->gregs.gintsts.b.goutnakeff));
|
| 27 |
#include "usbdrv.h"
|
27 |
union synopsysotg_depctl doepctl = { .b = { .snak = 1, .epdis = 1 } };
|
| 28 |
#include "thread.h"
|
28 |
data->core->outep_regs[ep].doepctl = doepctl;
|
| 29 |
#include "timer.h"
|
29 |
while (!(data->core->outep_regs[ep].doepint.b.epdisabled));
|
| 30 |
#include "usb.h"
|
30 |
data->core->dregs.dctl.b.cgoutnak = 1;
|
| 31 |
#include "usb_ch9.h"
|
31 |
synopsysotg_target_enable_irq(instance);
|
| 32 |
#include "synopsysotg.h"
|
32 |
}
|
| 33 |
#include "util.h"
|
33 |
data->core->outep_regs[ep].doepctl.b.usbactep = 0;
|
| 34 |
#include "interrupt.h"
|
34 |
// Reset the transfer size register. Not strictly necessary, but can't hurt.
|
| 35 |
#include "clockgates.h"
|
35 |
data->core->outep_regs[ep].doeptsiz.d32 = 0;
|
| 36 |
#include "power.h"
|
36 |
}
|
| 37 |
|
37 |
|
| 38 |
|
38 |
static void synopsysotg_flush_in_endpoint(const struct usb_instance* instance, int ep)
|
| 39 |
struct ep_type
|
39 |
{
|
| 40 |
{
|
40 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 41 |
bool active;
|
41 |
if (data->core->inep_regs[ep].diepctl.b.epena)
|
| 42 |
bool busy;
|
42 |
{
|
| 43 |
bool done;
|
43 |
// We are shutting down an endpoint that might still have IN packets in the FIFO.
|
| 44 |
int rc;
|
44 |
// Disable the endpoint, wait for things to settle, and flush the relevant FIFO.
|
| 45 |
int size;
|
45 |
synopsysotg_target_disable_irq(instance);
|
| 46 |
struct wakeup complete;
|
46 |
data->core->inep_regs[ep].diepctl.b.snak = 1;
|
| 47 |
} ;
|
47 |
while (!(data->core->inep_regs[ep].diepint.b.inepnakeff));
|
| 48 |
|
48 |
data->core->inep_regs[ep].diepctl.b.epdis = 1;
|
| 49 |
static struct ep_type endpoints[5];
|
49 |
while (!(data->core->inep_regs[ep].diepint.b.epdisabled));
|
| 50 |
static struct usb_ctrlrequest ctrlreq CACHEALIGN_ATTR;
|
50 |
if (ep) data->core->inep_regs[ep].diepctl.b.usbactep = 0;
|
| 51 |
static struct scheduler_thread synopsysotg_thread_handle;
|
51 |
synopsysotg_target_enable_irq(instance);
|
| 52 |
static uint32_t synopsysotg_stack[0x40] STACK_ATTR;
|
52 |
// Wait for any DMA activity to stop, to make sure nobody will touch the FIFO.
|
| 53 |
|
53 |
while (!data->core->gregs.grstctl.b.ahbidle);
|
| 54 |
int usb_drv_port_speed(void)
|
54 |
// Flush it all the way down!
|
| 55 |
{
|
55 |
union synopsysotg_grstctl grstctl = { .b = { .txfnum = data->core->inep_regs[ep].diepctl.b.txfnum, .txfflsh = 1 } };
|
| 56 |
return (DSTS & 2) == 0 ? 1 : 0;
|
56 |
data->core->gregs.grstctl = grstctl;
|
| 57 |
}
|
57 |
while (data->core->gregs.grstctl.b.txfflsh);
|
| 58 |
|
58 |
}
|
| 59 |
static void reset_endpoints(int reinit)
|
59 |
else if (ep) data->core->inep_regs[ep].diepctl.b.usbactep = 0;
|
| 60 |
{
|
60 |
// Reset the transfer size register. Not strictly necessary, but can't hurt.
|
| 61 |
unsigned int i;
|
61 |
data->core->inep_regs[ep].dieptsiz.d32 = 0;
|
| 62 |
for (i = 0; i < sizeof(endpoints)/sizeof(struct ep_type); i++)
|
62 |
}
|
| 63 |
{
|
63 |
|
| 64 |
if (reinit) endpoints[i].active = false;
|
64 |
static void synopsysotg_flush_ints(const struct usb_instance* instance)
|
| 65 |
endpoints[i].busy = false;
|
65 |
{
|
| 66 |
endpoints[i].rc = -1;
|
66 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 67 |
endpoints[i].done = true;
|
67 |
int i;
|
| 68 |
wakeup_signal(&endpoints[i].complete);
|
68 |
for (i = 0; i < 16; i++)
|
| 69 |
}
|
69 |
{
|
| 70 |
DIEPCTL0 = 0x8800; /* EP0 IN ACTIVE NEXT=1 */
|
70 |
data->core->outep_regs[i].doepint = data->core->outep_regs[i].doepint;
|
| 71 |
DOEPCTL0 = 0x8000; /* EP0 OUT ACTIVE */
|
71 |
data->core->inep_regs[i].diepint = data->core->inep_regs[i].diepint;
|
| 72 |
DOEPTSIZ0 = 0x20080040; /* EP0 OUT Transfer Size:
|
72 |
}
|
| 73 |
64 Bytes, 1 Packet, 1 Setup Packet */
|
73 |
data->core->gregs.gintsts = data->core->gregs.gintsts;
|
| 74 |
DOEPDMA0 = &ctrlreq;
|
74 |
}
|
| 75 |
DOEPCTL0 |= 0x84000000; /* EP0 OUT ENABLE CLEARNAK */
|
75 |
|
| 76 |
if (reinit)
|
76 |
static void synopsysotg_try_push(const struct usb_instance* instance, int ep)
|
| 77 |
{
|
77 |
{
|
| 78 |
/* The size is getting set to zero, because we don't know
|
78 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 79 |
whether we are Full Speed or High Speed at this stage */
|
79 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
| 80 |
/* EP1 IN INACTIVE DATA0 SIZE=0 NEXT=3 */
|
80 |
union synopsysotg_depctl depctl = data->core->inep_regs[ep].diepctl;
|
| 81 |
DIEPCTL1 = 0x10001800;
|
81 |
if (!depctl.b.epena || !depctl.b.usbactep || depctl.b.stall || depctl.b.naksts) return;
|
| 82 |
/* EP2 OUT INACTIVE DATA0 SIZE=0 */
|
82 |
int bytesleft = data->core->inep_regs[ep].dieptsiz.b.xfersize;
|
| 83 |
DOEPCTL2 = 0x10000000;
|
83 |
if (!bytesleft) return;
|
| 84 |
/* EP3 IN INACTIVE DATA0 SIZE=0 NEXT=0 */
|
84 |
int maxpacket = ep ? data->core->inep_regs[ep].diepctl.b.mps : 64;
|
| 85 |
DIEPCTL3 = 0x10000000;
|
85 |
union synopsysotg_hnptxsts fifospace = data->core->gregs.hnptxsts;
|
| 86 |
/* EP4 OUT INACTIVE DATA0 SIZE=0 */
|
86 |
int words = (MIN(maxpacket, bytesleft) + 3) >> 2;
|
| 87 |
DOEPCTL4 = 0x10000000;
|
87 |
if (fifospace.b.nptxqspcavail && fifospace.b.nptxfspcavail << 2 >= words)
|
| 88 |
}
|
88 |
while (words--) data->core->dfifo[ep][0] = *state->endpoints[ep].txaddr++;
|
| 89 |
else
|
89 |
if (!words && bytesleft <= maxpacket) return;
|
| 90 |
{
|
90 |
data->core->gregs.gintmsk.b.nptxfempty = true;
|
| 91 |
/* INACTIVE DATA0 */
|
91 |
}
|
| 92 |
DIEPCTL1 = (DIEPCTL1 & ~0x00008000) | 0x10000000;
|
92 |
|
| 93 |
DOEPCTL2 = (DOEPCTL2 & ~0x00008000) | 0x10000000;
|
93 |
void synopsysotg_start_rx(const struct usb_instance* instance, union usb_endpoint_number ep, void* buf, int size)
|
| 94 |
DIEPCTL3 = (DIEPCTL3 & ~0x00008000) | 0x10000000;
|
94 |
{
|
| 95 |
DOEPCTL4 = (DOEPCTL4 & ~0x00008000) | 0x10000000;
|
95 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 96 |
}
|
96 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
| 97 |
DAINTMSK = 0xFFFFFFFF; /* Enable interrupts on all EPs */
|
97 |
|
| 98 |
}
|
98 |
// Find the appropriate set of endpoint registers
|
| 99 |
|
99 |
volatile struct synopsysotg_outepregs* regs = &data->core->outep_regs[ep.number];
|
| 100 |
int usb_drv_request_endpoint(int type, int dir)
|
100 |
// Calculate number of packets (if size == 0 an empty packet will be sent)
|
| 101 |
{
|
101 |
int maxpacket = regs->doepctl.b.mps;
|
| 102 |
size_t ep;
|
102 |
int packets = (size + maxpacket - 1) / maxpacket;
|
| 103 |
int ret = -1;
|
103 |
if (!packets) packets = 1;
|
| 104 |
|
104 |
|
| 105 |
if (dir == USB_DIR_IN) ep = 1;
|
105 |
// Set up data destination
|
| 106 |
else ep = 2;
|
106 |
if (data->use_dma) regs->doepdma = buf;
|
| 107 |
|
107 |
else state->endpoints[ep.number].rxaddr = (uint32_t*)buf;
|
| 108 |
while (ep < 5)
|
108 |
union synopsysotg_depxfrsiz deptsiz = { .b = { .pktcnt = packets, .xfersize = size } };
|
| 109 |
{
|
109 |
regs->doeptsiz = deptsiz;
|
| 110 |
if (!endpoints[ep].active)
|
110 |
|
| 111 |
{
|
111 |
// Flush CPU cache if necessary
|
| 112 |
endpoints[ep].active = true;
|
112 |
if (data->use_dma) invalidate_dcache(buf, size);
|
| 113 |
ret = ep | dir;
|
113 |
|
| 114 |
uint32_t newbits = (type << 18) | 0x10000000;
|
114 |
// Enable the endpoint
|
| 115 |
if (dir) DIEPCTL(ep) = (DIEPCTL(ep) & ~0x000C0000) | newbits;
|
115 |
union synopsysotg_depctl depctl = regs->doepctl;
|
| 116 |
else DOEPCTL(ep) = (DOEPCTL(ep) & ~0x000C0000) | newbits;
|
116 |
depctl.b.epena = 1;
|
| 117 |
break;
|
117 |
depctl.b.cnak = 1;
|
| 118 |
}
|
118 |
regs->doepctl = depctl;
|
| 119 |
ep += 2;
|
119 |
}
|
| 120 |
}
|
120 |
|
| 121 |
|
121 |
void synopsysotg_start_tx(const struct usb_instance* instance, union usb_endpoint_number ep, const void* buf, int size)
|
| 122 |
return ret;
|
122 |
{
|
| 123 |
}
|
123 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 124 |
|
124 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
| 125 |
void usb_drv_release_endpoint(int ep)
|
125 |
|
| 126 |
{
|
126 |
// Find the appropriate set of endpoint registers
|
| 127 |
ep = ep & 0x7f;
|
127 |
volatile struct synopsysotg_inepregs* regs = &data->core->inep_regs[ep.number];
|
| 128 |
|
128 |
// Calculate number of packets (if size == 0 an empty packet will be sent)
|
| 129 |
if (ep < 1 || ep > USB_NUM_ENDPOINTS) return;
|
129 |
int maxpacket = regs->diepctl.b.mps;
|
| 130 |
|
130 |
int packets = (size + maxpacket - 1) / maxpacket;
|
| 131 |
endpoints[ep].active = false;
|
131 |
if (!packets) packets = 1;
|
| 132 |
}
|
132 |
|
| 133 |
|
133 |
// Set up data source
|
| 134 |
static void usb_reset(void)
|
134 |
if (data->use_dma) regs->diepdma = buf;
|
| 135 |
{
|
135 |
else state->endpoints[ep.number].txaddr = (uint32_t*)buf;
|
| 136 |
DCTL = 0x802; /* Soft Disconnect */
|
136 |
union synopsysotg_depxfrsiz deptsiz = { .b = { .pktcnt = packets, .xfersize = size } };
|
| 137 |
|
137 |
regs->dieptsiz = deptsiz;
|
| 138 |
OPHYPWR = 0; /* PHY: Power up */
|
138 |
|
| 139 |
udelay(10);
|
139 |
// Flush CPU cache if necessary
|
| 140 |
OPHYUNK1 = 1;
|
140 |
if (data->use_dma) clean_dcache(buf, size);
|
| 141 |
OPHYUNK2 = 0xE3F;
|
141 |
|
| 142 |
ORSTCON = 1; /* PHY: Assert Software Reset */
|
142 |
// Enable the endpoint
|
| 143 |
udelay(10);
|
143 |
union synopsysotg_depctl depctl = regs->diepctl;
|
| 144 |
ORSTCON = 0; /* PHY: Deassert Software Reset */
|
144 |
depctl.b.epena = 1;
|
| 145 |
udelay(10);
|
145 |
depctl.b.cnak = 1;
|
| 146 |
OPHYUNK3 = 0x600;
|
146 |
regs->diepctl = depctl;
|
| 147 |
OPHYCLK = SYNOPSYSOTG_CLOCK;
|
147 |
|
| 148 |
sleep(400);
|
148 |
// Start pushing data into the FIFO (must be done after enabling the endpoint)
|
| 149 |
|
149 |
if (size && !data->use_dma)
|
| 150 |
GRSTCTL = 1; /* OTG: Assert Software Reset */
|
150 |
{
|
| 151 |
while (GRSTCTL & 1); /* Wait for OTG to ack reset */
|
151 |
if (data->shared_txfifo) synopsysotg_try_push(instance, ep.number);
|
| 152 |
while (!(GRSTCTL & 0x80000000)); /* Wait for OTG AHB master idle */
|
152 |
else data->core->dregs.diepempmsk.ep.in |= (1 << ep.number);
|
| 153 |
|
153 |
}
|
| 154 |
GRXFSIZ = 0x00000200; /* RX FIFO: 512 bytes */
|
154 |
}
|
| 155 |
GNPTXFSIZ = 0x02000200; /* Non-periodic TX FIFO: 512 bytes */
|
155 |
|
| 156 |
GAHBCFG = SYNOPSYSOTG_AHBCFG;
|
156 |
int synopsysotg_get_stall(const struct usb_instance* instance, union usb_endpoint_number ep)
|
| 157 |
GUSBCFG = 0x1408; /* OTG: 16bit PHY and some reserved bits */
|
157 |
{
|
| 158 |
|
158 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 159 |
DCFG = 4; /* Address 0 */
|
159 |
if (ep.direction == USB_ENDPOINT_DIRECTION_IN)
|
| 160 |
DCTL = 0x800; /* Soft Reconnect */
|
160 |
return !!data->core->inep_regs[ep.number].diepctl.b.stall;
|
| 161 |
DIEPMSK = 0x0D; /* IN EP interrupt mask */
|
161 |
return !!data->core->outep_regs[ep.number].doepctl.b.stall;
|
| 162 |
DOEPMSK = 0x0D; /* IN EP interrupt mask */
|
162 |
}
|
| 163 |
DAINTMSK = 0xFFFFFFFF; /* Enable interrupts on all endpoints */
|
163 |
|
| 164 |
GINTMSK = 0xC3000; /* Interrupt mask: IN event, OUT event, bus reset */
|
164 |
void synopsysotg_set_stall(const struct usb_instance* instance, union usb_endpoint_number ep, int stall)
|
| 165 |
|
165 |
{
|
| 166 |
reset_endpoints(1);
|
166 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 167 |
}
|
167 |
if (ep.direction == USB_ENDPOINT_DIRECTION_IN)
|
| 168 |
|
168 |
{
|
| 169 |
/* IRQ handler */
|
169 |
data->core->inep_regs[ep.number].diepctl.b.stall = !!stall;
|
| 170 |
void INT_USB_FUNC(void)
|
170 |
if (!stall) data->core->inep_regs[ep.number].diepctl.b.setd0pid = true;
|
| 171 |
{
|
171 |
}
|
| 172 |
int i;
|
172 |
else
|
| 173 |
uint32_t ints = GINTSTS;
|
173 |
{
|
| 174 |
uint32_t epints;
|
174 |
data->core->outep_regs[ep.number].doepctl.b.stall = !!stall;
|
| 175 |
if (ints & 0x1000) /* bus reset */
|
175 |
if (!stall) data->core->outep_regs[ep.number].doepctl.b.setd0pid = true;
|
| 176 |
{
|
176 |
}
|
| 177 |
DCFG = 4; /* Address 0 */
|
177 |
|
| 178 |
reset_endpoints(1);
|
178 |
}
|
| 179 |
usb_handle_bus_reset();
|
179 |
|
| 180 |
}
|
180 |
void synopsysotg_set_address(const struct usb_instance* instance, uint8_t address)
|
| 181 |
|
181 |
{
|
| 182 |
if (ints & 0x2000) /* enumeration done, we now know the speed */
|
182 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 183 |
{
|
183 |
data->core->dregs.dcfg.b.devaddr = address;
|
| 184 |
/* Set up the maximum packet sizes accordingly */
|
184 |
}
|
| 185 |
uint32_t maxpacket = usb_drv_port_speed() ? 512 : 64;
|
185 |
|
| 186 |
DIEPCTL1 = (DIEPCTL1 & ~0x000003FF) | maxpacket;
|
186 |
void synopsysotg_unconfigure_ep(const struct usb_instance* instance, union usb_endpoint_number ep)
|
| 187 |
DOEPCTL2 = (DOEPCTL2 & ~0x000003FF) | maxpacket;
|
187 |
{
|
| 188 |
DIEPCTL3 = (DIEPCTL3 & ~0x000003FF) | maxpacket;
|
188 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 189 |
DOEPCTL4 = (DOEPCTL4 & ~0x000003FF) | maxpacket;
|
189 |
if (ep.direction == USB_ENDPOINT_DIRECTION_IN)
|
| 190 |
}
|
190 |
{
|
| 191 |
|
191 |
// Kill any outstanding IN transfers
|
| 192 |
if (ints & 0x40000) /* IN EP event */
|
192 |
synopsysotg_flush_in_endpoint(instance, ep.number);
|
| 193 |
for (i = 0; i < 4; i += i + 1) // 0, 1, 3
|
193 |
// Mask interrupts for this endpoint
|
| 194 |
if (epints = DIEPINT(i))
|
194 |
data->core->dregs.daintmsk.ep.in &= ~(1 << ep.number);
|
| 195 |
{
|
195 |
}
|
| 196 |
if (epints & 1) /* Transfer completed */
|
196 |
else
|
| 197 |
{
|
197 |
{
|
| 198 |
int bytes = endpoints[i].size - (DIEPTSIZ(i) & 0x3FFFF);
|
198 |
// Kill any outstanding OUT transfers
|
| 199 |
if (endpoints[i].busy)
|
199 |
synopsysotg_flush_out_endpoint(instance, ep.number);
|
| 200 |
{
|
200 |
// Mask interrupts for this endpoint
|
| 201 |
endpoints[i].busy = false;
|
201 |
data->core->dregs.daintmsk.ep.out &= ~(1 << ep.number);
|
| 202 |
endpoints[i].rc = 0;
|
202 |
}
|
| 203 |
endpoints[i].done = true;
|
203 |
}
|
| 204 |
usb_handle_transfer_complete(i, USB_DIR_IN, 0, bytes);
|
204 |
|
| 205 |
wakeup_signal(&endpoints[i].complete);
|
205 |
void synopsysotg_configure_ep(const struct usb_instance* instance, union usb_endpoint_number ep,
|
| 206 |
}
|
206 |
enum usb_endpoint_type type, int maxpacket)
|
| 207 |
}
|
207 |
{
|
| 208 |
if (epints & 4) /* AHB error */
|
208 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 209 |
panicf(PANIC_FATAL, "USB: AHB error on IN EP%d", i);
|
209 |
|
| 210 |
if (epints & 8) /* Timeout */
|
210 |
// Write the new configuration and unmask interrupts for the endpoint.
|
| 211 |
{
|
211 |
// Reset data toggle to DATA0, as required by the USB specification.
|
| 212 |
if (endpoints[i].busy)
|
212 |
int txfifo = data->shared_txfifo ? 0 : ep.number;
|
| 213 |
{
|
213 |
union synopsysotg_depctl depctl = { .b = { .usbactep = 1, .eptype = type, .mps = maxpacket,
|
| 214 |
endpoints[i].busy = false;
|
214 |
.txfnum = txfifo, .setd0pid = 1, .nextep = (ep.number + 1) & 0xf } };
|
| 215 |
endpoints[i].rc = 1;
|
215 |
if (ep.direction == USB_ENDPOINT_DIRECTION_IN)
|
| 216 |
endpoints[i].done = true;
|
216 |
{
|
| 217 |
wakeup_signal(&endpoints[i].complete);
|
217 |
data->core->inep_regs[ep.number].diepctl = depctl;
|
| 218 |
}
|
218 |
data->core->dregs.daintmsk.ep.in |= (1 << ep.number);
|
| 219 |
}
|
219 |
}
|
| 220 |
DIEPINT(i) = epints;
|
220 |
else
|
| 221 |
}
|
221 |
{
|
| 222 |
|
222 |
data->core->outep_regs[ep.number].doepctl = depctl;
|
| 223 |
if (ints & 0x80000) /* OUT EP event */
|
223 |
data->core->dregs.daintmsk.ep.out |= (1 << ep.number);
|
| 224 |
for (i = 0; i < 5; i += 2) // 0, 2, 4
|
224 |
}
|
| 225 |
if (epints = DOEPINT(i))
|
225 |
}
|
| 226 |
{
|
226 |
|
| 227 |
if (epints & 1) /* Transfer completed */
|
227 |
void synopsysotg_ep0_start_rx(const struct usb_instance* instance, int non_setup)
|
| 228 |
{
|
228 |
{
|
| 229 |
int bytes = endpoints[i].size - (DOEPTSIZ(i) & 0x3FFFF);
|
229 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 230 |
if (endpoints[i].busy)
|
230 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
| 231 |
{
|
231 |
|
| 232 |
endpoints[i].busy = false;
|
232 |
// Set up data destination
|
| 233 |
endpoints[i].rc = 0;
|
233 |
if (data->use_dma) data->core->outep_regs[0].doepdma = instance->buffer;
|
| 234 |
endpoints[i].done = true;
|
234 |
else state->endpoints[0].rxaddr = (uint32_t*)instance->buffer;
|
| 235 |
usb_handle_transfer_complete(i, USB_DIR_OUT, 0, bytes);
|
235 |
union synopsysotg_dep0xfrsiz deptsiz = { .b = { .supcnt = 3, .pktcnt = !!non_setup, .xfersize = 64 } };
|
| 236 |
wakeup_signal(&endpoints[i].complete);
|
236 |
data->core->outep_regs[0].doeptsiz.d32 = deptsiz.d32;
|
| 237 |
}
|
237 |
|
| 238 |
}
|
238 |
// Flush CPU cache if necessary
|
| 239 |
if (epints & 4) /* AHB error */
|
239 |
if (data->use_dma) invalidate_dcache(instance->buffer, sizeof(instance->buffer));
|
| 240 |
panicf(PANIC_FATAL, "USB: AHB error on OUT EP%d", i);
|
240 |
|
| 241 |
if (epints & 8) /* SETUP phase done */
|
241 |
// Enable the endpoint
|
| 242 |
{
|
242 |
union synopsysotg_depctl depctl = data->core->outep_regs[0].doepctl;
|
| 243 |
invalidate_dcache();
|
243 |
depctl.b.epena = 1;
|
| 244 |
if (i == 0) usb_handle_control_request(&ctrlreq);
|
244 |
depctl.b.cnak = non_setup;
|
| 245 |
else panicf(PANIC_FATAL, "USB: SETUP done on OUT EP%d!?", i);
|
245 |
data->core->outep_regs[0].doepctl = depctl;
|
| 246 |
}
|
246 |
}
|
| 247 |
/* Make sure EP0 OUT is set up to accept the next request */
|
247 |
|
| 248 |
if (!i)
|
248 |
void synopsysotg_ep0_start_tx(const struct usb_instance* instance, const void* buf, int len)
|
| 249 |
{
|
249 |
{
|
| 250 |
DOEPTSIZ0 = 0x20080040;
|
250 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 251 |
DOEPDMA0 = &ctrlreq;
|
251 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
| 252 |
DOEPCTL0 |= 0x84000000;
|
252 |
|
| 253 |
}
|
253 |
if (len)
|
| 254 |
DOEPINT(i) = epints;
|
254 |
{
|
| 255 |
}
|
255 |
// Set up data source
|
| 256 |
|
256 |
if (data->use_dma) data->core->inep_regs[0].diepdma = buf;
|
| 257 |
GINTSTS = ints;
|
257 |
else state->endpoints[0].txaddr = buf;
|
| 258 |
}
|
258 |
union synopsysotg_dep0xfrsiz deptsiz = { .b = { .pktcnt = (len + 63) >> 6, .xfersize = len } };
|
| 259 |
|
259 |
data->core->inep_regs[0].dieptsiz.d32 = deptsiz.d32;
|
| 260 |
void usb_drv_set_address(int address)
|
260 |
}
|
| 261 |
{
|
261 |
else
|
| 262 |
DCFG = (DCFG & ~0x7F0) | (address << 4);
|
262 |
{
|
| 263 |
}
|
263 |
// Set up the IN pipe for a zero-length packet
|
| 264 |
|
264 |
union synopsysotg_dep0xfrsiz deptsiz = { .b = { .pktcnt = 1 } };
|
| 265 |
static void ep_send(int ep, const void *ptr, int length)
|
265 |
data->core->inep_regs[0].dieptsiz.d32 = deptsiz.d32;
|
| 266 |
{
|
266 |
}
|
| 267 |
endpoints[ep].busy = true;
|
267 |
|
| 268 |
endpoints[ep].size = length;
|
268 |
// Flush CPU cache if necessary
|
| 269 |
DIEPCTL(ep) |= 0x8000; /* EPx OUT ACTIVE */
|
269 |
if (data->use_dma) clean_dcache(buf, len);
|
| 270 |
int blocksize = usb_drv_port_speed() ? 512 : 64;
|
270 |
|
| 271 |
int packets = (length + blocksize - 1) / blocksize;
|
271 |
// Enable the endpoint
|
| 272 |
if (!length)
|
272 |
union synopsysotg_depctl depctl = data->core->inep_regs[0].diepctl;
|
| 273 |
{
|
273 |
depctl.b.epena = 1;
|
| 274 |
DIEPTSIZ(ep) = 1 << 19; /* one empty packet */
|
274 |
depctl.b.cnak = 1;
|
| 275 |
DIEPDMA(ep) = NULL; /* dummy address */
|
275 |
data->core->inep_regs[0].diepctl = depctl;
|
| 276 |
}
|
276 |
|
| 277 |
else
|
277 |
// Start pushing data into the FIFO (must be done after enabling the endpoint)
|
| 278 |
{
|
278 |
if (len && !data->use_dma)
|
| 279 |
DIEPTSIZ(ep) = length | (packets << 19);
|
279 |
{
|
| 280 |
DIEPDMA(ep) = ptr;
|
280 |
if (data->shared_txfifo) synopsysotg_try_push(instance, 0);
|
| 281 |
}
|
281 |
else data->core->dregs.diepempmsk.ep.in |= 1;
|
| 282 |
clean_dcache();
|
282 |
}
|
| 283 |
DIEPCTL(ep) |= 0x84000000; /* EPx OUT ENABLE CLEARNAK */
|
283 |
}
|
| 284 |
}
|
284 |
|
| 285 |
|
285 |
static void synopsysotg_ep0_init(const struct usb_instance* instance)
|
| 286 |
static void ep_recv(int ep, void *ptr, int length)
|
286 |
{
|
| 287 |
{
|
287 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 288 |
endpoints[ep].busy = true;
|
288 |
|
| 289 |
endpoints[ep].size = length;
|
289 |
// Make sure both EP0 pipes are active.
|
| 290 |
DOEPCTL(ep) &= ~0x20000; /* EPx UNSTALL */
|
290 |
// (The hardware should take care of that, but who knows...)
|
| 291 |
DOEPCTL(ep) |= 0x8000; /* EPx OUT ACTIVE */
|
291 |
union synopsysotg_depctl depctl = { .b = { .usbactep = 1, .nextep = data->core->inep_regs[0].diepctl.b.nextep } };
|
| 292 |
int blocksize = usb_drv_port_speed() ? 512 : 64;
|
292 |
data->core->outep_regs[0].doepctl = depctl;
|
| 293 |
int packets = (length + blocksize - 1) / blocksize;
|
293 |
data->core->inep_regs[0].diepctl = depctl;
|
| 294 |
if (!length)
|
294 |
}
|
| 295 |
{
|
295 |
|
| 296 |
DOEPTSIZ(ep) = 1 << 19; /* one empty packet */
|
296 |
void synopsysotg_irq(const struct usb_instance* instance)
|
| 297 |
DOEPDMA(ep) = NULL; /* dummy address */
|
297 |
{
|
| 298 |
}
|
298 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 299 |
else
|
299 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
| 300 |
{
|
300 |
|
| 301 |
DOEPTSIZ(ep) = length | (packets << 19);
|
301 |
union synopsysotg_gintsts gintsts = data->core->gregs.gintsts;
|
| 302 |
DOEPDMA(ep) = ptr;
|
302 |
|
| 303 |
}
|
303 |
if (gintsts.b.usbreset)
|
| 304 |
invalidate_dcache();
|
304 |
{
|
| 305 |
DOEPCTL(ep) |= 0x84000000; /* EPx OUT ENABLE CLEARNAK */
|
305 |
data->core->dregs.dcfg.b.devaddr = 0;
|
| 306 |
}
|
306 |
usb_handle_bus_reset(instance, 0);
|
| 307 |
|
307 |
}
|
| 308 |
int usb_drv_send(int endpoint, const void *ptr, int length)
|
308 |
|
| 309 |
{
|
309 |
if (gintsts.b.enumdone)
|
| 310 |
endpoint &= 0x7f;
|
310 |
{
|
| 311 |
endpoints[endpoint].done = false;
|
311 |
usb_handle_bus_reset(instance, data->core->dregs.dsts.b.enumspd == 0);
|
| 312 |
ep_send(endpoint, ptr, length);
|
312 |
synopsysotg_ep0_init(instance);
|
| 313 |
while (!endpoints[endpoint].done && endpoints[endpoint].busy)
|
313 |
}
|
| 314 |
wakeup_wait(&endpoints[endpoint].complete, TIMEOUT_BLOCK);
|
314 |
|
| 315 |
return endpoints[endpoint].rc;
|
315 |
if (gintsts.b.rxstsqlvl && !data->use_dma)
|
| 316 |
}
|
316 |
{
|
| 317 |
|
317 |
// Device to memory part of the "software DMA" implementation, used to receive data if use_dma == 0.
|
| 318 |
int usb_drv_send_nonblocking(int endpoint, const void *ptr, int length)
|
318 |
// Handle one packet at a time, the IRQ will re-trigger if there's something left.
|
| 319 |
{
|
319 |
union synopsysotg_grxfsts rxsts = data->core->gregs.grxstsp;
|
| 320 |
ep_send(endpoint & 0x7f, ptr, length);
|
320 |
int ep = rxsts.b.chnum;
|
| 321 |
return 0;
|
321 |
int words = (rxsts.b.bcnt + 3) >> 2;
|
| 322 |
}
|
322 |
while (words--) *state->endpoints[ep].rxaddr++ = data->core->dfifo[0][0];
|
| 323 |
|
323 |
}
|
| 324 |
int usb_drv_recv(int endpoint, void* ptr, int length)
|
324 |
|
| 325 |
{
|
325 |
if (gintsts.b.nptxfempty && data->core->gregs.gintmsk.b.nptxfempty)
|
| 326 |
ep_recv(endpoint & 0x7f, ptr, length);
|
326 |
{
|
| 327 |
return 0;
|
327 |
// Old style, "shared TX FIFO" memory to device part of the "software DMA" implementation,
|
| 328 |
}
|
328 |
// used to send data if use_dma == 0 and the device doesn't support one non-periodic TX FIFO per endpoint.
|
| 329 |
|
329 |
|
| 330 |
void usb_drv_cancel_all_transfers(void)
|
330 |
// First disable the IRQ, it will be re-enabled later if there is anything left to be done.
|
| 331 |
{
|
331 |
data->core->gregs.gintmsk.b.nptxfempty = false;
|
| 332 |
uint32_t mode = enter_critical_section();
|
332 |
|
| 333 |
reset_endpoints(0);
|
333 |
// Check all endpoints for anything to be transmitted
|
| 334 |
leave_critical_section(mode);
|
334 |
int ep;
|
| 335 |
}
|
335 |
for (ep = 0; ep < 16; ep++) synopsysotg_try_push(instance, ep);
|
| 336 |
|
336 |
}
|
| 337 |
bool usb_drv_stalled(int endpoint, bool in)
|
337 |
|
| 338 |
{
|
338 |
if (gintsts.b.inepintr)
|
| 339 |
if (in) return DIEPCTL(endpoint) & 0x00200000 ? true : false;
|
339 |
{
|
| 340 |
else return DOEPCTL(endpoint) & 0x00200000 ? true : false;
|
340 |
union synopsysotg_daint daint = data->core->dregs.daint;
|
| 341 |
}
|
341 |
int ep;
|
| 342 |
|
342 |
for (ep = 0; ep < 16; ep++)
|
| 343 |
void usb_drv_stall(int endpoint, bool stall, bool in)
|
343 |
if (daint.ep.in & (1 << ep))
|
| 344 |
{
|
344 |
{
|
| 345 |
if (in)
|
345 |
union synopsysotg_diepintn epints = data->core->inep_regs[ep].diepint;
|
| 346 |
{
|
346 |
if (epints.b.emptyintr)
|
| 347 |
if (stall) DIEPCTL(endpoint) |= 0x00200000;
|
347 |
{
|
| 348 |
else DIEPCTL(endpoint) &= ~0x00200000;
|
348 |
// Memory to device part of the "software DMA" implementation, used to transmit data if use_dma == 0.
|
| 349 |
}
|
349 |
union synopsysotg_depxfrsiz deptsiz = data->core->inep_regs[ep].dieptsiz;
|
| 350 |
else
|
350 |
if (!deptsiz.b.xfersize) data->core->dregs.diepempmsk.ep.in &= ~(1 << ep);
|
| 351 |
{
|
351 |
else
|
| 352 |
if (stall) DOEPCTL(endpoint) |= 0x00200000;
|
352 |
{
|
| 353 |
else DOEPCTL(endpoint) &= ~0x00200000;
|
353 |
// Push data into the TX FIFO until we don't have anything left or the FIFO would overflow.
|
| 354 |
}
|
354 |
int left = (deptsiz.b.xfersize + 3) >> 2;
|
| 355 |
}
|
355 |
while (left)
|
| 356 |
|
356 |
{
|
| 357 |
void usb_drv_power_up(void)
|
357 |
int words = data->core->inep_regs[ep].dtxfsts.b.txfspcavail;
|
| 358 |
{
|
358 |
if (words > left) words = left;
|
| 359 |
/* Enable USB clock */
|
359 |
if (!words) break;
|
| 360 |
clockgate_enable(CLOCKGATE_USB_1, true);
|
360 |
left -= words;
|
| 361 |
clockgate_enable(CLOCKGATE_USB_2, true);
|
361 |
while (words--) data->core->dfifo[ep][0] = *state->endpoints[ep].txaddr++;
|
| 362 |
PCGCCTL = 0;
|
362 |
}
|
| 363 |
|
363 |
}
|
| 364 |
/* reset the beast */
|
364 |
}
|
| 365 |
usb_reset();
|
365 |
union usb_endpoint_number epnum = { .direction = USB_ENDPOINT_DIRECTION_IN, .number = ep };
|
| 366 |
}
|
366 |
int bytesleft = data->core->inep_regs[ep].dieptsiz.b.xfersize;
|
| 367 |
|
367 |
if (epints.b.timeout) usb_handle_timeout(instance, epnum, bytesleft);
|
| 368 |
void usb_drv_power_down(void)
|
368 |
if (epints.b.xfercompl) usb_handle_xfer_complete(instance, epnum, bytesleft);
|
| 369 |
{
|
369 |
data->core->inep_regs[ep].diepint = epints;
|
| 370 |
DCTL = 0x802; /* Soft Disconnect */
|
370 |
}
|
| 371 |
|
371 |
}
|
| 372 |
OPHYPWR = 0xF; /* PHY: Power down */
|
372 |
|
| 373 |
udelay(10);
|
373 |
if (gintsts.b.outepintr)
|
| 374 |
ORSTCON = 7; /* Put the PHY into reset (needed to get current down) */
|
374 |
{
|
| 375 |
udelay(10);
|
375 |
union synopsysotg_daint daint = data->core->dregs.daint;
|
| 376 |
PCGCCTL = 1; /* Shut down PHY clock */
|
376 |
int ep;
|
| 377 |
|
377 |
for (ep = 0; ep < 16; ep++)
|
| 378 |
clockgate_enable(CLOCKGATE_USB_1, false);
|
378 |
if (daint.ep.out & (1 << ep))
|
| 379 |
clockgate_enable(CLOCKGATE_USB_2, false);
|
379 |
{
|
| 380 |
}
|
380 |
union synopsysotg_doepintn epints = data->core->outep_regs[ep].doepint;
|
| 381 |
|
381 |
union usb_endpoint_number epnum = { .direction = USB_ENDPOINT_DIRECTION_OUT, .number = ep };
|
| 382 |
void usb_check_vbus(void* arg0, void* arg1, void* arg2, void* arg3)
|
382 |
if (epints.b.setup)
|
| 383 |
{
|
383 |
{
|
| 384 |
bool oldstate = false;
|
384 |
if (data->use_dma) invalidate_dcache(instance->buffer, sizeof(instance->buffer));
|
| 385 |
while (true)
|
385 |
synopsysotg_flush_in_endpoint(instance, ep);
|
| 386 |
{
|
386 |
usb_handle_setup_received(instance, epnum);
|
| 387 |
sleep(200000);
|
387 |
}
|
| 388 |
bool newstate = vbus_state();
|
388 |
else if (epints.b.xfercompl)
|
| 389 |
if (oldstate != newstate)
|
389 |
{
|
| 390 |
{
|
390 |
int bytesleft = data->core->inep_regs[ep].dieptsiz.b.xfersize;
|
| 391 |
if (newstate) usb_drv_power_up();
|
391 |
usb_handle_xfer_complete(instance, epnum, bytesleft);
|
| 392 |
else usb_drv_power_down();
|
392 |
}
|
| 393 |
oldstate = newstate;
|
393 |
data->core->outep_regs[ep].doepint = epints;
|
| 394 |
}
|
394 |
}
|
| 395 |
}
|
395 |
}
|
| 396 |
}
|
396 |
|
| 397 |
|
397 |
data->core->gregs.gintsts = gintsts;
|
| 398 |
void usb_drv_init(void)
|
398 |
}
|
| 399 |
{
|
399 |
|
| 400 |
unsigned int i;
|
400 |
void synopsysotg_init(const struct usb_instance* instance)
|
| 401 |
for (i = 0; i < sizeof(endpoints)/sizeof(struct ep_type); i++)
|
401 |
{
|
| 402 |
wakeup_init(&endpoints[i].complete);
|
402 |
int i;
|
| 403 |
|
403 |
|
| 404 |
/* Enable USB clock */
|
404 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| 405 |
clockgate_enable(CLOCKGATE_USB_1, true);
|
405 |
|
| 406 |
clockgate_enable(CLOCKGATE_USB_2, true);
|
406 |
// Disable IRQ during setup
|
| 407 |
PCGCCTL = 0;
|
407 |
synopsysotg_target_disable_irq(instance);
|
| 408 |
|
408 |
|
| 409 |
/* unmask irq */
|
409 |
// Enable OTG clocks
|
| 410 |
interrupt_enable(IRQ_USB_FUNC, true);
|
410 |
synopsysotg_target_enable_clocks(instance);
|
| 411 |
|
411 |
|
| 412 |
thread_create(&synopsysotg_thread_handle, "synopsysotg", usb_check_vbus,
|
412 |
// Enable PHY clocks
|
| 413 |
synopsysotg_stack, sizeof(synopsysotg_stack), OS_THREAD, 63, true,
|
413 |
union synopsysotg_pcgcctl pcgcctl = { .b = {} };
|
| 414 |
NULL, NULL, NULL, NULL);
|
414 |
data->core->pcgcctl = pcgcctl;
|
| 415 |
|
415 |
|
| 416 |
usb_drv_power_down();
|
416 |
// Configure PHY type (must be done before reset)
|
| 417 |
}
|
417 |
union synopsysotg_gccfg gccfg = { .b = { .disablevbussensing = 1, .pwdn = 0 } };
|
| 418 |
|
418 |
data->core->gregs.gccfg = gccfg;
|
| 419 |
void usb_drv_exit(void)
|
419 |
union synopsysotg_gusbcfg gusbcfg = { .b = { .force_dev = 1, .usbtrdtim = SYNOPSYSOTG_TURNAROUND } };
|
| 420 |
{
|
420 |
if (data->phy_16bit) gusbcfg.b.phyif = 1;
|
| 421 |
usb_drv_power_down();
|
421 |
else if (data->phy_ulpi) gusbcfg.b.ulpi_utmi_sel = 1;
|
| 422 |
}
|
422 |
else gusbcfg.b.physel = 1;
|
| 423 |
|
423 |
data->core->gregs.gusbcfg = gusbcfg;
|
| 424 |
int usb_drv_get_max_out_size()
|
424 |
|
| 425 |
{
|
425 |
// Reset the whole USB core
|
| 426 |
return usb_drv_port_speed() ? 262144 : 32768;
|
426 |
union synopsysotg_grstctl grstctl = { .b = { .csftrst = 1 } };
|
| 427 |
}
|
427 |
udelay(100);
|
| 428 |
|
428 |
while (!data->core->gregs.grstctl.b.ahbidle);
|
| 429 |
int usb_drv_get_max_in_size()
|
429 |
data->core->gregs.grstctl = grstctl;
|
| 430 |
{
|
430 |
while (data->core->gregs.grstctl.b.csftrst);
|
| 431 |
return usb_drv_port_speed() ? 262144 : 32768;
|
431 |
while (!data->core->gregs.grstctl.b.ahbidle);
|
| 432 |
}
|
432 |
|
| - |
|
433 |
// Soft disconnect
|
| - |
|
434 |
union synopsysotg_dctl dctl = { .b = { .sftdiscon = 1 } };
|
| - |
|
435 |
data->core->dregs.dctl = dctl;
|
| - |
|
436 |
|
| - |
|
437 |
// Configure the core
|
| - |
|
438 |
union synopsysotg_gahbcfg gahbcfg = { .b = { .dmaenable = data->use_dma, .hburstlen = SYNOPSYSOTG_AHB_BURST_LEN, .glblintrmsk = 1 } };
|
| - |
|
439 |
if (data->disable_double_buffering)
|
| - |
|
440 |
{
|
| - |
|
441 |
gahbcfg.b.nptxfemplvl_txfemplvl = 1;
|
| - |
|
442 |
gahbcfg.b.ptxfemplvl = 1;
|
| - |
|
443 |
}
|
| - |
|
444 |
data->core->gregs.gahbcfg = gahbcfg;
|
| - |
|
445 |
data->core->gregs.gusbcfg = gusbcfg;
|
| - |
|
446 |
gccfg.b.pwdn = 1;
|
| - |
|
447 |
data->core->gregs.gccfg = gccfg;
|
| - |
|
448 |
union synopsysotg_dcfg dcfg = { .b = { .nzstsouthshk = 1 } };
|
| - |
|
449 |
data->core->dregs.dcfg = dcfg;
|
| - |
|
450 |
|
| - |
|
451 |
// Configure the FIFOs
|
| - |
|
452 |
if (data->use_dma)
|
| - |
|
453 |
{
|
| - |
|
454 |
union synopsysotg_dthrctl dthrctl = { .b = { .arb_park_en = 1, .rx_thr_en = 1, .iso_thr_en = 0, .non_iso_thr_en = 0,
|
| - |
|
455 |
.rx_thr_len = SYNOPSYSOTG_AHB_THRESHOLD } };
|
| - |
|
456 |
data->core->dregs.dthrctl = dthrctl;
|
| - |
|
457 |
}
|
| - |
|
458 |
int addr = data->fifosize;
|
| - |
|
459 |
for (i = 0; i < 16; i++)
|
| - |
|
460 |
{
|
| - |
|
461 |
int size = data->txfifosize[i];
|
| - |
|
462 |
addr -= size;
|
| - |
|
463 |
if (size)
|
| - |
|
464 |
{
|
| - |
|
465 |
data->core->inep_regs[i].diepctl.b.nextep = (i + 1) & 0xf;
|
| - |
|
466 |
union synopsysotg_txfsiz fsiz = { .b = { .startaddr = addr, .depth = size } };
|
| - |
|
467 |
if (!i) data->core->gregs.dieptxf0_hnptxfsiz = fsiz;
|
| - |
|
468 |
else data->core->gregs.dieptxf[i - 1] = fsiz;
|
| - |
|
469 |
}
|
| - |
|
470 |
}
|
| - |
|
471 |
union synopsysotg_rxfsiz fsiz = { .b = { .depth = addr } };
|
| - |
|
472 |
data->core->gregs.grxfsiz = fsiz;
|
| - |
|
473 |
|
| - |
|
474 |
// Set up interrupts
|
| - |
|
475 |
union synopsysotg_doepintn doepmsk = { .b = { .xfercompl = 1, .setup = 1 } };
|
| - |
|
476 |
data->core->dregs.doepmsk = doepmsk;
|
| - |
|
477 |
union synopsysotg_diepintn diepmsk = { .b = { .xfercompl = 1, .timeout = 1 } };
|
| - |
|
478 |
data->core->dregs.diepmsk = diepmsk;
|
| - |
|
479 |
data->core->dregs.diepempmsk.d32 = 0;
|
| - |
|
480 |
union synopsysotg_daint daintmsk = { .ep = { .in = 0b0000000000000001, .out = 0b0000000000000001 } };
|
| - |
|
481 |
data->core->dregs.daintmsk = daintmsk;
|
| - |
|
482 |
union synopsysotg_gintmsk gintmsk = { .b = { .usbreset = 1, .enumdone = 1, .outepintr = 1, .inepintr = 1 } };
|
| - |
|
483 |
if (!data->use_dma) gintmsk.b.rxstsqlvl = 1;
|
| - |
|
484 |
data->core->gregs.gintmsk = gintmsk;
|
| - |
|
485 |
synopsysotg_flush_ints(instance);
|
| - |
|
486 |
synopsysotg_target_clear_irq(instance);
|
| - |
|
487 |
synopsysotg_target_enable_irq(instance);
|
| - |
|
488 |
|
| - |
|
489 |
// Soft reconnect
|
| - |
|
490 |
dctl.b.sftdiscon = 0;
|
| - |
|
491 |
data->core->dregs.dctl = dctl;
|
| - |
|
492 |
}
|
| - |
|
493 |
|
| - |
|
494 |
void synopsysotg_exit(const struct usb_instance* instance)
|
| - |
|
495 |
{
|
| - |
|
496 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
| - |
|
497 |
|
| - |
|
498 |
// Soft disconnect
|
| - |
|
499 |
union synopsysotg_dctl dctl = { .b = { .sftdiscon = 1 } };
|
| - |
|
500 |
data->core->dregs.dctl = dctl;
|
| - |
|
501 |
|
| - |
|
502 |
// Disable IRQs
|
| - |
|
503 |
synopsysotg_target_disable_irq(instance);
|
| - |
|
504 |
|
| - |
|
505 |
// Disable clocks
|
| - |
|
506 |
synopsysotg_target_disable_clocks(instance);
|
| - |
|
507 |
}
|
| - |
|
508 |
|
| - |
|
509 |
int synopsysotg_get_max_transfer_size(const struct usb_instance* data, union usb_endpoint_number ep)
|
| - |
|
510 |
{
|
| - |
|
511 |
return 512;
|
| - |
|
512 |
}
|
| - |
|
513 |
|
| - |
|
514 |
const struct usb_driver synopsysotg_driver =
|
| - |
|
515 |
{
|
| - |
|
516 |
.init = synopsysotg_init,
|
| - |
|
517 |
.exit = synopsysotg_exit,
|
| - |
|
518 |
.ep0_start_rx = synopsysotg_ep0_start_rx,
|
| - |
|
519 |
.ep0_start_tx = synopsysotg_ep0_start_tx,
|
| - |
|
520 |
.start_rx = synopsysotg_start_rx,
|
| - |
|
521 |
.start_tx = synopsysotg_start_tx,
|
| - |
|
522 |
.get_stall = synopsysotg_get_stall,
|
| - |
|
523 |
.set_stall = synopsysotg_set_stall,
|
| - |
|
524 |
.set_address = synopsysotg_set_address,
|
| - |
|
525 |
.configure_ep = synopsysotg_configure_ep,
|
| - |
|
526 |
.unconfigure_ep = synopsysotg_unconfigure_ep,
|
| - |
|
527 |
.get_max_transfer_size = synopsysotg_get_max_transfer_size,
|
| - |
|
528 |
};
|
| - |
|
529 |
|