~martin-decky/helenos/rcu

« back to all changes in this revision

Viewing changes to uspace/drv/ohci/hc.c

  • Committer: Jakub Jermar
  • Date: 2011-06-07 21:31:35 UTC
  • mfrom: (708.2.1 mainline)
  • Revision ID: jakub@jermar.eu-20110607213135-cxz8vhxq21pij1gb
Merge USB support.

Changes from bzr://helenos-usb.bzr.sourceforge.net/bzrroot/helenos-usb/mainline:

- replaced '-' with '_' in new driver names
- USB libs are built for each architecture
- devman starts early
- sys_thread_udelay() uses generic delay()
- sys_as_create_area() now creates cacheable areas by default

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2011 Jan Vesely
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
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.
 
16
 *
 
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.
 
27
 */
 
28
/** @addtogroup drvusbohcihc
 
29
 * @{
 
30
 */
 
31
/** @file
 
32
 * @brief OHCI Host controller driver routines
 
33
 */
 
34
#include <errno.h>
 
35
#include <str_error.h>
 
36
#include <adt/list.h>
 
37
#include <libarch/ddi.h>
 
38
 
 
39
#include <usb/debug.h>
 
40
#include <usb/usb.h>
 
41
#include <usb/ddfiface.h>
 
42
 
 
43
#include "hc.h"
 
44
#include "hcd_endpoint.h"
 
45
 
 
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
 
54
 *
 
55
 * @param[in] instance OHCI driver intance
 
56
 * @param[in] hub_fun DDF fuction representing OHCI root hub
 
57
 * @return Error code
 
58
 */
 
59
int hc_register_hub(hc_t *instance, ddf_fun_t *hub_fun)
 
60
{
 
61
        assert(instance);
 
62
        assert(hub_fun);
 
63
 
 
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",
 
68
                    hub_address);
 
69
                return hub_address;
 
70
        }
 
71
        instance->rh.address = hub_address;
 
72
        usb_device_keeper_bind(
 
73
            &instance->manager, hub_address, hub_fun->handle);
 
74
 
 
75
#define CHECK_RET_RELEASE(ret, message...) \
 
76
if (ret != EOK) { \
 
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); \
 
80
        return ret; \
 
81
} else (void)0
 
82
 
 
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);
 
86
 
 
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);
 
92
 
 
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);
 
95
 
 
96
        ret = ddf_fun_bind(hub_fun);
 
97
        CHECK_RET_RELEASE(ret, "Failed(%d) to bind root hub function.\n", ret);
 
98
 
 
99
        return EOK;
 
100
#undef CHECK_RET_RELEASE
 
101
}
 
102
/*----------------------------------------------------------------------------*/
 
103
/** Initialize OHCI hc driver structure
 
104
 *
 
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
 
109
 * @return Error code
 
110
 */
 
111
int hc_init(hc_t *instance, uintptr_t regs, size_t reg_size, bool interrupts)
 
112
{
 
113
        assert(instance);
 
114
        int ret = EOK;
 
115
#define CHECK_RET_RETURN(ret, message...) \
 
116
if (ret != EOK) { \
 
117
        usb_log_error(message); \
 
118
        return ret; \
 
119
} else (void)0
 
120
 
 
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));
 
125
 
 
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",
 
131
            str_error(ret));
 
132
 
 
133
        ret = hc_init_memory(instance);
 
134
        CHECK_RET_RETURN(ret, "Failed to create OHCI memory structures: %s.\n",
 
135
            str_error(ret));
 
136
#undef CHECK_RET_RETURN
 
137
 
 
138
        fibril_mutex_initialize(&instance->guard);
 
139
        hc_gain_control(instance);
 
140
 
 
141
        rh_init(&instance->rh, instance->registers);
 
142
 
 
143
        if (!interrupts) {
 
144
                instance->interrupt_emulator =
 
145
                    fibril_create((int(*)(void*))interrupt_emulator, instance);
 
146
                fibril_add_ready(instance->interrupt_emulator);
 
147
        }
 
148
 
 
149
        return EOK;
 
150
}
 
151
/*----------------------------------------------------------------------------*/
 
152
/** Create end register endpoint structures
 
153
 *
 
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).
 
163
 * @return Error code
 
164
 */
 
165
int hc_add_endpoint(
 
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)
 
169
{
 
170
        endpoint_t *ep = malloc(sizeof(endpoint_t));
 
171
        if (ep == NULL)
 
172
                return ENOMEM;
 
173
        int ret =
 
174
            endpoint_init(ep, address, endpoint, direction, type, speed, mps);
 
175
        if (ret != EOK) {
 
176
                free(ep);
 
177
                return ret;
 
178
        }
 
179
 
 
180
        hcd_endpoint_t *hcd_ep = hcd_endpoint_assign(ep);
 
181
        if (hcd_ep == NULL) {
 
182
                endpoint_destroy(ep);
 
183
                return ENOMEM;
 
184
        }
 
185
 
 
186
        ret = usb_endpoint_manager_register_ep(&instance->ep_manager, ep, size);
 
187
        if (ret != EOK) {
 
188
                hcd_endpoint_clear(ep);
 
189
                endpoint_destroy(ep);
 
190
                return ret;
 
191
        }
 
192
 
 
193
        /* Enqueue hcd_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;
 
201
                break;
 
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;
 
207
                break;
 
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;
 
214
                break;
 
215
        default:
 
216
                break;
 
217
        }
 
218
 
 
219
        return EOK;
 
220
}
 
221
/*----------------------------------------------------------------------------*/
 
222
/** Dequeue and delete endpoint structures
 
223
 *
 
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.
 
228
 * @return Error code
 
229
 */
 
230
int hc_remove_endpoint(hc_t *instance, usb_address_t address,
 
231
    usb_endpoint_t endpoint, usb_direction_t direction)
 
232
{
 
233
        assert(instance);
 
234
        fibril_mutex_lock(&instance->guard);
 
235
        endpoint_t *ep = usb_endpoint_manager_get_ep(&instance->ep_manager,
 
236
            address, endpoint, direction, NULL);
 
237
        if (ep == NULL) {
 
238
                usb_log_error("Endpoint unregister failed: No such EP.\n");
 
239
                fibril_mutex_unlock(&instance->guard);
 
240
                return ENOENT;
 
241
        }
 
242
 
 
243
        hcd_endpoint_t *hcd_ep = hcd_endpoint_get(ep);
 
244
        if (hcd_ep) {
 
245
                /* Dequeue hcd_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;
 
253
                        break;
 
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;
 
259
                        break;
 
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;
 
266
                        break;
 
267
                default:
 
268
                        break;
 
269
                }
 
270
                hcd_endpoint_clear(ep);
 
271
        } else {
 
272
                usb_log_warning("Endpoint without hcd equivalent structure.\n");
 
273
        }
 
274
        int ret = usb_endpoint_manager_unregister_ep(&instance->ep_manager,
 
275
            address, endpoint, direction);
 
276
        fibril_mutex_unlock(&instance->guard);
 
277
        return ret;
 
278
}
 
279
/*----------------------------------------------------------------------------*/
 
280
/** Get access to endpoint structures
 
281
 *
 
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.
 
287
 * @return Error code
 
288
 */
 
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)
 
291
{
 
292
        assert(instance);
 
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);
 
297
        return ep;
 
298
}
 
299
/*----------------------------------------------------------------------------*/
 
300
/** Add USB transfer to the schedule.
 
301
 *
 
302
 * @param[in] instance OHCI hc driver structure.
 
303
 * @param[in] batch Batch representing the transfer.
 
304
 * @return Error code.
 
305
 */
 
306
int hc_schedule(hc_t *instance, usb_transfer_batch_t *batch)
 
307
{
 
308
        assert(instance);
 
309
        assert(batch);
 
310
        assert(batch->ep);
 
311
 
 
312
        /* Check for root hub communication */
 
313
        if (batch->ep->address == instance->rh.address) {
 
314
                return rh_request(&instance->rh, batch);
 
315
        }
 
316
 
 
317
        fibril_mutex_lock(&instance->guard);
 
318
        list_append(&batch->link, &instance->pending_batches);
 
319
        batch_commit(batch);
 
320
 
 
321
        /* Control and bulk schedules need a kick to start working */
 
322
        switch (batch->ep->transfer_type)
 
323
        {
 
324
        case USB_TRANSFER_CONTROL:
 
325
                instance->registers->command_status |= CS_CLF;
 
326
                break;
 
327
        case USB_TRANSFER_BULK:
 
328
                instance->registers->command_status |= CS_BLF;
 
329
                break;
 
330
        default:
 
331
                break;
 
332
        }
 
333
        fibril_mutex_unlock(&instance->guard);
 
334
        return EOK;
 
335
}
 
336
/*----------------------------------------------------------------------------*/
 
337
/** Interrupt handling routine
 
338
 *
 
339
 * @param[in] instance OHCI hc driver structure.
 
340
 * @param[in] status Value of the status register at the time of interrupt.
 
341
 */
 
342
void hc_interrupt(hc_t *instance, uint32_t status)
 
343
{
 
344
        assert(instance);
 
345
        if ((status & ~I_SF) == 0) /* ignore sof status */
 
346
                return;
 
347
        usb_log_debug2("OHCI(%p) interrupt: %x.\n", instance, status);
 
348
        if (status & I_RHSC)
 
349
                rh_interrupt(&instance->rh);
 
350
 
 
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);
 
358
 
 
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);
 
364
 
 
365
                        if (batch_is_complete(batch)) {
 
366
                                list_remove(current);
 
367
                                usb_transfer_batch_finish(batch);
 
368
                        }
 
369
                        current = next;
 
370
                }
 
371
                fibril_mutex_unlock(&instance->guard);
 
372
        }
 
373
 
 
374
        if (status & I_UE) {
 
375
                hc_start_hw(instance);
 
376
        }
 
377
 
 
378
}
 
379
/*----------------------------------------------------------------------------*/
 
380
/** Check status register regularly
 
381
 *
 
382
 * @param[in] instance OHCI hc driver structure.
 
383
 * @return Error code
 
384
 */
 
385
int interrupt_emulator(hc_t *instance)
 
386
{
 
387
        assert(instance);
 
388
        usb_log_info("Started interrupt emulator.\n");
 
389
        while (1) {
 
390
                const uint32_t status = instance->registers->interrupt_status;
 
391
                instance->registers->interrupt_status = status;
 
392
                hc_interrupt(instance, status);
 
393
                async_usleep(10000);
 
394
        }
 
395
        return EOK;
 
396
}
 
397
/*----------------------------------------------------------------------------*/
 
398
/** Turn off any (BIOS)driver that might be in control of the device.
 
399
 *
 
400
 * @param[in] instance OHCI hc driver structure.
 
401
 */
 
402
void hc_gain_control(hc_t *instance)
 
403
{
 
404
        assert(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);
 
415
 
 
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) {
 
421
                        async_usleep(1000);
 
422
                }
 
423
                usb_log_info("SMM driver: Ownership taken.\n");
 
424
                instance->registers->control &= (C_HCFS_RESET << C_HCFS_SHIFT);
 
425
                async_usleep(50000);
 
426
                return;
 
427
        }
 
428
 
 
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");
 
436
                        return;
 
437
                }
 
438
                /* HC is suspended assert resume for 20ms */
 
439
                instance->registers->control &= (C_HCFS_RESUME << C_HCFS_SHIFT);
 
440
                async_usleep(20000);
 
441
                usb_log_info("BIOS driver: HC resumed.\n");
 
442
                return;
 
443
        }
 
444
 
 
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");
 
448
        async_usleep(50000);
 
449
}
 
450
/*----------------------------------------------------------------------------*/
 
451
/** OHCI hw initialization routine.
 
452
 *
 
453
 * @param[in] instance OHCI hc driver structure.
 
454
 */
 
455
void hc_start_hw(hc_t *instance)
 
456
{
 
457
        /* OHCI guide page 42 */
 
458
        assert(instance);
 
459
        usb_log_debug2("Started hc initialization routine.\n");
 
460
 
 
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);
 
464
 
 
465
        /* Reset hc */
 
466
        usb_log_debug2("HC reset.\n");
 
467
        size_t time = 0;
 
468
        instance->registers->command_status = CS_HCR;
 
469
        while (instance->registers->command_status & CS_HCR) {
 
470
                async_usleep(10);
 
471
                time += 10;
 
472
        }
 
473
        usb_log_debug2("HC reset complete in %zu us.\n", time);
 
474
 
 
475
        /* Restore fm_interval */
 
476
        instance->registers->fm_interval = fm_interval;
 
477
        assert((instance->registers->command_status & CS_HCR) == 0);
 
478
 
 
479
        /* hc is now in suspend state */
 
480
        usb_log_debug2("HC should be in suspend state(%x).\n",
 
481
            instance->registers->control);
 
482
 
 
483
        /* Use HCCA */
 
484
        instance->registers->hcca = addr_to_phys(instance->hcca);
 
485
 
 
486
        /* Use queues */
 
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);
 
492
 
 
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);
 
498
 
 
499
        /* Enable queues */
 
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);
 
503
 
 
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;
 
509
 
 
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);
 
516
 
 
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);
 
520
}
 
521
/*----------------------------------------------------------------------------*/
 
522
/** Initialize schedule queues
 
523
 *
 
524
 * @param[in] instance OHCI hc driver structure
 
525
 * @return Error code
 
526
 */
 
527
int hc_init_transfer_lists(hc_t *instance)
 
528
{
 
529
        assert(instance);
 
530
#define SETUP_ENDPOINT_LIST(type) \
 
531
do { \
 
532
        const char *name = usb_str_transfer_type(type); \
 
533
        int ret = endpoint_list_init(&instance->lists[type], name); \
 
534
        if (ret != EOK) { \
 
535
                usb_log_error("Failed(%d) to setup %s endpoint list.\n", \
 
536
                    ret, name); \
 
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]); \
 
541
                return ret; \
 
542
        } \
 
543
} while (0)
 
544
 
 
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]);
 
552
 
 
553
        return EOK;
 
554
}
 
555
/*----------------------------------------------------------------------------*/
 
556
/** Initialize memory structures used by the OHCI hcd.
 
557
 *
 
558
 * @param[in] instance OHCI hc driver structure.
 
559
 * @return Error code.
 
560
 */
 
561
int hc_init_memory(hc_t *instance)
 
562
{
 
563
        assert(instance);
 
564
 
 
565
        bzero(&instance->rh, sizeof(instance->rh));
 
566
        /* Init queues */
 
567
        const int ret = hc_init_transfer_lists(instance);
 
568
        if (ret != EOK) {
 
569
                return ret;
 
570
        }
 
571
 
 
572
        /*Init HCCA */
 
573
        instance->hcca = malloc32(sizeof(hcca_t));
 
574
        if (instance->hcca == NULL)
 
575
                return ENOMEM;
 
576
        bzero(instance->hcca, sizeof(hcca_t));
 
577
        usb_log_debug2("OHCI HCCA initialized at %p.\n", instance->hcca);
 
578
 
 
579
        unsigned i = 0;
 
580
        for (; i < 32; ++i) {
 
581
                instance->hcca->int_ep[i] =
 
582
                    instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa;
 
583
        }
 
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);
 
587
 
 
588
        /* Init interrupt code */
 
589
        instance->interrupt_code.cmds = instance->interrupt_commands;
 
590
        instance->interrupt_code.cmdcount = OHCI_NEEDED_IRQ_COMMANDS;
 
591
        {
 
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;
 
597
 
 
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;
 
604
 
 
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;
 
609
 
 
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;
 
615
 
 
616
                /* Accept interrupt */
 
617
                instance->interrupt_commands[4].cmd = CMD_ACCEPT;
 
618
        }
 
619
 
 
620
        return EOK;
 
621
}
 
622
/**
 
623
 * @}
 
624
 */