17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53b00f311Syq  * Common Development and Distribution License (the "License").
63b00f311Syq  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*112116d8Sfb  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * EHCI Host Controller Driver (EHCI)
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  * The EHCI driver is a software driver which interfaces to the Universal
327c478bd9Sstevel@tonic-gate  * Serial Bus layer (USBA) and the Host Controller (HC). The interface to
337c478bd9Sstevel@tonic-gate  * the Host Controller is defined by the EHCI Host Controller Interface.
347c478bd9Sstevel@tonic-gate  *
357c478bd9Sstevel@tonic-gate  * This module contains the code for root hub related functions.
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  * NOTE:
387c478bd9Sstevel@tonic-gate  *
397c478bd9Sstevel@tonic-gate  * ONE_XFER is not supported on root hub interrupt polling
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehcid.h>
437c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_util.h>
447c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_types.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate /* Static function prototypes */
477c478bd9Sstevel@tonic-gate static int	ehci_handle_set_clear_port_feature(
487c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
497c478bd9Sstevel@tonic-gate 				uchar_t 		bRequest,
507c478bd9Sstevel@tonic-gate 				uint16_t		wValue,
517c478bd9Sstevel@tonic-gate 				uint16_t		port);
527c478bd9Sstevel@tonic-gate static void	ehci_handle_port_power(
537c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
547c478bd9Sstevel@tonic-gate 				uint16_t		port,
557c478bd9Sstevel@tonic-gate 				uint_t			on);
567c478bd9Sstevel@tonic-gate static void	ehci_handle_port_enable(
577c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
587c478bd9Sstevel@tonic-gate 				uint16_t		port,
597c478bd9Sstevel@tonic-gate 				uint_t			on);
607c478bd9Sstevel@tonic-gate static void	ehci_handle_clrchng_port_enable(
617c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
627c478bd9Sstevel@tonic-gate 				uint16_t		port);
637c478bd9Sstevel@tonic-gate static void	ehci_handle_port_suspend(
647c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
657c478bd9Sstevel@tonic-gate 				uint16_t		port,
667c478bd9Sstevel@tonic-gate 				uint_t			on);
677c478bd9Sstevel@tonic-gate static void	ehci_handle_clrchng_port_suspend(
687c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
697c478bd9Sstevel@tonic-gate 				uint16_t		port);
707c478bd9Sstevel@tonic-gate static void	ehci_handle_port_reset(
717c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
727c478bd9Sstevel@tonic-gate 				uint16_t		port);
737c478bd9Sstevel@tonic-gate static void	ehci_root_hub_reset_occured(
747c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip);
757c478bd9Sstevel@tonic-gate static void	ehci_handle_complete_port_reset(
767c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
777c478bd9Sstevel@tonic-gate 				uint16_t		port);
787c478bd9Sstevel@tonic-gate static void	ehci_handle_clear_port_connection(
797c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
807c478bd9Sstevel@tonic-gate 				uint16_t		port);
817c478bd9Sstevel@tonic-gate static void	ehci_handle_clrchng_port_over_current(
827c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
837c478bd9Sstevel@tonic-gate 				uint16_t		port);
847c478bd9Sstevel@tonic-gate static void	ehci_handle_get_port_status(
857c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
867c478bd9Sstevel@tonic-gate 				uint16_t		port);
877c478bd9Sstevel@tonic-gate static void	ehci_handle_get_hub_descriptor(
887c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip);
897c478bd9Sstevel@tonic-gate static void	ehci_handle_get_hub_status(
907c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip);
9135f36846Ssl static void	ehci_handle_get_device_status(
9235f36846Ssl 				ehci_state_t		*ehcip);
937c478bd9Sstevel@tonic-gate static uint_t	ehci_get_root_hub_port_status(
947c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
957c478bd9Sstevel@tonic-gate 				uint16_t		port);
967c478bd9Sstevel@tonic-gate static int	ehci_is_port_owner(
977c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
987c478bd9Sstevel@tonic-gate 				uint16_t		port);
997c478bd9Sstevel@tonic-gate static int	ehci_root_hub_allocate_intr_pipe_resource(
1007c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1017c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1027c478bd9Sstevel@tonic-gate static void	ehci_root_hub_intr_pipe_cleanup(
1037c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1047c478bd9Sstevel@tonic-gate 				usb_cr_t		completion_reason);
1057c478bd9Sstevel@tonic-gate static void	ehci_handle_root_hub_status_change(void *arg);
1067c478bd9Sstevel@tonic-gate static void	ehci_root_hub_hcdi_callback(
1077c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1087c478bd9Sstevel@tonic-gate 				usb_cr_t		completion_reason);
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate /*
1127c478bd9Sstevel@tonic-gate  * ehci_init_root_hub:
1137c478bd9Sstevel@tonic-gate  *
1147c478bd9Sstevel@tonic-gate  * Initialize the root hub
1157c478bd9Sstevel@tonic-gate  */
1167c478bd9Sstevel@tonic-gate int
1177c478bd9Sstevel@tonic-gate ehci_init_root_hub(ehci_state_t	*ehcip)
1187c478bd9Sstevel@tonic-gate {
1197c478bd9Sstevel@tonic-gate 	usb_hub_descr_t		*root_hub_descr =
120*112116d8Sfb 	    &ehcip->ehci_root_hub.rh_descr;
1217c478bd9Sstevel@tonic-gate 	uint_t			i, length, port_state;
1227c478bd9Sstevel@tonic-gate 	uint32_t		capability;
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
1257c478bd9Sstevel@tonic-gate 	    "ehci_init_root_hub:");
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate 	/* Read the EHCI capability register */
1287c478bd9Sstevel@tonic-gate 	capability = Get_Cap(ehci_hcs_params);
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	/*
1317c478bd9Sstevel@tonic-gate 	 * Build the Root hub descriptor by looking EHCI capability
1327c478bd9Sstevel@tonic-gate 	 * and operational registers.
1337c478bd9Sstevel@tonic-gate 	 */
1347c478bd9Sstevel@tonic-gate 	root_hub_descr->bDescriptorType = ROOT_HUB_DESCRIPTOR_TYPE;
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	if ((capability & EHCI_HCS_NUM_PORTS) > EHCI_MAX_RH_PORTS) {
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
1397c478bd9Sstevel@tonic-gate 		    "ehci_init_root_hub: Invalid no of root hub ports 0x%x",
1407c478bd9Sstevel@tonic-gate 		    capability & EHCI_HCS_NUM_PORTS);
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
1437c478bd9Sstevel@tonic-gate 	}
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 	/* Obtain the number of downstream ports */
1467c478bd9Sstevel@tonic-gate 	root_hub_descr->bNbrPorts = capability & EHCI_HCS_NUM_PORTS;
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	length = root_hub_descr->bNbrPorts / 8;
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	if (length) {
1517c478bd9Sstevel@tonic-gate 		root_hub_descr->bDescLength = 7 + (2 * (length + 1));
1527c478bd9Sstevel@tonic-gate 	} else {
1537c478bd9Sstevel@tonic-gate 		root_hub_descr->bDescLength = ROOT_HUB_DESCRIPTOR_LENGTH;
1547c478bd9Sstevel@tonic-gate 	}
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	/*
1577c478bd9Sstevel@tonic-gate 	 * Obtain the number of Classic or Companion USB 1.1 (OHCI/UHCI)
1587c478bd9Sstevel@tonic-gate 	 * Host Controllers information.
1597c478bd9Sstevel@tonic-gate 	 */
1607c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_companion_controllers = (capability &
1617c478bd9Sstevel@tonic-gate 	    EHCI_HCS_NUM_COMP_CTRLS) >> EHCI_HCS_NUM_COMP_CTRL_SHIFT;
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	/*
1647c478bd9Sstevel@tonic-gate 	 * Determine the Power Switching Mode
1657c478bd9Sstevel@tonic-gate 	 *
1667c478bd9Sstevel@tonic-gate 	 * EHCI Specification, root hub supports either no power switching
1677c478bd9Sstevel@tonic-gate 	 * individual port power switching. Also determine the Over-current
1687c478bd9Sstevel@tonic-gate 	 * Protection Mode.
1697c478bd9Sstevel@tonic-gate 	 */
1707c478bd9Sstevel@tonic-gate 	if (capability & EHCI_HCS_PORT_POWER_CONTROL) {
1717c478bd9Sstevel@tonic-gate 		/* Each port is powered individually */
1727c478bd9Sstevel@tonic-gate 		root_hub_descr-> wHubCharacteristics =
1737c478bd9Sstevel@tonic-gate 		    HUB_CHARS_INDIVIDUAL_PORT_POWER;
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 		/* Assume individual overcurrent reporting */
1767c478bd9Sstevel@tonic-gate 		root_hub_descr->wHubCharacteristics |=
1777c478bd9Sstevel@tonic-gate 		    HUB_CHARS_INDIV_OVER_CURRENT;
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate 		/* Each port will start off in the POWERED_OFF mode */
1807c478bd9Sstevel@tonic-gate 		port_state = POWERED_OFF;
1817c478bd9Sstevel@tonic-gate 	} else {
1827c478bd9Sstevel@tonic-gate 		/* The ports are powered when the ctlr is powered */
1837c478bd9Sstevel@tonic-gate 		root_hub_descr->
1847c478bd9Sstevel@tonic-gate 		    wHubCharacteristics = HUB_CHARS_NO_POWER_SWITCHING;
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 		/* Assume no overcurrent reporting */
1877c478bd9Sstevel@tonic-gate 		root_hub_descr->wHubCharacteristics |=
1887c478bd9Sstevel@tonic-gate 		    HUB_CHARS_NO_OVER_CURRENT;
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 		port_state = DISCONNECTED;
1917c478bd9Sstevel@tonic-gate 	}
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate 	/* Look at the port indicator information */
1947c478bd9Sstevel@tonic-gate 	if (capability & EHCI_HCS_PORT_INDICATOR) {
1957c478bd9Sstevel@tonic-gate 		root_hub_descr->wHubCharacteristics |= HUB_CHARS_PORT_INDICATOR;
1967c478bd9Sstevel@tonic-gate 	}
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	/*
1997c478bd9Sstevel@tonic-gate 	 * Obtain the power on to power good time of the ports.
2007c478bd9Sstevel@tonic-gate 	 *
2017c478bd9Sstevel@tonic-gate 	 * Assume: Zero for this field.
2027c478bd9Sstevel@tonic-gate 	 */
2037c478bd9Sstevel@tonic-gate 	root_hub_descr->bPwrOn2PwrGood = 2;
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
2067c478bd9Sstevel@tonic-gate 	    "Power on to power good %d", root_hub_descr->bPwrOn2PwrGood);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	/* Indicate if the device is removable */
2097c478bd9Sstevel@tonic-gate 	root_hub_descr->DeviceRemovable = 0;
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	/* Set PortPowerControlMask to zero */
2127c478bd9Sstevel@tonic-gate 	root_hub_descr->PortPwrCtrlMask = 0;
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	/* Set the state of each port and initialize the status */
2157c478bd9Sstevel@tonic-gate 	for (i = 0; i < root_hub_descr->bNbrPorts; i++) {
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 		/* Initilize state/status of each root hub port */
2187c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_port_state[i] = port_state;
2197c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_port_status[i] = 0;
2207c478bd9Sstevel@tonic-gate 	}
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate /*
2277c478bd9Sstevel@tonic-gate  * ehci_load_root_hub_driver:
2287c478bd9Sstevel@tonic-gate  *
2297c478bd9Sstevel@tonic-gate  * Attach the root hub
2307c478bd9Sstevel@tonic-gate  */
2317c478bd9Sstevel@tonic-gate static usb_dev_descr_t ehci_root_hub_device_descriptor = {
2327c478bd9Sstevel@tonic-gate 	0x12,		/* bLength */
2337c478bd9Sstevel@tonic-gate 	0x01,		/* bDescriptorType, Device */
2347c478bd9Sstevel@tonic-gate 	0x200,		/* bcdUSB, v2.0 */
2357c478bd9Sstevel@tonic-gate 	0x09,		/* bDeviceClass */
2367c478bd9Sstevel@tonic-gate 	0x00,		/* bDeviceSubClass */
2377c478bd9Sstevel@tonic-gate 	0x01,		/* bDeviceProtocol */
2387c478bd9Sstevel@tonic-gate 	0x40,		/* bMaxPacketSize0 */
2397c478bd9Sstevel@tonic-gate 	0x00,		/* idVendor */
2407c478bd9Sstevel@tonic-gate 	0x00,		/* idProduct */
2417c478bd9Sstevel@tonic-gate 	0x00,		/* bcdDevice */
2427c478bd9Sstevel@tonic-gate 	0x00,		/* iManufacturer */
2437c478bd9Sstevel@tonic-gate 	0x00,		/* iProduct */
2447c478bd9Sstevel@tonic-gate 	0x00,		/* iSerialNumber */
2457c478bd9Sstevel@tonic-gate 	0x01		/* bNumConfigurations */
2467c478bd9Sstevel@tonic-gate };
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate static uchar_t ehci_root_hub_config_descriptor[] = {
2497c478bd9Sstevel@tonic-gate 	/* One configuartion */
2507c478bd9Sstevel@tonic-gate 	0x09,		/* bLength */
2517c478bd9Sstevel@tonic-gate 	0x02,		/* bDescriptorType, Configuartion */
2527c478bd9Sstevel@tonic-gate 	0x19, 0x00,	/* wTotalLength */
2537c478bd9Sstevel@tonic-gate 	0x01,		/* bNumInterfaces */
2547c478bd9Sstevel@tonic-gate 	0x01,		/* bConfigurationValue */
2557c478bd9Sstevel@tonic-gate 	0x00,		/* iConfiguration */
2567c478bd9Sstevel@tonic-gate 	0x40,		/* bmAttributes */
2577c478bd9Sstevel@tonic-gate 	0x00,		/* MaxPower */
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	/* One Interface */
2607c478bd9Sstevel@tonic-gate 	0x09,		/* bLength */
2617c478bd9Sstevel@tonic-gate 	0x04,		/* bDescriptorType, Interface */
2627c478bd9Sstevel@tonic-gate 	0x00,		/* bInterfaceNumber */
2637c478bd9Sstevel@tonic-gate 	0x00,		/* bAlternateSetting */
2647c478bd9Sstevel@tonic-gate 	0x01,		/* bNumEndpoints */
2657c478bd9Sstevel@tonic-gate 	0x09,		/* bInterfaceClass */
2667c478bd9Sstevel@tonic-gate 	0x01,		/* bInterfaceSubClass */
2677c478bd9Sstevel@tonic-gate 	0x00,		/* bInterfaceProtocol */
2687c478bd9Sstevel@tonic-gate 	0x00,		/* iInterface */
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 	/* One Endpoint (status change endpoint) */
2717c478bd9Sstevel@tonic-gate 	0x07,		/* bLength */
2727c478bd9Sstevel@tonic-gate 	0x05,		/* bDescriptorType, Endpoint */
2737c478bd9Sstevel@tonic-gate 	0x81,		/* bEndpointAddress */
2747c478bd9Sstevel@tonic-gate 	0x03,		/* bmAttributes */
275*112116d8Sfb 	0x01, 0x00,	/* wMaxPacketSize, 1 +	(EHCI_MAX_RH_PORTS / 8) */
2767c478bd9Sstevel@tonic-gate 	0xff		/* bInterval */
2777c478bd9Sstevel@tonic-gate };
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate int
2807c478bd9Sstevel@tonic-gate ehci_load_root_hub_driver(ehci_state_t	*ehcip)
2817c478bd9Sstevel@tonic-gate {
2827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
2837c478bd9Sstevel@tonic-gate 	    "ehci_load_root_hub_driver:");
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	return (usba_hubdi_bind_root_hub(ehcip->ehci_dip,
2867c478bd9Sstevel@tonic-gate 	    ehci_root_hub_config_descriptor,
2877c478bd9Sstevel@tonic-gate 	    sizeof (ehci_root_hub_config_descriptor),
2887c478bd9Sstevel@tonic-gate 	    &ehci_root_hub_device_descriptor));
2897c478bd9Sstevel@tonic-gate }
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate /*
2937c478bd9Sstevel@tonic-gate  * ehci_unload_root_hub_driver:
2947c478bd9Sstevel@tonic-gate  */
2957c478bd9Sstevel@tonic-gate int
2967c478bd9Sstevel@tonic-gate ehci_unload_root_hub_driver(ehci_state_t	*ehcip)
2977c478bd9Sstevel@tonic-gate {
2987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
2997c478bd9Sstevel@tonic-gate 	    "ehci_unload_root_hub_driver:");
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 	return (usba_hubdi_unbind_root_hub(ehcip->ehci_dip));
3027c478bd9Sstevel@tonic-gate }
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate /*
3067c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_pipe_open:
3077c478bd9Sstevel@tonic-gate  *
3087c478bd9Sstevel@tonic-gate  * Handle opening of control and interrupt pipes on root hub.
3097c478bd9Sstevel@tonic-gate  */
3107c478bd9Sstevel@tonic-gate /* ARGSUSED */
3117c478bd9Sstevel@tonic-gate int
3127c478bd9Sstevel@tonic-gate ehci_handle_root_hub_pipe_open(
3137c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
3147c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
3157c478bd9Sstevel@tonic-gate {
3167c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
317*112116d8Sfb 	    ph->p_usba_device->usb_root_hub_dip);
3187c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3217c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_pipe_open: Root hub pipe open");
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
3267c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
3277c478bd9Sstevel@tonic-gate 		/* Save control pipe handle */
3287c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_handle = ph;
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 		/* Set state of the root hub control pipe as idle */
3317c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_IDLE;
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_curr_ctrl_reqp = NULL;
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3367c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_open: Root hub control "
3377c478bd9Sstevel@tonic-gate 		    "pipe open succeeded");
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 		break;
3407c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
3417c478bd9Sstevel@tonic-gate 		/* Save interrupt pipe handle */
3427c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_handle = ph;
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 		/* Set state of the root hub interrupt pipe as idle */
3457c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_state = EHCI_PIPE_STATE_IDLE;
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_client_intr_reqp = NULL;
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_curr_intr_reqp = NULL;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3527c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_open: Root hub interrupt "
3537c478bd9Sstevel@tonic-gate 		    "pipe open succeeded");
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 		break;
3567c478bd9Sstevel@tonic-gate 	default:
3577c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3587c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_open: Root hub pipe open"
3597c478bd9Sstevel@tonic-gate 		    "failed");
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3627c478bd9Sstevel@tonic-gate 	}
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	ehcip->ehci_open_pipe_count++;
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
3677c478bd9Sstevel@tonic-gate }
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate /*
3717c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_pipe_close:
3727c478bd9Sstevel@tonic-gate  *
3737c478bd9Sstevel@tonic-gate  * Handle closing of control and interrupt pipes on root hub.
3747c478bd9Sstevel@tonic-gate  */
3757c478bd9Sstevel@tonic-gate /* ARGSUSED */
3767c478bd9Sstevel@tonic-gate int
3777c478bd9Sstevel@tonic-gate ehci_handle_root_hub_pipe_close(usba_pipe_handle_data_t	*ph)
3787c478bd9Sstevel@tonic-gate {
3797c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
380*112116d8Sfb 	    ph->p_usba_device->usb_root_hub_dip);
3817c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3847c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_pipe_close: Root hub pipe close");
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
3897c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
3907c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.
3917c478bd9Sstevel@tonic-gate 		    rh_ctrl_pipe_state != EHCI_PIPE_STATE_CLOSE);
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 		/* Set state of the root hub control pipe as close */
3947c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_CLOSE;
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 		/* Set root hub control pipe handle to null */
3977c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_handle = NULL;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4007c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_close: "
4017c478bd9Sstevel@tonic-gate 		    "Root hub control pipe close succeeded");
4027c478bd9Sstevel@tonic-gate 		break;
4037c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
4047c478bd9Sstevel@tonic-gate 		ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.
4077c478bd9Sstevel@tonic-gate 		    rh_intr_pipe_state != EHCI_PIPE_STATE_CLOSE);
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 		/* Set state of the root hub interrupt pipe as close */
4107c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_state = EHCI_PIPE_STATE_CLOSE;
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 		/* Do interrupt pipe cleanup */
4137c478bd9Sstevel@tonic-gate 		ehci_root_hub_intr_pipe_cleanup(ehcip, USB_CR_PIPE_CLOSING);
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 		/* Set root hub interrupt pipe handle to null */
4167c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_handle = NULL;
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4197c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_close: "
4207c478bd9Sstevel@tonic-gate 		    "Root hub interrupt pipe close succeeded");
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 		break;
4237c478bd9Sstevel@tonic-gate 	default:
4247c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4257c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_close: "
4267c478bd9Sstevel@tonic-gate 		    "Root hub pipe close failed");
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
4297c478bd9Sstevel@tonic-gate 	}
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 	ehcip->ehci_open_pipe_count--;
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
4347c478bd9Sstevel@tonic-gate }
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate /*
4387c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_pipe_reset:
4397c478bd9Sstevel@tonic-gate  *
4407c478bd9Sstevel@tonic-gate  * Handle resetting of control and interrupt pipes on root hub.
4417c478bd9Sstevel@tonic-gate  */
4427c478bd9Sstevel@tonic-gate /* ARGSUSED */
4437c478bd9Sstevel@tonic-gate int
4447c478bd9Sstevel@tonic-gate ehci_handle_root_hub_pipe_reset(
4457c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
4467c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
4477c478bd9Sstevel@tonic-gate {
4487c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
449*112116d8Sfb 	    ph->p_usba_device->usb_root_hub_dip);
4507c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
4517c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4547c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_pipe_reset: Root hub pipe reset");
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
4597c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
4607c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_IDLE;
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4637c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_reset: Pipe reset"
4647c478bd9Sstevel@tonic-gate 		    "for the root hub control pipe successful");
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 		break;
4677c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
4687c478bd9Sstevel@tonic-gate 		ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate 		if ((ehcip->ehci_root_hub.rh_client_intr_reqp) &&
4717c478bd9Sstevel@tonic-gate 		    (ehcip->ehci_root_hub.rh_intr_pipe_state !=
4727c478bd9Sstevel@tonic-gate 		    EHCI_PIPE_STATE_IDLE)) {
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.
4757c478bd9Sstevel@tonic-gate 			    rh_intr_pipe_state = EHCI_PIPE_STATE_RESET;
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
4787c478bd9Sstevel@tonic-gate 			ehci_root_hub_intr_pipe_cleanup(
4797c478bd9Sstevel@tonic-gate 			    ehcip, USB_CR_PIPE_RESET);
4807c478bd9Sstevel@tonic-gate 		}
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.
4837c478bd9Sstevel@tonic-gate 		    rh_intr_pipe_state == EHCI_PIPE_STATE_IDLE);
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4867c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_reset: "
4877c478bd9Sstevel@tonic-gate 		    "Pipe reset for root hub interrupt pipe successful");
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 		break;
4907c478bd9Sstevel@tonic-gate 	default:
4917c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4927c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_reset: "
4937c478bd9Sstevel@tonic-gate 		    "Root hub pipe reset failed");
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
4967c478bd9Sstevel@tonic-gate 		break;
4977c478bd9Sstevel@tonic-gate 	}
4987c478bd9Sstevel@tonic-gate 
4997c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	return (error);
5027c478bd9Sstevel@tonic-gate }
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate /*
5067c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_request:
5077c478bd9Sstevel@tonic-gate  *
5087c478bd9Sstevel@tonic-gate  * Intercept a root hub request. Handle the  root hub request through the
5097c478bd9Sstevel@tonic-gate  * registers
5107c478bd9Sstevel@tonic-gate  */
5117c478bd9Sstevel@tonic-gate /* ARGSUSED */
5127c478bd9Sstevel@tonic-gate int
5137c478bd9Sstevel@tonic-gate ehci_handle_root_hub_request(
5147c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
5157c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
5167c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp)
5177c478bd9Sstevel@tonic-gate {
5187c478bd9Sstevel@tonic-gate 	uchar_t			bmRequestType = ctrl_reqp->ctrl_bmRequestType;
5197c478bd9Sstevel@tonic-gate 	uchar_t			bRequest = ctrl_reqp->ctrl_bRequest;
5207c478bd9Sstevel@tonic-gate 	uint16_t		wValue = ctrl_reqp->ctrl_wValue;
5217c478bd9Sstevel@tonic-gate 	uint16_t		wIndex = ctrl_reqp->ctrl_wIndex;
5227c478bd9Sstevel@tonic-gate 	uint16_t		wLength = ctrl_reqp->ctrl_wLength;
5237c478bd9Sstevel@tonic-gate 	mblk_t			*data = ctrl_reqp->ctrl_data;
5247c478bd9Sstevel@tonic-gate 	uint16_t		port = wIndex - 1;
5257c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason;
5267c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
5297c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_request: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p",
5307c478bd9Sstevel@tonic-gate 	    bmRequestType, bRequest, wValue, wIndex, wLength, (void *)data);
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_root_hub.
5357c478bd9Sstevel@tonic-gate 	    rh_ctrl_pipe_state != EHCI_PIPE_STATE_IDLE) {
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
5387c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_request: Pipe is not idle");
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
5437c478bd9Sstevel@tonic-gate 	}
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 	/* Save the current control request pointer */
5467c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_curr_ctrl_reqp = ctrl_reqp;
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 	/* Set pipe state to active */
5497c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_ACTIVE;
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	switch (bmRequestType) {
55435f36846Ssl 	case HUB_GET_DEVICE_STATUS_TYPE:
55535f36846Ssl 		ehci_handle_get_device_status(ehcip);
55635f36846Ssl 		break;
55735f36846Ssl 	case HUB_HANDLE_PORT_FEATURE_TYPE:
5587c478bd9Sstevel@tonic-gate 		error = ehci_handle_set_clear_port_feature(ehcip,
5597c478bd9Sstevel@tonic-gate 		    bRequest, wValue, port);
5607c478bd9Sstevel@tonic-gate 		break;
56135f36846Ssl 	case HUB_GET_PORT_STATUS_TYPE:
5627c478bd9Sstevel@tonic-gate 		ehci_handle_get_port_status(ehcip, port);
5637c478bd9Sstevel@tonic-gate 		break;
56435f36846Ssl 	case HUB_CLASS_REQ_TYPE:
5657c478bd9Sstevel@tonic-gate 		switch (bRequest) {
5667c478bd9Sstevel@tonic-gate 		case USB_REQ_GET_STATUS:
5677c478bd9Sstevel@tonic-gate 			ehci_handle_get_hub_status(ehcip);
5687c478bd9Sstevel@tonic-gate 			break;
5697c478bd9Sstevel@tonic-gate 		case USB_REQ_GET_DESCR:
5707c478bd9Sstevel@tonic-gate 			ehci_handle_get_hub_descriptor(ehcip);
5717c478bd9Sstevel@tonic-gate 			break;
5727c478bd9Sstevel@tonic-gate 		default:
5737c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
5747c478bd9Sstevel@tonic-gate 			    "ehci_handle_root_hub_request:"
5757c478bd9Sstevel@tonic-gate 			    "Unsupported request 0x%x", bRequest);
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
5787c478bd9Sstevel@tonic-gate 			break;
5797c478bd9Sstevel@tonic-gate 		}
5807c478bd9Sstevel@tonic-gate 		break;
5817c478bd9Sstevel@tonic-gate 	default:
5827c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
5837c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_request: "
5847c478bd9Sstevel@tonic-gate 		    "Unsupported request 0x%x", bmRequestType);
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
5877c478bd9Sstevel@tonic-gate 		break;
5887c478bd9Sstevel@tonic-gate 	}
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 	completion_reason = (error) ? USB_CR_NOT_SUPPORTED : USB_CR_OK;
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
5937c478bd9Sstevel@tonic-gate 	ehci_root_hub_hcdi_callback(ph, completion_reason);
5947c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
5977c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_request: error = %d", error);
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
6007c478bd9Sstevel@tonic-gate }
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate /*
6047c478bd9Sstevel@tonic-gate  * ehci_handle_set_clear_port_feature:
6057c478bd9Sstevel@tonic-gate  */
6067c478bd9Sstevel@tonic-gate static int
6077c478bd9Sstevel@tonic-gate ehci_handle_set_clear_port_feature(
6087c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
6097c478bd9Sstevel@tonic-gate 	uchar_t 		bRequest,
6107c478bd9Sstevel@tonic-gate 	uint16_t		wValue,
6117c478bd9Sstevel@tonic-gate 	uint16_t		port)
6127c478bd9Sstevel@tonic-gate {
6137c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
6167c478bd9Sstevel@tonic-gate 	    "ehci_handle_set_clear_port_feature: 0x%x 0x%x 0x%x",
6177c478bd9Sstevel@tonic-gate 	    bRequest, wValue, port);
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	switch (bRequest) {
6207c478bd9Sstevel@tonic-gate 	case USB_REQ_SET_FEATURE:
6217c478bd9Sstevel@tonic-gate 		switch (wValue) {
6227c478bd9Sstevel@tonic-gate 		case CFS_PORT_ENABLE:
6237c478bd9Sstevel@tonic-gate 			ehci_handle_port_enable(ehcip, port, 1);
6247c478bd9Sstevel@tonic-gate 			break;
6257c478bd9Sstevel@tonic-gate 		case CFS_PORT_SUSPEND:
6267c478bd9Sstevel@tonic-gate 			ehci_handle_port_suspend(ehcip, port, 1);
6277c478bd9Sstevel@tonic-gate 			break;
6287c478bd9Sstevel@tonic-gate 		case CFS_PORT_RESET:
6297c478bd9Sstevel@tonic-gate 			ehci_handle_port_reset(ehcip, port);
6307c478bd9Sstevel@tonic-gate 			break;
6317c478bd9Sstevel@tonic-gate 		case CFS_PORT_POWER:
6327c478bd9Sstevel@tonic-gate 			ehci_handle_port_power(ehcip, port, 1);
6337c478bd9Sstevel@tonic-gate 			break;
6347c478bd9Sstevel@tonic-gate 		default:
6357c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
6367c478bd9Sstevel@tonic-gate 			    "ehci_handle_set_clear_port_feature: "
6377c478bd9Sstevel@tonic-gate 			    "Unsupported request 0x%x 0x%x", bRequest, wValue);
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
6407c478bd9Sstevel@tonic-gate 			break;
6417c478bd9Sstevel@tonic-gate 		}
6427c478bd9Sstevel@tonic-gate 		break;
6437c478bd9Sstevel@tonic-gate 	case USB_REQ_CLEAR_FEATURE:
6447c478bd9Sstevel@tonic-gate 		switch (wValue) {
6457c478bd9Sstevel@tonic-gate 		case CFS_PORT_ENABLE:
6467c478bd9Sstevel@tonic-gate 			ehci_handle_port_enable(ehcip, port, 0);
6477c478bd9Sstevel@tonic-gate 			break;
6487c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_ENABLE:
6497c478bd9Sstevel@tonic-gate 			ehci_handle_clrchng_port_enable(ehcip, port);
6507c478bd9Sstevel@tonic-gate 			break;
6517c478bd9Sstevel@tonic-gate 		case CFS_PORT_SUSPEND:
6527c478bd9Sstevel@tonic-gate 			ehci_handle_port_suspend(ehcip, port, 0);
6537c478bd9Sstevel@tonic-gate 			break;
6547c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_SUSPEND:
6557c478bd9Sstevel@tonic-gate 			ehci_handle_clrchng_port_suspend(ehcip, port);
6567c478bd9Sstevel@tonic-gate 			break;
6577c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_RESET:
6587c478bd9Sstevel@tonic-gate 			ehci_handle_complete_port_reset(ehcip, port);
6597c478bd9Sstevel@tonic-gate 			break;
6607c478bd9Sstevel@tonic-gate 		case CFS_PORT_POWER:
6617c478bd9Sstevel@tonic-gate 			ehci_handle_port_power(ehcip, port, 0);
6627c478bd9Sstevel@tonic-gate 			break;
6637c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_CONNECTION:
6647c478bd9Sstevel@tonic-gate 			ehci_handle_clear_port_connection(ehcip, port);
6657c478bd9Sstevel@tonic-gate 			break;
6667c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_OVER_CURRENT:
6677c478bd9Sstevel@tonic-gate 			ehci_handle_clrchng_port_over_current(ehcip, port);
6687c478bd9Sstevel@tonic-gate 			break;
6697c478bd9Sstevel@tonic-gate 		default:
6707c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
6717c478bd9Sstevel@tonic-gate 			    "ehci_handle_set_clear_port_feature: "
6727c478bd9Sstevel@tonic-gate 			    "Unsupported request 0x%x 0x%x", bRequest, wValue);
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
6757c478bd9Sstevel@tonic-gate 			break;
6767c478bd9Sstevel@tonic-gate 		}
677*112116d8Sfb 		break;
6787c478bd9Sstevel@tonic-gate 	default:
6797c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
6807c478bd9Sstevel@tonic-gate 		    "ehci_handle_set_clear_port_feature: "
6817c478bd9Sstevel@tonic-gate 		    "Unsupported request 0x%x 0x%x", bRequest, wValue);
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
6847c478bd9Sstevel@tonic-gate 		break;
6857c478bd9Sstevel@tonic-gate 	}
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 	return (error);
6887c478bd9Sstevel@tonic-gate }
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate /*
6927c478bd9Sstevel@tonic-gate  * ehci_handle_port_power:
6937c478bd9Sstevel@tonic-gate  *
6947c478bd9Sstevel@tonic-gate  * Turn on a root hub port.
6957c478bd9Sstevel@tonic-gate  */
6967c478bd9Sstevel@tonic-gate static void
6977c478bd9Sstevel@tonic-gate ehci_handle_port_power(
6987c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
6997c478bd9Sstevel@tonic-gate 	uint16_t		port,
7007c478bd9Sstevel@tonic-gate 	uint_t			on)
7017c478bd9Sstevel@tonic-gate {
7027c478bd9Sstevel@tonic-gate 	uint_t			port_status;
7037c478bd9Sstevel@tonic-gate 	ehci_root_hub_t		*rh;
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
7087c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 	rh = &ehcip->ehci_root_hub;
7117c478bd9Sstevel@tonic-gate 
7127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
7137c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_power: port = 0x%x status = 0x%x on = %d",
7147c478bd9Sstevel@tonic-gate 	    port, port_status, on);
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
7177c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
7187c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
7197c478bd9Sstevel@tonic-gate 
7207c478bd9Sstevel@tonic-gate 		return;
7217c478bd9Sstevel@tonic-gate 	}
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 	if (on) {
7247c478bd9Sstevel@tonic-gate 		/* See if the port power is already on */
7257c478bd9Sstevel@tonic-gate 		if (!(port_status & EHCI_RH_PORT_POWER)) {
7267c478bd9Sstevel@tonic-gate 			/* Turn the port on */
7277c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
7287c478bd9Sstevel@tonic-gate 			    port_status | EHCI_RH_PORT_POWER);
7297c478bd9Sstevel@tonic-gate 		}
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 		rh->rh_port_status[port] = 0;
7327c478bd9Sstevel@tonic-gate 		rh->rh_port_state[port] = DISCONNECTED;
7337c478bd9Sstevel@tonic-gate 	} else {
7347c478bd9Sstevel@tonic-gate 		/* See if the port power is already OFF */
7357c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_POWER) {
7367c478bd9Sstevel@tonic-gate 			/* Turn-off the port */
7377c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
7387c478bd9Sstevel@tonic-gate 			    port_status & ~EHCI_RH_PORT_POWER);
7397c478bd9Sstevel@tonic-gate 		}
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 		rh->rh_port_status[port] = 0;
7427c478bd9Sstevel@tonic-gate 		rh->rh_port_state[port] = POWERED_OFF;
7437c478bd9Sstevel@tonic-gate 	}
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
7467c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_power done: port = 0x%x status = 0x%x on = %d",
7477c478bd9Sstevel@tonic-gate 	    port, Get_OpReg(ehci_rh_port_status[port]), on);
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
7507c478bd9Sstevel@tonic-gate }
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate /*
7547c478bd9Sstevel@tonic-gate  * ehci_handle_port_enable:
7557c478bd9Sstevel@tonic-gate  *
7567c478bd9Sstevel@tonic-gate  * Handle port enable request.
7577c478bd9Sstevel@tonic-gate  */
7587c478bd9Sstevel@tonic-gate static void
7597c478bd9Sstevel@tonic-gate ehci_handle_port_enable(
7607c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
7617c478bd9Sstevel@tonic-gate 	uint16_t		port,
7627c478bd9Sstevel@tonic-gate 	uint_t			on)
7637c478bd9Sstevel@tonic-gate {
7647c478bd9Sstevel@tonic-gate 	uint_t			port_status;
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
7697c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
7727c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_enable: port = 0x%x, status = 0x%x",
7737c478bd9Sstevel@tonic-gate 	    port, port_status);
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
7767c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
7777c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 		return;
7807c478bd9Sstevel@tonic-gate 	}
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 	if (on) {
7837c478bd9Sstevel@tonic-gate 		/* See if the port enable is already on */
7847c478bd9Sstevel@tonic-gate 		if (!(port_status & EHCI_RH_PORT_ENABLE)) {
7857c478bd9Sstevel@tonic-gate 			/* Enable the port */
7867c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
7877c478bd9Sstevel@tonic-gate 			    port_status | EHCI_RH_PORT_ENABLE);
7887c478bd9Sstevel@tonic-gate 		}
7897c478bd9Sstevel@tonic-gate 	} else {
7907c478bd9Sstevel@tonic-gate 		/* See if the port enable is already off */
7917c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_ENABLE) {
7927c478bd9Sstevel@tonic-gate 			/* Disable the port */
7937c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
7947c478bd9Sstevel@tonic-gate 			    port_status & ~EHCI_RH_PORT_ENABLE);
7957c478bd9Sstevel@tonic-gate 		}
7967c478bd9Sstevel@tonic-gate 	}
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
7997c478bd9Sstevel@tonic-gate }
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate /*
8037c478bd9Sstevel@tonic-gate  * ehci_handle_clrchng_port_enable:
8047c478bd9Sstevel@tonic-gate  *
8057c478bd9Sstevel@tonic-gate  * Handle clear port enable change bit.
8067c478bd9Sstevel@tonic-gate  */
8077c478bd9Sstevel@tonic-gate static void
8087c478bd9Sstevel@tonic-gate ehci_handle_clrchng_port_enable(
8097c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
8107c478bd9Sstevel@tonic-gate 	uint16_t		port)
8117c478bd9Sstevel@tonic-gate {
8127c478bd9Sstevel@tonic-gate 	uint_t			port_status;
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
8177c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
8207c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_enable: port = 0x%x, status = 0x%x",
8217c478bd9Sstevel@tonic-gate 	    port, port_status);
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
8247c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
8257c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 		return;
8287c478bd9Sstevel@tonic-gate 	}
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 	/* Clear the PortEnableStatusChange Bit */
8317c478bd9Sstevel@tonic-gate 	Set_OpReg(ehci_rh_port_status[port],
8327c478bd9Sstevel@tonic-gate 	    port_status | EHCI_RH_PORT_ENABLE_CHANGE);
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
8357c478bd9Sstevel@tonic-gate }
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate /*
8397c478bd9Sstevel@tonic-gate  * ehci_handle_port_suspend:
8407c478bd9Sstevel@tonic-gate  *
8417c478bd9Sstevel@tonic-gate  * Handle port suspend/resume request.
8427c478bd9Sstevel@tonic-gate  */
8437c478bd9Sstevel@tonic-gate static void
8447c478bd9Sstevel@tonic-gate ehci_handle_port_suspend(
8457c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
8467c478bd9Sstevel@tonic-gate 	uint16_t		port,
8477c478bd9Sstevel@tonic-gate 	uint_t			on)
8487c478bd9Sstevel@tonic-gate {
8497c478bd9Sstevel@tonic-gate 	uint_t			port_status;
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
8547c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
8577c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_suspend: port = 0x%x, status = 0x%x",
8587c478bd9Sstevel@tonic-gate 	    port, port_status);
8597c478bd9Sstevel@tonic-gate 
8607c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
8617c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
8627c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 		return;
8657c478bd9Sstevel@tonic-gate 	}
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate 	if (on) {
8687c478bd9Sstevel@tonic-gate 		/*
8697c478bd9Sstevel@tonic-gate 		 * Suspend port only if port is enabled and
8707c478bd9Sstevel@tonic-gate 		 * it is not already in suspend state.
8717c478bd9Sstevel@tonic-gate 		 */
8727c478bd9Sstevel@tonic-gate 		if ((port_status & EHCI_RH_PORT_ENABLE) &&
8737c478bd9Sstevel@tonic-gate 		    (!(port_status & EHCI_RH_PORT_SUSPEND))) {
8747c478bd9Sstevel@tonic-gate 			/* Suspend the port */
8757c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
8767c478bd9Sstevel@tonic-gate 			    port_status | EHCI_RH_PORT_SUSPEND);
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 			mutex_exit(&ehcip->ehci_int_mutex);
8797c478bd9Sstevel@tonic-gate 
8807c478bd9Sstevel@tonic-gate 			/* Wait 10ms for port move to suspend state */
8817c478bd9Sstevel@tonic-gate 			delay(drv_usectohz(EHCI_PORT_SUSPEND_TIMEWAIT));
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate 			return;
8847c478bd9Sstevel@tonic-gate 		}
8857c478bd9Sstevel@tonic-gate 	} else {
8867c478bd9Sstevel@tonic-gate 		/* Perform resume only if port is in suspend state */
8877c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_SUSPEND) {
8887c478bd9Sstevel@tonic-gate 			/* Resume the port */
8897c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
8907c478bd9Sstevel@tonic-gate 			    port_status | EHCI_RH_PORT_RESUME);
8917c478bd9Sstevel@tonic-gate 		}
8927c478bd9Sstevel@tonic-gate 	}
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
8957c478bd9Sstevel@tonic-gate }
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate /*
8997c478bd9Sstevel@tonic-gate  * ehci_handle_clrchng_port_suspend:
9007c478bd9Sstevel@tonic-gate  *
9017c478bd9Sstevel@tonic-gate  * Handle port clear port suspend change bit.
9027c478bd9Sstevel@tonic-gate  */
9037c478bd9Sstevel@tonic-gate static void
9047c478bd9Sstevel@tonic-gate ehci_handle_clrchng_port_suspend(
9057c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
9067c478bd9Sstevel@tonic-gate 	uint16_t		port)
9077c478bd9Sstevel@tonic-gate {
9087c478bd9Sstevel@tonic-gate 	uint_t			port_status;
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
9137c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
9167c478bd9Sstevel@tonic-gate 	    "ehci_handle_clrchng_port_suspend: port = 0x%x, "
9177c478bd9Sstevel@tonic-gate 	    "status = 0x%x", port, port_status);
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
9207c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
9217c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 		return;
9247c478bd9Sstevel@tonic-gate 	}
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 	/* Return if port is not in resume state */
9277c478bd9Sstevel@tonic-gate 	if (!(port_status & EHCI_RH_PORT_RESUME)) {
9287c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 		return;
9317c478bd9Sstevel@tonic-gate 	}
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	/* Wait for 20ms to terminate resume */
9367c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(EHCI_PORT_RESUME_TIMEWAIT));
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 	Set_OpReg(ehci_rh_port_status[port],
9417c478bd9Sstevel@tonic-gate 	    port_status & ~EHCI_RH_PORT_RESUME);
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 	/* Wait 2ms for port to return to high speed mode */
9467c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(EHCI_PORT_RESUME_COMP_TIMEWAIT));
9477c478bd9Sstevel@tonic-gate }
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate /*
9517c478bd9Sstevel@tonic-gate  * ehci_handle_port_reset:
9527c478bd9Sstevel@tonic-gate  *
9537c478bd9Sstevel@tonic-gate  * Perform a port reset.
9547c478bd9Sstevel@tonic-gate  */
9557c478bd9Sstevel@tonic-gate static void
9567c478bd9Sstevel@tonic-gate ehci_handle_port_reset(
9577c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
9587c478bd9Sstevel@tonic-gate 	uint16_t		port)
9597c478bd9Sstevel@tonic-gate {
9607c478bd9Sstevel@tonic-gate 	ehci_root_hub_t		*rh;
9617c478bd9Sstevel@tonic-gate 	uint_t			port_status;
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate 	/* Get the root hub structure */
9667c478bd9Sstevel@tonic-gate 	rh = &ehcip->ehci_root_hub;
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	/* Get the port status information */
9697c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
9707c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
9737c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_reset: port = 0x%x status = 0x%x",
9747c478bd9Sstevel@tonic-gate 	    port, port_status);
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
9777c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
9787c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate 		return;
9817c478bd9Sstevel@tonic-gate 	}
9827c478bd9Sstevel@tonic-gate 
9837c478bd9Sstevel@tonic-gate 	if (port_status & EHCI_RH_PORT_LOW_SPEED) {
9847c478bd9Sstevel@tonic-gate 		/* Check for classic or companion host controllers */
9857c478bd9Sstevel@tonic-gate 		if (rh->rh_companion_controllers) {
9867c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
9877c478bd9Sstevel@tonic-gate 			    "ehci_handle_port_reset: Low speed device "
9887c478bd9Sstevel@tonic-gate 			    "and handover this port to Companion controller");
9897c478bd9Sstevel@tonic-gate 
9907c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
9917c478bd9Sstevel@tonic-gate 			    port_status | EHCI_RH_PORT_OWNER_CLASSIC);
9927c478bd9Sstevel@tonic-gate 		} else {
9937c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L1(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
9947c478bd9Sstevel@tonic-gate 			    "Low speed device is not supported");
9957c478bd9Sstevel@tonic-gate 		}
9967c478bd9Sstevel@tonic-gate 	} else {
9977c478bd9Sstevel@tonic-gate 		Set_OpReg(ehci_rh_port_status[port],
9987c478bd9Sstevel@tonic-gate 		    ((port_status | EHCI_RH_PORT_RESET) &
9997c478bd9Sstevel@tonic-gate 		    ~EHCI_RH_PORT_ENABLE));
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
10027c478bd9Sstevel@tonic-gate 
10037cf28e56Sgc 		/* Wait 20ms for reset to complete */
10047c478bd9Sstevel@tonic-gate 		delay(drv_usectohz(EHCI_PORT_RESET_TIMEWAIT));
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 		mutex_enter(&ehcip->ehci_int_mutex);
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate 		port_status = Get_OpReg(ehci_rh_port_status[port]) &
10097c478bd9Sstevel@tonic-gate 		    ~EHCI_RH_PORT_CLEAR_MASK;
10107c478bd9Sstevel@tonic-gate 
10117c478bd9Sstevel@tonic-gate 		Set_OpReg(ehci_rh_port_status[port],
10127c478bd9Sstevel@tonic-gate 		    (port_status & ~EHCI_RH_PORT_RESET));
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
10157c478bd9Sstevel@tonic-gate 
10167c478bd9Sstevel@tonic-gate 		/*
10177c478bd9Sstevel@tonic-gate 		 * Wait 2ms for hardware to enable this port
10187c478bd9Sstevel@tonic-gate 		 * if connected usb device is high speed.
10197c478bd9Sstevel@tonic-gate 		 */
10207c478bd9Sstevel@tonic-gate 		delay(drv_usectohz(EHCI_PORT_RESET_COMP_TIMEWAIT));
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 		mutex_enter(&ehcip->ehci_int_mutex);
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 		port_status = Get_OpReg(ehci_rh_port_status[port]) &
10257c478bd9Sstevel@tonic-gate 		    ~EHCI_RH_PORT_CLEAR_MASK;
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 		/*
10287c478bd9Sstevel@tonic-gate 		 * If port is not enabled, connected device is a
10297c478bd9Sstevel@tonic-gate 		 * Full-speed usb device.
10307c478bd9Sstevel@tonic-gate 		 */
10317c478bd9Sstevel@tonic-gate 		if (!(port_status & EHCI_RH_PORT_ENABLE)) {
10327c478bd9Sstevel@tonic-gate 			/* Check for classic or companion host controllers */
10337c478bd9Sstevel@tonic-gate 			if (rh->rh_companion_controllers) {
10347c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
10357c478bd9Sstevel@tonic-gate 				    ehcip->ehci_log_hdl,
10367c478bd9Sstevel@tonic-gate 				    "ehci_handle_port_reset: Full speed device "
10377c478bd9Sstevel@tonic-gate 				    "and handover this port to Companion host "
10387c478bd9Sstevel@tonic-gate 				    "controller");
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 				Set_OpReg(ehci_rh_port_status[port],
10417c478bd9Sstevel@tonic-gate 				    port_status | EHCI_RH_PORT_OWNER_CLASSIC);
10427c478bd9Sstevel@tonic-gate 			} else {
10437c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L1(PRINT_MASK_ROOT_HUB,
10447c478bd9Sstevel@tonic-gate 				    ehcip->ehci_log_hdl,
10457c478bd9Sstevel@tonic-gate 				    "Full speed device is not supported");
10467c478bd9Sstevel@tonic-gate 			}
10477c478bd9Sstevel@tonic-gate 		} else {
10487c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
10497c478bd9Sstevel@tonic-gate 			    "ehci_handle_port_reset: High speed device ");
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 			port_status = Get_OpReg(ehci_rh_port_status[port]) &
10527c478bd9Sstevel@tonic-gate 			    ~EHCI_RH_PORT_CLEAR_MASK;
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 			/*
10557c478bd9Sstevel@tonic-gate 			 * Enable over-current, connect, and disconnect
10567c478bd9Sstevel@tonic-gate 			 * wakeup bits.
10577c478bd9Sstevel@tonic-gate 			 */
10587c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port], (port_status |
10597c478bd9Sstevel@tonic-gate 			    EHCI_RH_PORT_OVER_CURENT_ENABLE |
10607c478bd9Sstevel@tonic-gate 			    EHCI_RH_PORT_DISCONNECT_ENABLE |
10617c478bd9Sstevel@tonic-gate 			    EHCI_RH_PORT_CONNECT_ENABLE));
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 			/*
10647c478bd9Sstevel@tonic-gate 			 * The next function is only called if the interrupt
10657c478bd9Sstevel@tonic-gate 			 * pipe is polling and the USBA is ready to receive
10667c478bd9Sstevel@tonic-gate 			 * the data.
10677c478bd9Sstevel@tonic-gate 			 */
10687c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.
10697c478bd9Sstevel@tonic-gate 			    rh_intr_pending_status |= (1 << port);
10707c478bd9Sstevel@tonic-gate 
10717c478bd9Sstevel@tonic-gate 			if (ehcip->ehci_root_hub.
10727c478bd9Sstevel@tonic-gate 			    rh_intr_pipe_state == EHCI_PIPE_STATE_ACTIVE) {
10737c478bd9Sstevel@tonic-gate 
10747c478bd9Sstevel@tonic-gate 				ehci_root_hub_reset_occured(ehcip);
10757c478bd9Sstevel@tonic-gate 			}
10767c478bd9Sstevel@tonic-gate 		}
10777c478bd9Sstevel@tonic-gate 	}
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
10807c478bd9Sstevel@tonic-gate }
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate /*
10847c478bd9Sstevel@tonic-gate  * ehci_root_hub_reset_occured:
10857c478bd9Sstevel@tonic-gate  *
10867c478bd9Sstevel@tonic-gate  * Inform the upper layer that reset has occured on the port. This is
10877c478bd9Sstevel@tonic-gate  * required because the upper layer is expecting a an evernt immidiately
10887c478bd9Sstevel@tonic-gate  * after doing reset. In case of OHCI, the controller gets an interrupt
10897c478bd9Sstevel@tonic-gate  * for the change in the root hub status but in case of EHCI, we dont.
10907c478bd9Sstevel@tonic-gate  * So, send a event to the upper layer as soon as we complete the reset.
10917c478bd9Sstevel@tonic-gate  */
10927c478bd9Sstevel@tonic-gate void
10937c478bd9Sstevel@tonic-gate ehci_root_hub_reset_occured(
10947c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip)
10957c478bd9Sstevel@tonic-gate {
10967c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp =
1097*112116d8Sfb 	    ehcip->ehci_root_hub.rh_curr_intr_reqp;
10987c478bd9Sstevel@tonic-gate 	usb_port_mask_t		port_mask;
10997c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
11027c478bd9Sstevel@tonic-gate 	    "ehci_root_hub_reset_occured: curr_intr_reqp = 0x%p data = 0x%p",
1103*112116d8Sfb 	    (void *)curr_intr_reqp, (void *)curr_intr_reqp->intr_data);
11047c478bd9Sstevel@tonic-gate 
11057c478bd9Sstevel@tonic-gate 	/* Get the interrupt pipe handle */
11067c478bd9Sstevel@tonic-gate 	ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
11077c478bd9Sstevel@tonic-gate 
11087c478bd9Sstevel@tonic-gate 	/* Get the pending status */
11097c478bd9Sstevel@tonic-gate 	port_mask = ehcip->ehci_root_hub.rh_intr_pending_status << 1;
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	do {
11127c478bd9Sstevel@tonic-gate 		*curr_intr_reqp->intr_data->b_wptr++ = (uchar_t)port_mask;
11137c478bd9Sstevel@tonic-gate 		port_mask >>= 8;
11147c478bd9Sstevel@tonic-gate 	} while (port_mask != 0);
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	ehci_root_hub_hcdi_callback(ph, USB_CR_OK);
11177c478bd9Sstevel@tonic-gate 
11187c478bd9Sstevel@tonic-gate 	/* Reset pending status */
11197c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_intr_pending_status = 0;
11207c478bd9Sstevel@tonic-gate 
11217c478bd9Sstevel@tonic-gate 	/* If needed, allocate new interrupt request */
11227c478bd9Sstevel@tonic-gate 	if ((ehci_root_hub_allocate_intr_pipe_resource(
11237c478bd9Sstevel@tonic-gate 	    ehcip, 0)) != USB_SUCCESS) {
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 		/* Do interrupt pipe cleanup */
11267c478bd9Sstevel@tonic-gate 		ehci_root_hub_intr_pipe_cleanup(ehcip, USB_CR_NO_RESOURCES);
11277c478bd9Sstevel@tonic-gate 	}
11287c478bd9Sstevel@tonic-gate }
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate /*
11327c478bd9Sstevel@tonic-gate  * ehci_handle_complete_port_reset:
11337c478bd9Sstevel@tonic-gate  *
11347c478bd9Sstevel@tonic-gate  * Perform a port reset change.
11357c478bd9Sstevel@tonic-gate  */
11367c478bd9Sstevel@tonic-gate static void
11377c478bd9Sstevel@tonic-gate ehci_handle_complete_port_reset(
11387c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
11397c478bd9Sstevel@tonic-gate 	uint16_t		port)
11407c478bd9Sstevel@tonic-gate {
11417c478bd9Sstevel@tonic-gate 	uint_t			port_status;
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
11447c478bd9Sstevel@tonic-gate 
11457c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
11467c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
11497c478bd9Sstevel@tonic-gate 	    "ehci_handle_complete_port_reset: port = 0x%x status = 0x%x",
11507c478bd9Sstevel@tonic-gate 	    port, port_status);
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
11537c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
11547c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
11557c478bd9Sstevel@tonic-gate 
11567c478bd9Sstevel@tonic-gate 		return;
11577c478bd9Sstevel@tonic-gate 	}
11587c478bd9Sstevel@tonic-gate 
11597c478bd9Sstevel@tonic-gate 	if (port_status & EHCI_RH_PORT_RESET) {
11607c478bd9Sstevel@tonic-gate 		Set_OpReg(ehci_rh_port_status[port],
11617c478bd9Sstevel@tonic-gate 		    port_status & ~EHCI_RH_PORT_RESET);
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate 	}
11647c478bd9Sstevel@tonic-gate 
11657c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
11667c478bd9Sstevel@tonic-gate }
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate /*
11707c478bd9Sstevel@tonic-gate  * ehci_handle_clear_port_connection:
11717c478bd9Sstevel@tonic-gate  *
11727c478bd9Sstevel@tonic-gate  * Perform a clear port connection.
11737c478bd9Sstevel@tonic-gate  */
11747c478bd9Sstevel@tonic-gate static void
11757c478bd9Sstevel@tonic-gate ehci_handle_clear_port_connection(
11767c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
11777c478bd9Sstevel@tonic-gate 	uint16_t		port)
11787c478bd9Sstevel@tonic-gate {
11797c478bd9Sstevel@tonic-gate 	uint_t			port_status;
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
11827c478bd9Sstevel@tonic-gate 
11837c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
11847c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
11877c478bd9Sstevel@tonic-gate 	    "ehci_handle_clear_port_connection: port = 0x%x"
11887c478bd9Sstevel@tonic-gate 	    "status = 0x%x", port, port_status);
11897c478bd9Sstevel@tonic-gate 
11907c478bd9Sstevel@tonic-gate 	Set_OpReg(ehci_rh_port_status[port],
11917c478bd9Sstevel@tonic-gate 	    port_status | EHCI_RH_PORT_CONNECT_STS_CHANGE);
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
11947c478bd9Sstevel@tonic-gate }
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate /*
11987c478bd9Sstevel@tonic-gate  * ehci_handle_clrchng_port_over_current:
11997c478bd9Sstevel@tonic-gate  *
12007c478bd9Sstevel@tonic-gate  * Perform a clear port connection.
12017c478bd9Sstevel@tonic-gate  */
12027c478bd9Sstevel@tonic-gate static void
12037c478bd9Sstevel@tonic-gate ehci_handle_clrchng_port_over_current(
12047c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
12057c478bd9Sstevel@tonic-gate 	uint16_t		port)
12067c478bd9Sstevel@tonic-gate {
12077c478bd9Sstevel@tonic-gate 	uint_t			port_status;
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
12107c478bd9Sstevel@tonic-gate 
12117c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
12127c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
12157c478bd9Sstevel@tonic-gate 	    "ehci_handle_clrchng_port_over_current: port = 0x%x"
12167c478bd9Sstevel@tonic-gate 	    "status = 0x%x", port, port_status);
12177c478bd9Sstevel@tonic-gate 
12187c478bd9Sstevel@tonic-gate 	Set_OpReg(ehci_rh_port_status[port],
12197c478bd9Sstevel@tonic-gate 	    port_status | EHCI_RH_PORT_OVER_CURR_CHANGE);
12207c478bd9Sstevel@tonic-gate 
12217c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
12227c478bd9Sstevel@tonic-gate }
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate 
12257c478bd9Sstevel@tonic-gate /*
12267c478bd9Sstevel@tonic-gate  * ehci_handle_get_port_status:
12277c478bd9Sstevel@tonic-gate  *
12287c478bd9Sstevel@tonic-gate  * Handle a get port status request.
12297c478bd9Sstevel@tonic-gate  */
12307c478bd9Sstevel@tonic-gate static void
12317c478bd9Sstevel@tonic-gate ehci_handle_get_port_status(
12327c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
12337c478bd9Sstevel@tonic-gate 	uint16_t		port)
12347c478bd9Sstevel@tonic-gate {
12357c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp;
12367c478bd9Sstevel@tonic-gate 	mblk_t			*message;
12377c478bd9Sstevel@tonic-gate 	uint_t			new_port_status = 0;
12387c478bd9Sstevel@tonic-gate 	uint_t			change_status = 0;
12397c478bd9Sstevel@tonic-gate 	uint_t			port_status;
12407c478bd9Sstevel@tonic-gate 
12417c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate 	ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate 	/* Get the root hub port status information */
12467c478bd9Sstevel@tonic-gate 	port_status = ehci_get_root_hub_port_status(ehcip, port);
12477c478bd9Sstevel@tonic-gate 
12487c478bd9Sstevel@tonic-gate 	new_port_status = port_status & PORT_STATUS_MASK;
12497c478bd9Sstevel@tonic-gate 	change_status = (port_status >> 16) & PORT_CHANGE_MASK;
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_port_status[port] = new_port_status;
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
12547c478bd9Sstevel@tonic-gate 	    "ehci_handle_get_port_status: port = %d new status = 0x%x"
12557c478bd9Sstevel@tonic-gate 	    "change = 0x%x", port, new_port_status, change_status);
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate 	message = ctrl_reqp->ctrl_data;
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate 	ASSERT(message != NULL);
12607c478bd9Sstevel@tonic-gate 
12617c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)new_port_status;
12627c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)(new_port_status >> 8);
12637c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)change_status;
12647c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)(change_status >> 8);
12657c478bd9Sstevel@tonic-gate 
12667c478bd9Sstevel@tonic-gate 	/* Save the data in control request */
12677c478bd9Sstevel@tonic-gate 	ctrl_reqp->ctrl_data = message;
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
12707c478bd9Sstevel@tonic-gate }
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate 
12737c478bd9Sstevel@tonic-gate /*
12747c478bd9Sstevel@tonic-gate  * ehci_handle_get_hub_descriptor:
12757c478bd9Sstevel@tonic-gate  */
12767c478bd9Sstevel@tonic-gate static void
12777c478bd9Sstevel@tonic-gate ehci_handle_get_hub_descriptor(
12787c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip)
12797c478bd9Sstevel@tonic-gate {
12807c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp;
12817c478bd9Sstevel@tonic-gate 	mblk_t			*message;
12827c478bd9Sstevel@tonic-gate 	usb_hub_descr_t		*root_hub_descr;
12837c478bd9Sstevel@tonic-gate 	size_t			length;
12847c478bd9Sstevel@tonic-gate 	uchar_t			raw_descr[ROOT_HUB_DESCRIPTOR_LENGTH];
12857c478bd9Sstevel@tonic-gate 
12867c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
12877c478bd9Sstevel@tonic-gate 
12887c478bd9Sstevel@tonic-gate 	ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
12897c478bd9Sstevel@tonic-gate 	root_hub_descr = &ehcip->ehci_root_hub.rh_descr;
12907c478bd9Sstevel@tonic-gate 	length = ctrl_reqp->ctrl_wLength;
12917c478bd9Sstevel@tonic-gate 
12927c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
1293*112116d8Sfb 	    "ehci_handle_get_hub_descriptor: Ctrl Req  = 0x%p",
1294*112116d8Sfb 	    (void *)ctrl_reqp);
12957c478bd9Sstevel@tonic-gate 
12967c478bd9Sstevel@tonic-gate 	message = ctrl_reqp->ctrl_data;
12977c478bd9Sstevel@tonic-gate 
12987c478bd9Sstevel@tonic-gate 	ASSERT(message != NULL);
12997c478bd9Sstevel@tonic-gate 
13007c478bd9Sstevel@tonic-gate 	bzero(&raw_descr, ROOT_HUB_DESCRIPTOR_LENGTH);
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate 	raw_descr[0] = root_hub_descr->bDescLength;
13037c478bd9Sstevel@tonic-gate 	raw_descr[1] = root_hub_descr->bDescriptorType;
13047c478bd9Sstevel@tonic-gate 	raw_descr[2] = root_hub_descr->bNbrPorts;
13057c478bd9Sstevel@tonic-gate 	raw_descr[3] = root_hub_descr->wHubCharacteristics & 0x00FF;
13067c478bd9Sstevel@tonic-gate 	raw_descr[4] = (root_hub_descr->wHubCharacteristics & 0xFF00) >> 8;
13077c478bd9Sstevel@tonic-gate 	raw_descr[5] = root_hub_descr->bPwrOn2PwrGood;
13087c478bd9Sstevel@tonic-gate 	raw_descr[6] = root_hub_descr->bHubContrCurrent;
13097c478bd9Sstevel@tonic-gate 	raw_descr[7] = root_hub_descr->DeviceRemovable;
13107c478bd9Sstevel@tonic-gate 	raw_descr[8] = root_hub_descr->PortPwrCtrlMask;
13117c478bd9Sstevel@tonic-gate 
13127c478bd9Sstevel@tonic-gate 	bcopy(raw_descr, message->b_wptr, length);
13137c478bd9Sstevel@tonic-gate 	message->b_wptr += length;
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate 	/* Save the data in control request */
13167c478bd9Sstevel@tonic-gate 	ctrl_reqp->ctrl_data = message;
13177c478bd9Sstevel@tonic-gate 
13187c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
13197c478bd9Sstevel@tonic-gate }
13207c478bd9Sstevel@tonic-gate 
13217c478bd9Sstevel@tonic-gate 
13227c478bd9Sstevel@tonic-gate /*
13237c478bd9Sstevel@tonic-gate  * ehci_handle_get_hub_status:
13247c478bd9Sstevel@tonic-gate  *
13257c478bd9Sstevel@tonic-gate  * Handle a get hub status request.
13267c478bd9Sstevel@tonic-gate  */
13277c478bd9Sstevel@tonic-gate static void
13287c478bd9Sstevel@tonic-gate ehci_handle_get_hub_status(
13297c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip)
13307c478bd9Sstevel@tonic-gate {
13317c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp;
13327c478bd9Sstevel@tonic-gate 	mblk_t			*message;
13337c478bd9Sstevel@tonic-gate 	uint_t			new_root_hub_status;
13347c478bd9Sstevel@tonic-gate 
13357c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate 	ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 	/*
13407c478bd9Sstevel@tonic-gate 	 * For EHCI, there is no overall hub status information.
13417c478bd9Sstevel@tonic-gate 	 * Only individual root hub port status information is
13427c478bd9Sstevel@tonic-gate 	 * available. So return zero for the root hub status
13437c478bd9Sstevel@tonic-gate 	 * request.
13447c478bd9Sstevel@tonic-gate 	 */
13457c478bd9Sstevel@tonic-gate 	new_root_hub_status = 0;
13467c478bd9Sstevel@tonic-gate 
13477c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
13487c478bd9Sstevel@tonic-gate 	    "ehci_handle_get_hub_status: new root hub status = 0x%x",
13497c478bd9Sstevel@tonic-gate 	    new_root_hub_status);
13507c478bd9Sstevel@tonic-gate 
13517c478bd9Sstevel@tonic-gate 	message = ctrl_reqp->ctrl_data;
13527c478bd9Sstevel@tonic-gate 
13537c478bd9Sstevel@tonic-gate 	ASSERT(message != NULL);
13547c478bd9Sstevel@tonic-gate 
13557c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)new_root_hub_status;
13567c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)(new_root_hub_status >> 8);
13577c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)(new_root_hub_status >> 16);
13587c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)(new_root_hub_status >> 24);
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate 	/* Save the data in control request */
13617c478bd9Sstevel@tonic-gate 	ctrl_reqp->ctrl_data = message;
13627c478bd9Sstevel@tonic-gate 
13637c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
13647c478bd9Sstevel@tonic-gate }
13657c478bd9Sstevel@tonic-gate 
13667c478bd9Sstevel@tonic-gate 
136735f36846Ssl /*
136835f36846Ssl  * ehci_handle_get_device_status:
136935f36846Ssl  *
137035f36846Ssl  * Handle a get device status request.
137135f36846Ssl  */
137235f36846Ssl static void
137335f36846Ssl ehci_handle_get_device_status(
137435f36846Ssl 	ehci_state_t		*ehcip)
137535f36846Ssl {
137635f36846Ssl 	usb_ctrl_req_t		*ctrl_reqp;
137735f36846Ssl 	mblk_t			*message;
137835f36846Ssl 	uint16_t		dev_status;
137935f36846Ssl 
138035f36846Ssl 	mutex_enter(&ehcip->ehci_int_mutex);
138135f36846Ssl 
138235f36846Ssl 	ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
138335f36846Ssl 
138435f36846Ssl 	/*
138535f36846Ssl 	 * For EHCI, there is no device status information.
138635f36846Ssl 	 * Simply return what is desired for the request.
138735f36846Ssl 	 */
138835f36846Ssl 	dev_status = USB_DEV_SLF_PWRD_STATUS;
138935f36846Ssl 
139035f36846Ssl 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
139135f36846Ssl 	    "ehci_handle_get_device_status: device status = 0x%x",
139235f36846Ssl 	    dev_status);
139335f36846Ssl 
139435f36846Ssl 	message = ctrl_reqp->ctrl_data;
139535f36846Ssl 
139635f36846Ssl 	ASSERT(message != NULL);
139735f36846Ssl 
139835f36846Ssl 	*message->b_wptr++ = (uchar_t)dev_status;
139935f36846Ssl 	*message->b_wptr++ = (uchar_t)(dev_status >> 8);
140035f36846Ssl 
140135f36846Ssl 	/* Save the data in control request */
140235f36846Ssl 	ctrl_reqp->ctrl_data = message;
140335f36846Ssl 
140435f36846Ssl 	mutex_exit(&ehcip->ehci_int_mutex);
140535f36846Ssl }
140635f36846Ssl 
140735f36846Ssl 
14087c478bd9Sstevel@tonic-gate /*
14097c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_pipe_start_intr_polling:
14107c478bd9Sstevel@tonic-gate  *
14117c478bd9Sstevel@tonic-gate  * Handle start polling on root hub interrupt pipe.
14127c478bd9Sstevel@tonic-gate  */
14137c478bd9Sstevel@tonic-gate /* ARGSUSED */
14147c478bd9Sstevel@tonic-gate int
14157c478bd9Sstevel@tonic-gate ehci_handle_root_hub_pipe_start_intr_polling(
14167c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
14177c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*client_intr_reqp,
14187c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
14197c478bd9Sstevel@tonic-gate {
14207c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
1421*112116d8Sfb 	    ph->p_usba_device->usb_root_hub_dip);
14227c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
14237c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
14247c478bd9Sstevel@tonic-gate 	uint_t			pipe_state;
14257c478bd9Sstevel@tonic-gate 
14267c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14277c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_pipe_start_intr_polling: "
14287c478bd9Sstevel@tonic-gate 	    "Root hub pipe start polling");
14297c478bd9Sstevel@tonic-gate 
14307c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
14317c478bd9Sstevel@tonic-gate 
14327c478bd9Sstevel@tonic-gate 	ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 	ASSERT((client_intr_reqp->intr_attributes & USB_ATTRS_ONE_XFER) == 0);
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate 	pipe_state = ehcip->ehci_root_hub.rh_intr_pipe_state;
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 	switch (pipe_state) {
14397c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_IDLE:
14407c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.rh_intr_pipe_timer_id == 0);
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 		/*
14437c478bd9Sstevel@tonic-gate 		 * Save the Original Client's Interrupt IN request
14447c478bd9Sstevel@tonic-gate 		 * information. We use this for final callback
14457c478bd9Sstevel@tonic-gate 		 */
14467c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.rh_client_intr_reqp == NULL);
14477c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_client_intr_reqp = client_intr_reqp;
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 		error = ehci_root_hub_allocate_intr_pipe_resource(ehcip, flags);
14507c478bd9Sstevel@tonic-gate 
14517c478bd9Sstevel@tonic-gate 		if (error != USB_SUCCESS) {
14527c478bd9Sstevel@tonic-gate 			/* Reset client interrupt request pointer */
14537c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.rh_client_intr_reqp = NULL;
14547c478bd9Sstevel@tonic-gate 
14557c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14567c478bd9Sstevel@tonic-gate 			    "ehci_handle_root_hub_pipe_start_intr_polling: "
14577c478bd9Sstevel@tonic-gate 			    "No Resources");
14587c478bd9Sstevel@tonic-gate 
14597c478bd9Sstevel@tonic-gate 			return (error);
14607c478bd9Sstevel@tonic-gate 		}
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate 		/* Check whether we need to send the reset data up */
14637c478bd9Sstevel@tonic-gate 		if (ehcip->ehci_root_hub.rh_intr_pending_status) {
14647c478bd9Sstevel@tonic-gate 			ehci_root_hub_reset_occured(ehcip);
14657c478bd9Sstevel@tonic-gate 		}
14667c478bd9Sstevel@tonic-gate 
14677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14687c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_start_intr_polling: "
14697c478bd9Sstevel@tonic-gate 		    "Start polling for root hub successful");
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate 		break;
14727c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_ACTIVE:
14737c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14747c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_start_intr_polling: "
14757c478bd9Sstevel@tonic-gate 		    "Polling for root hub is already in progress");
14767c478bd9Sstevel@tonic-gate 
14777c478bd9Sstevel@tonic-gate 		break;
14787c478bd9Sstevel@tonic-gate 	default:
14797c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14807c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_start_intr_polling: "
14817c478bd9Sstevel@tonic-gate 		    "Pipe is in error state 0x%x", pipe_state);
14827c478bd9Sstevel@tonic-gate 
14837c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
14847c478bd9Sstevel@tonic-gate 
14857c478bd9Sstevel@tonic-gate 		break;
14867c478bd9Sstevel@tonic-gate 	}
14877c478bd9Sstevel@tonic-gate 
14887c478bd9Sstevel@tonic-gate 	return (error);
14897c478bd9Sstevel@tonic-gate }
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate /*
14937c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_pipe_stop_intr_polling:
14947c478bd9Sstevel@tonic-gate  *
14957c478bd9Sstevel@tonic-gate  * Handle stop polling on root hub intr pipe.
14967c478bd9Sstevel@tonic-gate  */
14977c478bd9Sstevel@tonic-gate /* ARGSUSED */
14987c478bd9Sstevel@tonic-gate void
14997c478bd9Sstevel@tonic-gate ehci_handle_root_hub_pipe_stop_intr_polling(
15007c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
15017c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
15027c478bd9Sstevel@tonic-gate {
15037c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
1504*112116d8Sfb 	    ph->p_usba_device->usb_root_hub_dip);
15057c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
15087c478bd9Sstevel@tonic-gate 
15097c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
15107c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_pipe_stop_intr_polling: "
15117c478bd9Sstevel@tonic-gate 	    "Root hub pipe stop polling");
15127c478bd9Sstevel@tonic-gate 
15137c478bd9Sstevel@tonic-gate 	ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
15147c478bd9Sstevel@tonic-gate 
15157c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_root_hub.rh_intr_pipe_state ==
15167c478bd9Sstevel@tonic-gate 	    EHCI_PIPE_STATE_ACTIVE) {
15177c478bd9Sstevel@tonic-gate 
15187c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_state =
15197c478bd9Sstevel@tonic-gate 		    EHCI_PIPE_STATE_STOP_POLLING;
15207c478bd9Sstevel@tonic-gate 
15217c478bd9Sstevel@tonic-gate 		/* Do interrupt pipe cleanup */
15227c478bd9Sstevel@tonic-gate 		ehci_root_hub_intr_pipe_cleanup(ehcip, USB_CR_STOPPED_POLLING);
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.
15257c478bd9Sstevel@tonic-gate 		    rh_intr_pipe_state == EHCI_PIPE_STATE_IDLE);
15267c478bd9Sstevel@tonic-gate 
15277c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
15287c478bd9Sstevel@tonic-gate 		    "ehci_hcdi_pipe_stop_intr_polling: Stop polling for root"
15297c478bd9Sstevel@tonic-gate 		    "hub successful");
15307c478bd9Sstevel@tonic-gate 	} else {
15317c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB,
15327c478bd9Sstevel@tonic-gate 		    ehcip->ehci_log_hdl, "ehci_hcdi_pipe_stop_intr_polling: "
15337c478bd9Sstevel@tonic-gate 		    "Polling for root hub is already stopped");
15347c478bd9Sstevel@tonic-gate 	}
15357c478bd9Sstevel@tonic-gate }
15367c478bd9Sstevel@tonic-gate 
15377c478bd9Sstevel@tonic-gate 
15387c478bd9Sstevel@tonic-gate /*
15397c478bd9Sstevel@tonic-gate  * ehci_get_root_hub_port_status:
15407c478bd9Sstevel@tonic-gate  *
15417c478bd9Sstevel@tonic-gate  * Construct root hub port status and change information
15427c478bd9Sstevel@tonic-gate  */
15437c478bd9Sstevel@tonic-gate static uint_t
15447c478bd9Sstevel@tonic-gate ehci_get_root_hub_port_status(
15457c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
15467c478bd9Sstevel@tonic-gate 	uint16_t		port)
15477c478bd9Sstevel@tonic-gate {
15487c478bd9Sstevel@tonic-gate 	uint_t			new_port_status = 0;
15497c478bd9Sstevel@tonic-gate 	uint_t			change_status = 0;
15507c478bd9Sstevel@tonic-gate 	uint_t			port_status;
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
15537c478bd9Sstevel@tonic-gate 
15547c478bd9Sstevel@tonic-gate 	/* Read the current port status */
15557c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]);
15567c478bd9Sstevel@tonic-gate 
15577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
15587c478bd9Sstevel@tonic-gate 	    "ehci_get_root_hub_port_status: port %d "
15597c478bd9Sstevel@tonic-gate 	    "port status = 0x%x", port, port_status);
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate 	/*
15627c478bd9Sstevel@tonic-gate 	 * EHCI root hub port status and control register information
15637c478bd9Sstevel@tonic-gate 	 * format is different what Hub driver wants. So EHCI driver
15647c478bd9Sstevel@tonic-gate 	 * needs to contruct the proper root hub port status information.
15657c478bd9Sstevel@tonic-gate 	 *
15667c478bd9Sstevel@tonic-gate 	 * Send all port status information only if port is owned by EHCI
15677c478bd9Sstevel@tonic-gate 	 * host controller.
15687c478bd9Sstevel@tonic-gate 	 */
15697c478bd9Sstevel@tonic-gate 	if ((port_status & EHCI_RH_PORT_OWNER) == EHCI_RH_PORT_OWNER_EHCI) {
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate 		/* First construct port change information */
15727c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_ENABLE_CHANGE) {
15737c478bd9Sstevel@tonic-gate 			change_status |= PORT_CHANGE_PESC;
15747c478bd9Sstevel@tonic-gate 		}
15757c478bd9Sstevel@tonic-gate 
15767c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_RESUME) {
15777c478bd9Sstevel@tonic-gate 			change_status |= PORT_CHANGE_PSSC;
15787c478bd9Sstevel@tonic-gate 		}
15797c478bd9Sstevel@tonic-gate 
15807c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_OVER_CURR_CHANGE) {
15817c478bd9Sstevel@tonic-gate 			change_status |= PORT_CHANGE_OCIC;
15827c478bd9Sstevel@tonic-gate 		}
15837c478bd9Sstevel@tonic-gate 
15847c478bd9Sstevel@tonic-gate 		/* Now construct port status information */
15857c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_CONNECT_STATUS) {
15867c478bd9Sstevel@tonic-gate 			new_port_status |= PORT_STATUS_CCS;
15877c478bd9Sstevel@tonic-gate 		}
15887c478bd9Sstevel@tonic-gate 
15897c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_ENABLE) {
15907c478bd9Sstevel@tonic-gate 			new_port_status |=
15917c478bd9Sstevel@tonic-gate 			    (PORT_STATUS_PES | PORT_STATUS_HSDA);
15927c478bd9Sstevel@tonic-gate 		}
15937c478bd9Sstevel@tonic-gate 
15947c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_SUSPEND) {
15957c478bd9Sstevel@tonic-gate 			new_port_status |= PORT_STATUS_PSS;
15967c478bd9Sstevel@tonic-gate 		}
15977c478bd9Sstevel@tonic-gate 
15987c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_OVER_CURR_ACTIVE) {
15997c478bd9Sstevel@tonic-gate 			new_port_status |= PORT_STATUS_POCI;
16007c478bd9Sstevel@tonic-gate 		}
16017c478bd9Sstevel@tonic-gate 
16027c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_RESET) {
16037c478bd9Sstevel@tonic-gate 			new_port_status |= PORT_STATUS_PRS;
16047c478bd9Sstevel@tonic-gate 		}
16057c478bd9Sstevel@tonic-gate 
16067c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_INDICATOR) {
16077c478bd9Sstevel@tonic-gate 			new_port_status |= PORT_STATUS_PIC;
16087c478bd9Sstevel@tonic-gate 		}
16097c478bd9Sstevel@tonic-gate 	}
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 	/*
16127c478bd9Sstevel@tonic-gate 	 * Send the following port status and change information
16137c478bd9Sstevel@tonic-gate 	 * even if port is not owned by EHCI.
16147c478bd9Sstevel@tonic-gate 	 *
16157c478bd9Sstevel@tonic-gate 	 * Additional port change information.
16167c478bd9Sstevel@tonic-gate 	 */
16177c478bd9Sstevel@tonic-gate 	if (port_status & EHCI_RH_PORT_CONNECT_STS_CHANGE) {
16187c478bd9Sstevel@tonic-gate 		change_status |= PORT_CHANGE_CSC;
16197c478bd9Sstevel@tonic-gate 	}
16207c478bd9Sstevel@tonic-gate 
16217c478bd9Sstevel@tonic-gate 	/* Additional port status information */
16227c478bd9Sstevel@tonic-gate 	if (port_status & EHCI_RH_PORT_POWER) {
16237c478bd9Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_PPS;
16247c478bd9Sstevel@tonic-gate 	}
16257c478bd9Sstevel@tonic-gate 
16267c478bd9Sstevel@tonic-gate 	if ((!(port_status & EHCI_RH_PORT_ENABLE)) &&
16277c478bd9Sstevel@tonic-gate 	    (port_status & EHCI_RH_PORT_LOW_SPEED)) {
16287c478bd9Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_LSDA;
16297c478bd9Sstevel@tonic-gate 	}
16307c478bd9Sstevel@tonic-gate 
16317c478bd9Sstevel@tonic-gate 	/*
16327c478bd9Sstevel@tonic-gate 	 * Construct complete root hub port status and change information.
16337c478bd9Sstevel@tonic-gate 	 */
16347c478bd9Sstevel@tonic-gate 	port_status = ((change_status << 16) | new_port_status);
16357c478bd9Sstevel@tonic-gate 
16367c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
16377c478bd9Sstevel@tonic-gate 	    "ehci_get_root_hub_port_status: port = %d new status = 0x%x "
16387c478bd9Sstevel@tonic-gate 	    "change status = 0x%x complete port status 0x%x", port,
16397c478bd9Sstevel@tonic-gate 	    new_port_status, change_status, port_status);
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate 	return (port_status);
16427c478bd9Sstevel@tonic-gate }
16437c478bd9Sstevel@tonic-gate 
16447c478bd9Sstevel@tonic-gate 
16457c478bd9Sstevel@tonic-gate /*
16467c478bd9Sstevel@tonic-gate  * ehci_is_port_owner:
16477c478bd9Sstevel@tonic-gate  *
16487c478bd9Sstevel@tonic-gate  * Check whether given port is owned by ehci.
16497c478bd9Sstevel@tonic-gate  */
16507c478bd9Sstevel@tonic-gate static int
16517c478bd9Sstevel@tonic-gate ehci_is_port_owner(
16527c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
16537c478bd9Sstevel@tonic-gate 	uint16_t		port)
16547c478bd9Sstevel@tonic-gate {
16557c478bd9Sstevel@tonic-gate 	uint_t			port_status;
16567c478bd9Sstevel@tonic-gate 
16577c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
16587c478bd9Sstevel@tonic-gate 
16597c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
16607c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
16617c478bd9Sstevel@tonic-gate 
16627c478bd9Sstevel@tonic-gate 	/*
16637c478bd9Sstevel@tonic-gate 	 * Don't perform anything if port is owned by classis host
16647c478bd9Sstevel@tonic-gate 	 * controller and return success.
16657c478bd9Sstevel@tonic-gate 	 */
16667c478bd9Sstevel@tonic-gate 	if ((port_status & EHCI_RH_PORT_OWNER) == EHCI_RH_PORT_OWNER_CLASSIC) {
16677c478bd9Sstevel@tonic-gate 
16687c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
16697c478bd9Sstevel@tonic-gate 		    "ehci_handle_set_clear_port_feature: "
16707c478bd9Sstevel@tonic-gate 		    "Port %d is owned by classic host controller", port);
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
16737c478bd9Sstevel@tonic-gate 	}
16747c478bd9Sstevel@tonic-gate 
16757c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
16767c478bd9Sstevel@tonic-gate }
16777c478bd9Sstevel@tonic-gate 
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate /*
16807c478bd9Sstevel@tonic-gate  * ehci_root_hub_allocate_intr_pipe_resource:
16817c478bd9Sstevel@tonic-gate  *
16827c478bd9Sstevel@tonic-gate  * Allocate interrupt requests and initialize them.
16837c478bd9Sstevel@tonic-gate  */
16847c478bd9Sstevel@tonic-gate static int
16857c478bd9Sstevel@tonic-gate ehci_root_hub_allocate_intr_pipe_resource(
16867c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
16877c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
16887c478bd9Sstevel@tonic-gate {
16897c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
16907c478bd9Sstevel@tonic-gate 	size_t			length;
16917c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
16927c478bd9Sstevel@tonic-gate 
16937c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
16947c478bd9Sstevel@tonic-gate 	    "ehci_root_hub_allocate_intr_pipe_resource");
16957c478bd9Sstevel@tonic-gate 
16967c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
16977c478bd9Sstevel@tonic-gate 
16987c478bd9Sstevel@tonic-gate 	/* Get the interrupt pipe handle */
16997c478bd9Sstevel@tonic-gate 	ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 	/* Get the current interrupt request pointer */
17027c478bd9Sstevel@tonic-gate 	curr_intr_reqp = ehcip->ehci_root_hub.rh_curr_intr_reqp;
17037c478bd9Sstevel@tonic-gate 
17047c478bd9Sstevel@tonic-gate 	/*
17057c478bd9Sstevel@tonic-gate 	 * If current interrupt request pointer is null,
17067c478bd9Sstevel@tonic-gate 	 * allocate new interrupt request.
17077c478bd9Sstevel@tonic-gate 	 */
17087c478bd9Sstevel@tonic-gate 	if (curr_intr_reqp == NULL) {
17097c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.rh_client_intr_reqp);
17107c478bd9Sstevel@tonic-gate 
17117c478bd9Sstevel@tonic-gate 		/* Get the length of interrupt transfer */
17127c478bd9Sstevel@tonic-gate 		length = ehcip->ehci_root_hub.
17137c478bd9Sstevel@tonic-gate 		    rh_client_intr_reqp->intr_len;
17147c478bd9Sstevel@tonic-gate 
17157c478bd9Sstevel@tonic-gate 		curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip,
17167c478bd9Sstevel@tonic-gate 		    ehcip->ehci_root_hub.rh_client_intr_reqp,
17177c478bd9Sstevel@tonic-gate 		    length, flags);
17187c478bd9Sstevel@tonic-gate 
17197c478bd9Sstevel@tonic-gate 		if (curr_intr_reqp == NULL) {
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
17227c478bd9Sstevel@tonic-gate 			    "ehci_root_hub_allocate_intr_pipe_resource:"
17237c478bd9Sstevel@tonic-gate 			    "Interrupt request structure allocation failed");
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate 			return (USB_NO_RESOURCES);
17267c478bd9Sstevel@tonic-gate 		}
17277c478bd9Sstevel@tonic-gate 
17287c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_curr_intr_reqp = curr_intr_reqp;
17297c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
17307c478bd9Sstevel@tonic-gate 		ph->p_req_count++;
17317c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
17327c478bd9Sstevel@tonic-gate 	}
17337c478bd9Sstevel@tonic-gate 
17347c478bd9Sstevel@tonic-gate 	/* Start the timer for the root hub interrupt pipe polling */
17357c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_root_hub.rh_intr_pipe_timer_id == 0) {
17367c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_timer_id =
17377c478bd9Sstevel@tonic-gate 		    timeout(ehci_handle_root_hub_status_change,
17387c478bd9Sstevel@tonic-gate 		    (void *)ehcip, drv_usectohz(EHCI_RH_POLL_TIME));
17397c478bd9Sstevel@tonic-gate 
17407c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.
17417c478bd9Sstevel@tonic-gate 		    rh_intr_pipe_state = EHCI_PIPE_STATE_ACTIVE;
17427c478bd9Sstevel@tonic-gate 	}
17437c478bd9Sstevel@tonic-gate 
17447c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
17457c478bd9Sstevel@tonic-gate }
17467c478bd9Sstevel@tonic-gate 
17477c478bd9Sstevel@tonic-gate 
17487c478bd9Sstevel@tonic-gate /*
17497c478bd9Sstevel@tonic-gate  * ehci_root_hub_intr_pipe_cleanup:
17507c478bd9Sstevel@tonic-gate  *
17517c478bd9Sstevel@tonic-gate  * Deallocate all interrupt requests and do callback
17527c478bd9Sstevel@tonic-gate  * the original client interrupt request.
17537c478bd9Sstevel@tonic-gate  */
17547c478bd9Sstevel@tonic-gate static void
17557c478bd9Sstevel@tonic-gate ehci_root_hub_intr_pipe_cleanup(
17567c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
17577c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason)
17587c478bd9Sstevel@tonic-gate {
17597c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
17607c478bd9Sstevel@tonic-gate 	usb_opaque_t		client_intr_reqp;
17617c478bd9Sstevel@tonic-gate 	timeout_id_t		timer_id;
17627c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
17637c478bd9Sstevel@tonic-gate 
17647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
17657c478bd9Sstevel@tonic-gate 	    "ehci_root_hub_intr_pipe_cleanup");
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
17687c478bd9Sstevel@tonic-gate 
17697c478bd9Sstevel@tonic-gate 	/* Get the interrupt pipe handle */
17707c478bd9Sstevel@tonic-gate 	ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate 	/* Get the interrupt timerid */
17737c478bd9Sstevel@tonic-gate 	timer_id = ehcip->ehci_root_hub.rh_intr_pipe_timer_id;
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 	/* Stop the root hub interrupt timer */
17767c478bd9Sstevel@tonic-gate 	if (timer_id) {
17777c478bd9Sstevel@tonic-gate 		/* Reset the timer id to zero */
17787c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_timer_id = 0;
17797c478bd9Sstevel@tonic-gate 
17807c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
17817c478bd9Sstevel@tonic-gate 		(void) untimeout(timer_id);
17827c478bd9Sstevel@tonic-gate 		mutex_enter(&ehcip->ehci_int_mutex);
17837c478bd9Sstevel@tonic-gate 	}
17847c478bd9Sstevel@tonic-gate 
17857c478bd9Sstevel@tonic-gate 	/* Reset the current interrupt request pointer */
17867c478bd9Sstevel@tonic-gate 	curr_intr_reqp = ehcip->ehci_root_hub.rh_curr_intr_reqp;
17877c478bd9Sstevel@tonic-gate 
17887c478bd9Sstevel@tonic-gate 	/* Deallocate uncompleted interrupt request */
17897c478bd9Sstevel@tonic-gate 	if (curr_intr_reqp) {
17907c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_curr_intr_reqp = NULL;
17917c478bd9Sstevel@tonic-gate 		usb_free_intr_req(curr_intr_reqp);
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
17947c478bd9Sstevel@tonic-gate 		ph->p_req_count--;
17957c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
17967c478bd9Sstevel@tonic-gate 	}
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 	client_intr_reqp = (usb_opaque_t)
17997c478bd9Sstevel@tonic-gate 	    ehcip->ehci_root_hub.rh_client_intr_reqp;
18007c478bd9Sstevel@tonic-gate 
18017c478bd9Sstevel@tonic-gate 	/* Callback for original client interrupt request */
18027c478bd9Sstevel@tonic-gate 	if (client_intr_reqp) {
18037c478bd9Sstevel@tonic-gate 		ehci_root_hub_hcdi_callback(ph, completion_reason);
18047c478bd9Sstevel@tonic-gate 	}
18057c478bd9Sstevel@tonic-gate }
18067c478bd9Sstevel@tonic-gate 
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate /*
18097c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_status_change:
18107c478bd9Sstevel@tonic-gate  *
18117c478bd9Sstevel@tonic-gate  * A root hub status change interrupt will occur any time there is a change
18127c478bd9Sstevel@tonic-gate  * in the root hub status register or one of the port status registers.
18137c478bd9Sstevel@tonic-gate  */
18147c478bd9Sstevel@tonic-gate static void
18157c478bd9Sstevel@tonic-gate ehci_handle_root_hub_status_change(void *arg)
18167c478bd9Sstevel@tonic-gate {
18177c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = (ehci_state_t *)arg;
18187c478bd9Sstevel@tonic-gate 	usb_hub_descr_t		*root_hub_descr =
1819*112116d8Sfb 	    &ehcip->ehci_root_hub.rh_descr;
18207c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
18217c478bd9Sstevel@tonic-gate 	usb_port_mask_t		port_mask = 0;
18227c478bd9Sstevel@tonic-gate 	uint_t			new_port_status;
18237c478bd9Sstevel@tonic-gate 	uint_t			change_status;
18247c478bd9Sstevel@tonic-gate 	uint_t			port_status;
18257c478bd9Sstevel@tonic-gate 	mblk_t			*message;
18267c478bd9Sstevel@tonic-gate 	size_t			length;
18277c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd;
18287c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
18297c478bd9Sstevel@tonic-gate 	int			i;
18307c478bd9Sstevel@tonic-gate 
18317c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
18327c478bd9Sstevel@tonic-gate 
18337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
18347c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_status_change: state = %d",
18357c478bd9Sstevel@tonic-gate 	    ehcip->ehci_root_hub.rh_intr_pipe_state);
18367c478bd9Sstevel@tonic-gate 
18373b00f311Syq #if defined(__x86)
18383b00f311Syq 	/*
18393b00f311Syq 	 * When ohci are attached in ferrari 4000, SMI will reset ehci
18403b00f311Syq 	 * registers. If ehci registers have been reset, we must re-initialize
18413b00f311Syq 	 * them. During booting, this function will be called 2~3 times. When
18423b00f311Syq 	 * this function is called 16 times, ohci drivers have been attached
18433b00f311Syq 	 * and stop checking the ehci registers.
18443b00f311Syq 	 */
18453b00f311Syq 	if (ehcip->ehci_polled_root_hub_count < 16) {
18463b00f311Syq 
18473b00f311Syq 		if (Get_OpReg(ehci_config_flag) == 0) {
18483b00f311Syq 
18493b00f311Syq 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB,
18503b00f311Syq 			    ehcip->ehci_log_hdl,
1851*112116d8Sfb 			    "ehci_handle_root_hub_status_change:"
18523b00f311Syq 			    " EHCI have been reset");
18533b00f311Syq 
18543b00f311Syq 			/* Reinitialize the controller */
18553b00f311Syq 			if (ehci_init_ctlr(ehcip, EHCI_REINITIALIZATION) !=
18563b00f311Syq 			    DDI_SUCCESS) {
18573b00f311Syq 				mutex_exit(&ehcip->ehci_int_mutex);
18583b00f311Syq 
18593b00f311Syq 				return;
18603b00f311Syq 			}
18613b00f311Syq 		}
18623b00f311Syq 
18633b00f311Syq 		ehcip->ehci_polled_root_hub_count++;
18643b00f311Syq 	}
18653b00f311Syq #endif	/* __x86 */
18663b00f311Syq 
18677c478bd9Sstevel@tonic-gate 	/* Get the current interrupt request pointer */
18687c478bd9Sstevel@tonic-gate 	curr_intr_reqp = ehcip->ehci_root_hub.rh_curr_intr_reqp;
18697c478bd9Sstevel@tonic-gate 
18707c478bd9Sstevel@tonic-gate 	ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
18717c478bd9Sstevel@tonic-gate 
18727c478bd9Sstevel@tonic-gate 	/* Check whether timeout handler is valid */
18737c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_root_hub.rh_intr_pipe_timer_id) {
18747c478bd9Sstevel@tonic-gate 		/* Check host controller is in operational state */
18757c478bd9Sstevel@tonic-gate 		if ((ehci_state_is_operational(ehcip)) != USB_SUCCESS) {
18767c478bd9Sstevel@tonic-gate 			/* Reset the timer id */
18777c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.rh_intr_pipe_timer_id = 0;
18787c478bd9Sstevel@tonic-gate 
18797c478bd9Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
18807c478bd9Sstevel@tonic-gate 			ehci_root_hub_intr_pipe_cleanup(
18817c478bd9Sstevel@tonic-gate 			    ehcip, USB_CR_HC_HARDWARE_ERR);
18827c478bd9Sstevel@tonic-gate 
18837c478bd9Sstevel@tonic-gate 			mutex_exit(&ehcip->ehci_int_mutex);
18847c478bd9Sstevel@tonic-gate 
18857c478bd9Sstevel@tonic-gate 			return;
18867c478bd9Sstevel@tonic-gate 		}
18877c478bd9Sstevel@tonic-gate 	} else {
18887c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
18897c478bd9Sstevel@tonic-gate 
18907c478bd9Sstevel@tonic-gate 		return;
18917c478bd9Sstevel@tonic-gate 	}
18927c478bd9Sstevel@tonic-gate 
18937c478bd9Sstevel@tonic-gate 	eptd = &ehcip->ehci_root_hub.rh_intr_pipe_handle->p_ep;
18947c478bd9Sstevel@tonic-gate 
18957c478bd9Sstevel@tonic-gate 	/* Check each port */
18967c478bd9Sstevel@tonic-gate 	for (i = 0; i < root_hub_descr->bNbrPorts; i++) {
18977c478bd9Sstevel@tonic-gate 
18987c478bd9Sstevel@tonic-gate 		port_status = ehci_get_root_hub_port_status(ehcip, i);
18997c478bd9Sstevel@tonic-gate 
19007c478bd9Sstevel@tonic-gate 		new_port_status = port_status & PORT_STATUS_MASK;
19017c478bd9Sstevel@tonic-gate 		change_status = (port_status >> 16) & PORT_CHANGE_MASK;
19027c478bd9Sstevel@tonic-gate 
19037c478bd9Sstevel@tonic-gate 		/*
19047c478bd9Sstevel@tonic-gate 		 * If there is change in the port status then set the bit in the
19057c478bd9Sstevel@tonic-gate 		 * bitmap of changes and inform hub driver about these changes.
19067c478bd9Sstevel@tonic-gate 		 * Hub driver will take care of these changes.
19077c478bd9Sstevel@tonic-gate 		 */
19087c478bd9Sstevel@tonic-gate 		if (change_status) {
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 			/* See if a device was attached/detached */
19117c478bd9Sstevel@tonic-gate 			if (change_status & PORT_CHANGE_CSC) {
19127c478bd9Sstevel@tonic-gate 				/*
19137c478bd9Sstevel@tonic-gate 				 * Update the state depending on whether
19147c478bd9Sstevel@tonic-gate 				 * the port was attached or detached.
19157c478bd9Sstevel@tonic-gate 				 */
19167c478bd9Sstevel@tonic-gate 				if (new_port_status & PORT_STATUS_CCS) {
19177c478bd9Sstevel@tonic-gate 					ehcip->ehci_root_hub.
19187c478bd9Sstevel@tonic-gate 					    rh_port_state[i] = DISABLED;
19197c478bd9Sstevel@tonic-gate 
19207c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
19217c478bd9Sstevel@tonic-gate 					    ehcip->ehci_log_hdl,
19228f1c3597Shx 					    "Port %d connected", i+1);
19237c478bd9Sstevel@tonic-gate 				} else {
19247c478bd9Sstevel@tonic-gate 					ehcip->ehci_root_hub.
19257c478bd9Sstevel@tonic-gate 					    rh_port_state[i] = DISCONNECTED;
19267c478bd9Sstevel@tonic-gate 
19277c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
19287c478bd9Sstevel@tonic-gate 					    ehcip->ehci_log_hdl,
19298f1c3597Shx 					    "Port %d disconnected", i+1);
19307c478bd9Sstevel@tonic-gate 				}
19317c478bd9Sstevel@tonic-gate 			}
19327c478bd9Sstevel@tonic-gate 
19337c478bd9Sstevel@tonic-gate 			/* See if port enable status changed */
19347c478bd9Sstevel@tonic-gate 			if (change_status & PORT_CHANGE_PESC) {
19357c478bd9Sstevel@tonic-gate 				/*
19367c478bd9Sstevel@tonic-gate 				 * Update the state depending on whether
19377c478bd9Sstevel@tonic-gate 				 * the port was enabled or disabled.
19387c478bd9Sstevel@tonic-gate 				 */
19397c478bd9Sstevel@tonic-gate 				if (new_port_status & PORT_STATUS_PES) {
19407c478bd9Sstevel@tonic-gate 					ehcip->ehci_root_hub.
19417c478bd9Sstevel@tonic-gate 					    rh_port_state[i] = ENABLED;
19427c478bd9Sstevel@tonic-gate 
19437c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
19447c478bd9Sstevel@tonic-gate 					    ehcip->ehci_log_hdl,
19458f1c3597Shx 					    "Port %d enabled", i+1);
19467c478bd9Sstevel@tonic-gate 				} else {
19477c478bd9Sstevel@tonic-gate 					ehcip->ehci_root_hub.
19487c478bd9Sstevel@tonic-gate 					    rh_port_state[i] = DISABLED;
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
19517c478bd9Sstevel@tonic-gate 					    ehcip->ehci_log_hdl,
19528f1c3597Shx 					    "Port %d disabled", i+1);
19537c478bd9Sstevel@tonic-gate 				}
19547c478bd9Sstevel@tonic-gate 			}
19557c478bd9Sstevel@tonic-gate 
19567c478bd9Sstevel@tonic-gate 			port_mask |= 1 << (i + 1);
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 			/* Update the status */
19597c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.
19607c478bd9Sstevel@tonic-gate 			    rh_port_status[i] = new_port_status;
19617c478bd9Sstevel@tonic-gate 		}
19627c478bd9Sstevel@tonic-gate 	}
19637c478bd9Sstevel@tonic-gate 
19647c478bd9Sstevel@tonic-gate 	if (ph && port_mask && curr_intr_reqp) {
19657c478bd9Sstevel@tonic-gate 		length = eptd->wMaxPacketSize;
19667c478bd9Sstevel@tonic-gate 
19677c478bd9Sstevel@tonic-gate 		ASSERT(length != 0);
19687c478bd9Sstevel@tonic-gate 
19697c478bd9Sstevel@tonic-gate 		/* Get the  message block */
19707c478bd9Sstevel@tonic-gate 		message = curr_intr_reqp->intr_data;
19717c478bd9Sstevel@tonic-gate 
19727c478bd9Sstevel@tonic-gate 		ASSERT(message != NULL);
19737c478bd9Sstevel@tonic-gate 
19747c478bd9Sstevel@tonic-gate 		do {
19758f1c3597Shx 			/*
19768f1c3597Shx 			 * check that the mblk is big enough when we
19778f1c3597Shx 			 * are writing bytes into it
19788f1c3597Shx 			 */
19798f1c3597Shx 			if (message->b_wptr >= message->b_datap->db_lim) {
19808f1c3597Shx 
19818f1c3597Shx 				USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB,
19828f1c3597Shx 				    ehcip->ehci_log_hdl,
19838f1c3597Shx 				    "ehci_handle_root_hub_status_change"
19848f1c3597Shx 				    "mblk data overflow.");
19858f1c3597Shx 
19868f1c3597Shx 				break;
19878f1c3597Shx 			}
19887c478bd9Sstevel@tonic-gate 			*message->b_wptr++ = (uchar_t)port_mask;
19897c478bd9Sstevel@tonic-gate 			port_mask >>= 8;
19907c478bd9Sstevel@tonic-gate 		} while (port_mask != 0);
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate 		ehci_root_hub_hcdi_callback(ph, USB_CR_OK);
19937c478bd9Sstevel@tonic-gate 	}
19947c478bd9Sstevel@tonic-gate 
19957c478bd9Sstevel@tonic-gate 	/* Reset the timer id */
19967c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_intr_pipe_timer_id = 0;
19977c478bd9Sstevel@tonic-gate 
19987c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_root_hub.rh_intr_pipe_state ==
19997c478bd9Sstevel@tonic-gate 	    EHCI_PIPE_STATE_ACTIVE) {
20007c478bd9Sstevel@tonic-gate 		/*
20017c478bd9Sstevel@tonic-gate 		 * If needed, allocate new interrupt request. Also
20027c478bd9Sstevel@tonic-gate 		 * start the timer for the root hub interrupt polling.
20037c478bd9Sstevel@tonic-gate 		 */
20047c478bd9Sstevel@tonic-gate 		if ((ehci_root_hub_allocate_intr_pipe_resource(
20057c478bd9Sstevel@tonic-gate 		    ehcip, 0)) != USB_SUCCESS) {
20067c478bd9Sstevel@tonic-gate 
20077c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
20087c478bd9Sstevel@tonic-gate 			    "ehci_handle_root_hub_status_change: No Resources");
20097c478bd9Sstevel@tonic-gate 
20107c478bd9Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
20117c478bd9Sstevel@tonic-gate 			ehci_root_hub_intr_pipe_cleanup(
20127c478bd9Sstevel@tonic-gate 			    ehcip, USB_CR_NO_RESOURCES);
20137c478bd9Sstevel@tonic-gate 		}
20147c478bd9Sstevel@tonic-gate 	}
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
20177c478bd9Sstevel@tonic-gate }
20187c478bd9Sstevel@tonic-gate 
20197c478bd9Sstevel@tonic-gate 
20207c478bd9Sstevel@tonic-gate /*
20217c478bd9Sstevel@tonic-gate  * ehci_root_hub_hcdi_callback()
20227c478bd9Sstevel@tonic-gate  *
20237c478bd9Sstevel@tonic-gate  * Convenience wrapper around usba_hcdi_cb() for the root hub.
20247c478bd9Sstevel@tonic-gate  */
20257c478bd9Sstevel@tonic-gate static void
20267c478bd9Sstevel@tonic-gate ehci_root_hub_hcdi_callback(
20277c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
20287c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason)
20297c478bd9Sstevel@tonic-gate {
20307c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
2031*112116d8Sfb 	    ph->p_usba_device->usb_root_hub_dip);
20327c478bd9Sstevel@tonic-gate 	uchar_t			attributes = ph->p_ep.bmAttributes &
2033*112116d8Sfb 	    USB_EP_ATTR_MASK;
20347c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp;
20357c478bd9Sstevel@tonic-gate 	uint_t			pipe_state = 0;
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
20387c478bd9Sstevel@tonic-gate 	    "ehci_root_hub_hcdi_callback: ph = 0x%p, cr = 0x%x",
2039*112116d8Sfb 	    (void *)ph, completion_reason);
20407c478bd9Sstevel@tonic-gate 
20417c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 	/* Set the pipe state as per completion reason */
20447c478bd9Sstevel@tonic-gate 	switch (completion_reason) {
20457c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
20467c478bd9Sstevel@tonic-gate 		switch (attributes) {
20477c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
20487c478bd9Sstevel@tonic-gate 			pipe_state = EHCI_PIPE_STATE_IDLE;
20497c478bd9Sstevel@tonic-gate 			break;
20507c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
20517c478bd9Sstevel@tonic-gate 			pipe_state = ehcip->ehci_root_hub.
20527c478bd9Sstevel@tonic-gate 			    rh_intr_pipe_state;
20537c478bd9Sstevel@tonic-gate 			break;
20547c478bd9Sstevel@tonic-gate 		}
20557c478bd9Sstevel@tonic-gate 		break;
20567c478bd9Sstevel@tonic-gate 	case USB_CR_NO_RESOURCES:
20577c478bd9Sstevel@tonic-gate 	case USB_CR_NOT_SUPPORTED:
20587c478bd9Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
20597c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
20607c478bd9Sstevel@tonic-gate 	case USB_CR_HC_HARDWARE_ERR:
20617c478bd9Sstevel@tonic-gate 		/* Set pipe state to idle */
20627c478bd9Sstevel@tonic-gate 		pipe_state = EHCI_PIPE_STATE_IDLE;
20637c478bd9Sstevel@tonic-gate 		break;
20647c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_CLOSING:
20657c478bd9Sstevel@tonic-gate 		break;
20667c478bd9Sstevel@tonic-gate 	default:
20677c478bd9Sstevel@tonic-gate 		/* Set pipe state to error */
20687c478bd9Sstevel@tonic-gate 		pipe_state = EHCI_PIPE_STATE_ERROR;
20697c478bd9Sstevel@tonic-gate 		break;
20707c478bd9Sstevel@tonic-gate 	}
20717c478bd9Sstevel@tonic-gate 
20727c478bd9Sstevel@tonic-gate 	switch (attributes) {
20737c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
20747c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = (usb_opaque_t)
20757c478bd9Sstevel@tonic-gate 		    ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
20767c478bd9Sstevel@tonic-gate 
20777c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_curr_ctrl_reqp = NULL;
20787c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_state = pipe_state;
20797c478bd9Sstevel@tonic-gate 		break;
20807c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
20817c478bd9Sstevel@tonic-gate 		/* if curr_intr_reqp available then use this request */
20827c478bd9Sstevel@tonic-gate 		if (ehcip->ehci_root_hub.rh_curr_intr_reqp) {
20837c478bd9Sstevel@tonic-gate 			curr_xfer_reqp = (usb_opaque_t)ehcip->
20847c478bd9Sstevel@tonic-gate 			    ehci_root_hub.rh_curr_intr_reqp;
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.rh_curr_intr_reqp = NULL;
20877c478bd9Sstevel@tonic-gate 		} else {
20887c478bd9Sstevel@tonic-gate 			/* no current request, use client's request */
20897c478bd9Sstevel@tonic-gate 			curr_xfer_reqp = (usb_opaque_t)
20907c478bd9Sstevel@tonic-gate 			    ehcip->ehci_root_hub.rh_client_intr_reqp;
20917c478bd9Sstevel@tonic-gate 
20927c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.rh_client_intr_reqp = NULL;
20937c478bd9Sstevel@tonic-gate 		}
20947c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_state = pipe_state;
20957c478bd9Sstevel@tonic-gate 		break;
20967c478bd9Sstevel@tonic-gate 	}
20977c478bd9Sstevel@tonic-gate 
20987c478bd9Sstevel@tonic-gate 	ASSERT(curr_xfer_reqp != NULL);
20997c478bd9Sstevel@tonic-gate 
21007c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
21017c478bd9Sstevel@tonic-gate 	usba_hcdi_cb(ph, curr_xfer_reqp, completion_reason);
21027c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
21037c478bd9Sstevel@tonic-gate }
2104