summaryrefslogtreecommitdiff
path: root/utils/hwstub/stub/jz4760b/usb_drv_jz4760b.c
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2017-01-24 15:22:27 +0100
committerAmaury Pouly <amaury.pouly@gmail.com>2017-01-24 15:22:27 +0100
commitcc2389b7a61784c229b42da4abbc238a42e5173d (patch)
tree0aa51262daad31017d8ec19124f05638469b5270 /utils/hwstub/stub/jz4760b/usb_drv_jz4760b.c
parentd7c71a3fe80150ecc1196e34b55d1fdd1323057a (diff)
downloadrockbox-cc2389b7a61784c229b42da4abbc238a42e5173d.tar.gz
rockbox-cc2389b7a61784c229b42da4abbc238a42e5173d.zip
hwstub: add jz4760b stub
The stub is quite versatile: it can be loaded using bootrom or another other means (like factory boot on Fiio X1). It relocates itself to TCSM0 and provides basic functionality (it does not recover from failed read/writes at the moment). Change-Id: Ib646a4b43fba9358d6f93f0f73a5c2e9bcd775a7
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}