2
* Copyright (c) 2011 Jan Vesely
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
9
* - Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* - Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
* - The name of the author may not be used to endorse or promote products
15
* derived from this software without specific prior written permission.
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
/** @addtogroup drvusbohcihc
32
* @brief OHCI Host controller driver routines
35
#include <str_error.h>
37
#include <libarch/ddi.h>
39
#include <usb/debug.h>
41
#include <usb/ddfiface.h>
44
#include "hcd_endpoint.h"
46
#define OHCI_USED_INTERRUPTS \
47
(I_SO | I_WDH | I_UE | I_RHSC)
48
static int interrupt_emulator(hc_t *instance);
49
static void hc_gain_control(hc_t *instance);
50
static int hc_init_transfer_lists(hc_t *instance);
51
static int hc_init_memory(hc_t *instance);
52
/*----------------------------------------------------------------------------*/
53
/** Announce OHCI root hub to the DDF
55
* @param[in] instance OHCI driver intance
56
* @param[in] hub_fun DDF fuction representing OHCI root hub
59
int hc_register_hub(hc_t *instance, ddf_fun_t *hub_fun)
64
const usb_address_t hub_address =
65
device_keeper_get_free_address(&instance->manager, USB_SPEED_FULL);
66
if (hub_address <= 0) {
67
usb_log_error("Failed(%d) to get OHCI root hub address.\n",
71
instance->rh.address = hub_address;
72
usb_device_keeper_bind(
73
&instance->manager, hub_address, hub_fun->handle);
75
#define CHECK_RET_RELEASE(ret, message...) \
77
usb_log_error(message); \
78
hc_remove_endpoint(instance, hub_address, 0, USB_DIRECTION_BOTH); \
79
usb_device_keeper_release(&instance->manager, hub_address); \
83
int ret = hc_add_endpoint(instance, hub_address, 0, USB_SPEED_FULL,
84
USB_TRANSFER_CONTROL, USB_DIRECTION_BOTH, 64, 0, 0);
85
CHECK_RET_RELEASE(ret, "Failed(%d) to add OHCI rh endpoint 0.\n", ret);
87
char *match_str = NULL;
88
/* DDF needs heap allocated string */
89
ret = asprintf(&match_str, "usb&class=hub");
90
ret = ret > 0 ? 0 : ret;
91
CHECK_RET_RELEASE(ret, "Failed(%d) to create match-id string.\n", ret);
93
ret = ddf_fun_add_match_id(hub_fun, match_str, 100);
94
CHECK_RET_RELEASE(ret, "Failed(%d) add root hub match-id.\n", ret);
96
ret = ddf_fun_bind(hub_fun);
97
CHECK_RET_RELEASE(ret, "Failed(%d) to bind root hub function.\n", ret);
100
#undef CHECK_RET_RELEASE
102
/*----------------------------------------------------------------------------*/
103
/** Initialize OHCI hc driver structure
105
* @param[in] instance Memory place for the structure.
106
* @param[in] regs Address of the memory mapped I/O registers.
107
* @param[in] reg_size Size of the memory mapped area.
108
* @param[in] interrupts True if w interrupts should be used
111
int hc_init(hc_t *instance, uintptr_t regs, size_t reg_size, bool interrupts)
115
#define CHECK_RET_RETURN(ret, message...) \
117
usb_log_error(message); \
121
ret = pio_enable((void*)regs, reg_size, (void**)&instance->registers);
122
CHECK_RET_RETURN(ret,
123
"Failed(%d) to gain access to device registers: %s.\n",
124
ret, str_error(ret));
126
list_initialize(&instance->pending_batches);
127
usb_device_keeper_init(&instance->manager);
128
ret = usb_endpoint_manager_init(&instance->ep_manager,
129
BANDWIDTH_AVAILABLE_USB11);
130
CHECK_RET_RETURN(ret, "Failed to initialize endpoint manager: %s.\n",
133
ret = hc_init_memory(instance);
134
CHECK_RET_RETURN(ret, "Failed to create OHCI memory structures: %s.\n",
136
#undef CHECK_RET_RETURN
138
fibril_mutex_initialize(&instance->guard);
139
hc_gain_control(instance);
141
rh_init(&instance->rh, instance->registers);
144
instance->interrupt_emulator =
145
fibril_create((int(*)(void*))interrupt_emulator, instance);
146
fibril_add_ready(instance->interrupt_emulator);
151
/*----------------------------------------------------------------------------*/
152
/** Create end register endpoint structures
154
* @param[in] instance OHCI driver structure.
155
* @param[in] address USB address of the device.
156
* @param[in] endpoint USB endpoint number.
157
* @param[in] speed Communication speeed of the device.
158
* @param[in] type Endpoint's transfer type.
159
* @param[in] direction Endpoint's direction.
160
* @param[in] mps Maximum packet size the endpoint accepts.
161
* @param[in] size Maximum allowed buffer size.
162
* @param[in] interval Time between transfers(interrupt transfers only).
166
hc_t *instance, usb_address_t address, usb_endpoint_t endpoint,
167
usb_speed_t speed, usb_transfer_type_t type, usb_direction_t direction,
168
size_t mps, size_t size, unsigned interval)
170
endpoint_t *ep = malloc(sizeof(endpoint_t));
174
endpoint_init(ep, address, endpoint, direction, type, speed, mps);
180
hcd_endpoint_t *hcd_ep = hcd_endpoint_assign(ep);
181
if (hcd_ep == NULL) {
182
endpoint_destroy(ep);
186
ret = usb_endpoint_manager_register_ep(&instance->ep_manager, ep, size);
188
hcd_endpoint_clear(ep);
189
endpoint_destroy(ep);
194
switch (ep->transfer_type) {
195
case USB_TRANSFER_CONTROL:
196
instance->registers->control &= ~C_CLE;
197
endpoint_list_add_ep(
198
&instance->lists[ep->transfer_type], hcd_ep);
199
instance->registers->control_current = 0;
200
instance->registers->control |= C_CLE;
202
case USB_TRANSFER_BULK:
203
instance->registers->control &= ~C_BLE;
204
endpoint_list_add_ep(
205
&instance->lists[ep->transfer_type], hcd_ep);
206
instance->registers->control |= C_BLE;
208
case USB_TRANSFER_ISOCHRONOUS:
209
case USB_TRANSFER_INTERRUPT:
210
instance->registers->control &= (~C_PLE & ~C_IE);
211
endpoint_list_add_ep(
212
&instance->lists[ep->transfer_type], hcd_ep);
213
instance->registers->control |= C_PLE | C_IE;
221
/*----------------------------------------------------------------------------*/
222
/** Dequeue and delete endpoint structures
224
* @param[in] instance OHCI hc driver structure.
225
* @param[in] address USB address of the device.
226
* @param[in] endpoint USB endpoint number.
227
* @param[in] direction Direction of the endpoint.
230
int hc_remove_endpoint(hc_t *instance, usb_address_t address,
231
usb_endpoint_t endpoint, usb_direction_t direction)
234
fibril_mutex_lock(&instance->guard);
235
endpoint_t *ep = usb_endpoint_manager_get_ep(&instance->ep_manager,
236
address, endpoint, direction, NULL);
238
usb_log_error("Endpoint unregister failed: No such EP.\n");
239
fibril_mutex_unlock(&instance->guard);
243
hcd_endpoint_t *hcd_ep = hcd_endpoint_get(ep);
246
switch (ep->transfer_type) {
247
case USB_TRANSFER_CONTROL:
248
instance->registers->control &= ~C_CLE;
249
endpoint_list_remove_ep(
250
&instance->lists[ep->transfer_type], hcd_ep);
251
instance->registers->control_current = 0;
252
instance->registers->control |= C_CLE;
254
case USB_TRANSFER_BULK:
255
instance->registers->control &= ~C_BLE;
256
endpoint_list_remove_ep(
257
&instance->lists[ep->transfer_type], hcd_ep);
258
instance->registers->control |= C_BLE;
260
case USB_TRANSFER_ISOCHRONOUS:
261
case USB_TRANSFER_INTERRUPT:
262
instance->registers->control &= (~C_PLE & ~C_IE);
263
endpoint_list_remove_ep(
264
&instance->lists[ep->transfer_type], hcd_ep);
265
instance->registers->control |= C_PLE | C_IE;
270
hcd_endpoint_clear(ep);
272
usb_log_warning("Endpoint without hcd equivalent structure.\n");
274
int ret = usb_endpoint_manager_unregister_ep(&instance->ep_manager,
275
address, endpoint, direction);
276
fibril_mutex_unlock(&instance->guard);
279
/*----------------------------------------------------------------------------*/
280
/** Get access to endpoint structures
282
* @param[in] instance OHCI hc driver structure.
283
* @param[in] address USB address of the device.
284
* @param[in] endpoint USB endpoint number.
285
* @param[in] direction Direction of the endpoint.
286
* @param[out] bw Reserved bandwidth.
289
endpoint_t * hc_get_endpoint(hc_t *instance, usb_address_t address,
290
usb_endpoint_t endpoint, usb_direction_t direction, size_t *bw)
293
fibril_mutex_lock(&instance->guard);
294
endpoint_t *ep = usb_endpoint_manager_get_ep(&instance->ep_manager,
295
address, endpoint, direction, bw);
296
fibril_mutex_unlock(&instance->guard);
299
/*----------------------------------------------------------------------------*/
300
/** Add USB transfer to the schedule.
302
* @param[in] instance OHCI hc driver structure.
303
* @param[in] batch Batch representing the transfer.
304
* @return Error code.
306
int hc_schedule(hc_t *instance, usb_transfer_batch_t *batch)
312
/* Check for root hub communication */
313
if (batch->ep->address == instance->rh.address) {
314
return rh_request(&instance->rh, batch);
317
fibril_mutex_lock(&instance->guard);
318
list_append(&batch->link, &instance->pending_batches);
321
/* Control and bulk schedules need a kick to start working */
322
switch (batch->ep->transfer_type)
324
case USB_TRANSFER_CONTROL:
325
instance->registers->command_status |= CS_CLF;
327
case USB_TRANSFER_BULK:
328
instance->registers->command_status |= CS_BLF;
333
fibril_mutex_unlock(&instance->guard);
336
/*----------------------------------------------------------------------------*/
337
/** Interrupt handling routine
339
* @param[in] instance OHCI hc driver structure.
340
* @param[in] status Value of the status register at the time of interrupt.
342
void hc_interrupt(hc_t *instance, uint32_t status)
345
if ((status & ~I_SF) == 0) /* ignore sof status */
347
usb_log_debug2("OHCI(%p) interrupt: %x.\n", instance, status);
349
rh_interrupt(&instance->rh);
351
if (status & I_WDH) {
352
fibril_mutex_lock(&instance->guard);
353
usb_log_debug2("HCCA: %p-%#" PRIx32 " (%p).\n", instance->hcca,
354
instance->registers->hcca,
355
(void *) addr_to_phys(instance->hcca));
356
usb_log_debug2("Periodic current: %#" PRIx32 ".\n",
357
instance->registers->periodic_current);
359
link_t *current = instance->pending_batches.next;
360
while (current != &instance->pending_batches) {
361
link_t *next = current->next;
362
usb_transfer_batch_t *batch =
363
usb_transfer_batch_from_link(current);
365
if (batch_is_complete(batch)) {
366
list_remove(current);
367
usb_transfer_batch_finish(batch);
371
fibril_mutex_unlock(&instance->guard);
375
hc_start_hw(instance);
379
/*----------------------------------------------------------------------------*/
380
/** Check status register regularly
382
* @param[in] instance OHCI hc driver structure.
385
int interrupt_emulator(hc_t *instance)
388
usb_log_info("Started interrupt emulator.\n");
390
const uint32_t status = instance->registers->interrupt_status;
391
instance->registers->interrupt_status = status;
392
hc_interrupt(instance, status);
397
/*----------------------------------------------------------------------------*/
398
/** Turn off any (BIOS)driver that might be in control of the device.
400
* @param[in] instance OHCI hc driver structure.
402
void hc_gain_control(hc_t *instance)
405
usb_log_debug("Requesting OHCI control.\n");
406
/* Turn off legacy emulation */
407
volatile uint32_t *ohci_emulation_reg =
408
(uint32_t*)((char*)instance->registers + 0x100);
409
usb_log_debug("OHCI legacy register %p: %x.\n",
410
ohci_emulation_reg, *ohci_emulation_reg);
411
/* Do not change A20 state */
412
*ohci_emulation_reg &= 0x100;
413
usb_log_debug("OHCI legacy register %p: %x.\n",
414
ohci_emulation_reg, *ohci_emulation_reg);
416
/* Interrupt routing enabled => smm driver is active */
417
if (instance->registers->control & C_IR) {
418
usb_log_debug("SMM driver: request ownership change.\n");
419
instance->registers->command_status |= CS_OCR;
420
while (instance->registers->control & C_IR) {
423
usb_log_info("SMM driver: Ownership taken.\n");
424
instance->registers->control &= (C_HCFS_RESET << C_HCFS_SHIFT);
429
const unsigned hc_status =
430
(instance->registers->control >> C_HCFS_SHIFT) & C_HCFS_MASK;
431
/* Interrupt routing disabled && status != USB_RESET => BIOS active */
432
if (hc_status != C_HCFS_RESET) {
433
usb_log_debug("BIOS driver found.\n");
434
if (hc_status == C_HCFS_OPERATIONAL) {
435
usb_log_info("BIOS driver: HC operational.\n");
438
/* HC is suspended assert resume for 20ms */
439
instance->registers->control &= (C_HCFS_RESUME << C_HCFS_SHIFT);
441
usb_log_info("BIOS driver: HC resumed.\n");
445
/* HC is in reset (hw startup) => no other driver
446
* maintain reset for at least the time specified in USB spec (50 ms)*/
447
usb_log_info("HC found in reset.\n");
450
/*----------------------------------------------------------------------------*/
451
/** OHCI hw initialization routine.
453
* @param[in] instance OHCI hc driver structure.
455
void hc_start_hw(hc_t *instance)
457
/* OHCI guide page 42 */
459
usb_log_debug2("Started hc initialization routine.\n");
461
/* Save contents of fm_interval register */
462
const uint32_t fm_interval = instance->registers->fm_interval;
463
usb_log_debug2("Old value of HcFmInterval: %x.\n", fm_interval);
466
usb_log_debug2("HC reset.\n");
468
instance->registers->command_status = CS_HCR;
469
while (instance->registers->command_status & CS_HCR) {
473
usb_log_debug2("HC reset complete in %zu us.\n", time);
475
/* Restore fm_interval */
476
instance->registers->fm_interval = fm_interval;
477
assert((instance->registers->command_status & CS_HCR) == 0);
479
/* hc is now in suspend state */
480
usb_log_debug2("HC should be in suspend state(%x).\n",
481
instance->registers->control);
484
instance->registers->hcca = addr_to_phys(instance->hcca);
487
instance->registers->bulk_head =
488
instance->lists[USB_TRANSFER_BULK].list_head_pa;
489
usb_log_debug2("Bulk HEAD set to: %p (%#" PRIx32 ").\n",
490
instance->lists[USB_TRANSFER_BULK].list_head,
491
instance->lists[USB_TRANSFER_BULK].list_head_pa);
493
instance->registers->control_head =
494
instance->lists[USB_TRANSFER_CONTROL].list_head_pa;
495
usb_log_debug2("Control HEAD set to: %p (%#" PRIx32 ").\n",
496
instance->lists[USB_TRANSFER_CONTROL].list_head,
497
instance->lists[USB_TRANSFER_CONTROL].list_head_pa);
500
instance->registers->control |= (C_PLE | C_IE | C_CLE | C_BLE);
501
usb_log_debug2("All queues enabled(%x).\n",
502
instance->registers->control);
504
/* Enable interrupts */
505
instance->registers->interrupt_enable = OHCI_USED_INTERRUPTS;
506
usb_log_debug2("Enabled interrupts: %x.\n",
507
instance->registers->interrupt_enable);
508
instance->registers->interrupt_enable = I_MI;
510
/* Set periodic start to 90% */
511
uint32_t frame_length = ((fm_interval >> FMI_FI_SHIFT) & FMI_FI_MASK);
512
instance->registers->periodic_start = (frame_length / 10) * 9;
513
usb_log_debug2("All periodic start set to: %x(%u - 90%% of %d).\n",
514
instance->registers->periodic_start,
515
instance->registers->periodic_start, frame_length);
517
instance->registers->control &= (C_HCFS_OPERATIONAL << C_HCFS_SHIFT);
518
usb_log_info("OHCI HC up and running(%x).\n",
519
instance->registers->control);
521
/*----------------------------------------------------------------------------*/
522
/** Initialize schedule queues
524
* @param[in] instance OHCI hc driver structure
527
int hc_init_transfer_lists(hc_t *instance)
530
#define SETUP_ENDPOINT_LIST(type) \
532
const char *name = usb_str_transfer_type(type); \
533
int ret = endpoint_list_init(&instance->lists[type], name); \
535
usb_log_error("Failed(%d) to setup %s endpoint list.\n", \
537
endpoint_list_fini(&instance->lists[USB_TRANSFER_ISOCHRONOUS]);\
538
endpoint_list_fini(&instance->lists[USB_TRANSFER_INTERRUPT]); \
539
endpoint_list_fini(&instance->lists[USB_TRANSFER_CONTROL]); \
540
endpoint_list_fini(&instance->lists[USB_TRANSFER_BULK]); \
545
SETUP_ENDPOINT_LIST(USB_TRANSFER_ISOCHRONOUS);
546
SETUP_ENDPOINT_LIST(USB_TRANSFER_INTERRUPT);
547
SETUP_ENDPOINT_LIST(USB_TRANSFER_CONTROL);
548
SETUP_ENDPOINT_LIST(USB_TRANSFER_BULK);
549
#undef SETUP_ENDPOINT_LIST
550
endpoint_list_set_next(&instance->lists[USB_TRANSFER_INTERRUPT],
551
&instance->lists[USB_TRANSFER_ISOCHRONOUS]);
555
/*----------------------------------------------------------------------------*/
556
/** Initialize memory structures used by the OHCI hcd.
558
* @param[in] instance OHCI hc driver structure.
559
* @return Error code.
561
int hc_init_memory(hc_t *instance)
565
bzero(&instance->rh, sizeof(instance->rh));
567
const int ret = hc_init_transfer_lists(instance);
573
instance->hcca = malloc32(sizeof(hcca_t));
574
if (instance->hcca == NULL)
576
bzero(instance->hcca, sizeof(hcca_t));
577
usb_log_debug2("OHCI HCCA initialized at %p.\n", instance->hcca);
580
for (; i < 32; ++i) {
581
instance->hcca->int_ep[i] =
582
instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa;
584
usb_log_debug2("Interrupt HEADs set to: %p (%#" PRIx32 ").\n",
585
instance->lists[USB_TRANSFER_INTERRUPT].list_head,
586
instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa);
588
/* Init interrupt code */
589
instance->interrupt_code.cmds = instance->interrupt_commands;
590
instance->interrupt_code.cmdcount = OHCI_NEEDED_IRQ_COMMANDS;
592
/* Read status register */
593
instance->interrupt_commands[0].cmd = CMD_MEM_READ_32;
594
instance->interrupt_commands[0].dstarg = 1;
595
instance->interrupt_commands[0].addr =
596
(void*)&instance->registers->interrupt_status;
598
/* Test whether we are the interrupt cause */
599
instance->interrupt_commands[1].cmd = CMD_BTEST;
600
instance->interrupt_commands[1].value =
601
OHCI_USED_INTERRUPTS;
602
instance->interrupt_commands[1].srcarg = 1;
603
instance->interrupt_commands[1].dstarg = 2;
605
/* Predicate cleaning and accepting */
606
instance->interrupt_commands[2].cmd = CMD_PREDICATE;
607
instance->interrupt_commands[2].value = 2;
608
instance->interrupt_commands[2].srcarg = 2;
610
/* Write-clean status register */
611
instance->interrupt_commands[3].cmd = CMD_MEM_WRITE_A_32;
612
instance->interrupt_commands[3].srcarg = 1;
613
instance->interrupt_commands[3].addr =
614
(void*)&instance->registers->interrupt_status;
616
/* Accept interrupt */
617
instance->interrupt_commands[4].cmd = CMD_ACCEPT;