summaryrefslogtreecommitdiff
path: root/utils/hwstub/stub/jz4760b/usb_drv_jz4760b.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hwstub/stub/jz4760b/usb_drv_jz4760b.c')
-rw-r--r--utils/hwstub/stub/jz4760b/usb_drv_jz4760b.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/utils/hwstub/stub/jz4760b/usb_drv_jz4760b.c b/utils/hwstub/stub/jz4760b/usb_drv_jz4760b.c
new file mode 100644
index 0000000000..92880c5303
--- /dev/null
+++ b/utils/hwstub/stub/jz4760b/usb_drv_jz4760b.c
@@ -0,0 +1,240 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2014 by Marcin Bukat
10 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "usb_drv.h"
23#include "config.h"
24#include "memory.h"
25#include "target.h"
26#include "jz4760b.h"
27
28static void udc_reset(void)
29{
30 REG_USB_FADDR = 0;
31 /* Reset EP0 */
32 REG_USB_INDEX = 0;
33 REG_USB_CSR0 = USB_CSR0_FLUSHFIFO | USB_CSR0_SVDOUTPKTRDY | USB_CSR0_SVDSETUPEND; /* clear setupend and rxpktrdy */
34 REG_USB_POWER = USB_POWER_SOFTCONN | USB_POWER_HSENAB | USB_POWER_SUSPENDM;
35}
36
37void usb_drv_init(void)
38{
39 /* in case usb is running, soft disconnect */
40 REG_USB_POWER &= ~USB_POWER_SOFTCONN;
41 /* A delay seems necessary to avoid causing havoc. The USB spec says disconnect
42 * detection time (T_DDIS) is around 2us but in practice many hubs might
43 * require more. */
44 target_mdelay(1);
45 /* disable usb */
46 REG_CPM_CLKGR0 |= CLKGR0_OTG;
47 /* power up usb: assume EXCLK=12Mhz */
48 REG_CPM_CPCCR &= ~CPCCR_ECS; /* use EXCLK as source (and not EXCLK/2) */
49 REG_CPM_USBCDR = 0; /* use EXCLK as source, no divisor */
50 REG_CPM_CPCCR |= CPCCR_CE; /* change source now */
51 /* wait for stable clock */
52 target_udelay(3);
53 /* enable usb */
54 REG_CPM_CLKGR0 &= ~CLKGR0_OTG;
55 /* tweak various parameters */
56 REG_CPM_USBVBFIL = 0x80;
57 REG_CPM_USBRDT = 0x96;
58 REG_CPM_USBRDT |= (1 << 25);
59 REG_CPM_USBPCR &= ~0x3f;
60 REG_CPM_USBPCR |= 0x35;
61 REG_CPM_USBPCR &= ~USBPCR_USB_MODE;
62 REG_CPM_USBPCR |= USBPCR_VBUSVLDEXT;
63 /* reset otg phy */
64 REG_CPM_USBPCR |= USBPCR_POR;
65 target_udelay(30);
66 REG_CPM_USBPCR &= ~USBPCR_POR;
67 target_udelay(300);
68 /* enable otg phy */
69 REG_CPM_OPCR |= OPCR_OTGPHY_ENABLE;
70 /* wait for stable phy */
71 target_udelay(300);
72 /* reset */
73 udc_reset();
74}
75
76static void *read_fifo0(void *dst, unsigned size)
77{
78 unsigned char *p = dst;
79 while(size-- > 0)
80 *p++ = *(volatile uint8_t *)USB_FIFO_EP(0);
81 return p;
82}
83
84static void *write_fifo0(void *src, unsigned size)
85{
86 unsigned char *p = src;
87 while(size-- > 0)
88 *(volatile uint8_t *)USB_FIFO_EP(0) = *p++;
89 return p;
90}
91
92/* NOTE: this core is a bit weird, it handles the status stage automatically
93 * as soon as DataEnd is written to CSR. The problem is that DataEnd needs
94 * to be written as part of a read (INPKTRDY) or write (SVDOUTPKTRDY) request
95 * but not on its own.
96 * Thus the design is follows: after receiving the setup packet, we DO NOT
97 * acknowledge it with SVDOUTPKTRDY. Instead it will be acknowledged
98 * either as part of STALL or recv/send. If there is an OUT data stage, we use
99 * a similar trick: we do not acknowledge the last packet and leave a pending
100 * SVDOUTPKTRDY to be done as part of a final STALL or ZLP. */
101
102int usb_drv_recv_setup(struct usb_ctrlrequest *req)
103{
104 while(1)
105 {
106 unsigned intr = REG_USB_INTRUSB;
107 unsigned intrin = REG_USB_INTRIN;
108 /* handle reset */
109 if(intr & USB_INTR_RESET)
110 {
111 udc_reset();
112 continue;
113 }
114 /* ignore anything but EP0 irq */
115 if(!(intrin & 1))
116 continue;
117 /* select EP0 */
118 REG_USB_INDEX = 0;
119 /* load csr to examine the cause of the interrupt */
120 unsigned csr0 = REG_USB_CSR0;
121 /* wait setup: we expect to receive a packet */
122 if(csr0 & USB_CSR0_OUTPKTRDY)
123 {
124 unsigned cnt = REG_USB_COUNT0;
125 /* anything other than 8-byte is wrong */
126 if(cnt == 8)
127 {
128 read_fifo0(req, 8);
129 /* DO NOT acknowledge the packet, leave this to recv/send/stall */
130 return 0;
131 }
132 }
133 }
134 return 0;
135}
136
137int usb_drv_port_speed(void)
138{
139 return (REG_USB_POWER & USB_POWER_HSMODE) ? 1 : 0;
140}
141
142void usb_drv_set_address(int address)
143{
144 REG_USB_FADDR = address;
145}
146
147int usb_drv_send(int endpoint, void *ptr, int length)
148{
149 (void) endpoint;
150 /* select EP0 */
151 REG_USB_INDEX = 0;
152 /* clear packet ready for the PREVIOUS packet: warning, there is a trap here!
153 * if we are clearing without sending anything (length=0) then we must
154 * set DataEnd at the same time. Otherwise, we must set it by itself and then
155 * send data */
156 if(length > 0)
157 {
158 /* clear packet ready for the PREVIOUS packet (SETUP) */
159 REG_USB_CSR0 |= USB_CSR0_SVDOUTPKTRDY;
160 /* send data */
161 do
162 {
163 unsigned csr = REG_USB_CSR0;
164 /* write data */
165 int cnt = MIN(length, 64);
166 ptr = write_fifo0(ptr, cnt);
167 length -= cnt;
168 csr |= USB_CSR0_INPKTRDY;
169 /* last packet ? */
170 if(length == 0)
171 csr |= USB_CSR0_DATAEND;
172 /* write csr */
173 REG_USB_CSR0 = csr;
174 /* wait for packet to be transmitted */
175 while(REG_USB_CSR0 & USB_CSR0_INPKTRDY) {}
176 }while(length > 0);
177 }
178 else
179 {
180 /* clear packet ready for the PREVIOUS packet (SETUP or DATA) and finish */
181 REG_USB_CSR0 |= USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND;
182 }
183 /* wait until acknowledgement */
184 while(REG_USB_CSR0 & USB_CSR0_DATAEND) {}
185 return 0;
186}
187
188int usb_drv_recv(int endpoint, void* ptr, int length)
189{
190 (void) endpoint;
191 int old_len = length;
192 /* select EP0 */
193 REG_USB_INDEX = 0;
194 /* ZLP: ignore receive, the core does it automatically on DataEnd */
195 if(length == 0)
196 return 0;
197 /* receive data
198 * NOTE when we are called here, there is a pending SVDOUTPKTRDY to
199 * be done (see note above usb_drv_recv_setup), and when we will finish,
200 * we will also leave a pending SVDOUTPKTRDY to be done in stall or send */
201 while(length > 0)
202 {
203 /* clear packet ready for the PREVIOUS packet */
204 REG_USB_CSR0 |= USB_CSR0_SVDOUTPKTRDY;
205 /* wait for data */
206 while(!(REG_USB_CSR0 & USB_CSR0_OUTPKTRDY)) {}
207 int cnt = REG_USB_COUNT0;
208 /* clamp just in case */
209 cnt = MIN(cnt, length);
210 /* read fifo */
211 ptr = read_fifo0(ptr, cnt);
212 length -= cnt;
213 }
214 /* there is still a pending SVDOUTPKTRDY here */
215 return old_len;
216}
217
218void usb_drv_stall(int endpoint, bool stall, bool in)
219{
220 (void) endpoint;
221 (void) in;
222 if(!stall)
223 return; /* EP0 unstall automatically */
224 /* select EP0 */
225 REG_USB_INDEX = 0;
226 /* set stall */
227 REG_USB_CSR0 |= USB_CSR0_SVDOUTPKTRDY | USB_CSR0_SENDSTALL;
228}
229
230void usb_drv_exit(void)
231{
232 /* in case usb is running, soft disconnect */
233 REG_USB_POWER &= ~USB_POWER_SOFTCONN;
234 /* A delay seems necessary to avoid causing havoc. The USB spec says disconnect
235 * detection time (T_DDIS) is around 2us but in practice many hubs might
236 * require more. */
237 target_mdelay(1);
238 /* disable usb */
239 REG_CPM_CLKGR0 |= CLKGR0_OTG;
240}