ehci_util.c revision 269552cd714f2f1e308fdde0fc69b18a7f1142b2
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* EHCI Host Controller Driver (EHCI)
*
* The EHCI driver is a software driver which interfaces to the Universal
* Serial Bus layer (USBA) and the Host Controller (HC). The interface to
* the Host Controller is defined by the EHCI Host Controller Interface.
*
* This module contains the main EHCI driver code which handles all USB
* transfers, bandwidth allocations and other general functionalities.
*/
/*
* EHCI MSI tunable:
*
* By default MSI is enabled on all supported platforms except for the
* EHCI controller of ULI1575 South bridge.
*/
/* Pointer to the state structure */
extern void *ehci_statep;
extern void ehci_handle_endpoint_reclaimation(ehci_state_t *);
extern uint_t ehci_vt62x2_workaround;
extern int force_ehci_off;
/* Adjustable variables for the size of the pools */
/*
* Initialize the values which the order of 32ms intr qh are executed
* by the host controller in the lattice tree.
*/
{0x00, 0x10, 0x08, 0x18,
0x04, 0x14, 0x0c, 0x1c,
0x02, 0x12, 0x0a, 0x1a,
0x06, 0x16, 0x0e, 0x1e,
0x01, 0x11, 0x09, 0x19,
0x05, 0x15, 0x0d, 0x1d,
0x03, 0x13, 0x0b, 0x1b,
0x07, 0x17, 0x0f, 0x1f};
/*
* Initialize the values which are used to calculate start split mask
*/
/*
* device with polling interval greater than or equal
* to 8us (125us).
*/
0x01, /* 00000001 */
0x02, /* 00000010 */
0x04, /* 00000100 */
0x08, /* 00001000 */
0x10, /* 00010000 */
0x20, /* 00100000 */
0x40, /* 01000000 */
0x80, /* 10000000 */
/* Only for high speed devices with polling interval 4us */
0x11, /* 00010001 */
0x22, /* 00100010 */
0x44, /* 01000100 */
0x88, /* 10001000 */
/* Only for high speed devices with polling interval 2us */
0x55, /* 01010101 */
0xaa, /* 10101010 */
/* Only for high speed devices with polling interval 1us */
0xff /* 11111111 */
};
/*
* Initialize the values which are used to calculate complete split mask
*/
0x1c, /* 00011100 */
0x38, /* 00111000 */
0x70, /* 01110000 */
0xe0, /* 11100000 */
0x00, /* Need FSTN feature */
0x00, /* Need FSTN feature */
0x00 /* Need FSTN feature */
};
/*
* EHCI Internal Function Prototypes
*/
/* Host Controller Driver (HCD) initialization functions */
int result);
int intr_type);
int init_type);
static int ehci_init_periodic_frame_lst_table(
static void ehci_build_interrupt_lattice(
/* Host Controller Driver (HCD) deinitialization functions */
/* Bandwidth Allocation functions */
static int ehci_allocate_high_speed_bandwidth(
static int ehci_allocate_classic_tt_bandwidth(
static void ehci_deallocate_high_speed_bandwidth(
static void ehci_deallocate_classic_tt_bandwidth(
static int ehci_compute_high_speed_bandwidth(
uint_t *cbandwidth);
static int ehci_compute_classic_bandwidth(
static int ehci_adjust_high_speed_polling_interval(
static uint_t ehci_find_periodic_node(
int interval);
static int ehci_find_bestfit_hs_mask(
int interval);
static int ehci_find_bestfit_ls_intr_mask(
int interval);
static int ehci_find_bestfit_sitd_in_mask(
int interval);
static int ehci_find_bestfit_sitd_out_mask(
int interval);
int leaf,
int leaf_count,
static void ehci_update_bw_availability(
int bandwidth,
int leftmost_leaf,
int leaf_count,
/* Miscellaneous functions */
dev_info_t *dip);
int ehci_do_soft_reset(
static void ehci_cpr_cleanup(
int ehci_wait_for_sof(
void ehci_toggle_scheduler(
ehci_qtd_t *qtd);
int val);
/*
* check if this ehci controller can support PM
*/
int
{
return (USB_SUCCESS);
}
return (USB_FAILURE);
}
void
{
/*
* Some Nvidia chips can not handle qh dma address above 2G.
* The bit 31 of the dma address might be omitted and it will
* cause system crash or other unpredicable result. So force
* the dma address allocated below 2G to make ehci work.
*/
switch (ehcip->ehci_device_id) {
case PCI_DEVICE_NVIDIA_CK804:
case PCI_DEVICE_NVIDIA_MCP04:
"ehci_dma_attr_workaround: NVIDIA dma "
"workaround enabled, force dma address "
"to be allocated below 2G");
0x7fffffffull;
break;
default:
break;
}
}
}
/*
* Host Controller Driver (HCD) initialization functions
*/
/*
* ehci_set_dma_attributes:
*
* Set the limits in the DMA attributes structure. Most of the values used
* in the DMA limit structures are the default values as specified by the
* Writing PCI device drivers document.
*/
void
{
"ehci_set_dma_attributes:");
/* Initialize the DMA attributes */
/* 32 bit addressing */
/* Byte alignment */
/*
* Since PCI specification is byte alignment, the
* burst size field should be set to 1 for PCI devices.
*/
}
/*
* ehci_allocate_pools:
*
* Allocate the system memory for the Endpoint Descriptor (QH) and for the
* Transfer Descriptor (QTD) pools. Both QH and QTD structures must be aligned
* to a 16 byte boundary.
*/
int
{
int result;
int i;
"ehci_allocate_pools:");
/* The host controller will be little endian */
/* Byte alignment */
/* Allocate the QTD pool DMA handle */
DDI_DMA_SLEEP, 0,
goto failure;
}
/* Allocate the memory for the QTD pool */
ehci_qtd_pool_size * sizeof (ehci_qtd_t),
&dev_attr,
0,
goto failure;
}
/* Map the QTD pool into the I/O address space */
NULL,
NULL,
&ccount);
ehci_qtd_pool_size * sizeof (ehci_qtd_t));
/* Process the result */
if (result == DDI_DMA_MAPPED) {
/* The cookie count should be 1 */
if (ccount != 1) {
"ehci_allocate_pools: More than 1 cookie");
goto failure;
}
} else {
"ehci_allocate_pools: Result = %d", result);
goto failure;
}
/*
* DMA addresses for QTD pools are bound
*/
/* Initialize the QTD pool */
for (i = 0; i < ehci_qtd_pool_size; i ++) {
}
/* Allocate the QTD pool DMA handle */
0,
"ehci_allocate_pools: ddi_dma_alloc_handle failed");
goto failure;
}
/* Allocate the memory for the QH pool */
ehci_qh_pool_size * sizeof (ehci_qh_t),
&dev_attr,
0,
"ehci_allocate_pools: ddi_dma_mem_alloc failed");
goto failure;
}
NULL,
NULL,
&ccount);
ehci_qh_pool_size * sizeof (ehci_qh_t));
/* Process the result */
if (result == DDI_DMA_MAPPED) {
/* The cookie count should be 1 */
if (ccount != 1) {
"ehci_allocate_pools: More than 1 cookie");
goto failure;
}
} else {
goto failure;
}
/*
* DMA addresses for QH pools are bound
*/
/* Initialize the QH pool */
for (i = 0; i < ehci_qh_pool_size; i ++) {
}
/* Byte alignment */
return (DDI_SUCCESS);
/* Byte alignment */
return (DDI_FAILURE);
}
/*
* ehci_decode_ddi_dma_addr_bind_handle_result:
*
* Process the return values of ddi_dma_addr_bind_handle()
*/
void
int result)
{
"ehci_decode_ddi_dma_addr_bind_handle_result:");
switch (result) {
case DDI_DMA_PARTIAL_MAP:
"Partial transfers not allowed");
break;
case DDI_DMA_INUSE:
"Handle is in use");
break;
case DDI_DMA_NORESOURCES:
"No resources");
break;
case DDI_DMA_NOMAPPING:
"No mapping");
break;
case DDI_DMA_TOOBIG:
"Object is too big");
break;
default:
"Unknown dma error");
}
}
/*
* ehci_map_regs:
*
* The Host Controller (HC) contains a set of on-chip operational registers
* and which should be mapped into a non-cacheable portion of the system
* addressable space.
*/
int
{
/* Check to make sure we have memory access */
"ehci_map_regs: Config error");
return (DDI_FAILURE);
}
/* Make sure Memory Access Enable is set */
if (!(cmd_reg & PCI_COMM_MAE)) {
"ehci_map_regs: Memory base address access disabled");
return (DDI_FAILURE);
}
/* The host controller will be little endian */
/* Map in EHCI Capability registers */
sizeof (ehci_caps_t), &attr,
"ehci_map_regs: Map setup error");
return (DDI_FAILURE);
}
/* Free the original mapping */
/* Re-map in EHCI Capability and Operational registers */
"ehci_map_regs: Map setup error");
return (DDI_FAILURE);
}
/* Get the pointer to EHCI Operational Register */
"ehci_map_regs: Capsp 0x%p Regsp 0x%p\n",
return (DDI_SUCCESS);
}
/*
* The following simulated polling is for debugging purposes only.
* It is activated on x86 by setting usb-polling=true in GRUB or ehci.conf.
*/
static int
{
int ret;
char *propval;
return (0);
return (ret);
}
static void
ehci_poll_intr(void *arg)
{
/* poll every msec */
for (;;) {
}
}
/*
* ehci_register_intrs_and_init_mutex:
*
* Register interrupts and initialize each mutex and condition variables
*/
int
{
int intr_types;
#if defined(__x86)
#endif
"ehci_register_intrs_and_init_mutex:");
/*
* There is a known MSI hardware bug with the EHCI controller
* of ULI1575 southbridge. Hence MSI is disabled for this chip.
*/
} else {
/* Set the MSI enable flag from the global EHCI MSI tunable */
}
/* launch polling thread instead of enabling pci interrupt */
extern pri_t maxclsyspri;
"ehci_register_intrs_and_init_mutex: "
"running in simulated polled mode");
goto skip_intr;
}
#if defined(__x86)
/*
* Make sure that the interrupt pin is connected to the
* interrupt controller on x86. Interrupt line 255 means
* "unknown" or "not connected" (PCI spec 6.2.4, footnote 43).
* If we would return failure when interrupt line equals 255, then
* high speed devices will be routed to companion host controllers.
* However, it is not necessary to return failure here, and
* But it's good to log a message here for debug purposes.
*/
if (iline == 255) {
"ehci_register_intrs_and_init_mutex: "
"interrupt line value out of range (%d)",
iline);
}
#endif /* __x86 */
/* Get supported interrupt types */
&intr_types) != DDI_SUCCESS) {
"ehci_register_intrs_and_init_mutex: "
"ddi_intr_get_supported_types failed");
return (DDI_FAILURE);
}
"ehci_register_intrs_and_init_mutex: "
"supported interrupt types 0x%x", intr_types);
!= DDI_SUCCESS) {
"ehci_register_intrs_and_init_mutex: MSI "
"registration failed, trying FIXED interrupt \n");
} else {
"ehci_register_intrs_and_init_mutex: "
"Using MSI interrupt type\n");
}
}
(intr_types & DDI_INTR_TYPE_FIXED)) {
!= DDI_SUCCESS) {
"ehci_register_intrs_and_init_mutex: "
"FIXED interrupt registration failed\n");
return (DDI_FAILURE);
}
"ehci_register_intrs_and_init_mutex: "
"Using FIXED interrupt type\n");
}
/* Create prototype for advance on async schedule */
return (DDI_SUCCESS);
}
/*
* ehci_add_intrs:
*
* Register FIXED or MSI interrupts.
*/
static int
int intr_type)
{
"ehci_add_intrs: interrupt type 0x%x", intr_type);
/* Get number of interrupts */
"ehci_add_intrs: ddi_intr_get_nintrs() failure, "
return (DDI_FAILURE);
}
/* Get number of available interrupts */
"ehci_add_intrs: ddi_intr_get_navail() failure, "
return (DDI_FAILURE);
}
"ehci_add_intrs: ehci_add_intrs: nintrs () "
}
/* Allocate an array of interrupt handles */
/* call ddi_intr_alloc() */
"ehci_add_intrs: ddi_intr_alloc() failed %d", ret);
return (DDI_FAILURE);
}
"ehci_add_intrs: Requested: %d, Received: %d\n",
for (i = 0; i < actual; i++)
return (DDI_FAILURE);
}
"ehci_add_intrs: ddi_intr_get_pri() failed %d", ret);
for (i = 0; i < actual; i++)
return (DDI_FAILURE);
}
"ehci_add_intrs: Supported Interrupt priority 0x%x",
/* Test for high level mutex */
"ehci_add_intrs: Hi level interrupt not supported");
for (i = 0; i < actual; i++)
return (DDI_FAILURE);
}
/* Initialize the mutex */
/* Call ddi_intr_add_handler() */
for (i = 0; i < actual; i++) {
"ehci_add_intrs:ddi_intr_add_handler() "
"failed %d", ret);
for (i = 0; i < actual; i++)
return (DDI_FAILURE);
}
}
"ehci_add_intrs: ddi_intr_get_cap() failed %d", ret);
for (i = 0; i < actual; i++) {
}
return (DDI_FAILURE);
}
/* Enable all interrupts */
/* Call ddi_intr_block_enable() for MSI interrupts */
} else {
/* Call ddi_intr_enable for MSI or FIXED interrupts */
for (i = 0; i < ehcip->ehci_intr_cnt; i++)
}
return (DDI_SUCCESS);
}
/*
* ehci_init_hardware
*
* take control from BIOS, reset EHCI host controller, and check version, etc.
*/
int
{
int revision;
/* Take control from the BIOS */
/* read .conf file properties */
"abort-on-BIOS-take-over-failure", 0);
"Unable to take control from BIOS.");
return (DDI_FAILURE);
}
"Unable to take control from BIOS. Failure is ignored.");
}
/* set Memory Master Enable */
/* Reset the EHCI host controller */
/* Wait 10ms for reset to complete */
/* Verify the version number */
"ehci_init_hardware: Revision 0x%x", revision);
/*
* EHCI driver supports EHCI host controllers compliant to
* 0.95 and higher revisions of EHCI specifications.
*/
if (revision < EHCI_REVISION_0_95) {
"Revision 0x%x is not supported", revision);
return (DDI_FAILURE);
}
/* Initialize the Frame list base address area */
return (DDI_FAILURE);
}
/*
* For performance reasons, do not insert anything into the
* asynchronous list or activate the asynch list schedule until
* there is a valid QH.
*/
/*
* The driver is unable to reliably stop the asynch
* list schedule on VIA VT6202 controllers, so we
* always keep a dummy QH on the list.
*/
/* Set this QH to be the "head" of the circular list */
}
}
return (DDI_SUCCESS);
}
/*
* ehci_init_workaround
*
* some workarounds during initializing ehci
*/
int
{
/*
* Acer Labs Inc. M5273 EHCI controller does not send
* interrupts unless the Root hub ports are routed to the EHCI
* host controller; so route the ports now, before we test for
* the presence of SOFs interrupts.
*/
/* Route all Root hub ports to EHCI host controller */
}
/*
* VIA chips have some issues and may not work reliably.
* Revisions >= 0x80 are part of a southbridge and appear
* to be reliable with the workaround.
* For revisions < 0x80, if we were bound using class
* complain, else proceed. This will allow the user to
* bind ehci specifically to this chip and not have the
* warnings
*/
"ehci_init_workaround: Applying VIA workarounds "
"for the 6212 chip.");
"pciclass,0c0320") == 0) {
"Due to recently discovered incompatibilities");
"with this USB controller, USB2.x transfer");
"support has been disabled. This device will");
"continue to function as a USB1.x controller.");
"If you are interested in enabling USB2.x");
"support please, refer to the ehci(7D) man page.");
"Please also refer to www.sun.com/io for");
"Solaris Ready products and to");
"www.sun.com/bigadmin/hcl for additional");
"compatible USB products.");
return (DDI_FAILURE);
} else if (ehci_vt62x2_workaround) {
"Applying VIA workarounds");
}
}
return (DDI_SUCCESS);
}
/*
* ehci_init_check_status
*
* Check if EHCI host controller is running
*/
int
{
/*
* Get the number of clock ticks to wait.
* This is based on the maximum time it takes for a frame list rollover
* and maximum time wait for SOFs to begin.
*/
/* Tell the ISR to broadcast ehci_async_schedule_advance_cv */
/* We need to add a delay to allow the chip time to start running */
/*
* Check EHCI host controller is running, otherwise return failure.
*/
"No SOF interrupts have been received, this USB EHCI host"
"controller is unusable");
/*
* Route all Root hub ports to Classic host
* controller, in case this is an unusable ALI M5273
* EHCI controller.
*/
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* ehci_init_ctlr:
*
* Initialize the Host Controller (HC).
*/
int
int init_type)
{
if (init_type == EHCI_NORMAL_INITIALIZATION) {
return (DDI_FAILURE);
}
}
/*
* Check for Asynchronous schedule park capability feature. If this
* feature is supported, then, program ehci command register with
* appropriate values..
*/
"ehci_init_ctlr: Async park mode is supported");
}
/*
* Check for programmable periodic frame list feature. If this
* feature is supported, then, program ehci command register with
* 1024 frame list value.
*/
"ehci_init_ctlr: Variable programmable periodic "
"frame list is supported");
}
/*
* Currently EHCI driver doesn't support 64 bit addressing.
*
* If we are using 64 bit addressing capability, then, program
* ehci_ctrl_segment register with 4 Gigabyte segment where all
* of the interface data structures are allocated.
*/
"ehci_init_ctlr: EHCI driver doesn't support "
"64 bit addressing");
}
/* 64 bit addressing is not support */
/* Set host controller soft state to operational */
/*
* Set the Periodic Frame List Base Address register with the
* starting physical address of the Periodic Frame List.
*/
/*
* Set ehci_interrupt to enable all interrupts except Root
* Hub Status change interrupt.
*/
/*
* Set the desired interrupt threshold and turn on EHCI host controller.
*/
if (init_type == EHCI_NORMAL_INITIALIZATION) {
/* Set host controller soft state to error */
return (DDI_FAILURE);
}
/* Set host controller soft state to error */
return (DDI_FAILURE);
}
"ehci_init_ctlr: SOF's have started");
}
/* Route all Root hub ports to EHCI host controller */
return (DDI_SUCCESS);
}
/*
* ehci_take_control:
*
* Handshake to take EHCI control from BIOS if necessary. Its only valid for
* x86 machines, because sparc doesn't have a BIOS.
* On x86 machine, the take control process includes
* o get the base address of the extended capability list
* o find out the capability for handoff synchronization in the list.
* o check if BIOS has owned the host controller.
* o set the OS Owned semaphore bit, ask the BIOS to release the ownership.
* o wait for a constant time and check if BIOS has relinquished control.
*/
/* ARGSUSED */
static int
{
#if defined(__x86)
"ehci_take_control:");
/*
* According EHCI Spec 2.2.4, get EECP base address from HCCPARAMS
* register.
*/
/*
* According EHCI Spec 2.2.4, if the extended capability offset is
* less than 40h then its not valid. This means we don't need to
* worry about BIOS handoff.
*/
"ehci_take_control: Hardware doesn't support legacy.");
goto success;
}
/*
* According EHCI Spec 2.1.7, A zero offset indicates the
* end of the extended capability list.
*/
while (extended_cap_offset) {
/* Get the extended capability value. */
/* Get the capability ID */
/* Check if the card support legacy */
if (extended_cap_id == EHCI_EX_CAP_ID_BIOS_HANDOFF) {
break;
}
/* Get the offset of the next capability */
}
/*
* Unable to find legacy support in hardware's extended capability list.
* This means we don't need to worry about BIOS handoff.
*/
if (extended_cap_id != EHCI_EX_CAP_ID_BIOS_HANDOFF) {
"ehci_take_control: Hardware doesn't support legacy");
goto success;
}
/* Check if BIOS has owned it. */
if (!(extended_cap & EHCI_LEGSUP_BIOS_OWNED_SEM)) {
"ehci_take_control: BIOS does not own EHCI");
goto success;
}
/*
* According EHCI Spec 5.1, The OS driver initiates an ownership
* request by setting the OS Owned semaphore to a one. The OS
* waits for the BIOS Owned bit to go to a zero before attempting
* to use the EHCI controller. The time that OS must wait for BIOS
* to respond to the request for ownership is beyond the scope of
* this specification.
* It waits up to EHCI_TAKEOVER_WAIT_COUNT*EHCI_TAKEOVER_DELAY ms
* for BIOS to release the ownership.
*/
/* wait a special interval */
#ifndef __lock_lint
#endif
/* Check to see if the BIOS has released the ownership */
if (!(extended_cap & EHCI_LEGSUP_BIOS_OWNED_SEM)) {
"ehci_take_control: BIOS has released "
"the ownership. retry = %d", retry);
goto success;
}
}
"ehci_take_control: take control from BIOS failed.");
return (USB_FAILURE);
#endif /* __x86 */
return (USB_SUCCESS);
}
/*
* ehci_init_periodic_frame_list_table :
*
* Allocate the system memory and initialize Host Controller
* Periodic Frame List table area. The starting of the Periodic
* Frame List Table area must be 4096 byte aligned.
*/
static int
{
int result;
"ehci_init_periodic_frame_lst_table:");
/* The host controller will be little endian */
/* Force the required 4K restrictive alignment */
/* Create space for the Periodic Frame List */
goto failure;
}
sizeof (ehci_periodic_frame_list_t),
goto failure;
}
"ehci_init_periodic_frame_lst_table: "
"Real length %lu", real_length);
/* Map the whole Periodic Frame List into the I/O address space */
if (result == DDI_DMA_MAPPED) {
/* The cookie count should be 1 */
if (ccount != 1) {
"ehci_init_periodic_frame_lst_table: "
"More than 1 cookie");
goto failure;
}
} else {
goto failure;
}
"ehci_init_periodic_frame_lst_table: virtual 0x%p physical 0x%x",
(void *)ehcip->ehci_periodic_frame_list_tablep,
/*
* DMA addresses for Periodic Frame List are bound.
*/
/* Initialize the Periodic Frame List */
/* Reset Byte Alignment to Default */
return (DDI_SUCCESS);
/* Byte alignment */
return (DDI_FAILURE);
}
/*
* ehci_build_interrupt_lattice:
*
* Construct the interrupt lattice tree using static Endpoint Descriptors
* (QH). This interrupt lattice tree will have total of 32 interrupt QH
* lists and the Host Controller (HC) processes one interrupt QH list in
* every frame. The Host Controller traverses the periodic schedule by
* constructing an array offset reference from the Periodic List Base Address
* register and bits 12 to 3 of Frame Index register. It fetches the element
* and begins traversing the graph of linked schedule data structures.
*/
static void
{
int i, j, k;
"ehci_build_interrupt_lattice:");
/*
* Reserve the first 63 Endpoint Descriptor (QH) structures
* in the pool as static endpoints & these are required for
* constructing interrupt lattice tree.
*/
for (i = 0; i < EHCI_NUM_STATIC_NODES; i++) {
}
/*
* Make sure that last Endpoint on the periodic frame list terminates
* periodic schedule.
*/
/* Build the interrupt lattice tree */
for (i = 0; i < (EHCI_NUM_STATIC_NODES / 2); i++) {
/*
* The next pointer in the host controller endpoint
* descriptor must contain an iommu address. Calculate
* the offset into the cpu address and add this to the
* starting iommu address.
*/
}
/* Build the tree bottom */
temp = (unsigned short *)
num_of_nodes = 1;
/*
* Initialize the values which are used for setting up head pointers
* for the 32ms scheduling lists which starts from the Periodic Frame
* List.
*/
for (i = 0; i < ehci_log_2(EHCI_NUM_PERIODIC_FRAME_LISTS); i++) {
for (j = 0, k = 0; k < num_of_nodes; k++, j++) {
ehci_index[j++] = temp[k];
}
num_of_nodes *= 2;
for (k = 0; k < num_of_nodes; k++)
temp[k] = ehci_index[k];
}
/*
* Initialize the interrupt list in the Periodic Frame List Table
* so that it points to the bottom of the tree.
*/
for (i = 0, j = 0; i < ehci_pow_2(TREE_HEIGHT); i++) {
for (k = 0; k < ehci_pow_2(TREE_HEIGHT); k++) {
}
}
}
/*
* ehci_alloc_hcdi_ops:
*
* The HCDI interfaces or entry points are the software interfaces used by
* the Universal Serial Bus Driver (USBA) to access the services of the
* Host Controller Driver (HCD). During HCD initialization, inform USBA
* about all available HCDI interfaces or entry points.
*/
{
"ehci_alloc_hcdi_ops:");
return (usba_hcdi_ops);
}
/*
* Host Controller Driver (HCD) deinitialization functions
*/
/*
* ehci_cleanup:
*
* Cleanup on attach failure or detach
*/
int
{
if (flags & EHCI_RHREG) {
/* Unload the root hub driver */
return (DDI_FAILURE);
}
}
if (flags & EHCI_USBAREG) {
/* Unregister this HCD instance with USBA */
}
/* Disable all EHCI QH list processing */
/* Disable all EHCI interrupts */
Set_OpReg(ehci_interrupt, 0);
/* wait for the next SOF */
(void) ehci_wait_for_sof(ehcip);
/* Route all Root hub ports to Classic host controller */
/* Stop the EHCI host controller */
/* Wait for sometime */
}
/* Unmap the EHCI registers */
if (ehcip->ehci_caps_handle) {
}
if (ehcip->ehci_config_handle) {
}
/* Free all the buffers */
for (i = 0; i < ehci_qtd_pool_size; i ++) {
if ((ctrl != EHCI_QTD_FREE) &&
(ctrl != EHCI_QTD_DUMMY) &&
(qtd->qtd_trans_wrapper)) {
tw = (ehci_trans_wrapper_t *)
/* Obtain the pipe private structure */
/* Stop the the transfer timer */
}
}
/*
* If EHCI_QTD_POOL_BOUND flag is set, then unbind
* the handle for QTD pools.
*/
if ((ehcip->ehci_dma_addr_bind_flag &
}
}
/* Free the QTD pool */
if (ehcip->ehci_qtd_pool_dma_handle) {
}
/*
* If EHCI_QH_POOL_BOUND flag is set, then unbind
* the handle for QH pools.
*/
if ((ehcip->ehci_dma_addr_bind_flag &
}
}
/* Free the QH pool */
if (ehcip->ehci_qh_pool_dma_handle) {
}
/* Free the Periodic frame list table (PFLT) area */
if (ehcip->ehci_periodic_frame_list_tablep &&
/*
* If EHCI_PFLT_DMA_BOUND flag is set, then unbind
* the handle for PFLT.
*/
if ((ehcip->ehci_dma_addr_bind_flag &
}
}
(void) ehci_isoc_cleanup(ehcip);
if (ehcip->ehci_pflt_dma_handle) {
}
/* Destroy the mutex */
/* Destroy the async schedule advance condition variable */
}
/* clean up kstat structs */
/* Free ehci hcdi ops */
if (ehcip->ehci_hcdi_ops) {
}
if (flags & EHCI_ZALLOC) {
/* Remove all properties that might have been created */
/* Free the soft state */
}
return (DDI_SUCCESS);
}
/*
* ehci_rem_intrs:
*
* Unregister FIXED or MSI interrupts
*/
static void
{
int i;
/* Disable all interrupts */
} else {
for (i = 0; i < ehcip->ehci_intr_cnt; i++) {
}
}
/* Call ddi_intr_remove_handler() */
for (i = 0; i < ehcip->ehci_intr_cnt; i++) {
}
}
/*
* ehci_cpr_suspend
*/
int
{
int i;
"ehci_cpr_suspend:");
/* Call into the root hub and suspend it */
"ehci_cpr_suspend: root hub fails to suspend");
return (DDI_FAILURE);
}
/* Only root hub's intr pipe should be open at this time */
/* Just wait till all resources are reclaimed */
i = 0;
(void) ehci_wait_for_sof(ehcip);
}
"ehci_cpr_suspend: Disable HC QH list processing");
/* Disable all EHCI QH list processing */
"ehci_cpr_suspend: Disable HC interrupts");
/* Disable all EHCI interrupts */
Set_OpReg(ehci_interrupt, 0);
"ehci_cpr_suspend: Wait for the next SOF");
/* Wait for the next SOF */
"ehci_cpr_suspend: ehci host controller suspend failed");
return (DDI_FAILURE);
}
/*
* Stop the ehci host controller
* if usb keyboard is not connected.
*/
}
/* Set host controller soft state to suspend */
return (DDI_SUCCESS);
}
/*
* ehci_cpr_resume
*/
int
{
"ehci_cpr_resume: Restart the controller");
/* Cleanup ehci specific information across cpr */
/* Restart the controller */
"ehci_cpr_resume: ehci host controller resume failed ");
return (DDI_FAILURE);
}
/* Now resume the root hub */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Bandwidth Allocation functions
*/
/*
* ehci_allocate_bandwidth:
*
* Figure out whether or not this interval may be supported. Return the index
* into the lattice if it can be supported. Return allocation failure if it
* can not be supported.
*/
int
{
int error = USB_SUCCESS;
/* This routine is protected by the ehci_int_mutex */
/* Reset the pnode to the last checked pnode */
*pnode = 0;
/* Allocate high speed bandwidth */
return (error);
}
/*
* in additional to high speed bandwidth.
*/
/* Allocate classic TT bandwidth */
/* Deallocate high speed bandwidth */
}
}
return (error);
}
/*
* ehci_allocate_high_speed_bandwidth:
*
* isochronous endpoints.
*/
static int
{
int interval;
int error;
/* This routine is protected by the ehci_int_mutex */
/* Get child's usba device structure */
/* Get the current usb device's port status */
/*
* Calculate the length in bytes of a transaction on this
* periodic endpoint. Return failure if maximum packet is
* zero.
*/
if (error != USB_SUCCESS) {
return (error);
}
/*
* Adjust polling interval to be a power of 2.
* If this interval can't be supported, return
* allocation failure.
*/
if (interval == USB_FAILURE) {
return (USB_FAILURE);
}
if (port_status == USBA_HIGH_SPEED_DEV) {
/* Allocate bandwidth for high speed devices */
error = USB_SUCCESS;
} else {
}
*cmask = 0x00;
} else {
/* Allocate bandwidth for low speed interrupt */
interval);
} else {
if ((endpoint->bEndpointAddress &
USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
/* Allocate bandwidth for sitd in */
interval);
} else {
/* Allocate bandwidth for sitd out */
*cmask = 0x00;
}
}
}
if (error != USB_SUCCESS) {
"ehci_allocate_high_speed_bandwidth: Reached maximum "
"bandwidth value and cannot allocate bandwidth for a "
"given high-speed periodic endpoint");
return (USB_NO_BANDWIDTH);
}
return (error);
}
/*
* ehci_allocate_classic_tt_speed_bandwidth:
*
* isochronous endpoints.
*/
static int
{
int i, interval;
/* This routine is protected by the ehci_int_mutex */
/* Get child's usba device structure */
/* Get the current usb device's port status */
/* Get the parent high speed hub's usba device structure */
"ehci_allocate_classic_tt_bandwidth: "
"child_ud 0x%p parent_ud 0x%p",
/*
* Calculate the length in bytes of a transaction on this
* periodic endpoint. Return failure if maximum packet is
* zero.
*/
"ehci_allocate_classic_tt_bandwidth: Periodic endpoint "
"with zero endpoint maximum packet size is not supported");
return (USB_NOT_SUPPORTED);
}
"ehci_allocate_classic_tt_bandwidth: bandwidth %d", bandwidth);
/*
* If the length in bytes plus the allocated bandwidth exceeds
* the maximum, return bandwidth allocation failure.
*/
"ehci_allocate_classic_tt_bandwidth: Reached maximum "
"bandwidth value and cannot allocate bandwidth for a "
return (USB_NO_BANDWIDTH);
}
/* Adjust polling interval to be a power of 2 */
/* Find the height in the tree */
/* Find the leftmost leaf in the subtree specified by the node. */
for (i = 0; i < (EHCI_NUM_INTR_QH_LISTS/interval); i++) {
"ehci_allocate_classic_tt_bandwidth: Reached "
"maximum bandwidth value and cannot allocate "
return (USB_NO_BANDWIDTH);
}
}
/*
* All the leaves for this node must be updated with the bandwidth.
*/
for (i = 0; i < (EHCI_NUM_INTR_QH_LISTS/interval); i++) {
}
/* Find the leaf with the smallest allocated bandwidth */
for (i = 1; i < EHCI_NUM_INTR_QH_LISTS; i++) {
}
}
/* Save the minimum for later use */
return (USB_SUCCESS);
}
/*
* ehci_deallocate_bandwidth:
*
* Deallocate bandwidth for the given node in the lattice and the length
* of transfer.
*/
void
{
/* This routine is protected by the ehci_int_mutex */
/*
* in additional to high speed bandwidth.
*/
/* Deallocate classic TT bandwidth */
}
}
/*
* ehci_deallocate_high_speed_bandwidth:
*
* Deallocate high speed bandwidth of a interrupt or isochronous endpoint.
*/
static void
{
int interval;
/* This routine is protected by the ehci_int_mutex */
/* Get child's usba device structure */
/* Get the current usb device's port status */
/* Adjust polling interval to be a power of 2 */
/* Find the height in the tree */
/*
* Find the leftmost leaf in the subtree specified by the node
*/
/* Delete the bandwidth from the appropriate lists */
if (port_status == USBA_HIGH_SPEED_DEV) {
} else {
} else {
if ((endpoint->bEndpointAddress &
USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
list_count, cmask);
} else {
list_count, smask);
}
}
}
}
/*
* ehci_deallocate_classic_tt_bandwidth:
*
* Deallocate high speed bandwidth of a interrupt or isochronous endpoint.
*/
static void
{
int i, interval;
/* This routine is protected by the ehci_int_mutex */
/* Get child's usba device structure */
/* Get the current usb device's port status */
/* Get the parent high speed hub's usba device structure */
/* Obtain the bandwidth */
port_status, &bandwidth);
/* Adjust polling interval to be a power of 2 */
/* Find the height in the tree */
/* Find the leftmost leaf in the subtree specified by the node */
/* Delete the bandwidth from the appropriate lists */
for (i = 0; i < (EHCI_NUM_INTR_QH_LISTS/interval); i++) {
}
/* Find the leaf with the smallest allocated bandwidth */
for (i = 1; i < EHCI_NUM_INTR_QH_LISTS; i++) {
}
}
/* Save the minimum for later use */
}
/*
* ehci_compute_high_speed_bandwidth:
*
* Given a periodic endpoint (interrupt or isochronous) determine the total
* bandwidth for one transaction. The EHCI host controller traverses the
* endpoint descriptor lists on a first-come-first-serve basis. When the HC
* services an endpoint, only a single transaction attempt is made. The HC
* moves to the next Endpoint Descriptor after the first transaction attempt
* rather than finishing the entire Transfer Descriptor. Therefore, when a
* Transfer Descriptor is inserted into the lattice, we will only count the
* number of bytes for one transaction.
*
* The following are the formulas used for calculating bandwidth in terms
* bytes and it is for the single USB high speed transaction. The protocol
* overheads will be different for each of type of USB transfer & all these
* formulas & protocol overheads are derived from the 5.11.3 section of the
* USB 2.0 Specification.
*
* High-Speed:
* Protocol overhead + ((MaxPktSz * 7)/6) + Host_Delay
*
*
* Protocol overhead + Split transaction overhead +
* ((MaxPktSz * 7)/6) + Host_Delay;
*/
/* ARGSUSED */
static int
{
/* Return failure if endpoint maximum packet is zero */
if (maxpacketsize == 0) {
"ehci_allocate_high_speed_bandwidth: Periodic endpoint "
"with zero endpoint maximum packet size is not supported");
return (USB_NOT_SUPPORTED);
}
/* Add bit-stuffing overhead */
/* Add Host Controller specific delay to required bandwidth */
/* Add xfer specific protocol overheads */
if ((endpoint->bmAttributes &
USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) {
/* High speed interrupt transaction */
} else {
/* Isochronous transaction */
}
/*
* overheads.
*/
if (port_status != USBA_HIGH_SPEED_DEV) {
/*
* Add start and complete split transaction
* tokens overheads.
*/
/* Add data overhead depending on data direction */
if ((endpoint->bEndpointAddress &
USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
*cbandwidth += maxpacketsize;
} else {
if ((endpoint->bmAttributes &
/* There is no compete splits for out */
*cbandwidth = 0;
}
*sbandwidth += maxpacketsize;
}
} else {
/* Get the max transactions per microframe */
USB_EP_MAX_XACTS_SHIFT) + 1;
/* High speed transaction */
*sbandwidth += maxpacketsize;
/* Calculate bandwidth per micro-frame */
*sbandwidth *= xactions;
*cbandwidth = 0;
}
"ehci_allocate_high_speed_bandwidth: "
"Start split bandwidth %d Complete split bandwidth %d",
*sbandwidth, *cbandwidth);
return (USB_SUCCESS);
}
/*
* ehci_compute_classic_bandwidth:
*
* Given a periodic endpoint (interrupt or isochronous) determine the total
* bandwidth for one transaction. The EHCI host controller traverses the
* endpoint descriptor lists on a first-come-first-serve basis. When the HC
* services an endpoint, only a single transaction attempt is made. The HC
* moves to the next Endpoint Descriptor after the first transaction attempt
* rather than finishing the entire Transfer Descriptor. Therefore, when a
* Transfer Descriptor is inserted into the lattice, we will only count the
* number of bytes for one transaction.
*
* The following are the formulas used for calculating bandwidth in terms
* bytes and it is for the single USB high speed transaction. The protocol
* overheads will be different for each of type of USB transfer & all these
* formulas & protocol overheads are derived from the 5.11.3 section of the
* USB 2.0 Specification.
*
* Low-Speed:
* Protocol overhead + Hub LS overhead +
* (Low Speed clock * ((MaxPktSz * 7)/6)) + TT_Delay
*
* Full-Speed:
* Protocol overhead + ((MaxPktSz * 7)/6) + TT_Delay
*/
/* ARGSUSED */
static int
{
/*
* If endpoint maximum packet is zero, then return immediately.
*/
if (maxpacketsize == 0) {
return (USB_NOT_SUPPORTED);
}
/* Add TT delay to required bandwidth */
/* Add bit-stuffing overhead */
switch (port_status) {
case USBA_LOW_SPEED_DEV:
/* Low speed interrupt transaction */
(LOW_SPEED_CLOCK * maxpacketsize));
break;
case USBA_FULL_SPEED_DEV:
/* Full speed transaction */
*bandwidth += maxpacketsize;
/* Add xfer specific protocol overheads */
if ((endpoint->bmAttributes &
USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) {
/* Full speed interrupt transaction */
} else {
/* Isochronous and input transaction */
if ((endpoint->bEndpointAddress &
USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
} else {
/* Isochronous and output transaction */
}
}
break;
}
return (USB_SUCCESS);
}
/*
* ehci_adjust_polling_interval:
*
* Adjust bandwidth according usb device speed.
*/
/* ARGSUSED */
int
{
int i = 0;
/* Get the polling interval */
"ehci_adjust_polling_interval: Polling interval 0x%x", interval);
/*
* According USB 2.0 Specifications, a high-speed endpoint's
* polling intervals are specified interms of 125us or micro
* specified in milliseconds.
*
* A high speed interrupt/isochronous endpoints can specify
* desired polling interval between 1 to 16 micro-frames,
* milliseconds.
*/
switch (port_status) {
case USBA_LOW_SPEED_DEV:
/*
* Low speed endpoints are limited to specifying
* only 8ms to 255ms in this driver. If a device
* reports a polling interval that is less than 8ms,
* it will use 8 ms instead.
*/
if (interval < LS_MIN_POLL_INTERVAL) {
"Low speed endpoint's poll interval of %d ms "
"is below threshold. Rounding up to %d ms",
}
/*
* Return an error if the polling interval is greater
* than 255ms.
*/
if (interval > LS_MAX_POLL_INTERVAL) {
"Low speed endpoint's poll interval is "
"greater than %d ms", LS_MAX_POLL_INTERVAL);
return (USB_FAILURE);
}
break;
case USBA_FULL_SPEED_DEV:
/*
* Return an error if the polling interval is less
* than 1ms and greater than 255ms.
*/
if ((interval < FS_MIN_POLL_INTERVAL) &&
(interval > FS_MAX_POLL_INTERVAL)) {
"Full speed endpoint's poll interval must "
"be between %d and %d ms", FS_MIN_POLL_INTERVAL,
return (USB_FAILURE);
}
break;
case USBA_HIGH_SPEED_DEV:
/*
* Return an error if the polling interval is less 1
* and greater than 16. Convert this value to 125us
* units using 2^(bInterval -1). refer usb 2.0 spec
* page 51 for details.
*/
if ((interval < HS_MIN_POLL_INTERVAL) &&
(interval > HS_MAX_POLL_INTERVAL)) {
"High speed endpoint's poll interval "
"must be between %d and %d units",
return (USB_FAILURE);
}
/* Adjust high speed device polling interval */
interval =
break;
}
/*
* If polling interval is greater than 32ms,
* adjust polling interval equal to 32ms.
*/
if (interval > EHCI_NUM_INTR_QH_LISTS) {
}
/*
* Find the nearest power of 2 that's less
* than interval.
*/
while ((ehci_pow_2(i)) <= interval) {
i++;
}
return (ehci_pow_2((i - 1)));
}
/*
* ehci_adjust_high_speed_polling_interval:
*/
/* ARGSUSED */
static int
{
/* Get the polling interval */
/*
* Convert polling interval from micro seconds
* to milli seconds.
*/
if (interval <= EHCI_MAX_UFRAMES) {
interval = 1;
} else {
}
"ehci_adjust_high_speed_polling_interval: "
"High speed adjusted interval 0x%x", interval);
return (interval);
}
/*
* ehci_lattice_height:
*
* Given the requested bandwidth, find the height in the tree at which the
* nodes for this bandwidth fall. The height is measured as the number of
* nodes from the leaf to the level specified by bandwidth The root of the
* tree is at height TREE_HEIGHT.
*/
static uint_t
{
}
/*
* ehci_lattice_parent:
*
* Given a node in the lattice, find the index of the parent node
*/
static uint_t
{
if ((node % 2) == 0) {
} else {
}
}
/*
* ehci_find_periodic_node:
*
* Based on the "real" array leaf node and interval, get the periodic node.
*/
static uint_t
int i;
/* Get the leaf number in the lattice */
/* Get the node in the lattice based on the height and leaf */
for (i = 0; i < height; i++) {
}
return (pnode);
}
/*
* ehci_leftmost_leaf:
*
* Find the leftmost leaf in the subtree specified by the node. Height refers
* to number of nodes from the bottom of the tree to the node, including the
* node.
*
* The formula for a zero based tree is:
* 2^H * Node + 2^H - 1
* The leaf of the tree is an array, convert the number for the array.
* Subtract the size of nodes not in the array
* 2^H * Node + 2^H - 1 - (EHCI_NUM_INTR_QH_LISTS - 1) =
* 2^H * Node + 2^H - EHCI_NUM_INTR_QH_LISTS =
* 2^H * (Node + 1) - EHCI_NUM_INTR_QH_LISTS
* 0
* 1 2
* 0 1 2 3
*/
static uint_t
{
}
/*
* ehci_pow_2:
*
* Compute 2 to the power
*/
static uint_t
ehci_pow_2(uint_t x)
{
if (x == 0) {
return (1);
} else {
return (2 << (x - 1));
}
}
/*
* ehci_log_2:
*
* Compute log base 2 of x
*/
static uint_t
ehci_log_2(uint_t x)
{
int i = 0;
while (x != 1) {
x = x >> 1;
i++;
}
return (i);
}
/*
* ehci_find_bestfit_hs_mask:
*
* Find the smask and cmask in the bandwidth allocation, and update the
* bandwidth allocation.
*/
static int
int interval)
{
int i;
int array_leaf, best_array_leaf;
"ehci_find_bestfit_hs_mask: ");
/* Get all the valid smasks */
case EHCI_INTR_1US_POLL:
break;
case EHCI_INTR_2US_POLL:
break;
case EHCI_INTR_4US_POLL:
break;
case EHCI_INTR_XUS_POLL:
default:
break;
}
/*
* Because of the way the leaves are setup, we will automatically
* hit the leftmost leaf of every possible node with this interval.
*/
best_smask = 0x00;
best_node_bandwidth = 0;
/* Find the bandwidth mask */
/*
* If this node cannot support our requirements skip to the
* next leaf.
*/
if (bw_mask == 0x00) {
continue;
}
/*
* Now make sure our bandwidth requirements can be
* satisfied with one of smasks in this node.
*/
*smask = 0x00;
/* Check the start split mask value */
break;
}
}
/*
* If an appropriate smask is found save the information if:
* o best_smask has not been found yet.
* - or -
* o This is the node with the least amount of bandwidth
*/
if ((*smask != 0x00) &&
((best_smask == 0x00) ||
(best_node_bandwidth > node_bandwidth))) {
best_smask = *smask;
}
}
/*
* If we find node that can handle the bandwidth populate the
* appropriate variables and return success.
*/
if (best_smask) {
*smask = best_smask;
interval);
return (USB_SUCCESS);
}
return (USB_FAILURE);
}
/*
* ehci_find_bestfit_ls_intr_mask:
*
* Find the smask and cmask in the bandwidth allocation.
*/
static int
int interval)
{
int i;
int array_leaf, best_array_leaf;
"ehci_find_bestfit_ls_intr_mask: ");
/* For low and full speed devices */
/*
* Because of the way the leaves are setup, we will automatically
* hit the leftmost leaf of every possible node with this interval.
*/
best_smask = 0x00;
best_node_bandwidth = 0;
/* Find the bandwidth mask */
/*
* If this node cannot support our requirements skip to the
* next leaf.
*/
continue;
}
/*
* Now make sure our bandwidth requirements can be
* satisfied with one of smasks in this node.
*/
*smask = 0x00;
*cmask = 0x00;
/* Check the start split mask value */
break;
}
}
/*
* If an appropriate smask is found save the information if:
* o best_smask has not been found yet.
* - or -
* o This is the node with the least amount of bandwidth
*/
if ((*smask != 0x00) &&
((best_smask == 0x00) ||
(node_sbandwidth + node_cbandwidth)))) {
best_smask = *smask;
best_cmask = *cmask;
}
}
/*
* If we find node that can handle the bandwidth populate the
* appropriate variables and return success.
*/
if (best_smask) {
*smask = best_smask;
*cmask = best_cmask;
interval);
return (USB_SUCCESS);
}
return (USB_FAILURE);
}
/*
* ehci_find_bestfit_sitd_in_mask:
*
* Find the smask and cmask in the bandwidth allocation.
*/
static int
int interval)
{
int array_leaf, best_array_leaf;
"ehci_find_bestfit_sitd_in_mask: ");
/*
* Because of the way the leaves are setup, we will automatically
* hit the leftmost leaf of every possible node with this interval.
* You may only send MAX_UFRAME_SITD_XFER raw bits per uFrame.
*/
/*
* Need to add an additional 2 uFrames, if the "L"ast
* complete split is before uFrame 6. See section
* 11.8.4 in USB 2.0 Spec. Currently we do not support
* the "Back Ptr" which means we support on IN of
* ~4*MAX_UFRAME_SITD_XFER bandwidth/
*/
if (cbandwidth % MAX_UFRAME_SITD_XFER) {
uFrames++;
}
if (uFrames > 6) {
return (USB_FAILURE);
}
*smask = 0x1;
*cmask = 0x00;
for (i = 0; i < uFrames; i++) {
*cmask |= 0x1;
}
/* cmask must start 2 frames after the smask */
found = 0;
best_smask = 0x00;
best_node_bandwidth = 0;
&bw_cmask);
/*
* If this node cannot support our requirements skip to the
* next leaf.
*/
continue;
}
found = 1;
break;
}
}
/*
* If an appropriate smask is found save the information if:
* o best_smask has not been found yet.
* - or -
* o This is the node with the least amount of bandwidth
*/
if (found &&
((best_smask == 0x00) ||
(node_sbandwidth + node_cbandwidth)))) {
best_smask = *smask;
best_cmask = *cmask;
}
}
/*
* If we find node that can handle the bandwidth populate the
* appropriate variables and return success.
*/
if (best_smask) {
*smask = best_smask;
*cmask = best_cmask;
interval);
return (USB_SUCCESS);
}
return (USB_FAILURE);
}
/*
* ehci_find_bestfit_sitd_out_mask:
*
* Find the smask in the bandwidth allocation.
*/
static int
int interval)
{
int array_leaf, best_array_leaf;
"ehci_find_bestfit_sitd_out_mask: ");
/*
* Because of the way the leaves are setup, we will automatically
* hit the leftmost leaf of every possible node with this interval.
* You may only send MAX_UFRAME_SITD_XFER raw bits per uFrame.
*/
*smask = 0x00;
if (sbandwidth % MAX_UFRAME_SITD_XFER) {
uFrames++;
}
for (i = 0; i < uFrames; i++) {
*smask |= 0x1;
}
found = 0;
best_smask = 0x00;
best_node_bandwidth = 0;
&bw_smask);
/*
* If this node cannot support our requirements skip to the
* next leaf.
*/
if (bw_smask == 0x00) {
continue;
}
/* You cannot have a start split on the 8th uFrame */
for (i = 0; (*smask & 0x80) == 0; i++) {
found = 1;
break;
}
}
/*
* If an appropriate smask is found save the information if:
* o best_smask has not been found yet.
* - or -
* o This is the node with the least amount of bandwidth
*/
if (found &&
((best_smask == 0x00) ||
(best_node_bandwidth > node_sbandwidth))) {
best_smask = *smask;
}
}
/*
* If we find node that can handle the bandwidth populate the
* appropriate variables and return success.
*/
if (best_smask) {
*smask = best_smask;
interval);
return (USB_SUCCESS);
}
return (USB_FAILURE);
}
/*
* ehci_calculate_bw_availability_mask:
*
* Returns the "total bandwidth used" in this node.
* Populates bw_mask with the uFrames that can support the bandwidth.
*
* If all the Frames cannot support this bandwidth, then bw_mask
* will return 0x00 and the "total bandwidth used" will be invalid.
*/
static uint_t
int leaf,
int leaf_count,
{
int i, j;
int uframe_total;
uint_t total_bandwidth = 0;
"ehci_calculate_bw_availability_mask: leaf %d leaf count %d",
leaf, leaf_count);
/* Start by saying all uFrames are available */
*bw_mask = 0xFF;
for (j = 0; j < EHCI_MAX_UFRAMES; j++) {
/*
* If the uFrame in bw_mask is available check to see if
* it can support the additional bandwidth.
*/
if ((bw_uframe) &&
}
}
}
"ehci_calculate_bw_availability_mask: bandwidth mask 0x%x",
*bw_mask);
return (total_bandwidth);
}
/*
* ehci_update_bw_availability:
*
* The leftmost leaf needs to be in terms of array position and
* not the actual lattice position.
*/
static void
int bandwidth,
int leftmost_leaf,
int leaf_count,
{
int i, j;
int uFrame_bandwidth[8];
"ehci_update_bw_availability: "
"leaf %d count %d bandwidth 0x%x mask 0x%x",
ASSERT(leftmost_leaf >= 0);
for (j = 0; j < EHCI_MAX_UFRAMES; j++) {
if (mask & 0x1) {
uFrame_bandwidth[j] = bandwidth;
} else {
uFrame_bandwidth[j] = 0;
}
}
/* Updated all the effected leafs with the bandwidth */
for (i = 0; i < leaf_count; i++) {
for (j = 0; j < EHCI_MAX_UFRAMES; j++) {
fbp->ehci_micro_frame_bandwidth[j] +=
uFrame_bandwidth[j];
uFrame_bandwidth[j];
}
}
}
/*
* Miscellaneous functions
*/
/*
* ehci_obtain_state:
*
* NOTE: This function is also called from POLLED MODE.
*/
{
return (state);
}
/*
* ehci_state_is_operational:
*
* Check the Host controller state and return proper values.
*/
int
{
int val;
switch (ehcip->ehci_hc_soft_state) {
case EHCI_CTLR_INIT_STATE:
case EHCI_CTLR_SUSPEND_STATE:
val = USB_FAILURE;
break;
val = USB_SUCCESS;
break;
case EHCI_CTLR_ERROR_STATE:
break;
default:
val = USB_FAILURE;
break;
}
return (val);
}
/*
* ehci_do_soft_reset
*
* Do soft reset of ehci host controller.
*/
int
{
/* Increment host controller error count */
ehcip->ehci_hc_error++;
"ehci_do_soft_reset:"
/*
* Allocate space for saving current Host Controller
* registers. Don't do any recovery if allocation
* fails.
*/
ehci_save_regs = (ehci_regs_t *)
if (ehci_save_regs == NULL) {
"ehci_do_soft_reset: kmem_zalloc failed");
return (USB_FAILURE);
}
/* Save current ehci registers */
"ehci_do_soft_reset: Save reg = 0x%p", (void *)ehci_save_regs);
/* Disable all list processing and interrupts */
/* Disable all EHCI interrupts */
Set_OpReg(ehci_interrupt, 0);
/* Wait for few milliseconds */
/* Do light soft reset of ehci host controller */
"ehci_do_soft_reset: Reset in progress");
/* Wait for reset to complete */
/*
* Restore previous saved EHCI register value
* into the current EHCI registers.
*/
/*
* For some reason this register might get nulled out by
* the Uli M1575 South Bridge. To workaround the hardware
* problem, check the value after write and retry if the
* last write fails.
*/
int retry = 0;
if (retry >= EHCI_MAX_RETRY) {
" ASYNCLISTADDR write failed.");
return (USB_FAILURE);
}
"ehci_do_soft_reset: ASYNCLISTADDR "
"write failed, retry=%d", retry);
}
/* Enable both Asynchronous and Periodic Schedule if necessary */
/*
* Set ehci_interrupt to enable all interrupts except Root
* Hub Status change and frame list rollover interrupts.
*/
/*
* Deallocate the space that allocated for saving
* HC registers.
*/
/*
* Set the desired interrupt threshold, frame list size (if
* applicable) and turn EHCI host controller.
*/
/* Wait 10ms for EHCI to start sending SOF */
/*
* Get the current usb frame number before waiting for
* few milliseconds.
*/
/* Wait for few milliseconds */
/*
* Get the current usb frame number after waiting for
* few milliseconds.
*/
"ehci_do_soft_reset: Before Frame Number 0x%llx "
"After Frame Number 0x%llx",
(unsigned long long)before_frame_number,
(unsigned long long)after_frame_number);
if ((after_frame_number <= before_frame_number) &&
"ehci_do_soft_reset: Soft reset failed");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* ehci_get_xfer_attrs:
*
* Get the attributes of a particular xfer.
*
* NOTE: This function is also called from POLLED MODE.
*/
{
"ehci_get_xfer_attrs:");
case USB_EP_ATTR_CONTROL:
attrs = ((usb_ctrl_req_t *)
break;
case USB_EP_ATTR_BULK:
attrs = ((usb_bulk_req_t *)
break;
case USB_EP_ATTR_INTR:
attrs = ((usb_intr_req_t *)
break;
}
return (attrs);
}
/*
* ehci_get_current_frame_number:
*
* Get the current software based usb frame number.
*/
{
/*
* Calculate current software based usb frame number.
*
* This code accounts for the fact that frame number is
* updated by the Host Controller before the ehci driver
* gets an FrameListRollover interrupt that will adjust
* Frame higher part.
*
* Refer ehci specification 1.0, section 2.3.2, page 21.
*/
ehci_fno) & 0x2000);
/*
* Micro Frame number is equivalent to 125 usec. Eight
* Micro Frame numbers are equivalent to one millsecond
* or one usb frame number.
*/
"ehci_get_current_frame_number: "
"Current usb uframe number = 0x%llx "
"Current usb frame number = 0x%llx",
(unsigned long long)micro_frame_number,
(unsigned long long)usb_frame_number);
return (usb_frame_number);
}
/*
* ehci_cpr_cleanup:
*
* Cleanup ehci state and other ehci specific informations across
* Check Point Resume (CPR).
*/
static void
{
/* Reset software part of usb frame number */
}
/*
* ehci_wait_for_sof:
*
* Wait for couple of SOF interrupts
*/
int
{
int error = USB_SUCCESS;
if (error != USB_SUCCESS) {
return (error);
}
/* Get the current usb frame number before waiting for two SOFs */
/* Wait for few milliseconds */
/* Get the current usb frame number after woken up */
"ehci_wait_for_sof: framenumber: before 0x%llx "
"after 0x%llx",
(unsigned long long)before_frame_number,
(unsigned long long)after_frame_number);
/* Return failure, if usb frame number has not been changed */
if (after_frame_number <= before_frame_number) {
/* Set host controller soft state to error */
return (USB_FAILURE);
}
}
return (USB_SUCCESS);
}
/*
* ehci_toggle_scheduler:
*
* Turn scheduler based on pipe open count.
*/
void
/*
*/
if (ehcip->ehci_open_async_count) {
if (!(cmd_reg & EHCI_CMD_ASYNC_SCHED_ENABLE)) {
/*
* For some reason this address might get nulled out by
* the ehci chip. Set it here just in case it is null.
*/
/*
* For some reason this register might get nulled out by
* the Uli M1575 Southbridge. To workaround the HW
* problem, check the value after write and retry if the
* last write fails.
*
* If the ASYNCLISTADDR remains "stuck" after
* EHCI_MAX_RETRY retries, then the M1575 is broken
* and is stuck in an inconsistent state and is about
* to crash the machine with a trn_oor panic when it
* does a DMA read from 0x0. It is better to panic
* now rather than wait for the trn_oor crash; this
* way Customer Service will have a clean signature
* that indicts the M1575 chip rather than a
* mysterious and hard-to-diagnose trn_oor panic.
*/
int retry = 0;
retry);
if (retry >= EHCI_MAX_RETRY)
"ehci_toggle_scheduler: "
"ASYNCLISTADDR write failed.");
"ehci_toggle_scheduler: ASYNCLISTADDR "
"write failed, retry=%d", retry);
}
}
} else {
}
if (ehcip->ehci_open_periodic_count) {
if (!(cmd_reg & EHCI_CMD_PERIODIC_SCHED_ENABLE)) {
/*
* For some reason this address get's nulled out by
* the ehci chip. Set it here just in case it is null.
*/
0xFFFFF000));
}
} else {
}
/* Just an optimization */
}
}
/*
* ehci print functions
*/
/*
* ehci_print_caps:
*/
void
{
uint_t i;
"\n\tUSB 2.0 Host Controller Characteristics\n");
"Caps Length: 0x%x Version: 0x%x\n",
"Structural Parameters\n");
"No of Classic host controllers: 0x%x",
"No of ports per Classic host controller: 0x%x",
"No of root hub ports: 0x%x\n",
"Capability Parameters\n");
"Isoch schedule threshold: 0x%x",
"Classic Port Route Description");
}
}
/*
* ehci_print_regs:
*/
void
{
uint_t i;
"\n\tEHCI%d Operational Registers\n",
"Command: 0x%x Status: 0x%x",
"Interrupt: 0x%x Frame Index: 0x%x",
"Control Segment: 0x%x Periodic List Base: 0x%x",
"Async List Addr: 0x%x Config Flag: 0x%x",
"Root Hub Port Status");
"\tPort Status 0x%x: 0x%x ", i,
Get_OpReg(ehci_rh_port_status[i]));
}
}
/*
* ehci_print_qh:
*/
void
{
uint_t i;
"ehci_print_qh: qh = 0x%p", (void *)qh);
for (i = 0; i < 5; i++) {
}
for (i = 0; i < 5; i++) {
"\tqh_buf_high[%d]: 0x%x ",
}
}
/*
* ehci_print_qtd:
*/
void
{
uint_t i;
"ehci_print_qtd: qtd = 0x%p", (void *)qtd);
for (i = 0; i < 5; i++) {
}
for (i = 0; i < 5; i++) {
"\tqtd_buf_high[%d]: 0x%x ",
}
}
/*
* ehci kstat functions
*/
/*
* ehci_create_stats:
*
* Allocate and initialize the ehci kstat structures
*/
void
{
char kstatname[KSTAT_STRLEN];
char *usbtypes[USB_N_COUNT_KSTATS] =
{"ctrl", "isoch", "bulk", "intr"};
int i;
sizeof (ehci_intrs_stats_t) / sizeof (kstat_named_t),
if (EHCI_INTRS_STATS(ehcip)) {
"Interrupts Total", KSTAT_DATA_UINT64);
"Not Claimed", KSTAT_DATA_UINT64);
"Async schedule status", KSTAT_DATA_UINT64);
"Periodic sched status", KSTAT_DATA_UINT64);
"Empty async schedule", KSTAT_DATA_UINT64);
"Host controller Halted", KSTAT_DATA_UINT64);
"Intr on async advance", KSTAT_DATA_UINT64);
"Host system error", KSTAT_DATA_UINT64);
"Frame list rollover", KSTAT_DATA_UINT64);
"Port change detect", KSTAT_DATA_UINT64);
"USB error interrupt", KSTAT_DATA_UINT64);
"USB interrupt", KSTAT_DATA_UINT64);
}
}
if (EHCI_TOTAL_STATS(ehcip)) {
}
}
for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
if (ehcip->ehci_count_stats[i]) {
}
}
}
}
/*
* ehci_destroy_stats:
*
* Clean up ehci kstat structures
*/
void
{
int i;
if (EHCI_INTRS_STATS(ehcip)) {
}
if (EHCI_TOTAL_STATS(ehcip)) {
}
for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
if (ehcip->ehci_count_stats[i]) {
}
}
}
/*
* ehci_do_intrs_stats:
*
* ehci status information
*/
void
int val)
{
if (EHCI_INTRS_STATS(ehcip)) {
switch (val) {
break;
break;
break;
break;
break;
break;
break;
break;
case EHCI_STS_USB_ERROR_INTR:
break;
case EHCI_STS_USB_INTR:
break;
default:
break;
}
}
}
/*
* ehci_do_byte_stats:
*
* ehci data xfer information
*/
void
{
if (dir == USB_EP_DIR_IN) {
switch (type) {
case USB_EP_ATTR_CONTROL:
break;
case USB_EP_ATTR_BULK:
break;
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_ISOCH:
break;
}
} else if (dir == USB_EP_DIR_OUT) {
switch (type) {
case USB_EP_ATTR_CONTROL:
break;
case USB_EP_ATTR_BULK:
break;
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_ISOCH:
break;
}
}
}