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
53304303fSsl  * Common Development and Distribution License (the "License").
63304303fSsl  * 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  */
21dfbb3a42SRaymond Chen 
227c478bd9Sstevel@tonic-gate /*
23dfbb3a42SRaymond Chen  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * EHCI Host Controller Driver (EHCI)
287c478bd9Sstevel@tonic-gate  *
297c478bd9Sstevel@tonic-gate  * The EHCI driver is a software driver which interfaces to the Universal
307c478bd9Sstevel@tonic-gate  * Serial Bus layer (USBA) and the Host Controller (HC). The interface to
317c478bd9Sstevel@tonic-gate  * the Host Controller is defined by the EHCI Host Controller Interface.
327c478bd9Sstevel@tonic-gate  *
337c478bd9Sstevel@tonic-gate  * This module contains the main EHCI driver code which handles all USB
347c478bd9Sstevel@tonic-gate  * transfers, bandwidth allocations and other general functionalities.
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehcid.h>
387c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_intr.h>
397c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_util.h>
407c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_isoch.h>
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate /* Adjustable variables for the size of the pools */
437c478bd9Sstevel@tonic-gate extern int ehci_qh_pool_size;
447c478bd9Sstevel@tonic-gate extern int ehci_qtd_pool_size;
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate /* Endpoint Descriptor (QH) related functions */
487c478bd9Sstevel@tonic-gate ehci_qh_t	*ehci_alloc_qh(
497c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
507c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
517c478bd9Sstevel@tonic-gate 				uint_t			flag);
527c478bd9Sstevel@tonic-gate static void	ehci_unpack_endpoint(
537c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
547c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
557c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
567c478bd9Sstevel@tonic-gate void		ehci_insert_qh(
577c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
587c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
597c478bd9Sstevel@tonic-gate static void	ehci_insert_async_qh(
607c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
617c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
627c478bd9Sstevel@tonic-gate static void	ehci_insert_intr_qh(
637c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
647c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
657c478bd9Sstevel@tonic-gate static void	ehci_modify_qh_status_bit(
667c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
677c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
687c478bd9Sstevel@tonic-gate 				halt_bit_t		action);
697c478bd9Sstevel@tonic-gate static void	ehci_halt_hs_qh(
707c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
717c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
727c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
737c478bd9Sstevel@tonic-gate static void	ehci_halt_fls_ctrl_and_bulk_qh(
747c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
757c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
767c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
777c478bd9Sstevel@tonic-gate static void	ehci_clear_tt_buffer(
787c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
797c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
807c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
817c478bd9Sstevel@tonic-gate static void	ehci_halt_fls_intr_qh(
827c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
837c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
847c478bd9Sstevel@tonic-gate void		ehci_remove_qh(
857c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
867c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
877c478bd9Sstevel@tonic-gate 				boolean_t		reclaim);
887c478bd9Sstevel@tonic-gate static void	ehci_remove_async_qh(
897c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
907c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
917c478bd9Sstevel@tonic-gate 				boolean_t		reclaim);
927c478bd9Sstevel@tonic-gate static void	ehci_remove_intr_qh(
937c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
947c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
957c478bd9Sstevel@tonic-gate 				boolean_t		reclaim);
967c478bd9Sstevel@tonic-gate static void	ehci_insert_qh_on_reclaim_list(
977c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
987c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
997c478bd9Sstevel@tonic-gate void		ehci_deallocate_qh(
1007c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1017c478bd9Sstevel@tonic-gate 				ehci_qh_t		*old_qh);
1027c478bd9Sstevel@tonic-gate uint32_t	ehci_qh_cpu_to_iommu(
1037c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1047c478bd9Sstevel@tonic-gate 				ehci_qh_t		*addr);
1057c478bd9Sstevel@tonic-gate ehci_qh_t	*ehci_qh_iommu_to_cpu(
1067c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1077c478bd9Sstevel@tonic-gate 				uintptr_t		addr);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /* Transfer Descriptor (QTD) related functions */
1107c478bd9Sstevel@tonic-gate static int	ehci_initialize_dummy(
1117c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1127c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
1137c478bd9Sstevel@tonic-gate ehci_trans_wrapper_t *ehci_allocate_ctrl_resources(
1147c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1157c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
1167c478bd9Sstevel@tonic-gate 				usb_ctrl_req_t		*ctrl_reqp,
1177c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1187c478bd9Sstevel@tonic-gate void		ehci_insert_ctrl_req(
1197c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1207c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1217c478bd9Sstevel@tonic-gate 				usb_ctrl_req_t		*ctrl_reqp,
1227c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
1237c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1247c478bd9Sstevel@tonic-gate ehci_trans_wrapper_t *ehci_allocate_bulk_resources(
1257c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1267c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
1277c478bd9Sstevel@tonic-gate 				usb_bulk_req_t		*bulk_reqp,
1287c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1297c478bd9Sstevel@tonic-gate void		ehci_insert_bulk_req(
1307c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1317c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1327c478bd9Sstevel@tonic-gate 				usb_bulk_req_t		*bulk_reqp,
1337c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
1347c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1357c478bd9Sstevel@tonic-gate int		ehci_start_periodic_pipe_polling(
1367c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1377c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1387c478bd9Sstevel@tonic-gate 				usb_opaque_t		periodic_in_reqp,
1397c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1407c478bd9Sstevel@tonic-gate static int	ehci_start_pipe_polling(
1417c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1427c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1437c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1447c478bd9Sstevel@tonic-gate static int	ehci_start_intr_polling(
1457c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1467c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1477c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1487c478bd9Sstevel@tonic-gate static void	ehci_set_periodic_pipe_polling(
1497c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1507c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
1517c478bd9Sstevel@tonic-gate ehci_trans_wrapper_t *ehci_allocate_intr_resources(
1527c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1537c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1547c478bd9Sstevel@tonic-gate 				usb_intr_req_t		*intr_reqp,
1557c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1567c478bd9Sstevel@tonic-gate void		ehci_insert_intr_req(
1577c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1587c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
1597c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
1607c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1617c478bd9Sstevel@tonic-gate int		ehci_stop_periodic_pipe_polling(
1627c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1637c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1647c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1657c478bd9Sstevel@tonic-gate int		ehci_insert_qtd(
1667c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1677c478bd9Sstevel@tonic-gate 				uint32_t		qtd_ctrl,
1683304303fSsl 				size_t			qtd_dma_offs,
1697c478bd9Sstevel@tonic-gate 				size_t			qtd_length,
1707c478bd9Sstevel@tonic-gate 				uint32_t		qtd_ctrl_phase,
1717c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
1727c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
1737c478bd9Sstevel@tonic-gate static ehci_qtd_t *ehci_allocate_qtd_from_pool(
1747c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip);
1757c478bd9Sstevel@tonic-gate static void	ehci_fill_in_qtd(
1767c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1777c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*qtd,
1787c478bd9Sstevel@tonic-gate 				uint32_t		qtd_ctrl,
1793304303fSsl 				size_t			qtd_dma_offs,
1807c478bd9Sstevel@tonic-gate 				size_t			qtd_length,
1817c478bd9Sstevel@tonic-gate 				uint32_t		qtd_ctrl_phase,
1827c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
1837c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
1847c478bd9Sstevel@tonic-gate static void	ehci_insert_qtd_on_tw(
1857c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1867c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
1877c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*qtd);
1887c478bd9Sstevel@tonic-gate static void	ehci_insert_qtd_into_active_qtd_list(
1897c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1907c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*curr_qtd);
1917c478bd9Sstevel@tonic-gate void		ehci_remove_qtd_from_active_qtd_list(
1927c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1937c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*curr_qtd);
1947c478bd9Sstevel@tonic-gate static void	ehci_traverse_qtds(
1957c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1967c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
1977c478bd9Sstevel@tonic-gate void		ehci_deallocate_qtd(
1987c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1997c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*old_qtd);
2007c478bd9Sstevel@tonic-gate uint32_t	ehci_qtd_cpu_to_iommu(
2017c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2027c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*addr);
2037c478bd9Sstevel@tonic-gate ehci_qtd_t	*ehci_qtd_iommu_to_cpu(
2047c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2057c478bd9Sstevel@tonic-gate 				uintptr_t		addr);
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate /* Transfer Wrapper (TW) functions */
2087c478bd9Sstevel@tonic-gate static ehci_trans_wrapper_t  *ehci_create_transfer_wrapper(
2097c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2107c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2117c478bd9Sstevel@tonic-gate 				size_t			length,
2127c478bd9Sstevel@tonic-gate 				uint_t			usb_flags);
2137c478bd9Sstevel@tonic-gate int		ehci_allocate_tds_for_tw(
2147c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2157c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2167c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
2177c478bd9Sstevel@tonic-gate 				size_t			qtd_count);
2187c478bd9Sstevel@tonic-gate static ehci_trans_wrapper_t  *ehci_allocate_tw_resources(
2197c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2207c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2217c478bd9Sstevel@tonic-gate 				size_t			length,
2227c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags,
2237c478bd9Sstevel@tonic-gate 				size_t			td_count);
2247c478bd9Sstevel@tonic-gate static void	ehci_free_tw_td_resources(
2257c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2267c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2277c478bd9Sstevel@tonic-gate static void	ehci_start_xfer_timer(
2287c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2297c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2307c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2317c478bd9Sstevel@tonic-gate void		ehci_stop_xfer_timer(
2327c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2337c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
2347c478bd9Sstevel@tonic-gate 				uint_t			flag);
2357c478bd9Sstevel@tonic-gate static void	ehci_xfer_timeout_handler(void		*arg);
2367c478bd9Sstevel@tonic-gate static void	ehci_remove_tw_from_timeout_list(
2377c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2387c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2397c478bd9Sstevel@tonic-gate static void	ehci_start_timer(ehci_state_t		*ehcip,
2407c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
2417c478bd9Sstevel@tonic-gate void		ehci_deallocate_tw(
2427c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2437c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2447c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2457c478bd9Sstevel@tonic-gate void		ehci_free_dma_resources(
2467c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2477c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
2487c478bd9Sstevel@tonic-gate static void	ehci_free_tw(
2497c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2507c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2517c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate /* Miscellaneous functions */
2547c478bd9Sstevel@tonic-gate int		ehci_allocate_intr_in_resource(
2557c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2567c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2577c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
2587c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
2597c478bd9Sstevel@tonic-gate void		ehci_pipe_cleanup(
2607c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2617c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
2627c478bd9Sstevel@tonic-gate static void	ehci_wait_for_transfers_completion(
2637c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2647c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
2657c478bd9Sstevel@tonic-gate void		ehci_check_for_transfers_completion(
2667c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2677c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
2687c478bd9Sstevel@tonic-gate static void	ehci_save_data_toggle(
2697c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2707c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
2717c478bd9Sstevel@tonic-gate void		ehci_restore_data_toggle(
2727c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2737c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
2747c478bd9Sstevel@tonic-gate void		ehci_handle_outstanding_requests(
2757c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2767c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
2777c478bd9Sstevel@tonic-gate void		ehci_deallocate_intr_in_resource(
2787c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2797c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2807c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2817c478bd9Sstevel@tonic-gate void		ehci_do_client_periodic_in_req_callback(
2827c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2837c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2847c478bd9Sstevel@tonic-gate 				usb_cr_t		completion_reason);
2857c478bd9Sstevel@tonic-gate void		ehci_hcdi_callback(
2867c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
2877c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
2887c478bd9Sstevel@tonic-gate 				usb_cr_t		completion_reason);
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate /*
2927c478bd9Sstevel@tonic-gate  * Endpoint Descriptor (QH) manipulations functions
2937c478bd9Sstevel@tonic-gate  */
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate /*
2967c478bd9Sstevel@tonic-gate  * ehci_alloc_qh:
2977c478bd9Sstevel@tonic-gate  *
2987c478bd9Sstevel@tonic-gate  * Allocate an endpoint descriptor (QH)
2997c478bd9Sstevel@tonic-gate  *
3007c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
3017c478bd9Sstevel@tonic-gate  */
3027c478bd9Sstevel@tonic-gate ehci_qh_t *
ehci_alloc_qh(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,uint_t flag)3037c478bd9Sstevel@tonic-gate ehci_alloc_qh(
3047c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
3057c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
3067c478bd9Sstevel@tonic-gate 	uint_t			flag)
3077c478bd9Sstevel@tonic-gate {
3087c478bd9Sstevel@tonic-gate 	int			i, state;
3097c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3127c478bd9Sstevel@tonic-gate 	    "ehci_alloc_qh: ph = 0x%p flag = 0x%x", (void *)ph, flag);
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	/*
3177c478bd9Sstevel@tonic-gate 	 * If this is for a ISOC endpoint return null.
3187c478bd9Sstevel@tonic-gate 	 * Isochronous uses ITD put directly onto the PFL.
3197c478bd9Sstevel@tonic-gate 	 */
3207c478bd9Sstevel@tonic-gate 	if (ph) {
3217c478bd9Sstevel@tonic-gate 		if (EHCI_ISOC_ENDPOINT((&ph->p_ep))) {
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 			return (NULL);
3247c478bd9Sstevel@tonic-gate 		}
3257c478bd9Sstevel@tonic-gate 	}
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	/*
3287c478bd9Sstevel@tonic-gate 	 * The first 63 endpoints in the Endpoint Descriptor (QH)
3297c478bd9Sstevel@tonic-gate 	 * buffer pool are reserved for building interrupt lattice
3307c478bd9Sstevel@tonic-gate 	 * tree. Search for a blank endpoint descriptor in the QH
3317c478bd9Sstevel@tonic-gate 	 * buffer pool.
3327c478bd9Sstevel@tonic-gate 	 */
3337c478bd9Sstevel@tonic-gate 	for (i = EHCI_NUM_STATIC_NODES; i < ehci_qh_pool_size; i ++) {
3347c478bd9Sstevel@tonic-gate 		state = Get_QH(ehcip->ehci_qh_pool_addr[i].qh_state);
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 		if (state == EHCI_QH_FREE) {
3377c478bd9Sstevel@tonic-gate 			break;
3387c478bd9Sstevel@tonic-gate 		}
3397c478bd9Sstevel@tonic-gate 	}
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3427c478bd9Sstevel@tonic-gate 	    "ehci_alloc_qh: Allocated %d", i);
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	if (i == ehci_qh_pool_size) {
3457c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC,  ehcip->ehci_log_hdl,
3467c478bd9Sstevel@tonic-gate 		    "ehci_alloc_qh: QH exhausted");
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate 		return (NULL);
3497c478bd9Sstevel@tonic-gate 	} else {
3507c478bd9Sstevel@tonic-gate 		qh = &ehcip->ehci_qh_pool_addr[i];
3516a9de478Ssl 		bzero((void *)qh, sizeof (ehci_qh_t));
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3547c478bd9Sstevel@tonic-gate 		    "ehci_alloc_qh: Allocated address 0x%p", (void *)qh);
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 		/* Check polled mode flag */
3577c478bd9Sstevel@tonic-gate 		if (flag == EHCI_POLLED_MODE_FLAG) {
3587c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_link_ptr, EHCI_QH_LINK_PTR_VALID);
3597c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_ctrl, EHCI_QH_CTRL_ED_INACTIVATE);
3607c478bd9Sstevel@tonic-gate 		}
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 		/* Unpack the endpoint descriptor into a control field */
3637c478bd9Sstevel@tonic-gate 		if (ph) {
3647c478bd9Sstevel@tonic-gate 			if ((ehci_initialize_dummy(ehcip,
3657c478bd9Sstevel@tonic-gate 			    qh)) == USB_NO_RESOURCES) {
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 				Set_QH(qh->qh_state, EHCI_QH_FREE);
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 				return (NULL);
3707c478bd9Sstevel@tonic-gate 			}
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 			ehci_unpack_endpoint(ehcip, ph, qh);
3737c478bd9Sstevel@tonic-gate 
3746aef9e11SToomas Soome 			Set_QH(qh->qh_curr_qtd, 0);
3757c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_alt_next_qtd,
3767c478bd9Sstevel@tonic-gate 			    EHCI_QH_ALT_NEXT_QTD_PTR_VALID);
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 			/* Change QH's state Active */
3797c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_state, EHCI_QH_ACTIVE);
3807c478bd9Sstevel@tonic-gate 		} else {
3817c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_status, EHCI_QH_STS_HALTED);
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 			/* Change QH's state Static */
3847c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_state, EHCI_QH_STATIC);
3857c478bd9Sstevel@tonic-gate 		}
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 		ehci_print_qh(ehcip, qh);
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 		return (qh);
3907c478bd9Sstevel@tonic-gate 	}
3917c478bd9Sstevel@tonic-gate }
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate /*
3957c478bd9Sstevel@tonic-gate  * ehci_unpack_endpoint:
3967c478bd9Sstevel@tonic-gate  *
3977c478bd9Sstevel@tonic-gate  * Unpack the information in the pipe handle and create the first byte
3987c478bd9Sstevel@tonic-gate  * of the Host Controller's (HC) Endpoint Descriptor (QH).
3997c478bd9Sstevel@tonic-gate  */
4007c478bd9Sstevel@tonic-gate static void
ehci_unpack_endpoint(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,ehci_qh_t * qh)4017c478bd9Sstevel@tonic-gate ehci_unpack_endpoint(
4027c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
4037c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
4047c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh)
4057c478bd9Sstevel@tonic-gate {
4067c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*endpoint = &ph->p_ep;
4077c478bd9Sstevel@tonic-gate 	uint_t			maxpacketsize, addr, xactions;
4087c478bd9Sstevel@tonic-gate 	uint_t			ctrl = 0, status = 0, split_ctrl = 0;
4097c478bd9Sstevel@tonic-gate 	usb_port_status_t	usb_port_status;
4107c478bd9Sstevel@tonic-gate 	usba_device_t		*usba_device = ph->p_usba_device;
4117c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
4147c478bd9Sstevel@tonic-gate 	    "ehci_unpack_endpoint:");
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
4177c478bd9Sstevel@tonic-gate 	ctrl = usba_device->usb_addr;
4187c478bd9Sstevel@tonic-gate 	usb_port_status = usba_device->usb_port_status;
4197c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 	addr = endpoint->bEndpointAddress;
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	/* Assign the endpoint's address */
4247c478bd9Sstevel@tonic-gate 	ctrl |= ((addr & USB_EP_NUM_MASK) << EHCI_QH_CTRL_ED_NUMBER_SHIFT);
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	/* Assign the speed */
4277c478bd9Sstevel@tonic-gate 	switch (usb_port_status) {
4287c478bd9Sstevel@tonic-gate 	case USBA_LOW_SPEED_DEV:
4297c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_QH_CTRL_ED_LOW_SPEED;
4307c478bd9Sstevel@tonic-gate 		break;
4317c478bd9Sstevel@tonic-gate 	case USBA_FULL_SPEED_DEV:
4327c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_QH_CTRL_ED_FULL_SPEED;
4337c478bd9Sstevel@tonic-gate 		break;
4347c478bd9Sstevel@tonic-gate 	case USBA_HIGH_SPEED_DEV:
4357c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_QH_CTRL_ED_HIGH_SPEED;
4367c478bd9Sstevel@tonic-gate 		break;
4377c478bd9Sstevel@tonic-gate 	}
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	switch (endpoint->bmAttributes & USB_EP_ATTR_MASK) {
4407c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
4417c478bd9Sstevel@tonic-gate 		/* Assign data toggle information */
4427c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_QH_CTRL_DATA_TOGGLE;
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 		if (usb_port_status != USBA_HIGH_SPEED_DEV) {
4457c478bd9Sstevel@tonic-gate 			ctrl |= EHCI_QH_CTRL_CONTROL_ED_FLAG;
4467c478bd9Sstevel@tonic-gate 		}
4477c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
4487c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
4497c478bd9Sstevel@tonic-gate 		/* Maximum nak counter */
4507c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_QH_CTRL_MAX_NC;
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 		if (usb_port_status == USBA_HIGH_SPEED_DEV) {
4537c478bd9Sstevel@tonic-gate 			/*
4547c478bd9Sstevel@tonic-gate 			 * Perform ping before executing control
4557c478bd9Sstevel@tonic-gate 			 * and bulk transactions.
4567c478bd9Sstevel@tonic-gate 			 */
4577c478bd9Sstevel@tonic-gate 			status = EHCI_QH_STS_DO_PING;
4587c478bd9Sstevel@tonic-gate 		}
4597c478bd9Sstevel@tonic-gate 		break;
4607c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
4617c478bd9Sstevel@tonic-gate 		/* Set start split mask */
4627c478bd9Sstevel@tonic-gate 		split_ctrl = (pp->pp_smask & EHCI_QH_SPLIT_CTRL_INTR_MASK);
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 		/*
4657c478bd9Sstevel@tonic-gate 		 * Set complete split mask for low/full speed
4667c478bd9Sstevel@tonic-gate 		 * usb devices.
4677c478bd9Sstevel@tonic-gate 		 */
4687c478bd9Sstevel@tonic-gate 		if (usb_port_status != USBA_HIGH_SPEED_DEV) {
4697c478bd9Sstevel@tonic-gate 			split_ctrl |= ((pp->pp_cmask <<
4707c478bd9Sstevel@tonic-gate 			    EHCI_QH_SPLIT_CTRL_COMP_SHIFT) &
4717c478bd9Sstevel@tonic-gate 			    EHCI_QH_SPLIT_CTRL_COMP_MASK);
4727c478bd9Sstevel@tonic-gate 		}
4737c478bd9Sstevel@tonic-gate 		break;
4747c478bd9Sstevel@tonic-gate 	}
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	/* Get the max transactions per microframe */
4777c478bd9Sstevel@tonic-gate 	xactions = (endpoint->wMaxPacketSize &
4787c478bd9Sstevel@tonic-gate 	    USB_EP_MAX_XACTS_MASK) >>  USB_EP_MAX_XACTS_SHIFT;
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	switch (xactions) {
4817c478bd9Sstevel@tonic-gate 	case 0:
4827c478bd9Sstevel@tonic-gate 		split_ctrl |= EHCI_QH_SPLIT_CTRL_1_XACTS;
4837c478bd9Sstevel@tonic-gate 		break;
4847c478bd9Sstevel@tonic-gate 	case 1:
4857c478bd9Sstevel@tonic-gate 		split_ctrl |= EHCI_QH_SPLIT_CTRL_2_XACTS;
4867c478bd9Sstevel@tonic-gate 		break;
4877c478bd9Sstevel@tonic-gate 	case 2:
4887c478bd9Sstevel@tonic-gate 		split_ctrl |= EHCI_QH_SPLIT_CTRL_3_XACTS;
4897c478bd9Sstevel@tonic-gate 		break;
4907c478bd9Sstevel@tonic-gate 	default:
4917c478bd9Sstevel@tonic-gate 		split_ctrl |= EHCI_QH_SPLIT_CTRL_1_XACTS;
4927c478bd9Sstevel@tonic-gate 		break;
4937c478bd9Sstevel@tonic-gate 	}
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	/*
4967c478bd9Sstevel@tonic-gate 	 * For low/full speed devices, program high speed hub
4977c478bd9Sstevel@tonic-gate 	 * address and port number.
4987c478bd9Sstevel@tonic-gate 	 */
4997c478bd9Sstevel@tonic-gate 	if (usb_port_status != USBA_HIGH_SPEED_DEV) {
5007c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
5017c478bd9Sstevel@tonic-gate 		split_ctrl |= ((usba_device->usb_hs_hub_addr
5027c478bd9Sstevel@tonic-gate 		    << EHCI_QH_SPLIT_CTRL_HUB_ADDR_SHIFT) &
5037c478bd9Sstevel@tonic-gate 		    EHCI_QH_SPLIT_CTRL_HUB_ADDR);
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 		split_ctrl |= ((usba_device->usb_hs_hub_port
5067c478bd9Sstevel@tonic-gate 		    << EHCI_QH_SPLIT_CTRL_HUB_PORT_SHIFT) &
5077c478bd9Sstevel@tonic-gate 		    EHCI_QH_SPLIT_CTRL_HUB_PORT);
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 		/* Set start split transaction state */
5127c478bd9Sstevel@tonic-gate 		status = EHCI_QH_STS_DO_START_SPLIT;
5137c478bd9Sstevel@tonic-gate 	}
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate 	/* Assign endpoint's maxpacketsize */
5167c478bd9Sstevel@tonic-gate 	maxpacketsize = endpoint->wMaxPacketSize & USB_EP_MAX_PKTSZ_MASK;
5177c478bd9Sstevel@tonic-gate 	maxpacketsize = maxpacketsize << EHCI_QH_CTRL_MAXPKTSZ_SHIFT;
5187c478bd9Sstevel@tonic-gate 	ctrl |= (maxpacketsize & EHCI_QH_CTRL_MAXPKTSZ);
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_ctrl, ctrl);
5217c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_split_ctrl, split_ctrl);
5227c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_status, status);
5237c478bd9Sstevel@tonic-gate }
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate /*
5277c478bd9Sstevel@tonic-gate  * ehci_insert_qh:
5287c478bd9Sstevel@tonic-gate  *
5297c478bd9Sstevel@tonic-gate  * Add the Endpoint Descriptor (QH) into the Host Controller's
5307c478bd9Sstevel@tonic-gate  * (HC) appropriate endpoint list.
5317c478bd9Sstevel@tonic-gate  */
5327c478bd9Sstevel@tonic-gate void
ehci_insert_qh(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)5337c478bd9Sstevel@tonic-gate ehci_insert_qh(
5347c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
5357c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
5367c478bd9Sstevel@tonic-gate {
5377c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
540112116d8Sfb 	    "ehci_insert_qh: qh=0x%p", (void *)pp->pp_qh);
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	switch (ph->p_ep.bmAttributes & USB_EP_ATTR_MASK) {
5457c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
5467c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
5477c478bd9Sstevel@tonic-gate 		ehci_insert_async_qh(ehcip, pp);
5487c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_async_count++;
5497c478bd9Sstevel@tonic-gate 		break;
5507c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
5517c478bd9Sstevel@tonic-gate 		ehci_insert_intr_qh(ehcip, pp);
5527c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_periodic_count++;
5537c478bd9Sstevel@tonic-gate 		break;
5547c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
5557c478bd9Sstevel@tonic-gate 		/* ISOCH does not use QH, don't do anything but update count */
5567c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_periodic_count++;
5577c478bd9Sstevel@tonic-gate 		break;
5587c478bd9Sstevel@tonic-gate 	}
5597c478bd9Sstevel@tonic-gate }
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate /*
5637c478bd9Sstevel@tonic-gate  * ehci_insert_async_qh:
5647c478bd9Sstevel@tonic-gate  *
5657c478bd9Sstevel@tonic-gate  * Insert a control/bulk endpoint into the Host Controller's (HC)
5667c478bd9Sstevel@tonic-gate  * Asynchronous schedule endpoint list.
5677c478bd9Sstevel@tonic-gate  */
5687c478bd9Sstevel@tonic-gate static void
ehci_insert_async_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp)5697c478bd9Sstevel@tonic-gate ehci_insert_async_qh(
5707c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
5717c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
5727c478bd9Sstevel@tonic-gate {
5737c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh;
5747c478bd9Sstevel@tonic-gate 	ehci_qh_t		*async_head_qh;
5757c478bd9Sstevel@tonic-gate 	ehci_qh_t		*next_qh;
5767c478bd9Sstevel@tonic-gate 	uintptr_t		qh_addr;
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
5797c478bd9Sstevel@tonic-gate 	    "ehci_insert_async_qh:");
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 	/* Make sure this QH is not already in the list */
584*0a1044f1SToomas Soome 	ASSERT((Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR) == 0);
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	qh_addr = ehci_qh_cpu_to_iommu(ehcip, qh);
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	/* Obtain a ptr to the head of the Async schedule list */
5897c478bd9Sstevel@tonic-gate 	async_head_qh = ehcip->ehci_head_of_async_sched_list;
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	if (async_head_qh == NULL) {
5927c478bd9Sstevel@tonic-gate 		/* Set this QH to be the "head" of the circular list */
5937c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_ctrl,
5947c478bd9Sstevel@tonic-gate 		    (Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_RECLAIM_HEAD));
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 		/* Set new QH's link and previous pointer to itself */
5977c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_link_ptr, qh_addr | EHCI_QH_LINK_REF_QH);
5987c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_prev, qh_addr);
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 		ehcip->ehci_head_of_async_sched_list = qh;
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 		/* Set the head ptr to the new endpoint */
6037c478bd9Sstevel@tonic-gate 		Set_OpReg(ehci_async_list_addr, qh_addr);
604921cd50dSgk 
605921cd50dSgk 		/*
606921cd50dSgk 		 * For some reason this register might get nulled out by
607921cd50dSgk 		 * the Uli M1575 South Bridge. To workaround the hardware
608921cd50dSgk 		 * problem, check the value after write and retry if the
609921cd50dSgk 		 * last write fails.
610921cd50dSgk 		 *
611921cd50dSgk 		 * If the ASYNCLISTADDR remains "stuck" after
612921cd50dSgk 		 * EHCI_MAX_RETRY retries, then the M1575 is broken
613921cd50dSgk 		 * and is stuck in an inconsistent state and is about
614921cd50dSgk 		 * to crash the machine with a trn_oor panic when it
615921cd50dSgk 		 * does a DMA read from 0x0.  It is better to panic
616921cd50dSgk 		 * now rather than wait for the trn_oor crash; this
617921cd50dSgk 		 * way Customer Service will have a clean signature
618921cd50dSgk 		 * that indicts the M1575 chip rather than a
619921cd50dSgk 		 * mysterious and hard-to-diagnose trn_oor panic.
620921cd50dSgk 		 */
621921cd50dSgk 		if ((ehcip->ehci_vendor_id == PCI_VENDOR_ULi_M1575) &&
622921cd50dSgk 		    (ehcip->ehci_device_id == PCI_DEVICE_ULi_M1575) &&
623921cd50dSgk 		    (qh_addr != Get_OpReg(ehci_async_list_addr))) {
624921cd50dSgk 			int retry = 0;
625921cd50dSgk 
626921cd50dSgk 			Set_OpRegRetry(ehci_async_list_addr, qh_addr, retry);
627921cd50dSgk 			if (retry >= EHCI_MAX_RETRY)
628921cd50dSgk 				cmn_err(CE_PANIC, "ehci_insert_async_qh:"
629921cd50dSgk 				    " ASYNCLISTADDR write failed.");
630921cd50dSgk 
631921cd50dSgk 			USB_DPRINTF_L2(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
632921cd50dSgk 			    "ehci_insert_async_qh: ASYNCLISTADDR "
6336a9de478Ssl 			    "write failed, retry=%d", retry);
634921cd50dSgk 		}
6357c478bd9Sstevel@tonic-gate 	} else {
6367c478bd9Sstevel@tonic-gate 		ASSERT(Get_QH(async_head_qh->qh_ctrl) &
6377c478bd9Sstevel@tonic-gate 		    EHCI_QH_CTRL_RECLAIM_HEAD);
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 		/* Ensure this QH's "H" bit is not set */
6407c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_ctrl,
6417c478bd9Sstevel@tonic-gate 		    (Get_QH(qh->qh_ctrl) & ~EHCI_QH_CTRL_RECLAIM_HEAD));
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 		next_qh = ehci_qh_iommu_to_cpu(ehcip,
6447c478bd9Sstevel@tonic-gate 		    Get_QH(async_head_qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 		/* Set new QH's link and previous pointers */
6477c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_link_ptr,
6487c478bd9Sstevel@tonic-gate 		    Get_QH(async_head_qh->qh_link_ptr) | EHCI_QH_LINK_REF_QH);
6497c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, async_head_qh));
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 		/* Set next QH's prev pointer */
6527c478bd9Sstevel@tonic-gate 		Set_QH(next_qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, qh));
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 		/* Set QH Head's link pointer points to new QH */
6557c478bd9Sstevel@tonic-gate 		Set_QH(async_head_qh->qh_link_ptr,
6567c478bd9Sstevel@tonic-gate 		    qh_addr | EHCI_QH_LINK_REF_QH);
6577c478bd9Sstevel@tonic-gate 	}
6587c478bd9Sstevel@tonic-gate }
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate /*
6627c478bd9Sstevel@tonic-gate  * ehci_insert_intr_qh:
6637c478bd9Sstevel@tonic-gate  *
6647c478bd9Sstevel@tonic-gate  * Insert a interrupt endpoint into the Host Controller's (HC) interrupt
6657c478bd9Sstevel@tonic-gate  * lattice tree.
6667c478bd9Sstevel@tonic-gate  */
6677c478bd9Sstevel@tonic-gate static void
ehci_insert_intr_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp)6687c478bd9Sstevel@tonic-gate ehci_insert_intr_qh(
6697c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
6707c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
6717c478bd9Sstevel@tonic-gate {
6727c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh;
6737c478bd9Sstevel@tonic-gate 	ehci_qh_t		*next_lattice_qh, *lattice_qh;
6747c478bd9Sstevel@tonic-gate 	uint_t			hnode;
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
6777c478bd9Sstevel@tonic-gate 	    "ehci_insert_intr_qh:");
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	/* Make sure this QH is not already in the list */
682*0a1044f1SToomas Soome 	ASSERT((Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR) == 0);
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	/*
6857c478bd9Sstevel@tonic-gate 	 * The appropriate high speed node was found
6867c478bd9Sstevel@tonic-gate 	 * during the opening of the pipe.
6877c478bd9Sstevel@tonic-gate 	 */
6887c478bd9Sstevel@tonic-gate 	hnode = pp->pp_pnode;
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	/* Find the lattice endpoint */
6917c478bd9Sstevel@tonic-gate 	lattice_qh = &ehcip->ehci_qh_pool_addr[hnode];
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	/* Find the next lattice endpoint */
6947c478bd9Sstevel@tonic-gate 	next_lattice_qh = ehci_qh_iommu_to_cpu(
6957c478bd9Sstevel@tonic-gate 	    ehcip, (Get_QH(lattice_qh->qh_link_ptr) & EHCI_QH_LINK_PTR));
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 	/* Update the previous pointer */
6987c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, lattice_qh));
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 	/* Check next_lattice_qh value */
7017c478bd9Sstevel@tonic-gate 	if (next_lattice_qh) {
7027c478bd9Sstevel@tonic-gate 		/* Update this qh to point to the next one in the lattice */
7037c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_link_ptr, Get_QH(lattice_qh->qh_link_ptr));
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 		/* Update the previous pointer of qh->qh_link_ptr */
7067c478bd9Sstevel@tonic-gate 		if (Get_QH(next_lattice_qh->qh_state) != EHCI_QH_STATIC) {
7077c478bd9Sstevel@tonic-gate 			Set_QH(next_lattice_qh->qh_prev,
7087c478bd9Sstevel@tonic-gate 			    ehci_qh_cpu_to_iommu(ehcip, qh));
7097c478bd9Sstevel@tonic-gate 		}
7107c478bd9Sstevel@tonic-gate 	} else {
7117c478bd9Sstevel@tonic-gate 		/* Update qh's link pointer to terminate periodic list */
7127c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_link_ptr,
7137c478bd9Sstevel@tonic-gate 		    (Get_QH(lattice_qh->qh_link_ptr) | EHCI_QH_LINK_PTR_VALID));
7147c478bd9Sstevel@tonic-gate 	}
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	/* Insert this endpoint into the lattice */
7177c478bd9Sstevel@tonic-gate 	Set_QH(lattice_qh->qh_link_ptr,
7187c478bd9Sstevel@tonic-gate 	    (ehci_qh_cpu_to_iommu(ehcip, qh) | EHCI_QH_LINK_REF_QH));
7197c478bd9Sstevel@tonic-gate }
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate /*
7237c478bd9Sstevel@tonic-gate  * ehci_modify_qh_status_bit:
7247c478bd9Sstevel@tonic-gate  *
7257c478bd9Sstevel@tonic-gate  * Modify the halt bit on the Host Controller (HC) Endpoint Descriptor (QH).
7267c478bd9Sstevel@tonic-gate  *
7277c478bd9Sstevel@tonic-gate  * If several threads try to halt the same pipe, they will need to wait on
7287c478bd9Sstevel@tonic-gate  * a condition variable.  Only one thread is allowed to halt or unhalt the
7297c478bd9Sstevel@tonic-gate  * pipe at a time.
7307c478bd9Sstevel@tonic-gate  *
7317c478bd9Sstevel@tonic-gate  * Usually after a halt pipe, an unhalt pipe will follow soon after.  There
7327c478bd9Sstevel@tonic-gate  * is an assumption that an Unhalt pipe will never occur without a halt pipe.
7337c478bd9Sstevel@tonic-gate  */
7347c478bd9Sstevel@tonic-gate static void
ehci_modify_qh_status_bit(ehci_state_t * ehcip,ehci_pipe_private_t * pp,halt_bit_t action)7357c478bd9Sstevel@tonic-gate ehci_modify_qh_status_bit(
7367c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
7377c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
7387c478bd9Sstevel@tonic-gate 	halt_bit_t		action)
7397c478bd9Sstevel@tonic-gate {
7407c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh;
7417c478bd9Sstevel@tonic-gate 	uint_t			smask, eps, split_intr_qh;
7427c478bd9Sstevel@tonic-gate 	uint_t			status;
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
7457c478bd9Sstevel@tonic-gate 	    "ehci_modify_qh_status_bit: action=0x%x qh=0x%p",
746112116d8Sfb 	    action, (void *)qh);
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	ehci_print_qh(ehcip, qh);
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	/*
7537c478bd9Sstevel@tonic-gate 	 * If this pipe is in the middle of halting don't allow another
7547c478bd9Sstevel@tonic-gate 	 * thread to come in and modify the same pipe.
7557c478bd9Sstevel@tonic-gate 	 */
7567c478bd9Sstevel@tonic-gate 	while (pp->pp_halt_state & EHCI_HALT_STATE_HALTING) {
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 		cv_wait(&pp->pp_halt_cmpl_cv,
7596a9de478Ssl 		    &ehcip->ehci_int_mutex);
7607c478bd9Sstevel@tonic-gate 	}
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	/* Sync the QH QTD pool to get up to date information */
7637c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	if (action == CLEAR_HALT) {
7677c478bd9Sstevel@tonic-gate 		/*
7687c478bd9Sstevel@tonic-gate 		 * If the halt bit is to be cleared, just clear it.
7697c478bd9Sstevel@tonic-gate 		 * there shouldn't be any race condition problems.
7707c478bd9Sstevel@tonic-gate 		 * If the host controller reads the bit before the
7717c478bd9Sstevel@tonic-gate 		 * driver has a chance to set the bit, the bit will
7727c478bd9Sstevel@tonic-gate 		 * be reread on the next frame.
7737c478bd9Sstevel@tonic-gate 		 */
7747c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_ctrl,
7757c478bd9Sstevel@tonic-gate 		    (Get_QH(qh->qh_ctrl) & ~EHCI_QH_CTRL_ED_INACTIVATE));
7767c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_status,
7777c478bd9Sstevel@tonic-gate 		    Get_QH(qh->qh_status) & ~(EHCI_QH_STS_XACT_STATUS));
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 		goto success;
7807c478bd9Sstevel@tonic-gate 	}
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 	/* Halt the the QH, but first check to see if it is already halted */
7837c478bd9Sstevel@tonic-gate 	status = Get_QH(qh->qh_status);
7847c478bd9Sstevel@tonic-gate 	if (!(status & EHCI_QH_STS_HALTED)) {
7857c478bd9Sstevel@tonic-gate 		/* Indicate that this pipe is in the middle of halting. */
7867c478bd9Sstevel@tonic-gate 		pp->pp_halt_state |= EHCI_HALT_STATE_HALTING;
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 		/*
7897c478bd9Sstevel@tonic-gate 		 * Find out if this is an full/low speed interrupt endpoint.
7907c478bd9Sstevel@tonic-gate 		 * A non-zero Cmask indicates that this QH is an interrupt
7917c478bd9Sstevel@tonic-gate 		 * endpoint.  Check the endpoint speed to see if it is either
7927c478bd9Sstevel@tonic-gate 		 * FULL or LOW .
7937c478bd9Sstevel@tonic-gate 		 */
7947c478bd9Sstevel@tonic-gate 		smask = Get_QH(qh->qh_split_ctrl) &
7957c478bd9Sstevel@tonic-gate 		    EHCI_QH_SPLIT_CTRL_INTR_MASK;
7967c478bd9Sstevel@tonic-gate 		eps = Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_ED_SPEED;
7977c478bd9Sstevel@tonic-gate 		split_intr_qh = ((smask != 0) &&
7987c478bd9Sstevel@tonic-gate 		    (eps != EHCI_QH_CTRL_ED_HIGH_SPEED));
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 		if (eps == EHCI_QH_CTRL_ED_HIGH_SPEED) {
8017c478bd9Sstevel@tonic-gate 			ehci_halt_hs_qh(ehcip, pp, qh);
8027c478bd9Sstevel@tonic-gate 		} else {
8037c478bd9Sstevel@tonic-gate 			if (split_intr_qh) {
8047c478bd9Sstevel@tonic-gate 				ehci_halt_fls_intr_qh(ehcip, qh);
8057c478bd9Sstevel@tonic-gate 			} else {
8067c478bd9Sstevel@tonic-gate 				ehci_halt_fls_ctrl_and_bulk_qh(ehcip, pp, qh);
8077c478bd9Sstevel@tonic-gate 			}
8087c478bd9Sstevel@tonic-gate 		}
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 		/* Indicate that this pipe is not in the middle of halting. */
8117c478bd9Sstevel@tonic-gate 		pp->pp_halt_state &= ~EHCI_HALT_STATE_HALTING;
8127c478bd9Sstevel@tonic-gate 	}
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate 	/* Sync the QH QTD pool again to get the most up to date information */
8157c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	ehci_print_qh(ehcip, qh);
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	status = Get_QH(qh->qh_status);
8207c478bd9Sstevel@tonic-gate 	if (!(status & EHCI_QH_STS_HALTED)) {
8217c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
822112116d8Sfb 		    "ehci_modify_qh_status_bit: Failed to halt qh=0x%p",
823112116d8Sfb 		    (void *)qh);
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 		ehci_print_qh(ehcip, qh);
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 		/* Set host controller soft state to error */
8287c478bd9Sstevel@tonic-gate 		ehcip->ehci_hc_soft_state = EHCI_CTLR_ERROR_STATE;
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 		ASSERT(status & EHCI_QH_STS_HALTED);
8317c478bd9Sstevel@tonic-gate 	}
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate success:
8347c478bd9Sstevel@tonic-gate 	/* Wake up threads waiting for this pipe to be halted. */
8357c478bd9Sstevel@tonic-gate 	cv_signal(&pp->pp_halt_cmpl_cv);
8367c478bd9Sstevel@tonic-gate }
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate /*
8407c478bd9Sstevel@tonic-gate  * ehci_halt_hs_qh:
8417c478bd9Sstevel@tonic-gate  *
8427c478bd9Sstevel@tonic-gate  * Halts all types of HIGH SPEED QHs.
8437c478bd9Sstevel@tonic-gate  */
8447c478bd9Sstevel@tonic-gate static void
ehci_halt_hs_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_qh_t * qh)8457c478bd9Sstevel@tonic-gate ehci_halt_hs_qh(
8467c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
8477c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
8487c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh)
8497c478bd9Sstevel@tonic-gate {
8507c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
8537c478bd9Sstevel@tonic-gate 	    "ehci_halt_hs_qh:");
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	/* Remove this qh from the HCD's view, but do not reclaim it */
8567c478bd9Sstevel@tonic-gate 	ehci_remove_qh(ehcip, pp, B_FALSE);
8577484d118SRaymond Chen 	ehci_toggle_scheduler_on_pipe(ehcip);
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 	/*
8607c478bd9Sstevel@tonic-gate 	 * Wait for atleast one SOF, just in case the HCD is in the
8617c478bd9Sstevel@tonic-gate 	 * middle accessing this QH.
8627c478bd9Sstevel@tonic-gate 	 */
8637c478bd9Sstevel@tonic-gate 	(void) ehci_wait_for_sof(ehcip);
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 	/* Sync the QH QTD pool to get up to date information */
8667c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	/* Modify the status bit and halt this QH. */
8697c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_status,
8707c478bd9Sstevel@tonic-gate 	    ((Get_QH(qh->qh_status) &
8716a9de478Ssl 	    ~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	/* Insert this QH back into the HCD's view */
8747c478bd9Sstevel@tonic-gate 	ehci_insert_qh(ehcip, ph);
8757484d118SRaymond Chen 	ehci_toggle_scheduler_on_pipe(ehcip);
8767c478bd9Sstevel@tonic-gate }
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate /*
8807c478bd9Sstevel@tonic-gate  * ehci_halt_fls_ctrl_and_bulk_qh:
8817c478bd9Sstevel@tonic-gate  *
8827c478bd9Sstevel@tonic-gate  * Halts FULL/LOW Ctrl and Bulk QHs only.
8837c478bd9Sstevel@tonic-gate  */
8847c478bd9Sstevel@tonic-gate static void
ehci_halt_fls_ctrl_and_bulk_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_qh_t * qh)8857c478bd9Sstevel@tonic-gate ehci_halt_fls_ctrl_and_bulk_qh(
8867c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
8877c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
8887c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh)
8897c478bd9Sstevel@tonic-gate {
8907c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
8917c478bd9Sstevel@tonic-gate 	uint_t			status, split_status, bytes_left;
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
8957c478bd9Sstevel@tonic-gate 	    "ehci_halt_fls_ctrl_and_bulk_qh:");
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 	/* Remove this qh from the HCD's view, but do not reclaim it */
8987c478bd9Sstevel@tonic-gate 	ehci_remove_qh(ehcip, pp, B_FALSE);
8997484d118SRaymond Chen 	ehci_toggle_scheduler_on_pipe(ehcip);
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	/*
9027c478bd9Sstevel@tonic-gate 	 * Wait for atleast one SOF, just in case the HCD is in the
9037c478bd9Sstevel@tonic-gate 	 * middle accessing this QH.
9047c478bd9Sstevel@tonic-gate 	 */
9057c478bd9Sstevel@tonic-gate 	(void) ehci_wait_for_sof(ehcip);
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate 	/* Sync the QH QTD pool to get up to date information */
9087c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate 	/* Modify the status bit and halt this QH. */
9117c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_status,
9127c478bd9Sstevel@tonic-gate 	    ((Get_QH(qh->qh_status) &
9136a9de478Ssl 	    ~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	/* Check to see if the QH was in the middle of a transaction */
9167c478bd9Sstevel@tonic-gate 	status = Get_QH(qh->qh_status);
9177c478bd9Sstevel@tonic-gate 	split_status = status & EHCI_QH_STS_SPLIT_XSTATE;
9187c478bd9Sstevel@tonic-gate 	bytes_left = status & EHCI_QH_STS_BYTES_TO_XFER;
9197c478bd9Sstevel@tonic-gate 	if ((split_status == EHCI_QH_STS_DO_COMPLETE_SPLIT) &&
9207c478bd9Sstevel@tonic-gate 	    (bytes_left != 0)) {
9217c478bd9Sstevel@tonic-gate 		/* send ClearTTBuffer to this device's parent 2.0 hub */
9227c478bd9Sstevel@tonic-gate 		ehci_clear_tt_buffer(ehcip, ph, qh);
9237c478bd9Sstevel@tonic-gate 	}
9247c478bd9Sstevel@tonic-gate 
9257c478bd9Sstevel@tonic-gate 	/* Insert this QH back into the HCD's view */
9267c478bd9Sstevel@tonic-gate 	ehci_insert_qh(ehcip, ph);
9277484d118SRaymond Chen 	ehci_toggle_scheduler_on_pipe(ehcip);
9287c478bd9Sstevel@tonic-gate }
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate /*
9327c478bd9Sstevel@tonic-gate  * ehci_clear_tt_buffer
9337c478bd9Sstevel@tonic-gate  *
9347c478bd9Sstevel@tonic-gate  * This function will sent a Clear_TT_Buffer request to the pipe's
9357c478bd9Sstevel@tonic-gate  * parent 2.0 hub.
9367c478bd9Sstevel@tonic-gate  */
9377c478bd9Sstevel@tonic-gate static void
ehci_clear_tt_buffer(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,ehci_qh_t * qh)9387c478bd9Sstevel@tonic-gate ehci_clear_tt_buffer(
9397c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
9407c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
9417c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh)
9427c478bd9Sstevel@tonic-gate {
9437c478bd9Sstevel@tonic-gate 	usba_device_t		*usba_device;
9447c478bd9Sstevel@tonic-gate 	usba_device_t		*hub_usba_device;
9457c478bd9Sstevel@tonic-gate 	usb_pipe_handle_t	hub_def_ph;
9467c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd;
9477c478bd9Sstevel@tonic-gate 	uchar_t			attributes;
9487c478bd9Sstevel@tonic-gate 	uint16_t		wValue;
9497c478bd9Sstevel@tonic-gate 	usb_ctrl_setup_t	setup;
9507c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason;
9517c478bd9Sstevel@tonic-gate 	usb_cb_flags_t		cb_flags;
9527c478bd9Sstevel@tonic-gate 	int			retry;
9537c478bd9Sstevel@tonic-gate 
9547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
9557c478bd9Sstevel@tonic-gate 	    "ehci_clear_tt_buffer: ");
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 	/* Get some information about the current pipe */
9587c478bd9Sstevel@tonic-gate 	usba_device = ph->p_usba_device;
9597c478bd9Sstevel@tonic-gate 	eptd = &ph->p_ep;
9607c478bd9Sstevel@tonic-gate 	attributes = eptd->bmAttributes & USB_EP_ATTR_MASK;
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 	/*
9637c478bd9Sstevel@tonic-gate 	 * Create the wIndex for this request (usb spec 11.24.2.3)
9647c478bd9Sstevel@tonic-gate 	 * 3..0		Endpoint Number
9657c478bd9Sstevel@tonic-gate 	 * 10..4	Device Address
9667c478bd9Sstevel@tonic-gate 	 * 12..11	Endpoint Type
9677c478bd9Sstevel@tonic-gate 	 * 14..13	Reserved (must be 0)
9687c478bd9Sstevel@tonic-gate 	 * 15		Direction 1 = IN, 0 = OUT
9697c478bd9Sstevel@tonic-gate 	 */
9707c478bd9Sstevel@tonic-gate 	wValue = 0;
9717c478bd9Sstevel@tonic-gate 	if ((eptd->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
9727c478bd9Sstevel@tonic-gate 		wValue |= 0x8000;
9737c478bd9Sstevel@tonic-gate 	}
9747c478bd9Sstevel@tonic-gate 	wValue |= attributes << 11;
9757c478bd9Sstevel@tonic-gate 	wValue |= (Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_DEVICE_ADDRESS) << 4;
9767c478bd9Sstevel@tonic-gate 	wValue |= (Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_ED_HIGH_SPEED) >>
9777c478bd9Sstevel@tonic-gate 	    EHCI_QH_CTRL_ED_NUMBER_SHIFT;
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate 	/* Manually fill in the request. */
9827c478bd9Sstevel@tonic-gate 	setup.bmRequestType = EHCI_CLEAR_TT_BUFFER_REQTYPE;
9837c478bd9Sstevel@tonic-gate 	setup.bRequest = EHCI_CLEAR_TT_BUFFER_BREQ;
9847c478bd9Sstevel@tonic-gate 	setup.wValue = wValue;
9857c478bd9Sstevel@tonic-gate 	setup.wIndex = 1;
9867c478bd9Sstevel@tonic-gate 	setup.wLength = 0;
9877c478bd9Sstevel@tonic-gate 	setup.attrs = USB_ATTRS_NONE;
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 	/* Get the usba_device of the parent 2.0 hub. */
9907c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
9917c478bd9Sstevel@tonic-gate 	hub_usba_device = usba_device->usb_hs_hub_usba_dev;
9927c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
9937c478bd9Sstevel@tonic-gate 
9947c478bd9Sstevel@tonic-gate 	/* Get the default ctrl pipe for the parent 2.0 hub */
9957c478bd9Sstevel@tonic-gate 	mutex_enter(&hub_usba_device->usb_mutex);
9967c478bd9Sstevel@tonic-gate 	hub_def_ph = (usb_pipe_handle_t)&hub_usba_device->usb_ph_list[0];
9977c478bd9Sstevel@tonic-gate 	mutex_exit(&hub_usba_device->usb_mutex);
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 	for (retry = 0; retry < 3; retry++) {
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 		/* sync send the request to the default pipe */
10027c478bd9Sstevel@tonic-gate 		if (usb_pipe_ctrl_xfer_wait(
10037c478bd9Sstevel@tonic-gate 		    hub_def_ph,
10047c478bd9Sstevel@tonic-gate 		    &setup,
10057c478bd9Sstevel@tonic-gate 		    NULL,
10067c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0) == USB_SUCCESS) {
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate 			break;
10097c478bd9Sstevel@tonic-gate 		}
10107c478bd9Sstevel@tonic-gate 
10117c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10127c478bd9Sstevel@tonic-gate 		    "ehci_clear_tt_buffer: Failed to clear tt buffer,"
10137c478bd9Sstevel@tonic-gate 		    "retry = %d, cr = %d, cb_flags = 0x%x\n",
10147c478bd9Sstevel@tonic-gate 		    retry, completion_reason, cb_flags);
10157c478bd9Sstevel@tonic-gate 	}
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate 	if (retry >= 3) {
10187c478bd9Sstevel@tonic-gate 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
10197c478bd9Sstevel@tonic-gate 		dev_info_t *dip = hub_usba_device->usb_dip;
10207c478bd9Sstevel@tonic-gate 
10217c478bd9Sstevel@tonic-gate 		/*
10227c478bd9Sstevel@tonic-gate 		 * Ask the user to hotplug the 2.0 hub, to make sure that
10237c478bd9Sstevel@tonic-gate 		 * all the buffer is in sync since this command has failed.
10247c478bd9Sstevel@tonic-gate 		 */
10257c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10267c478bd9Sstevel@tonic-gate 		    "Error recovery failure: Please hotplug the 2.0 hub at"
10277c478bd9Sstevel@tonic-gate 		    "%s", ddi_pathname(dip, path));
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 		kmem_free(path, MAXPATHLEN);
10307c478bd9Sstevel@tonic-gate 	}
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
10337c478bd9Sstevel@tonic-gate }
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate /*
10367c478bd9Sstevel@tonic-gate  * ehci_halt_fls_intr_qh:
10377c478bd9Sstevel@tonic-gate  *
10387c478bd9Sstevel@tonic-gate  * Halts FULL/LOW speed Intr QHs.
10397c478bd9Sstevel@tonic-gate  */
10407c478bd9Sstevel@tonic-gate static void
ehci_halt_fls_intr_qh(ehci_state_t * ehcip,ehci_qh_t * qh)10417c478bd9Sstevel@tonic-gate ehci_halt_fls_intr_qh(
10427c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
10437c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh)
10447c478bd9Sstevel@tonic-gate {
10457c478bd9Sstevel@tonic-gate 	usb_frame_number_t	starting_frame;
10467c478bd9Sstevel@tonic-gate 	usb_frame_number_t	frames_past;
10477c478bd9Sstevel@tonic-gate 	uint_t			status, i;
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10507c478bd9Sstevel@tonic-gate 	    "ehci_halt_fls_intr_qh:");
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	/*
10537c478bd9Sstevel@tonic-gate 	 * Ask the HC to deactivate the QH in a
10547c478bd9Sstevel@tonic-gate 	 * full/low periodic QH.
10557c478bd9Sstevel@tonic-gate 	 */
10567c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_ctrl,
10577c478bd9Sstevel@tonic-gate 	    (Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_ED_INACTIVATE));
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 	starting_frame = ehci_get_current_frame_number(ehcip);
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate 	/*
10627c478bd9Sstevel@tonic-gate 	 * Wait at least EHCI_NUM_INTR_QH_LISTS+2 frame or until
10637c478bd9Sstevel@tonic-gate 	 * the QH has been halted.
10647c478bd9Sstevel@tonic-gate 	 */
10657c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
10667c478bd9Sstevel@tonic-gate 	frames_past = 0;
10677c478bd9Sstevel@tonic-gate 	status = Get_QH(qh->qh_status) & EHCI_QTD_CTRL_ACTIVE_XACT;
10687c478bd9Sstevel@tonic-gate 
10697c478bd9Sstevel@tonic-gate 	while ((frames_past <= (EHCI_NUM_INTR_QH_LISTS + 2)) &&
10707c478bd9Sstevel@tonic-gate 	    (status != 0)) {
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 		(void) ehci_wait_for_sof(ehcip);
10737c478bd9Sstevel@tonic-gate 
10747c478bd9Sstevel@tonic-gate 		Sync_QH_QTD_Pool(ehcip);
10757c478bd9Sstevel@tonic-gate 		status = Get_QH(qh->qh_status) & EHCI_QTD_CTRL_ACTIVE_XACT;
10767c478bd9Sstevel@tonic-gate 		frames_past = ehci_get_current_frame_number(ehcip) -
10777c478bd9Sstevel@tonic-gate 		    starting_frame;
10787c478bd9Sstevel@tonic-gate 	}
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate 	/* Modify the status bit and halt this QH. */
10817c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate 	status = Get_QH(qh->qh_status);
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 	for (i = 0; i < EHCI_NUM_INTR_QH_LISTS; i++) {
10867c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_status,
10876a9de478Ssl 		    ((Get_QH(qh->qh_status) &
10886a9de478Ssl 		    ~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
10897c478bd9Sstevel@tonic-gate 
10907c478bd9Sstevel@tonic-gate 		Sync_QH_QTD_Pool(ehcip);
10917c478bd9Sstevel@tonic-gate 
10927c478bd9Sstevel@tonic-gate 		(void) ehci_wait_for_sof(ehcip);
10937c478bd9Sstevel@tonic-gate 		Sync_QH_QTD_Pool(ehcip);
10947c478bd9Sstevel@tonic-gate 
10957c478bd9Sstevel@tonic-gate 		if (Get_QH(qh->qh_status) & EHCI_QH_STS_HALTED) {
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 			break;
10987c478bd9Sstevel@tonic-gate 		}
10997c478bd9Sstevel@tonic-gate 	}
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
1104112116d8Sfb 	    "ehci_halt_fls_intr_qh: qh=0x%p frames past=%llu,"
1105112116d8Sfb 	    " status=0x%x, 0x%x", (void *)qh,
1106112116d8Sfb 	    (unsigned long long)(ehci_get_current_frame_number(ehcip) -
1107112116d8Sfb 	    starting_frame), status, Get_QH(qh->qh_status));
11087c478bd9Sstevel@tonic-gate }
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate /*
11127c478bd9Sstevel@tonic-gate  * ehci_remove_qh:
11137c478bd9Sstevel@tonic-gate  *
11147c478bd9Sstevel@tonic-gate  * Remove the Endpoint Descriptor (QH) from the Host Controller's appropriate
11157c478bd9Sstevel@tonic-gate  * endpoint list.
11167c478bd9Sstevel@tonic-gate  */
11177c478bd9Sstevel@tonic-gate void
ehci_remove_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,boolean_t reclaim)11187c478bd9Sstevel@tonic-gate ehci_remove_qh(
11197c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
11207c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
11217c478bd9Sstevel@tonic-gate 	boolean_t		reclaim)
11227c478bd9Sstevel@tonic-gate {
11237c478bd9Sstevel@tonic-gate 	uchar_t			attributes;
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
1128112116d8Sfb 	    "ehci_remove_qh: qh=0x%p", (void *)pp->pp_qh);
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate 	attributes = pp->pp_pipe_handle->p_ep.bmAttributes & USB_EP_ATTR_MASK;
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 	switch (attributes) {
11337c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
11347c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
11357c478bd9Sstevel@tonic-gate 		ehci_remove_async_qh(ehcip, pp, reclaim);
11367c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_async_count--;
11377c478bd9Sstevel@tonic-gate 		break;
11387c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
11397c478bd9Sstevel@tonic-gate 		ehci_remove_intr_qh(ehcip, pp, reclaim);
11407c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_periodic_count--;
11417c478bd9Sstevel@tonic-gate 		break;
11427c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
11437c478bd9Sstevel@tonic-gate 		/* ISOCH does not use QH, don't do anything but update count */
11447c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_periodic_count--;
11457c478bd9Sstevel@tonic-gate 		break;
11467c478bd9Sstevel@tonic-gate 	}
11477c478bd9Sstevel@tonic-gate }
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate /*
11517c478bd9Sstevel@tonic-gate  * ehci_remove_async_qh:
11527c478bd9Sstevel@tonic-gate  *
11537c478bd9Sstevel@tonic-gate  * Remove a control/bulk endpoint into the Host Controller's (HC)
11547c478bd9Sstevel@tonic-gate  * Asynchronous schedule endpoint list.
11557c478bd9Sstevel@tonic-gate  */
11567c478bd9Sstevel@tonic-gate static void
ehci_remove_async_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,boolean_t reclaim)11577c478bd9Sstevel@tonic-gate ehci_remove_async_qh(
11587c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
11597c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
11607c478bd9Sstevel@tonic-gate 	boolean_t		reclaim)
11617c478bd9Sstevel@tonic-gate {
11627c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh; /* qh to be removed */
11637c478bd9Sstevel@tonic-gate 	ehci_qh_t		*prev_qh, *next_qh;
11647c478bd9Sstevel@tonic-gate 
11657c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
11667c478bd9Sstevel@tonic-gate 	    "ehci_remove_async_qh:");
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
11697c478bd9Sstevel@tonic-gate 
11707c478bd9Sstevel@tonic-gate 	prev_qh = ehci_qh_iommu_to_cpu(ehcip,
11717c478bd9Sstevel@tonic-gate 	    Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR);
11727c478bd9Sstevel@tonic-gate 	next_qh = ehci_qh_iommu_to_cpu(ehcip,
11737c478bd9Sstevel@tonic-gate 	    Get_QH(qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 	/* Make sure this QH is in the list */
11767c478bd9Sstevel@tonic-gate 	ASSERT(prev_qh != NULL);
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 	/*
1179