/[pearpc]/src/io/usb/usb.cc
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /src/io/usb/usb.cc

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Wed Sep 5 17:11:21 2007 UTC (16 years, 6 months ago) by dpavlin
File size: 11850 byte(s)
import upstream CVS
1 /*
2 * PearPC
3 * usb.cc
4 *
5 * Copyright (C) 2003 Sebastian Biallas (sb@biallas.net)
6 *
7 * References:
8 * [1] OpenHCI - Open Host Controller Interface Specification for USB
9 * Revision 1.0a - hcir1_0a.pdf
10 * [2] Linux USB ohci-driver
11 * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
12 * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License version 2 as
16 * published by the Free Software Foundation.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 */
27
28 #include "debug/tracers.h"
29 #include "system/arch/sysendian.h"
30 #include "io/pci/pci.h"
31 #include "usb.h"
32
33 #include <cstring>
34
35 #define NUM_INTS 32 /* part of the OHCI standard */
36 #define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */
37
38 struct ohci_hcca {
39 uint32 int_table[NUM_INTS]; /* Interrupt ED table */
40 uint16 frame_no; /* current frame number */
41 uint16 pad1; /* set to 0 on each frame_no change */
42 uint32 done_head; /* info returned for an interrupt */
43 uint8 reserved_for_hc[116];
44 } PACKED;
45
46 // [1].122
47 #define OHCI_REG_REVISION 0x00 // [1].123
48 #define OHCI_REG_CONTROL 0x04 // [1].123
49 #define OHCI_REG_CMDSTATUS 0x08 // [1].126
50 #define OHCI_REG_INTRSTATUS 0x0c // [1].126
51 #define OHCI_REG_INTRENABLE 0x10 // [1].126
52 #define OHCI_REG_INTRDISABLE 0x14 // [1].126
53 #define OHCI_REG_HCCA 0x18 // [1].126
54 #define OHCI_REG_ED_PERIODCUR 0x1c // [1].126
55 #define OHCI_REG_ED_CONTROL_HD 0x20 // [1].126
56 #define OHCI_REG_ED_CONTROL_CUR 0x24 // [1].126
57 #define OHCI_REG_ED_BULK_HD 0x28 // [1].126
58 #define OHCI_REG_ED_BULK_CUR 0x2c // [1].126
59 #define OHCI_REG_DONEHEAD 0x30 // [1].126
60 #define OHCI_REG_FMINTERVAL 0x34 // [1].126
61 #define OHCI_REG_FMREMAIN 0x38 // [1].126
62 #define OHCI_REG_FMNUMBER 0x3c // [1].126
63 #define OHCI_REG_PERIODICSTART 0x40 // [1].126
64 #define OHCI_REG_LSTHRESH 0x44 // [1].126
65 #define OHCI_REG_ROOTHUB_A 0x48
66 #define OHCI_REG_ROOTHUB_B 0x4c
67 #define OHCI_REG_ROOTHUB_STAT 0x50
68 #define OHCI_REG_ROOTHUB_PORTS 0x54
69
70 /*
71 * bits in ohci_hcregs.control
72 */
73 #define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
74 #define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
75 #define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
76 #define OHCI_CTRL_CLE (1 << 4) /* control list enable */
77 #define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
78 #define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
79 #define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
80 #define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
81 #define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
82
83 /* pre-shifted values for HCFS */
84 # define OHCI_USB_RESET (0 << 6)
85 # define OHCI_USB_RESUME (1 << 6)
86 # define OHCI_USB_OPER (2 << 6)
87 # define OHCI_USB_SUSPEND (3 << 6)
88
89 /*
90 * bits in ohci_hcregs.{intrstatus|intrenable|intrdisable}
91 */
92 #define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
93 #define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
94 #define OHCI_INTR_SF (1 << 2) /* start frame */
95 #define OHCI_INTR_RD (1 << 3) /* resume detect */
96 #define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
97 #define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
98 #define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
99 #define OHCI_INTR_OC (1 << 30) /* ownership change */
100 #define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
101
102 /*
103 * bits in ohci_hcregs.cmdstatus
104 */
105 #define OHCI_HCR (1 << 0) /* host controller reset */
106 #define OHCI_CLF (1 << 1) /* control list filled */
107 #define OHCI_BLF (1 << 2) /* bulk list filled */
108 #define OHCI_OCR (1 << 3) /* ownership change request */
109 #define OHCI_SOC (3 << 16) /* scheduling overrun count */
110
111 /*
112 * bits in ohci_hcregs.roothub.portstatus [1].142
113 */
114 #define OHCI_RH_PS_CCS (1 << 0) /* current connect status */
115 #define OHCI_RH_PS_PES (1 << 1) /* port enable status */
116 #define OHCI_RH_PS_PSS (1 << 2) /* port suspend status */
117 #define OHCI_RH_PS_POCI (1 << 3) /* port overrun current indicator */
118 #define OHCI_RH_PS_PRS (1 << 4) /* port reset status */
119 #define OHCI_RH_PS_PPS (1 << 8) /* port power status */
120 #define OHCI_RH_PS_LSDA (1 << 8) /* low speed device attached */
121 #define OHCI_RH_PS_CSC (1 << 16) /* connect status change */
122 #define OHCI_RH_PS_PESC (1 << 17) /* port enable status change */
123 #define OHCI_RH_PS_PSSC (1 << 18) /* port suspend status change */
124 #define OHCI_RH_PS_OCIC (1 << 19) /* overrun current indicator change */
125 #define OHCI_RH_PS_PRSC (1 << 20) /* port reset status change */
126
127 struct ohci_hcregs {
128 /* control and status registers */
129 uint32 control;
130 uint32 cmdstatus;
131 uint32 intrstatus;
132 uint32 intrenable;
133 uint32 intrdisable;
134 /* memory pointers */
135 uint32 hcca;
136 uint32 ed_periodcurrent;
137 uint32 ed_controlhead;
138 uint32 ed_controlcurrent;
139 uint32 ed_bulkhead;
140 uint32 ed_bulkcurrent;
141 uint32 donehead;
142 /* frame counters */
143 uint32 fminterval;
144 uint32 fmremaining;
145 uint32 fmnumber;
146 uint32 periodicstart;
147 uint32 lsthresh;
148 /* Root hub ports */
149 struct ohci_roothub_regs {
150 uint32 a;
151 uint32 b;
152 uint32 status;
153 uint32 portstatus[MAX_ROOT_PORTS];
154 } roothub;
155 };
156
157 static inline const char *hc_regname(uint32 a)
158 {
159 a >>= 2;
160 if (a > 20) return "unknown";
161 char *names[] = {"revision","control","cmdstatus","intrstatus","intrenable",
162 "intrdisable","hcca","ed_periodcurrent","ed_controlhead","ed_controlcurrent",
163 "ed_bulkhead","ed_bulkcurrent","donehead","fminterval","fmremaining",
164 "fmnumber","periodicstart", "lsthresh", "roothub.a", "roothub.b", "roothub.status"};
165 return names[a];
166 }
167
168 extern bool gSinglestep;
169
170 /*
171 *
172 */
173 class PCI_USB: public PCI_Device {
174 public:
175 ohci_hcregs hcregs;
176 uint rootport_count;
177
178 PCI_USB()
179 :PCI_Device("pci-usb", 0x01, 0x06)
180 {
181 mIORegSize[0] = 0x1000;
182 mIORegType[0] = PCI_ADDRESS_SPACE_MEM;
183
184 mConfig[0x00] = 0x45; // vendor ID
185 mConfig[0x01] = 0x10;
186 mConfig[0x02] = 0x61; // unit ID
187 mConfig[0x03] = 0xc8;
188
189 mConfig[0x08] = 0x10; // revision
190 mConfig[0x09] = 0x10; //
191 mConfig[0x0a] = 0x03; //
192 mConfig[0x0b] = 0x0c; //
193
194 mConfig[0x0e] = 0x00; // header-type
195
196 assignMemAddress(0, 0x80881000);
197
198 mConfig[0x3c] = 0x03;
199 mConfig[0x3d] = 0x03;
200 mConfig[0x3e] = 0x03;
201 mConfig[0x3f] = 0x03;
202
203 rootport_count = 1;
204 reset();
205 }
206
207 void reset()
208 {
209 memset(&hcregs, 0, sizeof hcregs);
210 hcregs.fminterval = 0x2edf; // [1].134
211 hcregs.lsthresh = 0x628; // [1].137
212 hcregs.roothub.a = 0 // [1].138
213 | (1<<12) // No overcurrent protection supported
214 | (0<<10) // always 0
215 | (1<<9) // Ports are always powered on when the HC is powered on
216 | (0<<8) // all ports are powered at the same time.
217 | rootport_count; // number of rootports
218 // hcregs.roothub.portstatus[0] = ;
219 }
220
221 virtual bool readDeviceMem(uint r, uint32 address, uint32 &data, uint size)
222 {
223 if (r != 0) return false;
224 if (size != 4) return false;
225 IO_USB_TRACE("read(r=%d, a=%08x (%s), %d)\n", r, address, hc_regname(address), size);
226
227 switch (address) {
228 case OHCI_REG_REVISION:
229 // [1].123
230 data = 0x10;
231 break;
232 case OHCI_REG_CONTROL:
233 data = hcregs.control;
234 break;
235 case OHCI_REG_CMDSTATUS:
236 data = hcregs.cmdstatus;
237 break;
238 case OHCI_REG_INTRSTATUS:
239 data = hcregs.intrstatus;
240 break;
241 case OHCI_REG_INTRENABLE:
242 data = hcregs.intrenable;
243 break;
244 case OHCI_REG_INTRDISABLE:
245 data = hcregs.intrdisable;
246 break;
247 case OHCI_REG_HCCA:
248 data = hcregs.hcca;
249 break;
250 case OHCI_REG_ED_PERIODCUR:
251 data = hcregs.ed_periodcurrent;
252 break;
253 case OHCI_REG_ED_CONTROL_HD:
254 data = hcregs.ed_controlhead;
255 break;
256 case OHCI_REG_ED_CONTROL_CUR:
257 data = hcregs.ed_controlcurrent;
258 break;
259 case OHCI_REG_ED_BULK_HD:
260 data = hcregs.ed_bulkhead;
261 break;
262 case OHCI_REG_ED_BULK_CUR:
263 data = hcregs.ed_bulkcurrent;
264 break;
265 case OHCI_REG_DONEHEAD:
266 data = hcregs.donehead;
267 break;
268 case OHCI_REG_FMINTERVAL:
269 data = hcregs.fminterval;
270 break;
271 case OHCI_REG_FMREMAIN:
272 data = hcregs.fmremaining;
273 break;
274 case OHCI_REG_FMNUMBER:
275 data = hcregs.fmnumber;
276 break;
277 case OHCI_REG_PERIODICSTART:
278 data = hcregs.periodicstart;
279 break;
280 case OHCI_REG_LSTHRESH:
281 data = hcregs.lsthresh;
282 break;
283 case OHCI_REG_ROOTHUB_A:
284 data = hcregs.roothub.a;
285 break;
286 case OHCI_REG_ROOTHUB_B:
287 data = hcregs.roothub.b;
288 break;
289 case OHCI_REG_ROOTHUB_STAT:
290 data = hcregs.roothub.status;
291 break;
292 default:
293 address -= OHCI_REG_ROOTHUB_PORTS;
294 address >>= 2;
295 if (address < rootport_count) {
296 data = hcregs.roothub.portstatus[address];
297 break;
298 }
299 return false;
300 }
301
302 // gSinglestep = true;
303 return true;
304 }
305
306 virtual bool writeDeviceMem(uint r, uint32 address, uint32 data, uint size)
307 {
308 if (r != 0) return false;
309 if (size != 4) return false;
310 IO_USB_TRACE("write(r=%d, a=%08x (%s), data=%08x, %d)\n", r, address, hc_regname(address), data, size);
311
312 switch (address) {
313 case OHCI_REG_REVISION:
314 // [1].123
315 IO_USB_WARN("revision is read only.\n");
316 return true;
317 case OHCI_REG_CONTROL:
318 hcregs.control = data;
319 break;
320 case OHCI_REG_CMDSTATUS:
321 if (data & OHCI_HCR) {
322 reset();
323 return true;
324 }
325 hcregs.cmdstatus = data;
326 break;
327 case OHCI_REG_INTRSTATUS:
328 hcregs.intrstatus = data;
329 break;
330 case OHCI_REG_INTRENABLE:
331 hcregs.intrenable = data;
332 break;
333 case OHCI_REG_INTRDISABLE:
334 hcregs.intrdisable = data;
335 break;
336 case OHCI_REG_HCCA:
337 hcregs.hcca = data;
338 break;
339 case OHCI_REG_ED_PERIODCUR:
340 hcregs.ed_periodcurrent = data;
341 break;
342 case OHCI_REG_ED_CONTROL_HD:
343 hcregs.ed_controlhead = data;
344 break;
345 case OHCI_REG_ED_CONTROL_CUR:
346 hcregs.ed_controlcurrent = data;
347 break;
348 case OHCI_REG_ED_BULK_HD:
349 hcregs.ed_bulkhead = data;
350 break;
351 case OHCI_REG_ED_BULK_CUR:
352 hcregs.ed_bulkcurrent = data;
353 break;
354 case OHCI_REG_DONEHEAD:
355 hcregs.donehead = data;
356 break;
357 case OHCI_REG_FMINTERVAL:
358 hcregs.fminterval = data;
359 break;
360 case OHCI_REG_FMREMAIN:
361 hcregs.fmremaining = data;
362 break;
363 case OHCI_REG_FMNUMBER:
364 hcregs.fmnumber = data;
365 break;
366 case OHCI_REG_PERIODICSTART:
367 hcregs.periodicstart = data;
368 break;
369 case OHCI_REG_LSTHRESH:
370 hcregs.lsthresh = data;
371 break;
372 case OHCI_REG_ROOTHUB_A:
373 hcregs.roothub.a = data;
374 break;
375 case OHCI_REG_ROOTHUB_B:
376 hcregs.roothub.b = data;
377 break;
378 case OHCI_REG_ROOTHUB_STAT:
379 hcregs.roothub.status = data;
380 break;
381 default:
382 address -= OHCI_REG_ROOTHUB_PORTS;
383 address >>= 2;
384 if (address < rootport_count) {
385 if (data & OHCI_RH_PS_CCS) {
386 // writing 1 to CCS clears PES
387 hcregs.roothub.portstatus[address] &= ~OHCI_RH_PS_PES;
388 }
389
390 // writing 1 to these bits clears them
391 hcregs.roothub.portstatus[address] &= ~(data &
392 (OHCI_RH_PS_CSC | OHCI_RH_PS_PESC | OHCI_RH_PS_PSSC
393 | OHCI_RH_PS_OCIC | OHCI_RH_PS_PRSC));
394
395 // writing 1 to these bits set them only if CCS is set
396 uint32 p = data & OHCI_RH_PS_PES | OHCI_RH_PS_PSS | OHCI_RH_PS_PRS;
397 if (p) {
398 if (hcregs.roothub.portstatus[address] & OHCI_RH_PS_CCS) {
399 hcregs.roothub.portstatus[address] |= p;
400 } else {
401 // attempt to enable/suspend/reset disconnected port
402 hcregs.roothub.portstatus[address] |= OHCI_RH_PS_CSC;
403 }
404 }
405
406 // we dont support power switching
407 hcregs.roothub.portstatus[address] |= OHCI_RH_PS_PPS;
408 break;
409 }
410 }
411
412 // gSinglestep = true;
413 return true;
414 }
415
416 };
417
418
419 #include "configparser.h"
420
421 #define USB_KEY_INSTALLED "pci_usb_installed"
422
423 void usb_init()
424 {
425 if (gConfig->getConfigInt(USB_KEY_INSTALLED)) {
426 gPCI_Devices->insert(new PCI_USB());
427 }
428 }
429
430 void usb_done()
431 {
432 }
433
434 void usb_init_config()
435 {
436 gConfig->acceptConfigEntryIntDef(USB_KEY_INSTALLED, 0);
437 }

  ViewVC Help
Powered by ViewVC 1.1.26