source: mainline/uspace/drv/bus/usb/ohci/hc.c@ cfb79747

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since cfb79747 was d57122c, checked in by Jakub Jermar <jakub@…>, 14 years ago

UHCI and OHCI HC's now define and use proper IRQ PIO range.

  • Property mode set to 100644
File size: 19.1 KB
RevLine 
[41b96b4]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
[bab71635]43#include "hc.h"
[e20eaed]44#include "ohci_endpoint.h"
[41b96b4]45
[561112f]46#define OHCI_USED_INTERRUPTS \
47 (I_SO | I_WDH | I_UE | I_RHSC)
[1ecc5de]48
[d57122c]49static const irq_pio_range_t ohci_pio_ranges[] = {
50 {
51 .base = 0, /* filled later */
52 .size = sizeof(ohci_regs_t)
53 }
54};
55
56static const irq_cmd_t ohci_irq_commands[] = {
57 { .cmd = CMD_PIO_READ_32, .dstarg = 1, .addr = NULL /* filled later */ },
[1ecc5de]58 { .cmd = CMD_BTEST, .srcarg = 1, .dstarg = 2, .value = OHCI_USED_INTERRUPTS },
59 { .cmd = CMD_PREDICATE, .srcarg = 2, .value = 2 },
[d57122c]60 { .cmd = CMD_PIO_WRITE_A_32, .srcarg = 1, .addr = NULL /* filled later */ },
[1ecc5de]61 { .cmd = CMD_ACCEPT },
62};
63
[2c617b0]64static void hc_gain_control(hc_t *instance);
[1cb4f05]65static void hc_start(hc_t *instance);
[6b6e3ed3]66static int hc_init_transfer_lists(hc_t *instance);
[344925c]67static int hc_init_memory(hc_t *instance);
[1cb4f05]68static int interrupt_emulator(hc_t *instance);
[09ace19]69static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch);
[1cb4f05]70/*----------------------------------------------------------------------------*/
[d57122c]71/** Get number of PIO ranges used in IRQ code.
72 * @return Number of ranges.
73 */
74size_t hc_irq_pio_range_count(void)
75{
76 return sizeof(ohci_pio_ranges) / sizeof(irq_pio_range_t);
77}
78/*----------------------------------------------------------------------------*/
79/*----------------------------------------------------------------------------*/
[1cb4f05]80/** Get number of commands used in IRQ code.
81 * @return Number of commands.
82 */
83size_t hc_irq_cmd_count(void)
84{
85 return sizeof(ohci_irq_commands) / sizeof(irq_cmd_t);
86}
87/*----------------------------------------------------------------------------*/
[d57122c]88/** Generate IRQ code.
89 * @param[out] ranges PIO ranges buffer.
90 * @param[in] ranges_size Size of the ranges buffer (bytes).
91 * @param[out] cmds Commands buffer.
92 * @param[in] cmds_size Size of the commands buffer (bytes).
[1cb4f05]93 * @param[in] regs Physical address of device's registers.
94 * @param[in] reg_size Size of the register area (bytes).
95 *
96 * @return Error code.
97 */
[d57122c]98int
99hc_get_irq_code(irq_pio_range_t ranges[], size_t ranges_size, irq_cmd_t cmds[],
100 size_t cmds_size, uintptr_t regs, size_t reg_size)
[1cb4f05]101{
[d57122c]102 if ((ranges_size < sizeof(ohci_pio_ranges)) ||
103 (cmds_size < sizeof(ohci_irq_commands)) ||
104 (reg_size < sizeof(ohci_regs_t)))
[1cb4f05]105 return EOVERFLOW;
106
[d57122c]107 memcpy(ranges, ohci_pio_ranges, sizeof(ohci_pio_ranges));
108 ranges[0].base = regs;
[1cb4f05]109
110 memcpy(cmds, ohci_irq_commands, sizeof(ohci_irq_commands));
[d57122c]111 ohci_regs_t *registers = (ohci_regs_t *) regs;
112 cmds[0].addr = (void *) &registers->interrupt_status;
113 cmds[3].addr = (void *) &registers->interrupt_status;
[1cb4f05]114
115 return EOK;
116}
[a6d1bc1]117/*----------------------------------------------------------------------------*/
[02cacce]118/** Announce OHCI root hub to the DDF
119 *
120 * @param[in] instance OHCI driver intance
121 * @param[in] hub_fun DDF fuction representing OHCI root hub
122 * @return Error code
123 */
[53f1c87]124int hc_register_hub(hc_t *instance, ddf_fun_t *hub_fun)
125{
126 assert(instance);
127 assert(hub_fun);
128
[0cd8089]129 /* Try to get address 1 for root hub. */
130 instance->rh.address = 1;
131 int ret = usb_device_manager_request_address(
132 &instance->generic.dev_manager, &instance->rh.address, false,
133 USB_SPEED_FULL);
134 if (ret != EOK) {
[c4fb5ecd]135 usb_log_error("Failed to get OHCI root hub address: %s\n",
[0cd8089]136 str_error(ret));
137 return ret;
[8148ee3a]138 }
[53f1c87]139
[5ec492b]140#define CHECK_RET_UNREG_RETURN(ret, message...) \
[2ff7360]141if (ret != EOK) { \
142 usb_log_error(message); \
[48ae3ef]143 usb_endpoint_manager_remove_ep( \
[0cd8089]144 &instance->generic.ep_manager, instance->rh.address, 0, \
145 USB_DIRECTION_BOTH, NULL, NULL); \
146 usb_device_manager_release_address( \
147 &instance->generic.dev_manager, instance->rh.address); \
[2ff7360]148 return ret; \
149} else (void)0
[9c986d3]150
[0cd8089]151 ret = usb_endpoint_manager_add_ep(
152 &instance->generic.ep_manager, instance->rh.address, 0,
153 USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL, USB_SPEED_FULL, 64,
154 0, NULL, NULL);
[5ec492b]155 CHECK_RET_UNREG_RETURN(ret,
[0f1586d0]156 "Failed to register root hub control endpoint: %s.\n",
157 str_error(ret));
[6bec59b]158
[ef9460b]159 ret = ddf_fun_add_match_id(hub_fun, "usb&class=hub", 100);
[5ec492b]160 CHECK_RET_UNREG_RETURN(ret,
[1cb4f05]161 "Failed to add root hub match-id: %s.\n", str_error(ret));
[2ff7360]162
[5d07f54]163 ret = ddf_fun_bind(hub_fun);
[5ec492b]164 CHECK_RET_UNREG_RETURN(ret,
[1cb4f05]165 "Failed to bind root hub function: %s.\n", str_error(ret));
[2ff7360]166
[890a3454]167 ret = usb_device_manager_bind_address(&instance->generic.dev_manager,
168 instance->rh.address, hub_fun->handle);
169 if (ret != EOK)
170 usb_log_warning("Failed to bind root hub address: %s.\n",
171 str_error(ret));
172
[2ff7360]173 return EOK;
174#undef CHECK_RET_RELEASE
[53f1c87]175}
176/*----------------------------------------------------------------------------*/
[02cacce]177/** Initialize OHCI hc driver structure
178 *
179 * @param[in] instance Memory place for the structure.
180 * @param[in] regs Address of the memory mapped I/O registers.
181 * @param[in] reg_size Size of the memory mapped area.
182 * @param[in] interrupts True if w interrupts should be used
183 * @return Error code
184 */
[62265ce]185int hc_init(hc_t *instance, uintptr_t regs, size_t reg_size, bool interrupts)
[41b96b4]186{
187 assert(instance);
[1cb4f05]188
[c2be0e5]189#define CHECK_RET_RETURN(ret, message...) \
190if (ret != EOK) { \
191 usb_log_error(message); \
192 return ret; \
193} else (void)0
[ff582d47]194
[1cb4f05]195 int ret =
196 pio_enable((void*)regs, reg_size, (void**)&instance->registers);
[c2be0e5]197 CHECK_RET_RETURN(ret,
[1cb4f05]198 "Failed to gain access to device registers: %s.\n", str_error(ret));
[c2be0e5]199
[bba0dc20]200 list_initialize(&instance->pending_batches);
[e7bc999]201
[5e07cbc0]202 hcd_init(&instance->generic, USB_SPEED_FULL,
203 BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
[90dd59dc]204 instance->generic.private_data = instance;
[09ace19]205 instance->generic.schedule = hc_schedule;
[620c710]206 instance->generic.ep_add_hook = ohci_endpoint_init;
[48ae3ef]207 instance->generic.ep_remove_hook = ohci_endpoint_fini;
[e2976bb]208
[8790650]209 ret = hc_init_memory(instance);
[4125b7d]210 CHECK_RET_RETURN(ret, "Failed to create OHCI memory structures: %s.\n",
211 str_error(ret));
[bba0dc20]212#undef CHECK_RET_RETURN
213
[aa9ccf7]214 fibril_mutex_initialize(&instance->guard);
[2c617b0]215
[78ab6d4]216 hc_gain_control(instance);
[ff582d47]217
[ff0e354]218 if (!interrupts) {
219 instance->interrupt_emulator =
220 fibril_create((int(*)(void*))interrupt_emulator, instance);
221 fibril_add_ready(instance->interrupt_emulator);
222 }
[7013b14]223
[78ab6d4]224 rh_init(&instance->rh, instance->registers);
[1ef93fa]225 hc_start(instance);
[78ab6d4]226
[8627377]227 return EOK;
[a6d1bc1]228}
229/*----------------------------------------------------------------------------*/
[57e06ef]230void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep)
[620c710]231{
[57e06ef]232 assert(instance);
233 assert(ep);
234
[620c710]235 endpoint_list_t *list = &instance->lists[ep->transfer_type];
236 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ep);
[57e06ef]237 assert(list);
238 assert(ohci_ep);
239
[620c710]240 /* Enqueue ep */
241 switch (ep->transfer_type) {
242 case USB_TRANSFER_CONTROL:
243 instance->registers->control &= ~C_CLE;
244 endpoint_list_add_ep(list, ohci_ep);
245 instance->registers->control_current = 0;
246 instance->registers->control |= C_CLE;
247 break;
248 case USB_TRANSFER_BULK:
249 instance->registers->control &= ~C_BLE;
[f974519]250 endpoint_list_add_ep(list, ohci_ep);
[620c710]251 instance->registers->control |= C_BLE;
252 break;
253 case USB_TRANSFER_ISOCHRONOUS:
254 case USB_TRANSFER_INTERRUPT:
255 instance->registers->control &= (~C_PLE & ~C_IE);
[f974519]256 endpoint_list_add_ep(list, ohci_ep);
[620c710]257 instance->registers->control |= C_PLE | C_IE;
258 break;
259 }
260}
261/*----------------------------------------------------------------------------*/
[57e06ef]262void hc_dequeue_endpoint(hc_t *instance, const endpoint_t *ep)
[620c710]263{
[57e06ef]264 assert(instance);
265 assert(ep);
266
[620c710]267 /* Dequeue ep */
268 endpoint_list_t *list = &instance->lists[ep->transfer_type];
269 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ep);
[57e06ef]270
271 assert(list);
272 assert(ohci_ep);
[620c710]273 switch (ep->transfer_type) {
274 case USB_TRANSFER_CONTROL:
275 instance->registers->control &= ~C_CLE;
276 endpoint_list_remove_ep(list, ohci_ep);
277 instance->registers->control_current = 0;
278 instance->registers->control |= C_CLE;
279 break;
280 case USB_TRANSFER_BULK:
281 instance->registers->control &= ~C_BLE;
282 endpoint_list_remove_ep(list, ohci_ep);
283 instance->registers->control |= C_BLE;
284 break;
285 case USB_TRANSFER_ISOCHRONOUS:
286 case USB_TRANSFER_INTERRUPT:
287 instance->registers->control &= (~C_PLE & ~C_IE);
288 endpoint_list_remove_ep(list, ohci_ep);
289 instance->registers->control |= C_PLE | C_IE;
290 break;
291 default:
292 break;
293 }
294}
295/*----------------------------------------------------------------------------*/
[02cacce]296/** Add USB transfer to the schedule.
297 *
298 * @param[in] instance OHCI hc driver structure.
299 * @param[in] batch Batch representing the transfer.
300 * @return Error code.
301 */
[09ace19]302int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
[41b96b4]303{
[09ace19]304 assert(hcd);
305 hc_t *instance = hcd->private_data;
[41b96b4]306 assert(instance);
[9ff5ff82]307
[02cacce]308 /* Check for root hub communication */
[d017cea]309 if (batch->ep->address == instance->rh.address) {
[7d5708d]310 rh_request(&instance->rh, batch);
311 return EOK;
[41b96b4]312 }
[9c10e51]313 ohci_transfer_batch_t *ohci_batch = ohci_transfer_batch_get(batch);
314 if (!ohci_batch)
315 return ENOMEM;
[7013b14]316
[aa9ccf7]317 fibril_mutex_lock(&instance->guard);
[9c10e51]318 list_append(&ohci_batch->link, &instance->pending_batches);
319 ohci_transfer_batch_commit(ohci_batch);
[02cacce]320
321 /* Control and bulk schedules need a kick to start working */
322 switch (batch->ep->transfer_type)
323 {
[9ff5ff82]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 }
[aa9ccf7]333 fibril_mutex_unlock(&instance->guard);
[4c28d17]334 return EOK;
[41b96b4]335}
336/*----------------------------------------------------------------------------*/
[02cacce]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 */
[7d6a676]342void hc_interrupt(hc_t *instance, uint32_t status)
[41b96b4]343{
344 assert(instance);
[561112f]345 if ((status & ~I_SF) == 0) /* ignore sof status */
[eaf1e3d]346 return;
[2df648c2]347 usb_log_debug2("OHCI(%p) interrupt: %x.\n", instance, status);
[561112f]348 if (status & I_RHSC)
[7d6a676]349 rh_interrupt(&instance->rh);
350
[561112f]351 if (status & I_WDH) {
[aa9ccf7]352 fibril_mutex_lock(&instance->guard);
[4125b7d]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",
[aa9ccf7]357 instance->registers->periodic_current);
[eaf1e3d]358
[9c10e51]359 link_t *current = list_first(&instance->pending_batches);
360 while (current && current != &instance->pending_batches.head) {
[7013b14]361 link_t *next = current->next;
[9c10e51]362 ohci_transfer_batch_t *batch =
363 ohci_transfer_batch_from_link(current);
[7013b14]364
[9c10e51]365 if (ohci_transfer_batch_is_complete(batch)) {
[d6522dd]366 list_remove(current);
[9c10e51]367 ohci_transfer_batch_finish_dispose(batch);
[7013b14]368 }
[b72efe8]369
[7013b14]370 current = next;
[eaf1e3d]371 }
[aa9ccf7]372 fibril_mutex_unlock(&instance->guard);
[4c28d17]373 }
[68b9f148]374
375 if (status & I_UE) {
[f974519]376 usb_log_fatal("Error like no other!\n");
[1ef93fa]377 hc_start(instance);
[68b9f148]378 }
379
[41b96b4]380}
[7d6a676]381/*----------------------------------------------------------------------------*/
[02cacce]382/** Check status register regularly
383 *
384 * @param[in] instance OHCI hc driver structure.
385 * @return Error code
386 */
[53f1c87]387int interrupt_emulator(hc_t *instance)
[7d6a676]388{
389 assert(instance);
390 usb_log_info("Started interrupt emulator.\n");
391 while (1) {
[2c617b0]392 const uint32_t status = instance->registers->interrupt_status;
[7d6a676]393 instance->registers->interrupt_status = status;
394 hc_interrupt(instance, status);
[02cacce]395 async_usleep(10000);
[7d6a676]396 }
397 return EOK;
398}
[2c617b0]399/*----------------------------------------------------------------------------*/
[02cacce]400/** Turn off any (BIOS)driver that might be in control of the device.
[78ab6d4]401 *
402 * This function implements routines described in chapter 5.1.1.3 of the OHCI
403 * specification (page 40, pdf page 54).
[02cacce]404 *
405 * @param[in] instance OHCI hc driver structure.
406 */
[2c617b0]407void hc_gain_control(hc_t *instance)
408{
409 assert(instance);
[78ab6d4]410
[c8eddf4]411 usb_log_debug("Requesting OHCI control.\n");
[78ab6d4]412 if (instance->registers->revision & R_LEGACY_FLAG) {
413 /* Turn off legacy emulation, it should be enough to zero
414 * the lowest bit, but it caused problems. Thus clear all
415 * except GateA20 (causes restart on some hw).
416 * See page 145 of the specs for details.
417 */
418 volatile uint32_t *ohci_emulation_reg =
419 (uint32_t*)((char*)instance->registers + LEGACY_REGS_OFFSET);
420 usb_log_debug("OHCI legacy register %p: %x.\n",
421 ohci_emulation_reg, *ohci_emulation_reg);
422 /* Zero everything but A20State */
423 *ohci_emulation_reg &= 0x100;
424 usb_log_debug(
425 "OHCI legacy register (should be 0 or 0x100) %p: %x.\n",
426 ohci_emulation_reg, *ohci_emulation_reg);
427 }
[112d159]428
[2c617b0]429 /* Interrupt routing enabled => smm driver is active */
430 if (instance->registers->control & C_IR) {
[112d159]431 usb_log_debug("SMM driver: request ownership change.\n");
[2c617b0]432 instance->registers->command_status |= CS_OCR;
[78ab6d4]433 /* Hope that SMM actually knows its stuff or we can hang here */
[2c617b0]434 while (instance->registers->control & C_IR) {
435 async_usleep(1000);
436 }
[112d159]437 usb_log_info("SMM driver: Ownership taken.\n");
[78ab6d4]438 C_HCFS_SET(instance->registers->control, C_HCFS_RESET);
[5d07f54]439 async_usleep(50000);
[2c617b0]440 return;
441 }
442
[78ab6d4]443 const unsigned hc_status = C_HCFS_GET(instance->registers->control);
[2c617b0]444 /* Interrupt routing disabled && status != USB_RESET => BIOS active */
445 if (hc_status != C_HCFS_RESET) {
[112d159]446 usb_log_debug("BIOS driver found.\n");
[2c617b0]447 if (hc_status == C_HCFS_OPERATIONAL) {
[112d159]448 usb_log_info("BIOS driver: HC operational.\n");
[2c617b0]449 return;
450 }
[78ab6d4]451 /* HC is suspended assert resume for 20ms, */
452 C_HCFS_SET(instance->registers->control, C_HCFS_RESUME);
[2c617b0]453 async_usleep(20000);
[112d159]454 usb_log_info("BIOS driver: HC resumed.\n");
[2c617b0]455 return;
456 }
457
458 /* HC is in reset (hw startup) => no other driver
459 * maintain reset for at least the time specified in USB spec (50 ms)*/
[c4fb5ecd]460 usb_log_debug("Host controller found in reset state.\n");
[2c617b0]461 async_usleep(50000);
462}
463/*----------------------------------------------------------------------------*/
[02cacce]464/** OHCI hw initialization routine.
465 *
466 * @param[in] instance OHCI hc driver structure.
467 */
[1ef93fa]468void hc_start(hc_t *instance)
[2c617b0]469{
[112d159]470 /* OHCI guide page 42 */
[2c617b0]471 assert(instance);
[112d159]472 usb_log_debug2("Started hc initialization routine.\n");
473
474 /* Save contents of fm_interval register */
[2c617b0]475 const uint32_t fm_interval = instance->registers->fm_interval;
[112d159]476 usb_log_debug2("Old value of HcFmInterval: %x.\n", fm_interval);
[344925c]477
[112d159]478 /* Reset hc */
479 usb_log_debug2("HC reset.\n");
480 size_t time = 0;
[2c617b0]481 instance->registers->command_status = CS_HCR;
[112d159]482 while (instance->registers->command_status & CS_HCR) {
483 async_usleep(10);
484 time += 10;
485 }
486 usb_log_debug2("HC reset complete in %zu us.\n", time);
[344925c]487
[112d159]488 /* Restore fm_interval */
[2c617b0]489 instance->registers->fm_interval = fm_interval;
490 assert((instance->registers->command_status & CS_HCR) == 0);
[344925c]491
[2c617b0]492 /* hc is now in suspend state */
[112d159]493 usb_log_debug2("HC should be in suspend state(%x).\n",
494 instance->registers->control);
[344925c]495
[78d4e1f]496 /* Use HCCA */
497 instance->registers->hcca = addr_to_phys(instance->hcca);
498
499 /* Use queues */
[5a2c42b]500 instance->registers->bulk_head =
501 instance->lists[USB_TRANSFER_BULK].list_head_pa;
[4125b7d]502 usb_log_debug2("Bulk HEAD set to: %p (%#" PRIx32 ").\n",
[5a2c42b]503 instance->lists[USB_TRANSFER_BULK].list_head,
504 instance->lists[USB_TRANSFER_BULK].list_head_pa);
[78d4e1f]505
506 instance->registers->control_head =
[5a2c42b]507 instance->lists[USB_TRANSFER_CONTROL].list_head_pa;
[4125b7d]508 usb_log_debug2("Control HEAD set to: %p (%#" PRIx32 ").\n",
[5a2c42b]509 instance->lists[USB_TRANSFER_CONTROL].list_head,
510 instance->lists[USB_TRANSFER_CONTROL].list_head_pa);
[78d4e1f]511
[112d159]512 /* Enable queues */
[344925c]513 instance->registers->control |= (C_PLE | C_IE | C_CLE | C_BLE);
[112d159]514 usb_log_debug2("All queues enabled(%x).\n",
515 instance->registers->control);
516
[561112f]517 /* Enable interrupts */
518 instance->registers->interrupt_enable = OHCI_USED_INTERRUPTS;
[112d159]519 usb_log_debug2("Enabled interrupts: %x.\n",
520 instance->registers->interrupt_enable);
[561112f]521 instance->registers->interrupt_enable = I_MI;
[112d159]522
523 /* Set periodic start to 90% */
524 uint32_t frame_length = ((fm_interval >> FMI_FI_SHIFT) & FMI_FI_MASK);
525 instance->registers->periodic_start = (frame_length / 10) * 9;
526 usb_log_debug2("All periodic start set to: %x(%u - 90%% of %d).\n",
527 instance->registers->periodic_start,
528 instance->registers->periodic_start, frame_length);
[2c617b0]529
[78ab6d4]530 C_HCFS_SET(instance->registers->control, C_HCFS_OPERATIONAL);
[c4fb5ecd]531 usb_log_debug("OHCI HC up and running (ctl_reg=0x%x).\n",
[112d159]532 instance->registers->control);
[2c617b0]533}
[6b6e3ed3]534/*----------------------------------------------------------------------------*/
[02cacce]535/** Initialize schedule queues
536 *
537 * @param[in] instance OHCI hc driver structure
538 * @return Error code
539 */
[6b6e3ed3]540int hc_init_transfer_lists(hc_t *instance)
541{
542 assert(instance);
[5a2c42b]543#define SETUP_ENDPOINT_LIST(type) \
[344925c]544do { \
[5a2c42b]545 const char *name = usb_str_transfer_type(type); \
546 int ret = endpoint_list_init(&instance->lists[type], name); \
[6b6e3ed3]547 if (ret != EOK) { \
[1cb4f05]548 usb_log_error("Failed to setup %s endpoint list: %s.\n", \
549 name, str_error(ret)); \
[68b9f148]550 endpoint_list_fini(&instance->lists[USB_TRANSFER_ISOCHRONOUS]);\
[5a2c42b]551 endpoint_list_fini(&instance->lists[USB_TRANSFER_INTERRUPT]); \
552 endpoint_list_fini(&instance->lists[USB_TRANSFER_CONTROL]); \
553 endpoint_list_fini(&instance->lists[USB_TRANSFER_BULK]); \
[70c85320]554 return ret; \
[344925c]555 } \
556} while (0)
[6b6e3ed3]557
[5a2c42b]558 SETUP_ENDPOINT_LIST(USB_TRANSFER_ISOCHRONOUS);
559 SETUP_ENDPOINT_LIST(USB_TRANSFER_INTERRUPT);
560 SETUP_ENDPOINT_LIST(USB_TRANSFER_CONTROL);
561 SETUP_ENDPOINT_LIST(USB_TRANSFER_BULK);
562#undef SETUP_ENDPOINT_LIST
563 endpoint_list_set_next(&instance->lists[USB_TRANSFER_INTERRUPT],
564 &instance->lists[USB_TRANSFER_ISOCHRONOUS]);
[6b6e3ed3]565
566 return EOK;
567}
[344925c]568/*----------------------------------------------------------------------------*/
[02cacce]569/** Initialize memory structures used by the OHCI hcd.
570 *
571 * @param[in] instance OHCI hc driver structure.
572 * @return Error code.
573 */
[344925c]574int hc_init_memory(hc_t *instance)
575{
576 assert(instance);
[5d07f54]577
578 bzero(&instance->rh, sizeof(instance->rh));
[8790650]579 /* Init queues */
[8953514]580 const int ret = hc_init_transfer_lists(instance);
581 if (ret != EOK) {
582 return ret;
583 }
[344925c]584
[8790650]585 /*Init HCCA */
[f8dfb40]586 instance->hcca = hcca_get();
[344925c]587 if (instance->hcca == NULL)
588 return ENOMEM;
589 bzero(instance->hcca, sizeof(hcca_t));
[78d4e1f]590 usb_log_debug2("OHCI HCCA initialized at %p.\n", instance->hcca);
[344925c]591
[9b8958b]592 for (unsigned i = 0; i < 32; ++i) {
[344925c]593 instance->hcca->int_ep[i] =
[5a2c42b]594 instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa;
[344925c]595 }
[4125b7d]596 usb_log_debug2("Interrupt HEADs set to: %p (%#" PRIx32 ").\n",
[5a2c42b]597 instance->lists[USB_TRANSFER_INTERRUPT].list_head,
598 instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa);
[344925c]599
600 return EOK;
601}
[1ecc5de]602
[41b96b4]603/**
604 * @}
605 */
Note: See TracBrowser for help on using the repository browser.