xref: /illumos-gate/usr/src/uts/common/io/usb/hcd/openhci/ohci.c (revision 9c75c6bf17b72bb057d7a8879feba77ece65241a)
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
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * Open Host Controller Driver (OHCI)
317c478bd9Sstevel@tonic-gate  *
327c478bd9Sstevel@tonic-gate  * The USB Open Host Controller driver is a software driver which interfaces
337c478bd9Sstevel@tonic-gate  * to the Universal Serial Bus layer (USBA) and the USB Open Host Controller.
347c478bd9Sstevel@tonic-gate  * The interface to USB Open Host Controller is defined by the OpenHCI  Host
357c478bd9Sstevel@tonic-gate  * Controller Interface.
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  * NOTE:
387c478bd9Sstevel@tonic-gate  *
397c478bd9Sstevel@tonic-gate  * Currently OHCI driver does not support the following features
407c478bd9Sstevel@tonic-gate  *
417c478bd9Sstevel@tonic-gate  * - Handle request with multiple TDs under short xfer conditions except for
427c478bd9Sstevel@tonic-gate  *   bulk transfers.
437c478bd9Sstevel@tonic-gate  */
447c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/openhci/ohcid.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate /* Pointer to the state structure */
477c478bd9Sstevel@tonic-gate static void *ohci_statep;
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate /* Number of instances */
507c478bd9Sstevel@tonic-gate #define	OHCI_INSTS	1
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate /* Adjustable variables for the size of the pools */
534610e4a0Sfrits int ohci_ed_pool_size = OHCI_ED_POOL_SIZE;
544610e4a0Sfrits int ohci_td_pool_size = OHCI_TD_POOL_SIZE;
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /*
577c478bd9Sstevel@tonic-gate  * Initialize the values which are used for setting up head pointers for
587c478bd9Sstevel@tonic-gate  * the 32ms scheduling lists which starts from the HCCA.
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate static uchar_t ohci_index[NUM_INTR_ED_LISTS / 2] = {0x0, 0x8, 0x4, 0xc,
617c478bd9Sstevel@tonic-gate 						0x2, 0xa, 0x6, 0xe,
627c478bd9Sstevel@tonic-gate 						0x1, 0x9, 0x5, 0xd,
637c478bd9Sstevel@tonic-gate 						0x3, 0xb, 0x7, 0xf};
647c478bd9Sstevel@tonic-gate /* Debugging information */
657c478bd9Sstevel@tonic-gate uint_t ohci_errmask	= (uint_t)PRINT_MASK_ALL;
667c478bd9Sstevel@tonic-gate uint_t ohci_errlevel	= USB_LOG_L2;
677c478bd9Sstevel@tonic-gate uint_t ohci_instance_debug = (uint_t)-1;
687c478bd9Sstevel@tonic-gate 
69*9c75c6bfSgovinda /*
70*9c75c6bfSgovinda  * OHCI MSI tunable:
71*9c75c6bfSgovinda  *
72*9c75c6bfSgovinda  * By default MSI is enabled on all supported platforms.
73*9c75c6bfSgovinda  */
74*9c75c6bfSgovinda boolean_t ohci_enable_msi = B_TRUE;
75*9c75c6bfSgovinda 
767c478bd9Sstevel@tonic-gate /*
777c478bd9Sstevel@tonic-gate  * HCDI entry points
787c478bd9Sstevel@tonic-gate  *
797c478bd9Sstevel@tonic-gate  * The Host Controller Driver Interfaces (HCDI) are the software interfaces
807c478bd9Sstevel@tonic-gate  * between the Universal Serial Bus Driver (USBA) and the Host	Controller
817c478bd9Sstevel@tonic-gate  * Driver (HCD). The HCDI interfaces or entry points are subject to change.
827c478bd9Sstevel@tonic-gate  */
837c478bd9Sstevel@tonic-gate static int	ohci_hcdi_pipe_open(
847c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
857c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
867c478bd9Sstevel@tonic-gate static int	ohci_hcdi_pipe_close(
877c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
887c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
897c478bd9Sstevel@tonic-gate static int	ohci_hcdi_pipe_reset(
907c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
917c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
927c478bd9Sstevel@tonic-gate static int	ohci_hcdi_pipe_ctrl_xfer(
937c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
947c478bd9Sstevel@tonic-gate 				usb_ctrl_req_t		*ctrl_reqp,
957c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
967c478bd9Sstevel@tonic-gate static int	ohci_hcdi_bulk_transfer_size(
977c478bd9Sstevel@tonic-gate 				usba_device_t		*usba_device,
987c478bd9Sstevel@tonic-gate 				size_t			*size);
997c478bd9Sstevel@tonic-gate static int	ohci_hcdi_pipe_bulk_xfer(
1007c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1017c478bd9Sstevel@tonic-gate 				usb_bulk_req_t		*bulk_reqp,
1027c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1037c478bd9Sstevel@tonic-gate static int	ohci_hcdi_pipe_intr_xfer(
1047c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1057c478bd9Sstevel@tonic-gate 				usb_intr_req_t		*intr_req,
1067c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1077c478bd9Sstevel@tonic-gate static int	ohci_hcdi_pipe_stop_intr_polling(
1087c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1097c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1107c478bd9Sstevel@tonic-gate static usb_frame_number_t ohci_hcdi_get_current_frame_number(
1117c478bd9Sstevel@tonic-gate 				usba_device_t		*usba_device);
1127c478bd9Sstevel@tonic-gate static uint_t	ohci_hcdi_get_max_isoc_pkts(
1137c478bd9Sstevel@tonic-gate 				usba_device_t		*usba_device);
1147c478bd9Sstevel@tonic-gate static int	ohci_hcdi_pipe_isoc_xfer(
1157c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1167c478bd9Sstevel@tonic-gate 				usb_isoc_req_t		*isoc_reqp,
1177c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1187c478bd9Sstevel@tonic-gate static int	ohci_hcdi_pipe_stop_isoc_polling(
1197c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1207c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate /*
1237c478bd9Sstevel@tonic-gate  * Internal Function Prototypes
1247c478bd9Sstevel@tonic-gate  */
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate /* Host Controller Driver (HCD) initialization functions */
1277c478bd9Sstevel@tonic-gate static void	ohci_set_dma_attributes(ohci_state_t	*ohcip);
1287c478bd9Sstevel@tonic-gate static int	ohci_allocate_pools(ohci_state_t	*ohcip);
1297c478bd9Sstevel@tonic-gate static void	ohci_decode_ddi_dma_addr_bind_handle_result(
1307c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
1317c478bd9Sstevel@tonic-gate 				int			result);
1327c478bd9Sstevel@tonic-gate static int	ohci_map_regs(ohci_state_t		*ohcip);
1337c478bd9Sstevel@tonic-gate static int	ohci_register_intrs_and_init_mutex(
1347c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip);
135*9c75c6bfSgovinda static int	ohci_add_intrs(ohci_state_t		*ohcip,
136*9c75c6bfSgovinda 				int			intr_type);
1377c478bd9Sstevel@tonic-gate static int	ohci_init_ctlr(ohci_state_t		*ohcip);
1387c478bd9Sstevel@tonic-gate static int	ohci_init_hcca(ohci_state_t		*ohcip);
1397c478bd9Sstevel@tonic-gate static void	ohci_build_interrupt_lattice(
1407c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip);
1417c478bd9Sstevel@tonic-gate static int	ohci_take_control(ohci_state_t		*ohcip);
1427c478bd9Sstevel@tonic-gate static usba_hcdi_ops_t *ohci_alloc_hcdi_ops(
1437c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip);
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate /* Host Controller Driver (HCD) deinitialization functions */
1467c478bd9Sstevel@tonic-gate static int	ohci_cleanup(ohci_state_t		*ohcip);
147*9c75c6bfSgovinda static void	ohci_rem_intrs(ohci_state_t		*ohcip);
1487c478bd9Sstevel@tonic-gate static int	ohci_cpr_suspend(ohci_state_t		*ohcip);
1497c478bd9Sstevel@tonic-gate static int	ohci_cpr_resume(ohci_state_t		*ohcip);
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate /* Bandwidth Allocation functions */
1527c478bd9Sstevel@tonic-gate static int	ohci_allocate_bandwidth(ohci_state_t	*ohcip,
1537c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1547c478bd9Sstevel@tonic-gate 				uint_t			*node);
1557c478bd9Sstevel@tonic-gate static void	ohci_deallocate_bandwidth(ohci_state_t	*ohcip,
1567c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
1577c478bd9Sstevel@tonic-gate static int	ohci_compute_total_bandwidth(
1587c478bd9Sstevel@tonic-gate 				usb_ep_descr_t		*endpoint,
1597c478bd9Sstevel@tonic-gate 				usb_port_status_t	port_status,
1607c478bd9Sstevel@tonic-gate 				uint_t			*bandwidth);
1617c478bd9Sstevel@tonic-gate static int	ohci_adjust_polling_interval(
1627c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
1637c478bd9Sstevel@tonic-gate 				usb_ep_descr_t		*endpoint,
1647c478bd9Sstevel@tonic-gate 				usb_port_status_t	port_status);
1657c478bd9Sstevel@tonic-gate static uint_t	ohci_lattice_height(uint_t		interval);
1667c478bd9Sstevel@tonic-gate static uint_t	ohci_lattice_parent(uint_t		node);
1677c478bd9Sstevel@tonic-gate static uint_t	ohci_leftmost_leaf(uint_t		node,
1687c478bd9Sstevel@tonic-gate 				uint_t			height);
1697c478bd9Sstevel@tonic-gate static uint_t	ohci_hcca_intr_index(
1707c478bd9Sstevel@tonic-gate 				uint_t			node);
1717c478bd9Sstevel@tonic-gate static uint_t	ohci_hcca_leaf_index(
1727c478bd9Sstevel@tonic-gate 				uint_t			leaf);
1737c478bd9Sstevel@tonic-gate static uint_t	ohci_pow_2(uint_t x);
1747c478bd9Sstevel@tonic-gate static uint_t	ohci_log_2(uint_t x);
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate /* Endpoint Descriptor (ED) related functions */
1777c478bd9Sstevel@tonic-gate static uint_t	ohci_unpack_endpoint(ohci_state_t	*ohcip,
1787c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
1797c478bd9Sstevel@tonic-gate static void	ohci_insert_ed(ohci_state_t		*ohcip,
1807c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
1817c478bd9Sstevel@tonic-gate static void	ohci_insert_ctrl_ed(
1827c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
1837c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
1847c478bd9Sstevel@tonic-gate static void	ohci_insert_bulk_ed(
1857c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
1867c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
1877c478bd9Sstevel@tonic-gate static void	ohci_insert_intr_ed(
1887c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
1897c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
1907c478bd9Sstevel@tonic-gate static void	ohci_insert_isoc_ed(
1917c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
1927c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
1937c478bd9Sstevel@tonic-gate static void	ohci_modify_sKip_bit(ohci_state_t	*ohcip,
1947c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
1957c478bd9Sstevel@tonic-gate 				skip_bit_t		action,
1967c478bd9Sstevel@tonic-gate 				usb_flags_t		flag);
1977c478bd9Sstevel@tonic-gate static void	ohci_remove_ed(ohci_state_t		*ohcip,
1987c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
1997c478bd9Sstevel@tonic-gate static void	ohci_remove_ctrl_ed(
2007c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2017c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
2027c478bd9Sstevel@tonic-gate static void	ohci_remove_bulk_ed(
2037c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2047c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
2057c478bd9Sstevel@tonic-gate static void	ohci_remove_periodic_ed(
2067c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2077c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
2087c478bd9Sstevel@tonic-gate static void	ohci_insert_ed_on_reclaim_list(
2097c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2107c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
2117c478bd9Sstevel@tonic-gate static void	ohci_detach_ed_from_list(
2127c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2137c478bd9Sstevel@tonic-gate 				ohci_ed_t		*ept,
2147c478bd9Sstevel@tonic-gate 				uint_t			ept_type);
2157c478bd9Sstevel@tonic-gate static ohci_ed_t *ohci_ed_iommu_to_cpu(
2167c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2177c478bd9Sstevel@tonic-gate 				uintptr_t		addr);
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate /* Transfer Descriptor (TD) related functions */
2207c478bd9Sstevel@tonic-gate static int	ohci_initialize_dummy(ohci_state_t	*ohcip,
2217c478bd9Sstevel@tonic-gate 				ohci_ed_t		*ept);
2227c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t *ohci_allocate_ctrl_resources(
2237c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2247c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
2257c478bd9Sstevel@tonic-gate 				usb_ctrl_req_t		*ctrl_reqp,
2267c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
2277c478bd9Sstevel@tonic-gate static void	ohci_insert_ctrl_req(
2287c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2297c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
2307c478bd9Sstevel@tonic-gate 				usb_ctrl_req_t		*ctrl_reqp,
2317c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
2327c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
2337c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t *ohci_allocate_bulk_resources(
2347c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2357c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
2367c478bd9Sstevel@tonic-gate 				usb_bulk_req_t		*bulk_reqp,
2377c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
2387c478bd9Sstevel@tonic-gate static void	ohci_insert_bulk_req(ohci_state_t	*ohcip,
2397c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
2407c478bd9Sstevel@tonic-gate 				usb_bulk_req_t		*bulk_reqp,
2417c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
2427c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
2437c478bd9Sstevel@tonic-gate static int	ohci_start_pipe_polling(ohci_state_t	*ohcip,
2447c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
2457c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
2467c478bd9Sstevel@tonic-gate static void	ohci_set_periodic_pipe_polling(
2477c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2487c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
2497c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t *ohci_allocate_intr_resources(
2507c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2517c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
2527c478bd9Sstevel@tonic-gate 				usb_intr_req_t		*intr_reqp,
2537c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
2547c478bd9Sstevel@tonic-gate static void	ohci_insert_intr_req(ohci_state_t	*ohcip,
2557c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
2567c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
2577c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
2587c478bd9Sstevel@tonic-gate static int	ohci_stop_periodic_pipe_polling(
2597c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2607c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
2617c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
2627c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t *ohci_allocate_isoc_resources(
2637c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2647c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
2657c478bd9Sstevel@tonic-gate 				usb_isoc_req_t		*isoc_reqp,
2667c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
2677c478bd9Sstevel@tonic-gate static int	ohci_insert_isoc_req(ohci_state_t	*ohcip,
2687c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
2697c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
2707c478bd9Sstevel@tonic-gate 				uint_t			flags);
2717c478bd9Sstevel@tonic-gate static int	ohci_insert_hc_td(ohci_state_t			*ohcip,
2727c478bd9Sstevel@tonic-gate 				uint_t			hctd_ctrl,
2737c478bd9Sstevel@tonic-gate 				uint32_t		hctd_iommu_cbp,
2747c478bd9Sstevel@tonic-gate 				size_t			hctd_length,
2757c478bd9Sstevel@tonic-gate 				uint32_t		hctd_ctrl_phase,
2767c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
2777c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
2787c478bd9Sstevel@tonic-gate static ohci_td_t *ohci_allocate_td_from_pool(
2797c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip);
2807c478bd9Sstevel@tonic-gate static void	ohci_fill_in_td(ohci_state_t		*ohcip,
2817c478bd9Sstevel@tonic-gate 				ohci_td_t		*td,
2827c478bd9Sstevel@tonic-gate 				ohci_td_t		*new_dummy,
2837c478bd9Sstevel@tonic-gate 				uint_t			hctd_ctrl,
2847c478bd9Sstevel@tonic-gate 				uint32_t		hctd_iommu_cbp,
2857c478bd9Sstevel@tonic-gate 				size_t			hctd_length,
2867c478bd9Sstevel@tonic-gate 				uint32_t		hctd_ctrl_phase,
2877c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
2887c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
2897c478bd9Sstevel@tonic-gate static void	ohci_init_itd(
2907c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2917c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
2927c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
2937c478bd9Sstevel@tonic-gate 				uint_t			hctd_ctrl,
2947c478bd9Sstevel@tonic-gate 				uint32_t		hctd_iommu_cbp,
2957c478bd9Sstevel@tonic-gate 				ohci_td_t		*td);
2967c478bd9Sstevel@tonic-gate static int	ohci_insert_td_with_frame_number(
2977c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
2987c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
2997c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
3007c478bd9Sstevel@tonic-gate 				ohci_td_t		*current_td,
3017c478bd9Sstevel@tonic-gate 				ohci_td_t		*dummy_td);
3027c478bd9Sstevel@tonic-gate static void	ohci_insert_td_on_tw(ohci_state_t	*ohcip,
3037c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
3047c478bd9Sstevel@tonic-gate 				ohci_td_t		*td);
3057c478bd9Sstevel@tonic-gate static void	ohci_done_list_tds(ohci_state_t 	*ohcip,
3067c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate /* Transfer Wrapper (TW) functions */
3097c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t  *ohci_create_transfer_wrapper(
3107c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3117c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
3127c478bd9Sstevel@tonic-gate 				size_t			length,
3137c478bd9Sstevel@tonic-gate 				uint_t			usb_flags);
3147c478bd9Sstevel@tonic-gate static int	ohci_allocate_tds_for_tw(
3157c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3167c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
3177c478bd9Sstevel@tonic-gate 				size_t			td_count);
3187c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t  *ohci_allocate_tw_resources(
3197c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3207c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
3217c478bd9Sstevel@tonic-gate 				size_t			length,
3227c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags,
3237c478bd9Sstevel@tonic-gate 				size_t			td_count);
3247c478bd9Sstevel@tonic-gate static void	ohci_free_tw_tds_resources(
3257c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3267c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
3277c478bd9Sstevel@tonic-gate static void	ohci_start_xfer_timer(
3287c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3297c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
3307c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
3317c478bd9Sstevel@tonic-gate static void	ohci_stop_xfer_timer(
3327c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3337c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
3347c478bd9Sstevel@tonic-gate 				uint_t			flag);
3357c478bd9Sstevel@tonic-gate static void	ohci_xfer_timeout_handler(void		*arg);
3367c478bd9Sstevel@tonic-gate static void	ohci_remove_tw_from_timeout_list(
3377c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3387c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
3397c478bd9Sstevel@tonic-gate static void	ohci_start_timer(ohci_state_t		*ohcip);
3407c478bd9Sstevel@tonic-gate static void	ohci_free_dma_resources(ohci_state_t	*ohcip,
3417c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
3427c478bd9Sstevel@tonic-gate static void	ohci_free_tw(ohci_state_t		*ohcip,
3437c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate /* Interrupt Handling functions */
346*9c75c6bfSgovinda static uint_t	ohci_intr(caddr_t			arg1,
347*9c75c6bfSgovinda 				caddr_t			arg2);
3487c478bd9Sstevel@tonic-gate static void	ohci_handle_missed_intr(
3497c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip);
3507c478bd9Sstevel@tonic-gate static void	ohci_handle_ue(ohci_state_t		*ohcip);
3517c478bd9Sstevel@tonic-gate static void	ohci_handle_endpoint_reclaimation(
3527c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip);
3537c478bd9Sstevel@tonic-gate static void	ohci_traverse_done_list(
3547c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3557c478bd9Sstevel@tonic-gate 				ohci_td_t		*head_done_list);
3567c478bd9Sstevel@tonic-gate static ohci_td_t *ohci_reverse_done_list(
3577c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3587c478bd9Sstevel@tonic-gate 				ohci_td_t		*head_done_list);
3597c478bd9Sstevel@tonic-gate static usb_cr_t	ohci_parse_error(ohci_state_t		*ohcip,
3607c478bd9Sstevel@tonic-gate 				ohci_td_t		*td);
3617c478bd9Sstevel@tonic-gate static void	ohci_parse_isoc_error(
3627c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3637c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
3647c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
3657c478bd9Sstevel@tonic-gate 				ohci_td_t		*td);
3667c478bd9Sstevel@tonic-gate static usb_cr_t ohci_check_for_error(
3677c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3687c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
3697c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
3707c478bd9Sstevel@tonic-gate 				ohci_td_t		*td,
3717c478bd9Sstevel@tonic-gate 				uint_t			ctrl);
3727c478bd9Sstevel@tonic-gate static void	ohci_handle_error(
3737c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3747c478bd9Sstevel@tonic-gate 				ohci_td_t		*td,
3757c478bd9Sstevel@tonic-gate 				usb_cr_t		error);
3767c478bd9Sstevel@tonic-gate static int	ohci_cleanup_data_underrun(
3777c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3787c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
3797c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
3807c478bd9Sstevel@tonic-gate 				ohci_td_t		*td);
3817c478bd9Sstevel@tonic-gate static void	ohci_handle_normal_td(
3827c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
3837c478bd9Sstevel@tonic-gate 				ohci_td_t		*td,
3847c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
3857c478bd9Sstevel@tonic-gate static void	ohci_handle_ctrl_td(ohci_state_t	*ohcip,
3867c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
3877c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
3887c478bd9Sstevel@tonic-gate 				ohci_td_t		*td,
3897c478bd9Sstevel@tonic-gate 				void			*);
3907c478bd9Sstevel@tonic-gate static void	ohci_handle_bulk_td(ohci_state_t	*ohcip,
3917c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
3927c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
3937c478bd9Sstevel@tonic-gate 				ohci_td_t		*td,
3947c478bd9Sstevel@tonic-gate 				void			*);
3957c478bd9Sstevel@tonic-gate static void	ohci_handle_intr_td(ohci_state_t	*ohcip,
3967c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
3977c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
3987c478bd9Sstevel@tonic-gate 				ohci_td_t		*td,
3997c478bd9Sstevel@tonic-gate 				void			*);
4007c478bd9Sstevel@tonic-gate static void	ohci_handle_one_xfer_completion(
4017c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
4027c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
4037c478bd9Sstevel@tonic-gate static void	ohci_handle_isoc_td(ohci_state_t	*ohcip,
4047c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
4057c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
4067c478bd9Sstevel@tonic-gate 				ohci_td_t		*td,
4077c478bd9Sstevel@tonic-gate 				void			*);
4087c478bd9Sstevel@tonic-gate static void	ohci_sendup_td_message(
4097c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
4107c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
4117c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
4127c478bd9Sstevel@tonic-gate 				ohci_td_t		*td,
4137c478bd9Sstevel@tonic-gate 				usb_cr_t		error);
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate /* Miscillaneous functions */
4167c478bd9Sstevel@tonic-gate static void	ohci_cpr_cleanup(
4177c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip);
4187c478bd9Sstevel@tonic-gate static usb_req_attrs_t ohci_get_xfer_attrs(ohci_state_t *ohcip,
4197c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
4207c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
4217c478bd9Sstevel@tonic-gate static int	ohci_allocate_periodic_in_resource(
4227c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
4237c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
4247c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
4257c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
4267c478bd9Sstevel@tonic-gate static int	ohci_wait_for_sof(
4277c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip);
4287c478bd9Sstevel@tonic-gate static void	ohci_pipe_cleanup(
4297c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
4307c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
4317c478bd9Sstevel@tonic-gate static void	ohci_wait_for_transfers_completion(
4327c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
4337c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
4347c478bd9Sstevel@tonic-gate static void	ohci_check_for_transfers_completion(
4357c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
4367c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp);
4377c478bd9Sstevel@tonic-gate static void	ohci_save_data_toggle(ohci_state_t	*ohcip,
4387c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
4397c478bd9Sstevel@tonic-gate static void	ohci_restore_data_toggle(ohci_state_t	*ohcip,
4407c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
4417c478bd9Sstevel@tonic-gate static void	ohci_deallocate_periodic_in_resource(
4427c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
4437c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
4447c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
4457c478bd9Sstevel@tonic-gate static void	ohci_do_client_periodic_in_req_callback(
4467c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
4477c478bd9Sstevel@tonic-gate 				ohci_pipe_private_t	*pp,
4487c478bd9Sstevel@tonic-gate 				usb_cr_t		completion_reason);
4497c478bd9Sstevel@tonic-gate static void	ohci_hcdi_callback(
4507c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
4517c478bd9Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
4527c478bd9Sstevel@tonic-gate 				usb_cr_t		completion_reason);
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate /* Kstat Support */
4557c478bd9Sstevel@tonic-gate static void	ohci_create_stats(ohci_state_t		*ohcip);
4567c478bd9Sstevel@tonic-gate static void	ohci_destroy_stats(ohci_state_t 	*ohcip);
4577c478bd9Sstevel@tonic-gate static void	ohci_do_byte_stats(
4587c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
4597c478bd9Sstevel@tonic-gate 				size_t			len,
4607c478bd9Sstevel@tonic-gate 				uint8_t 		attr,
4617c478bd9Sstevel@tonic-gate 				uint8_t 		addr);
4627c478bd9Sstevel@tonic-gate static void	ohci_do_intrs_stats(
4637c478bd9Sstevel@tonic-gate 				ohci_state_t		*ohcip,
4647c478bd9Sstevel@tonic-gate 				int			val);
4657c478bd9Sstevel@tonic-gate static void	ohci_print_op_regs(ohci_state_t 	*ohcip);
4667c478bd9Sstevel@tonic-gate static void	ohci_print_ed(ohci_state_t		*ohcip,
4677c478bd9Sstevel@tonic-gate 				ohci_ed_t		*ed);
4687c478bd9Sstevel@tonic-gate static void	ohci_print_td(ohci_state_t		*ohcip,
4697c478bd9Sstevel@tonic-gate 				ohci_td_t		*td);
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate /* extern */
4727c478bd9Sstevel@tonic-gate int usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level);
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate /*
4757c478bd9Sstevel@tonic-gate  * Device operations (dev_ops) entries function prototypes.
4767c478bd9Sstevel@tonic-gate  *
4777c478bd9Sstevel@tonic-gate  * We use the hub cbops since all nexus ioctl operations defined so far will
4787c478bd9Sstevel@tonic-gate  * be executed by the root hub. The following are the Host Controller Driver
4797c478bd9Sstevel@tonic-gate  * (HCD) entry points.
4807c478bd9Sstevel@tonic-gate  *
4817c478bd9Sstevel@tonic-gate  * the open/close/ioctl functions call the corresponding usba_hubdi_*
4827c478bd9Sstevel@tonic-gate  * calls after looking up the dip thru the dev_t.
4837c478bd9Sstevel@tonic-gate  */
4847c478bd9Sstevel@tonic-gate static int	ohci_open(dev_t	*devp, int flags, int otyp, cred_t *credp);
4857c478bd9Sstevel@tonic-gate static int	ohci_close(dev_t dev, int flag, int otyp, cred_t *credp);
4867c478bd9Sstevel@tonic-gate static int	ohci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
4877c478bd9Sstevel@tonic-gate 				cred_t *credp, int *rvalp);
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate static int	ohci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
4907c478bd9Sstevel@tonic-gate static int	ohci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
4917c478bd9Sstevel@tonic-gate static int	ohci_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
4927c478bd9Sstevel@tonic-gate 				void *arg, void **result);
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate static struct cb_ops ohci_cb_ops = {
4957c478bd9Sstevel@tonic-gate 	ohci_open,			/* Open */
4967c478bd9Sstevel@tonic-gate 	ohci_close,			/* Close */
4977c478bd9Sstevel@tonic-gate 	nodev,				/* Strategy */
4987c478bd9Sstevel@tonic-gate 	nodev,				/* Print */
4997c478bd9Sstevel@tonic-gate 	nodev,				/* Dump */
5007c478bd9Sstevel@tonic-gate 	nodev,				/* Read */
5017c478bd9Sstevel@tonic-gate 	nodev,				/* Write */
5027c478bd9Sstevel@tonic-gate 	ohci_ioctl,			/* Ioctl */
5037c478bd9Sstevel@tonic-gate 	nodev,				/* Devmap */
5047c478bd9Sstevel@tonic-gate 	nodev,				/* Mmap */
5057c478bd9Sstevel@tonic-gate 	nodev,				/* Segmap */
5067c478bd9Sstevel@tonic-gate 	nochpoll,			/* Poll */
5077c478bd9Sstevel@tonic-gate 	ddi_prop_op,			/* cb_prop_op */
5087c478bd9Sstevel@tonic-gate 	NULL,				/* Streamtab */
5097c478bd9Sstevel@tonic-gate 	D_MP				/* Driver compatibility flag */
5107c478bd9Sstevel@tonic-gate };
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate static struct dev_ops ohci_ops = {
5137c478bd9Sstevel@tonic-gate 	DEVO_REV,			/* Devo_rev */
5147c478bd9Sstevel@tonic-gate 	0,				/* Refcnt */
5157c478bd9Sstevel@tonic-gate 	ohci_info,			/* Info */
5167c478bd9Sstevel@tonic-gate 	nulldev,			/* Identify */
5177c478bd9Sstevel@tonic-gate 	nulldev,			/* Probe */
5187c478bd9Sstevel@tonic-gate 	ohci_attach,			/* Attach */
5197c478bd9Sstevel@tonic-gate 	ohci_detach,			/* Detach */
5207c478bd9Sstevel@tonic-gate 	nodev,				/* Reset */
5217c478bd9Sstevel@tonic-gate 	&ohci_cb_ops,			/* Driver operations */
5227c478bd9Sstevel@tonic-gate 	&usba_hubdi_busops,		/* Bus operations */
5237c478bd9Sstevel@tonic-gate 	usba_hubdi_root_hub_power	/* Power */
5247c478bd9Sstevel@tonic-gate };
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate /*
5277c478bd9Sstevel@tonic-gate  * The USBA library must be loaded for this driver.
5287c478bd9Sstevel@tonic-gate  */
5297c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
5307c478bd9Sstevel@tonic-gate 	&mod_driverops, 	/* Type of module. This one is a driver */
5317c478bd9Sstevel@tonic-gate 	"USB OpenHCI Driver %I%", /* Name of the module. */
5327c478bd9Sstevel@tonic-gate 	&ohci_ops,		/* Driver ops */
5337c478bd9Sstevel@tonic-gate };
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
5367c478bd9Sstevel@tonic-gate 	MODREV_1, (void *)&modldrv, NULL
5377c478bd9Sstevel@tonic-gate };
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate int
5417c478bd9Sstevel@tonic-gate _init(void)
5427c478bd9Sstevel@tonic-gate {
5437c478bd9Sstevel@tonic-gate 	int error;
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 	/* Initialize the soft state structures */
5467c478bd9Sstevel@tonic-gate 	if ((error = ddi_soft_state_init(&ohci_statep, sizeof (ohci_state_t),
5477c478bd9Sstevel@tonic-gate 	    OHCI_INSTS)) != 0) {
5487c478bd9Sstevel@tonic-gate 		return (error);
5497c478bd9Sstevel@tonic-gate 	}
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	/* Install the loadable module */
5527c478bd9Sstevel@tonic-gate 	if ((error = mod_install(&modlinkage)) != 0) {
5537c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&ohci_statep);
5547c478bd9Sstevel@tonic-gate 	}
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	return (error);
5577c478bd9Sstevel@tonic-gate }
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate int
5617c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
5627c478bd9Sstevel@tonic-gate {
5637c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
5647c478bd9Sstevel@tonic-gate }
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate int
5687c478bd9Sstevel@tonic-gate _fini(void)
5697c478bd9Sstevel@tonic-gate {
5707c478bd9Sstevel@tonic-gate 	int error;
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 	if ((error = mod_remove(&modlinkage)) == 0) {
5737c478bd9Sstevel@tonic-gate 		/* Release per module resources */
5747c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&ohci_statep);
5757c478bd9Sstevel@tonic-gate 	}
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	return (error);
5787c478bd9Sstevel@tonic-gate }
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate /*
5827c478bd9Sstevel@tonic-gate  * Host Controller Driver (HCD) entry points
5837c478bd9Sstevel@tonic-gate  */
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate /*
5867c478bd9Sstevel@tonic-gate  * ohci_attach:
5877c478bd9Sstevel@tonic-gate  */
5887c478bd9Sstevel@tonic-gate static int
5897c478bd9Sstevel@tonic-gate ohci_attach(dev_info_t		*dip,
5907c478bd9Sstevel@tonic-gate 	ddi_attach_cmd_t	cmd)
5917c478bd9Sstevel@tonic-gate {
5927c478bd9Sstevel@tonic-gate 	int			instance;
5937c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = NULL;
5947c478bd9Sstevel@tonic-gate 	usba_hcdi_register_args_t hcdi_args;
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 	switch (cmd) {
5977c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
5987c478bd9Sstevel@tonic-gate 		break;
5997c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
6007c478bd9Sstevel@tonic-gate 		ohcip = ohci_obtain_state(dip);
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 		return (ohci_cpr_resume(ohcip));
6037c478bd9Sstevel@tonic-gate 	default:
6047c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6057c478bd9Sstevel@tonic-gate 	}
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	/* Get the instance and create soft state */
6087c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(ohci_statep, instance) != 0) {
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6137c478bd9Sstevel@tonic-gate 	}
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	ohcip = ddi_get_soft_state(ohci_statep, instance);
6167c478bd9Sstevel@tonic-gate 	if (ohcip == NULL) {
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6197c478bd9Sstevel@tonic-gate 	}
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	ohcip->ohci_flags = OHCI_ATTACH;
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	ohcip->ohci_log_hdl = usb_alloc_log_hdl(dip, "ohci", &ohci_errlevel,
6247c478bd9Sstevel@tonic-gate 	    &ohci_errmask, &ohci_instance_debug, 0);
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	ohcip->ohci_flags |= OHCI_ZALLOC;
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	/* Set host controller soft state to initilization */
6297c478bd9Sstevel@tonic-gate 	ohcip->ohci_hc_soft_state = OHCI_CTLR_INIT_STATE;
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
6327c478bd9Sstevel@tonic-gate 	    "ohcip = 0x%p", (void *)ohcip);
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 	/* Initialize the DMA attributes */
6357c478bd9Sstevel@tonic-gate 	ohci_set_dma_attributes(ohcip);
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	/* Save the dip and instance */
6387c478bd9Sstevel@tonic-gate 	ohcip->ohci_dip = dip;
6397c478bd9Sstevel@tonic-gate 	ohcip->ohci_instance = instance;
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	/* Initialize the kstat structures */
6427c478bd9Sstevel@tonic-gate 	ohci_create_stats(ohcip);
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 	/* Create the td and ed pools */
6457c478bd9Sstevel@tonic-gate 	if (ohci_allocate_pools(ohcip) != DDI_SUCCESS) {
6467c478bd9Sstevel@tonic-gate 		(void) ohci_cleanup(ohcip);
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6497c478bd9Sstevel@tonic-gate 	}
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 	/* Map the registers */
6527c478bd9Sstevel@tonic-gate 	if (ohci_map_regs(ohcip) != DDI_SUCCESS) {
6537c478bd9Sstevel@tonic-gate 		(void) ohci_cleanup(ohcip);
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6567c478bd9Sstevel@tonic-gate 	}
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate 	/* Register interrupts */
6597c478bd9Sstevel@tonic-gate 	if (ohci_register_intrs_and_init_mutex(ohcip) != DDI_SUCCESS) {
6607c478bd9Sstevel@tonic-gate 		(void) ohci_cleanup(ohcip);
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6637c478bd9Sstevel@tonic-gate 	}
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	/* Initialize the controller */
6687c478bd9Sstevel@tonic-gate 	if (ohci_init_ctlr(ohcip) != DDI_SUCCESS) {
6697c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
6707c478bd9Sstevel@tonic-gate 		(void) ohci_cleanup(ohcip);
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6737c478bd9Sstevel@tonic-gate 	}
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 	/*
6767c478bd9Sstevel@tonic-gate 	 * At this point, the hardware wiil be okay.
6777c478bd9Sstevel@tonic-gate 	 * Initialize the usba_hcdi structure
6787c478bd9Sstevel@tonic-gate 	 */
6797c478bd9Sstevel@tonic-gate 	ohcip->ohci_hcdi_ops = ohci_alloc_hcdi_ops(ohcip);
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	/*
6847c478bd9Sstevel@tonic-gate 	 * Make this HCD instance known to USBA
6857c478bd9Sstevel@tonic-gate 	 * (dma_attr must be passed for USBA busctl's)
6867c478bd9Sstevel@tonic-gate 	 */
6877c478bd9Sstevel@tonic-gate 	hcdi_args.usba_hcdi_register_version = HCDI_REGISTER_VERSION;
6887c478bd9Sstevel@tonic-gate 	hcdi_args.usba_hcdi_register_dip = dip;
6897c478bd9Sstevel@tonic-gate 	hcdi_args.usba_hcdi_register_ops = ohcip->ohci_hcdi_ops;
6907c478bd9Sstevel@tonic-gate 	hcdi_args.usba_hcdi_register_dma_attr = &ohcip->ohci_dma_attr;
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate 	/*
6937c478bd9Sstevel@tonic-gate 	 * Priority and iblock_cookie are one and the same
6947c478bd9Sstevel@tonic-gate 	 * (However, retaining hcdi_soft_iblock_cookie for now
6957c478bd9Sstevel@tonic-gate 	 * assigning it w/ priority. In future all iblock_cookie
6967c478bd9Sstevel@tonic-gate 	 * could just go)
6977c478bd9Sstevel@tonic-gate 	 */
6987c478bd9Sstevel@tonic-gate 	hcdi_args.usba_hcdi_register_iblock_cookie =
699abdbd06dSagiri 	    (ddi_iblock_cookie_t)(uintptr_t)ohcip->ohci_intr_pri;
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 	if (usba_hcdi_register(&hcdi_args, 0) != DDI_SUCCESS) {
7027c478bd9Sstevel@tonic-gate 		(void) ohci_cleanup(ohcip);
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7057c478bd9Sstevel@tonic-gate 	}
7067c478bd9Sstevel@tonic-gate 	ohcip->ohci_flags |= OHCI_USBAREG;
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 	if ((ohci_init_root_hub(ohcip)) != USB_SUCCESS) {
7117c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
7127c478bd9Sstevel@tonic-gate 		(void) ohci_cleanup(ohcip);
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7157c478bd9Sstevel@tonic-gate 	}
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 	/* Finally load the root hub driver */
7207c478bd9Sstevel@tonic-gate 	if (ohci_load_root_hub_driver(ohcip) != USB_SUCCESS) {
7217c478bd9Sstevel@tonic-gate 		(void) ohci_cleanup(ohcip);
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7247c478bd9Sstevel@tonic-gate 	}
7257c478bd9Sstevel@tonic-gate 	ohcip->ohci_flags |= OHCI_RHREG;
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 	/* Display information in the banner */
7287c478bd9Sstevel@tonic-gate 	ddi_report_dev(dip);
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 	/* Reset the ohci initilization flag */
7337c478bd9Sstevel@tonic-gate 	ohcip->ohci_flags &= ~OHCI_ATTACH;
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	/* Print the Host Control's Operational registers */
7367c478bd9Sstevel@tonic-gate 	ohci_print_op_regs(ohcip);
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 	/* For RIO we need to call pci_report_pmcap */
7397c478bd9Sstevel@tonic-gate 	if (OHCI_IS_RIO(ohcip)) {
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 		(void) pci_report_pmcap(dip, PCI_PM_IDLESPEED, (void *)4000);
7427c478bd9Sstevel@tonic-gate 	}
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
7477c478bd9Sstevel@tonic-gate 	    "ohci_attach: dip = 0x%p done", (void *)dip);
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
7507c478bd9Sstevel@tonic-gate }
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate /*
7547c478bd9Sstevel@tonic-gate  * ohci_detach:
7557c478bd9Sstevel@tonic-gate  */
7567c478bd9Sstevel@tonic-gate int
7577c478bd9Sstevel@tonic-gate ohci_detach(dev_info_t		*dip,
7587c478bd9Sstevel@tonic-gate 	ddi_detach_cmd_t	cmd)
7597c478bd9Sstevel@tonic-gate {
7607c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(dip);
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl, "ohci_detach:");
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	switch (cmd) {
7657c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 		return (ohci_cleanup(ohcip));
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 		return (ohci_cpr_suspend(ohcip));
7727c478bd9Sstevel@tonic-gate 	default:
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7757c478bd9Sstevel@tonic-gate 	}
7767c478bd9Sstevel@tonic-gate }
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate /*
7807c478bd9Sstevel@tonic-gate  * ohci_info:
7817c478bd9Sstevel@tonic-gate  */
7827c478bd9Sstevel@tonic-gate /* ARGSUSED */
7837c478bd9Sstevel@tonic-gate static int
7847c478bd9Sstevel@tonic-gate ohci_info(dev_info_t		*dip,
7857c478bd9Sstevel@tonic-gate 	ddi_info_cmd_t		infocmd,
7867c478bd9Sstevel@tonic-gate 	void			*arg,
7877c478bd9Sstevel@tonic-gate 	void			**result)
7887c478bd9Sstevel@tonic-gate {
7897c478bd9Sstevel@tonic-gate 	dev_t			dev;
7907c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip;
7917c478bd9Sstevel@tonic-gate 	int			instance;
7927c478bd9Sstevel@tonic-gate 	int			error = DDI_FAILURE;
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	switch (infocmd) {
7957c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
7967c478bd9Sstevel@tonic-gate 		dev = (dev_t)arg;
7977c478bd9Sstevel@tonic-gate 		instance = OHCI_UNIT(dev);
7987c478bd9Sstevel@tonic-gate 		ohcip = ddi_get_soft_state(ohci_statep, instance);
7997c478bd9Sstevel@tonic-gate 		if (ohcip != NULL) {
8007c478bd9Sstevel@tonic-gate 			*result = (void *)ohcip->ohci_dip;
8017c478bd9Sstevel@tonic-gate 			if (*result != NULL) {
8027c478bd9Sstevel@tonic-gate 				error = DDI_SUCCESS;
8037c478bd9Sstevel@tonic-gate 			}
8047c478bd9Sstevel@tonic-gate 		} else {
8057c478bd9Sstevel@tonic-gate 			*result = NULL;
8067c478bd9Sstevel@tonic-gate 		}
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 		break;
8097c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
8107c478bd9Sstevel@tonic-gate 		dev = (dev_t)arg;
8117c478bd9Sstevel@tonic-gate 		instance = OHCI_UNIT(dev);
8127c478bd9Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
8137c478bd9Sstevel@tonic-gate 		error = DDI_SUCCESS;
8147c478bd9Sstevel@tonic-gate 		break;
8157c478bd9Sstevel@tonic-gate 	default:
8167c478bd9Sstevel@tonic-gate 		break;
8177c478bd9Sstevel@tonic-gate 	}
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	return (error);
8207c478bd9Sstevel@tonic-gate }
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate /*
8247c478bd9Sstevel@tonic-gate  * cb_ops entry points
8257c478bd9Sstevel@tonic-gate  */
8267c478bd9Sstevel@tonic-gate static dev_info_t *
8277c478bd9Sstevel@tonic-gate ohci_get_dip(dev_t	dev)
8287c478bd9Sstevel@tonic-gate {
8297c478bd9Sstevel@tonic-gate 	int		instance = OHCI_UNIT(dev);
8307c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip = ddi_get_soft_state(ohci_statep, instance);
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	if (ohcip) {
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 		return (ohcip->ohci_dip);
8357c478bd9Sstevel@tonic-gate 	} else {
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 		return (NULL);
8387c478bd9Sstevel@tonic-gate 	}
8397c478bd9Sstevel@tonic-gate }
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate static int
8437c478bd9Sstevel@tonic-gate ohci_open(dev_t		*devp,
8447c478bd9Sstevel@tonic-gate 	int		flags,
8457c478bd9Sstevel@tonic-gate 	int		otyp,
8467c478bd9Sstevel@tonic-gate 	cred_t		*credp)
8477c478bd9Sstevel@tonic-gate {
8487c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = ohci_get_dip(*devp);
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate 	return (usba_hubdi_open(dip, devp, flags, otyp, credp));
8517c478bd9Sstevel@tonic-gate }
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 
8547c478bd9Sstevel@tonic-gate static int
8557c478bd9Sstevel@tonic-gate ohci_close(dev_t	dev,
8567c478bd9Sstevel@tonic-gate 	int		flag,
8577c478bd9Sstevel@tonic-gate 	int		otyp,
8587c478bd9Sstevel@tonic-gate 	cred_t		*credp)
8597c478bd9Sstevel@tonic-gate {
8607c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = ohci_get_dip(dev);
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	return (usba_hubdi_close(dip, dev, flag, otyp, credp));
8637c478bd9Sstevel@tonic-gate }
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate static int
8677c478bd9Sstevel@tonic-gate ohci_ioctl(dev_t	dev,
8687c478bd9Sstevel@tonic-gate 	int		cmd,
8697c478bd9Sstevel@tonic-gate 	intptr_t	arg,
8707c478bd9Sstevel@tonic-gate 	int		mode,
8717c478bd9Sstevel@tonic-gate 	cred_t		*credp,
8727c478bd9Sstevel@tonic-gate 	int		*rvalp)
8737c478bd9Sstevel@tonic-gate {
8747c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = ohci_get_dip(dev);
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 	return (usba_hubdi_ioctl(dip,
8777c478bd9Sstevel@tonic-gate 	    dev, cmd, arg, mode, credp, rvalp));
8787c478bd9Sstevel@tonic-gate }
8797c478bd9Sstevel@tonic-gate 
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate /*
8827c478bd9Sstevel@tonic-gate  * Host Controller Driver (HCD) initialization functions
8837c478bd9Sstevel@tonic-gate  */
8847c478bd9Sstevel@tonic-gate 
8857c478bd9Sstevel@tonic-gate /*
8867c478bd9Sstevel@tonic-gate  * ohci_set_dma_attributes:
8877c478bd9Sstevel@tonic-gate  *
8887c478bd9Sstevel@tonic-gate  * Set the limits in the DMA attributes structure. Most of the values used
8897c478bd9Sstevel@tonic-gate  * in the  DMA limit structres are the default values as specified by  the
8907c478bd9Sstevel@tonic-gate  * Writing PCI device drivers document.
8917c478bd9Sstevel@tonic-gate  */
8927c478bd9Sstevel@tonic-gate static void
8937c478bd9Sstevel@tonic-gate ohci_set_dma_attributes(ohci_state_t	*ohcip)
8947c478bd9Sstevel@tonic-gate {
8957c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
8967c478bd9Sstevel@tonic-gate 	    "ohci_set_dma_attributes:");
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 	/* Initialize the DMA attributes */
8997c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_version = DMA_ATTR_V0;
9007c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_addr_lo = 0x00000000ull;
9017c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_addr_hi = 0xfffffffeull;
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	/* 32 bit addressing */
9047c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_count_max = OHCI_DMA_ATTR_COUNT_MAX;
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 	/* Byte alignment */
9077c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_align = OHCI_DMA_ATTR_ALIGNMENT;
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	/*
9107c478bd9Sstevel@tonic-gate 	 * Since PCI  specification is byte alignment, the
9117c478bd9Sstevel@tonic-gate 	 * burstsize field should be set to 1 for PCI devices.
9127c478bd9Sstevel@tonic-gate 	 */
9137c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_burstsizes = 0x1;
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_minxfer = 0x1;
9167c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_maxxfer = OHCI_DMA_ATTR_MAX_XFER;
9177c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_seg = 0xffffffffull;
9187c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_sgllen = 1;
9197c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_granular = OHCI_DMA_ATTR_GRANULAR;
9207c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_flags = 0;
9217c478bd9Sstevel@tonic-gate }
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate /*
9257c478bd9Sstevel@tonic-gate  * ohci_allocate_pools:
9267c478bd9Sstevel@tonic-gate  *
9277c478bd9Sstevel@tonic-gate  * Allocate the system memory for the Endpoint Descriptor (ED) and for the
9287c478bd9Sstevel@tonic-gate  * Transfer Descriptor (TD) pools. Both ED and TD structures must be aligned
9297c478bd9Sstevel@tonic-gate  * to a 16 byte boundary.
9307c478bd9Sstevel@tonic-gate  */
9317c478bd9Sstevel@tonic-gate static int
9327c478bd9Sstevel@tonic-gate ohci_allocate_pools(ohci_state_t	*ohcip)
9337c478bd9Sstevel@tonic-gate {
9347c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t		dev_attr;
9357c478bd9Sstevel@tonic-gate 	size_t				real_length;
9367c478bd9Sstevel@tonic-gate 	int				result;
9377c478bd9Sstevel@tonic-gate 	uint_t				ccount;
9387c478bd9Sstevel@tonic-gate 	int				i;
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
9417c478bd9Sstevel@tonic-gate 	    "ohci_allocate_pools:");
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 	/* The host controller will be little endian */
9447c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_version	= DDI_DEVICE_ATTR_V0;
9457c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_endian_flags  = DDI_STRUCTURE_LE_ACC;
9467c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_dataorder	= DDI_STRICTORDER_ACC;
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	/* Byte alignment to TD alignment */
9497c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_align = OHCI_DMA_ATTR_TD_ALIGNMENT;
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 	/* Allocate the TD pool DMA handle */
9527c478bd9Sstevel@tonic-gate 	if (ddi_dma_alloc_handle(ohcip->ohci_dip, &ohcip->ohci_dma_attr,
9537c478bd9Sstevel@tonic-gate 			DDI_DMA_SLEEP, 0,
9547c478bd9Sstevel@tonic-gate 			&ohcip->ohci_td_pool_dma_handle) != DDI_SUCCESS) {
9557c478bd9Sstevel@tonic-gate 
9567c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9577c478bd9Sstevel@tonic-gate 	}
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 	/* Allocate the memory for the TD pool */
9607c478bd9Sstevel@tonic-gate 	if (ddi_dma_mem_alloc(ohcip->ohci_td_pool_dma_handle,
9617c478bd9Sstevel@tonic-gate 			ohci_td_pool_size * sizeof (ohci_td_t),
9627c478bd9Sstevel@tonic-gate 			&dev_attr,
9637c478bd9Sstevel@tonic-gate 			DDI_DMA_CONSISTENT,
9647c478bd9Sstevel@tonic-gate 			DDI_DMA_SLEEP,
9657c478bd9Sstevel@tonic-gate 			0,
9667c478bd9Sstevel@tonic-gate 			(caddr_t *)&ohcip->ohci_td_pool_addr,
9677c478bd9Sstevel@tonic-gate 			&real_length,
9687c478bd9Sstevel@tonic-gate 			&ohcip->ohci_td_pool_mem_handle)) {
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9717c478bd9Sstevel@tonic-gate 	}
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 	/* Map the TD pool into the I/O address space */
9747c478bd9Sstevel@tonic-gate 	result = ddi_dma_addr_bind_handle(
9757c478bd9Sstevel@tonic-gate 			ohcip->ohci_td_pool_dma_handle,
9767c478bd9Sstevel@tonic-gate 			NULL,
9777c478bd9Sstevel@tonic-gate 			(caddr_t)ohcip->ohci_td_pool_addr,
9787c478bd9Sstevel@tonic-gate 			real_length,
9797c478bd9Sstevel@tonic-gate 			DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
9807c478bd9Sstevel@tonic-gate 			DDI_DMA_SLEEP,
9817c478bd9Sstevel@tonic-gate 			NULL,
9827c478bd9Sstevel@tonic-gate 			&ohcip->ohci_td_pool_cookie,
9837c478bd9Sstevel@tonic-gate 			&ccount);
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 	bzero((void *)ohcip->ohci_td_pool_addr,
9867c478bd9Sstevel@tonic-gate 			ohci_td_pool_size * sizeof (ohci_td_t));
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	/* Process the result */
9897c478bd9Sstevel@tonic-gate 	if (result == DDI_DMA_MAPPED) {
9907c478bd9Sstevel@tonic-gate 		/* The cookie count should be 1 */
9917c478bd9Sstevel@tonic-gate 		if (ccount != 1) {
9927c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
9937c478bd9Sstevel@tonic-gate 			    "ohci_allocate_pools: More than 1 cookie");
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
9967c478bd9Sstevel@tonic-gate 		}
9977c478bd9Sstevel@tonic-gate 	} else {
9987c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
9997c478bd9Sstevel@tonic-gate 		    "ohci_allocate_pools: Result = %d", result);
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 		ohci_decode_ddi_dma_addr_bind_handle_result(ohcip, result);
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
10047c478bd9Sstevel@tonic-gate 	}
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 	/*
10077c478bd9Sstevel@tonic-gate 	 * DMA addresses for TD pools are bound
10087c478bd9Sstevel@tonic-gate 	 */
10097c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_addr_bind_flag |= OHCI_TD_POOL_BOUND;
10107c478bd9Sstevel@tonic-gate 
10117c478bd9Sstevel@tonic-gate 	/* Initialize the TD pool */
10127c478bd9Sstevel@tonic-gate 	for (i = 0; i < ohci_td_pool_size; i ++) {
10137c478bd9Sstevel@tonic-gate 		Set_TD(ohcip->ohci_td_pool_addr[i].hctd_state, HC_TD_FREE);
10147c478bd9Sstevel@tonic-gate 	}
10157c478bd9Sstevel@tonic-gate 
10167c478bd9Sstevel@tonic-gate 	/* Byte alignment to ED alignment */
10177c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_align = OHCI_DMA_ATTR_ED_ALIGNMENT;
10187c478bd9Sstevel@tonic-gate 
10197c478bd9Sstevel@tonic-gate 	/* Allocate the ED pool DMA handle */
10207c478bd9Sstevel@tonic-gate 	if (ddi_dma_alloc_handle(ohcip->ohci_dip,
10217c478bd9Sstevel@tonic-gate 			&ohcip->ohci_dma_attr,
10227c478bd9Sstevel@tonic-gate 			DDI_DMA_SLEEP,
10237c478bd9Sstevel@tonic-gate 			0,
10247c478bd9Sstevel@tonic-gate 			&ohcip->ohci_ed_pool_dma_handle) != DDI_SUCCESS) {
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
10277c478bd9Sstevel@tonic-gate 	}
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 	/* Allocate the memory for the ED pool */
10307c478bd9Sstevel@tonic-gate 	if (ddi_dma_mem_alloc(ohcip->ohci_ed_pool_dma_handle,
10317c478bd9Sstevel@tonic-gate 			ohci_ed_pool_size * sizeof (ohci_ed_t),
10327c478bd9Sstevel@tonic-gate 			&dev_attr,
10337c478bd9Sstevel@tonic-gate 			DDI_DMA_CONSISTENT,
10347c478bd9Sstevel@tonic-gate 			DDI_DMA_SLEEP,
10357c478bd9Sstevel@tonic-gate 			0,
10367c478bd9Sstevel@tonic-gate 			(caddr_t *)&ohcip->ohci_ed_pool_addr,
10377c478bd9Sstevel@tonic-gate 			&real_length,
10387c478bd9Sstevel@tonic-gate 			&ohcip->ohci_ed_pool_mem_handle) != DDI_SUCCESS) {
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
10417c478bd9Sstevel@tonic-gate 	}
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate 	result = ddi_dma_addr_bind_handle(ohcip->ohci_ed_pool_dma_handle,
10447c478bd9Sstevel@tonic-gate 			NULL,
10457c478bd9Sstevel@tonic-gate 			(caddr_t)ohcip->ohci_ed_pool_addr,
10467c478bd9Sstevel@tonic-gate 			real_length,
10477c478bd9Sstevel@tonic-gate 			DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
10487c478bd9Sstevel@tonic-gate 			DDI_DMA_SLEEP,
10497c478bd9Sstevel@tonic-gate 			NULL,
10507c478bd9Sstevel@tonic-gate 			&ohcip->ohci_ed_pool_cookie,
10517c478bd9Sstevel@tonic-gate 			&ccount);
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 	bzero((void *)ohcip->ohci_ed_pool_addr,
10547c478bd9Sstevel@tonic-gate 			ohci_ed_pool_size * sizeof (ohci_ed_t));
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 	/* Process the result */
10577c478bd9Sstevel@tonic-gate 	if (result == DDI_DMA_MAPPED) {
10587c478bd9Sstevel@tonic-gate 		/* The cookie count should be 1 */
10597c478bd9Sstevel@tonic-gate 		if (ccount != 1) {
10607c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
10617c478bd9Sstevel@tonic-gate 			    "ohci_allocate_pools: More than 1 cookie");
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
10647c478bd9Sstevel@tonic-gate 		}
10657c478bd9Sstevel@tonic-gate 	} else {
10667c478bd9Sstevel@tonic-gate 		ohci_decode_ddi_dma_addr_bind_handle_result(ohcip, result);
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
10697c478bd9Sstevel@tonic-gate 	}
10707c478bd9Sstevel@tonic-gate 
10717c478bd9Sstevel@tonic-gate 	/*
10727c478bd9Sstevel@tonic-gate 	 * DMA addresses for ED pools are bound
10737c478bd9Sstevel@tonic-gate 	 */
10747c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_addr_bind_flag |= OHCI_ED_POOL_BOUND;
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate 	/* Initialize the ED pool */
10777c478bd9Sstevel@tonic-gate 	for (i = 0; i < ohci_ed_pool_size; i ++) {
10787c478bd9Sstevel@tonic-gate 		Set_ED(ohcip->ohci_ed_pool_addr[i].hced_state, HC_EPT_FREE);
10797c478bd9Sstevel@tonic-gate 	}
10807c478bd9Sstevel@tonic-gate 
10817c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
10827c478bd9Sstevel@tonic-gate }
10837c478bd9Sstevel@tonic-gate 
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate /*
10867c478bd9Sstevel@tonic-gate  * ohci_decode_ddi_dma_addr_bind_handle_result:
10877c478bd9Sstevel@tonic-gate  *
10887c478bd9Sstevel@tonic-gate  * Process the return values of ddi_dma_addr_bind_handle()
10897c478bd9Sstevel@tonic-gate  */
10907c478bd9Sstevel@tonic-gate static void
10917c478bd9Sstevel@tonic-gate ohci_decode_ddi_dma_addr_bind_handle_result(
10927c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
10937c478bd9Sstevel@tonic-gate 	int		result)
10947c478bd9Sstevel@tonic-gate {
10957c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
10967c478bd9Sstevel@tonic-gate 	    "ohci_decode_ddi_dma_addr_bind_handle_result:");
10977c478bd9Sstevel@tonic-gate 
10987c478bd9Sstevel@tonic-gate 	switch (result) {
10997c478bd9Sstevel@tonic-gate 	case DDI_DMA_PARTIAL_MAP:
11007c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL, ohcip->ohci_log_hdl,
11017c478bd9Sstevel@tonic-gate 		    "Partial transfers not allowed");
11027c478bd9Sstevel@tonic-gate 		break;
11037c478bd9Sstevel@tonic-gate 	case DDI_DMA_INUSE:
11047c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL,	ohcip->ohci_log_hdl,
11057c478bd9Sstevel@tonic-gate 		    "Handle is in use");
11067c478bd9Sstevel@tonic-gate 		break;
11077c478bd9Sstevel@tonic-gate 	case DDI_DMA_NORESOURCES:
11087c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL,	ohcip->ohci_log_hdl,
11097c478bd9Sstevel@tonic-gate 		    "No resources");
11107c478bd9Sstevel@tonic-gate 		break;
11117c478bd9Sstevel@tonic-gate 	case DDI_DMA_NOMAPPING:
11127c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL,	ohcip->ohci_log_hdl,
11137c478bd9Sstevel@tonic-gate 		    "No mapping");
11147c478bd9Sstevel@tonic-gate 		break;
11157c478bd9Sstevel@tonic-gate 	case DDI_DMA_TOOBIG:
11167c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL,	ohcip->ohci_log_hdl,
11177c478bd9Sstevel@tonic-gate 		    "Object is too big");
11187c478bd9Sstevel@tonic-gate 		break;
11197c478bd9Sstevel@tonic-gate 	default:
11207c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL,	ohcip->ohci_log_hdl,
11217c478bd9Sstevel@tonic-gate 		    "Unknown dma error");
11227c478bd9Sstevel@tonic-gate 	}
11237c478bd9Sstevel@tonic-gate }
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate /*
11277c478bd9Sstevel@tonic-gate  * ohci_map_regs:
11287c478bd9Sstevel@tonic-gate  *
11297c478bd9Sstevel@tonic-gate  * The Host Controller (HC) contains a set of on-chip operational registers
11307c478bd9Sstevel@tonic-gate  * and which should be mapped into a non-cacheable portion of the  system
11317c478bd9Sstevel@tonic-gate  * addressable space.
11327c478bd9Sstevel@tonic-gate  */
11337c478bd9Sstevel@tonic-gate static int
11347c478bd9Sstevel@tonic-gate ohci_map_regs(ohci_state_t	*ohcip)
11357c478bd9Sstevel@tonic-gate {
11367c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t	attr;
11377c478bd9Sstevel@tonic-gate 	uint16_t		cmd_reg;
11387c478bd9Sstevel@tonic-gate 
11397c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl, "ohci_map_regs:");
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate 	/* The host controller will be little endian */
11427c478bd9Sstevel@tonic-gate 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
11437c478bd9Sstevel@tonic-gate 	attr.devacc_attr_endian_flags  = DDI_STRUCTURE_LE_ACC;
11447c478bd9Sstevel@tonic-gate 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 	/* Map in operational registers */
11477c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(ohcip->ohci_dip, 1,
11487c478bd9Sstevel@tonic-gate 	    (caddr_t *)&ohcip->ohci_regsp, 0,
11497c478bd9Sstevel@tonic-gate 	    sizeof (ohci_regs_t), &attr,
11507c478bd9Sstevel@tonic-gate 	    &ohcip->ohci_regs_handle) != DDI_SUCCESS) {
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
11537c478bd9Sstevel@tonic-gate 		    "ohci_map_regs: Map setup error");
11547c478bd9Sstevel@tonic-gate 
11557c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
11567c478bd9Sstevel@tonic-gate 	}
11577c478bd9Sstevel@tonic-gate 
11587c478bd9Sstevel@tonic-gate 	if (pci_config_setup(ohcip->ohci_dip,
11597c478bd9Sstevel@tonic-gate 	    &ohcip->ohci_config_handle) != DDI_SUCCESS) {
11607c478bd9Sstevel@tonic-gate 
11617c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
11627c478bd9Sstevel@tonic-gate 		    "ohci_map_regs: Config error");
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
11657c478bd9Sstevel@tonic-gate 	}
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate 	/* Make sure Memory Access Enable and Master Enable are set */
11687c478bd9Sstevel@tonic-gate 	cmd_reg = pci_config_get16(ohcip->ohci_config_handle, PCI_CONF_COMM);
11697c478bd9Sstevel@tonic-gate 
11707c478bd9Sstevel@tonic-gate 	if (!(cmd_reg & PCI_COMM_MAE)) {
11717c478bd9Sstevel@tonic-gate 
11727c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
11737c478bd9Sstevel@tonic-gate 		    "ohci_map_regs: Memory base address access disabled");
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
11767c478bd9Sstevel@tonic-gate 	}
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 	cmd_reg |= (PCI_COMM_MAE | PCI_COMM_ME);
11797c478bd9Sstevel@tonic-gate 
11807c478bd9Sstevel@tonic-gate 	pci_config_put16(ohcip->ohci_config_handle, PCI_CONF_COMM, cmd_reg);
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
11837c478bd9Sstevel@tonic-gate }
11847c478bd9Sstevel@tonic-gate 
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate /*
11877c478bd9Sstevel@tonic-gate  * ohci_register_intrs_and_init_mutex:
11887c478bd9Sstevel@tonic-gate  *
11897c478bd9Sstevel@tonic-gate  * Register interrupts and initialize each mutex and condition variables
11907c478bd9Sstevel@tonic-gate  */
11917c478bd9Sstevel@tonic-gate static int
11927c478bd9Sstevel@tonic-gate ohci_register_intrs_and_init_mutex(ohci_state_t	*ohcip)
11937c478bd9Sstevel@tonic-gate {
1194*9c75c6bfSgovinda 	int	intr_types;
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
11977c478bd9Sstevel@tonic-gate 	    "ohci_register_intrs_and_init_mutex:");
11987c478bd9Sstevel@tonic-gate 
1199*9c75c6bfSgovinda 	/* Get supported interrupt types */
1200*9c75c6bfSgovinda 	if (ddi_intr_get_supported_types(ohcip->ohci_dip,
1201*9c75c6bfSgovinda 	    &intr_types) != DDI_SUCCESS) {
12027c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
12037c478bd9Sstevel@tonic-gate 		    "ohci_register_intrs_and_init_mutex: "
1204*9c75c6bfSgovinda 		    "ddi_intr_get_supported_types failed");
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
12077c478bd9Sstevel@tonic-gate 	}
12087c478bd9Sstevel@tonic-gate 
1209*9c75c6bfSgovinda 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1210*9c75c6bfSgovinda 	    "ohci_register_intrs_and_init_mutex: "
1211*9c75c6bfSgovinda 	    "supported interrupt types 0x%x", intr_types);
1212*9c75c6bfSgovinda 
1213*9c75c6bfSgovinda 	if ((intr_types & DDI_INTR_TYPE_MSI) && ohci_enable_msi) {
1214*9c75c6bfSgovinda 		if (ohci_add_intrs(ohcip, DDI_INTR_TYPE_MSI)
1215*9c75c6bfSgovinda 		    != DDI_SUCCESS) {
1216*9c75c6bfSgovinda 			USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1217*9c75c6bfSgovinda 			    "ohci_register_intrs_and_init_mutex: MSI "
1218*9c75c6bfSgovinda 			    "registration failed, trying FIXED interrupt \n");
1219*9c75c6bfSgovinda 		} else {
1220*9c75c6bfSgovinda 			USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1221*9c75c6bfSgovinda 			    "ohci_register_intrs_and_init_mutex: "
1222*9c75c6bfSgovinda 			    "Using MSI interrupt type\n");
12237c478bd9Sstevel@tonic-gate 
1224*9c75c6bfSgovinda 			ohcip->ohci_intr_type = DDI_INTR_TYPE_MSI;
1225*9c75c6bfSgovinda 			ohcip->ohci_flags |= OHCI_INTR;
1226*9c75c6bfSgovinda 		}
1227*9c75c6bfSgovinda 	}
1228*9c75c6bfSgovinda 
1229*9c75c6bfSgovinda 	if ((!(ohcip->ohci_flags & OHCI_INTR)) &&
1230*9c75c6bfSgovinda 	    (intr_types & DDI_INTR_TYPE_FIXED)) {
1231*9c75c6bfSgovinda 		if (ohci_add_intrs(ohcip, DDI_INTR_TYPE_FIXED)
1232*9c75c6bfSgovinda 		    != DDI_SUCCESS) {
1233*9c75c6bfSgovinda 			USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1234*9c75c6bfSgovinda 			    "ohci_register_intrs_and_init_mutex: "
1235*9c75c6bfSgovinda 			    "FIXED interrupt registration failed\n");
1236*9c75c6bfSgovinda 
1237*9c75c6bfSgovinda 			return (DDI_FAILURE);
1238*9c75c6bfSgovinda 		}
1239*9c75c6bfSgovinda 
1240*9c75c6bfSgovinda 		USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
12417c478bd9Sstevel@tonic-gate 		    "ohci_register_intrs_and_init_mutex: "
1242*9c75c6bfSgovinda 		    "Using FIXED interrupt type\n");
1243*9c75c6bfSgovinda 
1244*9c75c6bfSgovinda 		ohcip->ohci_intr_type = DDI_INTR_TYPE_FIXED;
1245*9c75c6bfSgovinda 		ohcip->ohci_flags |= OHCI_INTR;
1246*9c75c6bfSgovinda 	}
1247*9c75c6bfSgovinda 
1248*9c75c6bfSgovinda 	/* Create prototype for SOF condition variable */
1249*9c75c6bfSgovinda 	cv_init(&ohcip->ohci_SOF_cv, NULL, CV_DRIVER, NULL);
1250*9c75c6bfSgovinda 
1251*9c75c6bfSgovinda 	/* Semaphore to serialize opens and closes */
1252*9c75c6bfSgovinda 	sema_init(&ohcip->ohci_ocsem, 1, NULL, SEMA_DRIVER, NULL);
1253*9c75c6bfSgovinda 
1254*9c75c6bfSgovinda 	return (DDI_SUCCESS);
1255*9c75c6bfSgovinda }
1256*9c75c6bfSgovinda 
1257*9c75c6bfSgovinda 
1258*9c75c6bfSgovinda /*
1259*9c75c6bfSgovinda  * ohci_add_intrs:
1260*9c75c6bfSgovinda  *
1261*9c75c6bfSgovinda  * Register FIXED or MSI interrupts.
1262*9c75c6bfSgovinda  */
1263*9c75c6bfSgovinda static int
1264*9c75c6bfSgovinda ohci_add_intrs(ohci_state_t	*ohcip,
1265*9c75c6bfSgovinda 		int		intr_type)
1266*9c75c6bfSgovinda {
1267*9c75c6bfSgovinda 	int	actual, avail, intr_size, count = 0;
1268*9c75c6bfSgovinda 	int 	i, flag, ret;
1269*9c75c6bfSgovinda 
1270*9c75c6bfSgovinda 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1271*9c75c6bfSgovinda 	    "ohci_add_intrs: interrupt type 0x%x", intr_type);
1272*9c75c6bfSgovinda 
1273*9c75c6bfSgovinda 	/* Get number of interrupts */
1274*9c75c6bfSgovinda 	ret = ddi_intr_get_nintrs(ohcip->ohci_dip, intr_type, &count);
1275*9c75c6bfSgovinda 	if ((ret != DDI_SUCCESS) || (count == 0)) {
1276*9c75c6bfSgovinda 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1277*9c75c6bfSgovinda 		    "ohci_add_intrs: ddi_intr_get_nintrs() failure, "
1278*9c75c6bfSgovinda 		    "ret: %d, count: %d", ret, count);
1279*9c75c6bfSgovinda 
1280*9c75c6bfSgovinda 		return (DDI_FAILURE);
1281*9c75c6bfSgovinda 	}
1282*9c75c6bfSgovinda 
1283*9c75c6bfSgovinda 	/* Get number of available interrupts */
1284*9c75c6bfSgovinda 	ret = ddi_intr_get_navail(ohcip->ohci_dip, intr_type, &avail);
1285*9c75c6bfSgovinda 	if ((ret != DDI_SUCCESS) || (avail == 0)) {
1286*9c75c6bfSgovinda 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1287*9c75c6bfSgovinda 		    "ohci_add_intrs: ddi_intr_get_navail() failure, "
1288*9c75c6bfSgovinda 		    "ret: %d, count: %d", ret, count);
12897c478bd9Sstevel@tonic-gate 
12907c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
12917c478bd9Sstevel@tonic-gate 	}
12927c478bd9Sstevel@tonic-gate 
1293*9c75c6bfSgovinda 	if (avail < count) {
1294*9c75c6bfSgovinda 		USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1295*9c75c6bfSgovinda 		    "ohci_add_intrs: ohci_add_intrs: nintrs () "
1296*9c75c6bfSgovinda 		    "returned %d, navail returned %d\n", count, avail);
1297*9c75c6bfSgovinda 	}
1298*9c75c6bfSgovinda 
1299*9c75c6bfSgovinda 	/* Allocate an array of interrupt handles */
1300*9c75c6bfSgovinda 	intr_size = count * sizeof (ddi_intr_handle_t);
1301*9c75c6bfSgovinda 	ohcip->ohci_htable = kmem_zalloc(intr_size, KM_SLEEP);
1302*9c75c6bfSgovinda 
1303*9c75c6bfSgovinda 	flag = (intr_type == DDI_INTR_TYPE_MSI) ?
1304*9c75c6bfSgovinda 	    DDI_INTR_ALLOC_STRICT:DDI_INTR_ALLOC_NORMAL;
1305*9c75c6bfSgovinda 
1306*9c75c6bfSgovinda 	/* call ddi_intr_alloc() */
13077c478bd9Sstevel@tonic-gate 	ret = ddi_intr_alloc(ohcip->ohci_dip, ohcip->ohci_htable,
1308*9c75c6bfSgovinda 	    intr_type, 0, count, &actual, flag);
13097c478bd9Sstevel@tonic-gate 
1310*9c75c6bfSgovinda 	if ((ret != DDI_SUCCESS) || (actual == 0)) {
13117c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1312*9c75c6bfSgovinda 		    "ohci_add_intrs: ddi_intr_alloc() failed %d", ret);
13137c478bd9Sstevel@tonic-gate 
1314*9c75c6bfSgovinda 		kmem_free(ohcip->ohci_htable, intr_size);
13157c478bd9Sstevel@tonic-gate 
13167c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
13177c478bd9Sstevel@tonic-gate 	}
13187c478bd9Sstevel@tonic-gate 
1319*9c75c6bfSgovinda 	if (actual < count) {
1320*9c75c6bfSgovinda 		USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1321*9c75c6bfSgovinda 		    "ohci_add_intrs: Requested: %d, Received: %d\n",
1322*9c75c6bfSgovinda 		    count, actual);
13237c478bd9Sstevel@tonic-gate 
1324*9c75c6bfSgovinda 		for (i = 0; i < actual; i++)
1325*9c75c6bfSgovinda 			(void) ddi_intr_free(ohcip->ohci_htable[i]);
1326*9c75c6bfSgovinda 
1327*9c75c6bfSgovinda 		kmem_free(ohcip->ohci_htable, intr_size);
1328*9c75c6bfSgovinda 
1329*9c75c6bfSgovinda 		return (DDI_FAILURE);
1330*9c75c6bfSgovinda 	}
1331*9c75c6bfSgovinda 
1332*9c75c6bfSgovinda 	ohcip->ohci_intr_cnt = actual;
1333*9c75c6bfSgovinda 
1334*9c75c6bfSgovinda 	if ((ret = ddi_intr_get_pri(ohcip->ohci_htable[0],
1335*9c75c6bfSgovinda 	    &ohcip->ohci_intr_pri)) != DDI_SUCCESS) {
13367c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1337*9c75c6bfSgovinda 		    "ohci_add_intrs: ddi_intr_get_pri() failed %d", ret);
13387c478bd9Sstevel@tonic-gate 
1339*9c75c6bfSgovinda 		for (i = 0; i < actual; i++)
1340*9c75c6bfSgovinda 			(void) ddi_intr_free(ohcip->ohci_htable[i]);
1341*9c75c6bfSgovinda 
1342*9c75c6bfSgovinda 		kmem_free(ohcip->ohci_htable, intr_size);
13437c478bd9Sstevel@tonic-gate 
13447c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
13457c478bd9Sstevel@tonic-gate 	}
13467c478bd9Sstevel@tonic-gate 
1347*9c75c6bfSgovinda 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1348*9c75c6bfSgovinda 	    "ohci_add_intrs: Supported Interrupt priority 0x%x",
1349*9c75c6bfSgovinda 	    ohcip->ohci_intr_pri);
13507c478bd9Sstevel@tonic-gate 
13517c478bd9Sstevel@tonic-gate 	/* Test for high level mutex */
13527c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_intr_pri >= ddi_intr_get_hilevel_pri()) {
13537c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1354*9c75c6bfSgovinda 		    "ohci_add_intrs: Hi level interrupt not supported");
1355*9c75c6bfSgovinda 
1356*9c75c6bfSgovinda 		for (i = 0; i < actual; i++)
1357*9c75c6bfSgovinda 			(void) ddi_intr_free(ohcip->ohci_htable[i]);
13587c478bd9Sstevel@tonic-gate 
1359*9c75c6bfSgovinda 		kmem_free(ohcip->ohci_htable, intr_size);
13607c478bd9Sstevel@tonic-gate 
13617c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
13627c478bd9Sstevel@tonic-gate 	}
13637c478bd9Sstevel@tonic-gate 
13647c478bd9Sstevel@tonic-gate 	/* Initialize the mutex */
13657c478bd9Sstevel@tonic-gate 	mutex_init(&ohcip->ohci_int_mutex, NULL, MUTEX_DRIVER,
1366a195726fSgovinda 	    DDI_INTR_PRI(ohcip->ohci_intr_pri));
13677c478bd9Sstevel@tonic-gate 
1368*9c75c6bfSgovinda 	/* Call ddi_intr_add_handler() */
1369*9c75c6bfSgovinda 	for (i = 0; i < actual; i++) {
1370*9c75c6bfSgovinda 		if ((ret = ddi_intr_add_handler(ohcip->ohci_htable[i],
1371*9c75c6bfSgovinda 		    ohci_intr, (caddr_t)ohcip,
1372*9c75c6bfSgovinda 		    (caddr_t)(uintptr_t)i)) != DDI_SUCCESS) {
1373*9c75c6bfSgovinda 			USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1374*9c75c6bfSgovinda 			    "ohci_add_intrs: ddi_intr_add_handler() "
1375*9c75c6bfSgovinda 			    "failed %d", ret);
13767c478bd9Sstevel@tonic-gate 
1377*9c75c6bfSgovinda 			for (i = 0; i < actual; i++)
1378*9c75c6bfSgovinda 				(void) ddi_intr_free(ohcip->ohci_htable[i]);
13797c478bd9Sstevel@tonic-gate 
1380*9c75c6bfSgovinda 			mutex_destroy(&ohcip->ohci_int_mutex);
1381*9c75c6bfSgovinda 			kmem_free(ohcip->ohci_htable, intr_size);
1382*9c75c6bfSgovinda 
1383*9c75c6bfSgovinda 			return (DDI_FAILURE);
1384*9c75c6bfSgovinda 		}
13857c478bd9Sstevel@tonic-gate 	}
13867c478bd9Sstevel@tonic-gate 
1387*9c75c6bfSgovinda 	if ((ret = ddi_intr_get_cap(ohcip->ohci_htable[0],
1388*9c75c6bfSgovinda 	    &ohcip->ohci_intr_cap)) != DDI_SUCCESS) {
13897c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
1390*9c75c6bfSgovinda 		    "ohci_add_intrs: ddi_intr_get_cap() failed %d", ret);
1391*9c75c6bfSgovinda 
1392*9c75c6bfSgovinda 		for (i = 0; i < actual; i++) {
1393*9c75c6bfSgovinda 			(void) ddi_intr_remove_handler(ohcip->ohci_htable[i]);
1394*9c75c6bfSgovinda 			(void) ddi_intr_free(ohcip->ohci_htable[i]);
1395*9c75c6bfSgovinda 		}
13967c478bd9Sstevel@tonic-gate 
13977c478bd9Sstevel@tonic-gate 		mutex_destroy(&ohcip->ohci_int_mutex);
1398*9c75c6bfSgovinda 		kmem_free(ohcip->ohci_htable, intr_size);
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
14017c478bd9Sstevel@tonic-gate 	}
14027c478bd9Sstevel@tonic-gate 
1403*9c75c6bfSgovinda 	/* Enable all interrupts */
1404*9c75c6bfSgovinda 	if (ohcip->ohci_intr_cap & DDI_INTR_FLAG_BLOCK) {
1405*9c75c6bfSgovinda 		/* Call ddi_intr_block_enable() for MSI interrupts */
1406*9c75c6bfSgovinda 		(void) ddi_intr_block_enable(ohcip->ohci_htable,
1407*9c75c6bfSgovinda 		    ohcip->ohci_intr_cnt);
1408*9c75c6bfSgovinda 	} else {
1409*9c75c6bfSgovinda 		/* Call ddi_intr_enable for MSI or FIXED interrupts */
1410*9c75c6bfSgovinda 		for (i = 0; i < ohcip->ohci_intr_cnt; i++)
1411*9c75c6bfSgovinda 			(void) ddi_intr_enable(ohcip->ohci_htable[i]);
1412*9c75c6bfSgovinda 	}
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
14157c478bd9Sstevel@tonic-gate }
14167c478bd9Sstevel@tonic-gate 
14177c478bd9Sstevel@tonic-gate 
14187c478bd9Sstevel@tonic-gate /*
14197c478bd9Sstevel@tonic-gate  * ohci_init_ctlr:
14207c478bd9Sstevel@tonic-gate  *
14217c478bd9Sstevel@tonic-gate  * Initialize the Host Controller (HC).
14227c478bd9Sstevel@tonic-gate  */
14237c478bd9Sstevel@tonic-gate static int
14247c478bd9Sstevel@tonic-gate ohci_init_ctlr(ohci_state_t	*ohcip)
14257c478bd9Sstevel@tonic-gate {
14267c478bd9Sstevel@tonic-gate 	int			revision, curr_control, max_packet = 0;
14277c478bd9Sstevel@tonic-gate 	clock_t			sof_time_wait;
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl, "ohci_init_ctlr:");
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 	if (ohci_take_control(ohcip) != DDI_SUCCESS) {
14327c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
14337c478bd9Sstevel@tonic-gate 		    "ohci_init_ctlr: ohci_take_control failed\n");
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
14367c478bd9Sstevel@tonic-gate 	}
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 	/*
14397c478bd9Sstevel@tonic-gate 	 * Soft reset the host controller.
14407c478bd9Sstevel@tonic-gate 	 *
14417c478bd9Sstevel@tonic-gate 	 * On soft reset, the ohci host controller moves to the
14427c478bd9Sstevel@tonic-gate 	 * USB Suspend state in which most of the ohci operational
14437c478bd9Sstevel@tonic-gate 	 * registers are reset except stated ones. The soft reset
14447c478bd9Sstevel@tonic-gate 	 * doesn't cause a reset to the ohci root hub and even no
14457c478bd9Sstevel@tonic-gate 	 * subsequent reset signaling should be asserterd to its
14467c478bd9Sstevel@tonic-gate 	 * down stream.
14477c478bd9Sstevel@tonic-gate 	 */
14487c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_cmd_status, HCR_STATUS_RESET);
14497c478bd9Sstevel@tonic-gate 
14507c478bd9Sstevel@tonic-gate 	/* Wait 10ms for reset to complete */
14517c478bd9Sstevel@tonic-gate 	drv_usecwait(OHCI_RESET_TIMEWAIT);
14527c478bd9Sstevel@tonic-gate 
14537c478bd9Sstevel@tonic-gate 	/*
14547c478bd9Sstevel@tonic-gate 	 * Do hard reset the host controller.
14557c478bd9Sstevel@tonic-gate 	 *
14567c478bd9Sstevel@tonic-gate 	 * Now perform USB reset in order to reset the ohci root
14577c478bd9Sstevel@tonic-gate 	 * hub.
14587c478bd9Sstevel@tonic-gate 	 */
14597c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_control, HCR_CONTROL_RESET);
14607c478bd9Sstevel@tonic-gate 
14617c478bd9Sstevel@tonic-gate 	/*
14627c478bd9Sstevel@tonic-gate 	 * According to Section 5.1.2.3 of the specification, the
14637c478bd9Sstevel@tonic-gate 	 * host controller will go into suspend state immediately
14647c478bd9Sstevel@tonic-gate 	 * after the reset.
14657c478bd9Sstevel@tonic-gate 	 */
14667c478bd9Sstevel@tonic-gate 
14677c478bd9Sstevel@tonic-gate 	/* Verify the version number */
14687c478bd9Sstevel@tonic-gate 	revision = Get_OpReg(hcr_revision);
14697c478bd9Sstevel@tonic-gate 
14707c478bd9Sstevel@tonic-gate 	if ((revision & HCR_REVISION_MASK) != HCR_REVISION_1_0) {
14717c478bd9Sstevel@tonic-gate 
14727c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
14737c478bd9Sstevel@tonic-gate 	}
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
14767c478bd9Sstevel@tonic-gate 	    "ohci_init_ctlr: Revision verified");
14777c478bd9Sstevel@tonic-gate 
14787c478bd9Sstevel@tonic-gate 	/* hcca area need not be initialized on resume */
14797c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_hc_soft_state == OHCI_CTLR_INIT_STATE) {
14807c478bd9Sstevel@tonic-gate 
14817c478bd9Sstevel@tonic-gate 		/* Get the ohci chip vendor and device id */
14827c478bd9Sstevel@tonic-gate 		ohcip->ohci_vendor_id = pci_config_get16(
14837c478bd9Sstevel@tonic-gate 		    ohcip->ohci_config_handle, PCI_CONF_VENID);
14847c478bd9Sstevel@tonic-gate 		ohcip->ohci_device_id = pci_config_get16(
14857c478bd9Sstevel@tonic-gate 		    ohcip->ohci_config_handle, PCI_CONF_DEVID);
14867c478bd9Sstevel@tonic-gate 		ohcip->ohci_rev_id = pci_config_get8(
14877c478bd9Sstevel@tonic-gate 		    ohcip->ohci_config_handle, PCI_CONF_REVID);
14887c478bd9Sstevel@tonic-gate 
14897c478bd9Sstevel@tonic-gate 		/* Initialize the hcca area */
14907c478bd9Sstevel@tonic-gate 		if (ohci_init_hcca(ohcip) != DDI_SUCCESS) {
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
14937c478bd9Sstevel@tonic-gate 		}
14947c478bd9Sstevel@tonic-gate 	}
14957c478bd9Sstevel@tonic-gate 
1496f806f48bShs 	/*
1497f806f48bShs 	 * Workaround for ULI1575 chipset. Following OHCI Operational Memory
1498f806f48bShs 	 * Registers are not cleared to their default value on reset.
1499f806f48bShs 	 * Explicitly set the registers to default value.
1500f806f48bShs 	 */
1501f806f48bShs 	if (ohcip->ohci_vendor_id == PCI_ULI1575_VENID &&
1502f806f48bShs 			ohcip->ohci_device_id == PCI_ULI1575_DEVID) {
1503f806f48bShs 		Set_OpReg(hcr_control, HCR_CONTROL_DEFAULT);
1504f806f48bShs 		Set_OpReg(hcr_intr_enable, HCR_INT_ENABLE_DEFAULT);
1505f806f48bShs 		Set_OpReg(hcr_HCCA, HCR_HCCA_DEFAULT);
1506f806f48bShs 		Set_OpReg(hcr_ctrl_head, HCR_CONTROL_HEAD_ED_DEFAULT);
1507f806f48bShs 		Set_OpReg(hcr_bulk_head, HCR_BULK_HEAD_ED_DEFAULT);
1508f806f48bShs 		Set_OpReg(hcr_frame_interval, HCR_FRAME_INTERVAL_DEFAULT);
1509f806f48bShs 		Set_OpReg(hcr_periodic_strt, HCR_PERIODIC_START_DEFAULT);
1510f806f48bShs 	}
1511f806f48bShs 
15127c478bd9Sstevel@tonic-gate 	/* Set the HcHCCA to the physical address of the HCCA block */
15137c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_HCCA, (uint_t)ohcip->ohci_hcca_cookie.dmac_address);
15147c478bd9Sstevel@tonic-gate 
15157c478bd9Sstevel@tonic-gate 	/*
15167c478bd9Sstevel@tonic-gate 	 * Set HcInterruptEnable to enable all interrupts except Root
15177c478bd9Sstevel@tonic-gate 	 * Hub Status change and SOF interrupts.
15187c478bd9Sstevel@tonic-gate 	 */
15197c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_enable, HCR_INTR_SO | HCR_INTR_WDH |
15207c478bd9Sstevel@tonic-gate 	    HCR_INTR_RD | HCR_INTR_UE | HCR_INTR_FNO | HCR_INTR_MIE);
15217c478bd9Sstevel@tonic-gate 
15227c478bd9Sstevel@tonic-gate 	/*
15237c478bd9Sstevel@tonic-gate 	 * For non-periodic transfers, reserve atleast for one low-speed
15247c478bd9Sstevel@tonic-gate 	 * device transaction. According to USB Bandwidth Analysis white
15257c478bd9Sstevel@tonic-gate 	 * paper and also as per OHCI Specification 1.0a, section 7.3.5,
15267c478bd9Sstevel@tonic-gate 	 * page 123, one low-speed transaction takes 0x628h full speed
15277c478bd9Sstevel@tonic-gate 	 * bits (197 bytes), which comes to around 13% of USB frame time.
15287c478bd9Sstevel@tonic-gate 	 *
15297c478bd9Sstevel@tonic-gate 	 * The periodic transfers will get around 87% of USB frame time.
15307c478bd9Sstevel@tonic-gate 	 */
15317c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_periodic_strt,
15327c478bd9Sstevel@tonic-gate 	    ((PERIODIC_XFER_STARTS * BITS_PER_BYTE) - 1));
15337c478bd9Sstevel@tonic-gate 
15347c478bd9Sstevel@tonic-gate 	/* Save the contents of the Frame Interval Registers */
15357c478bd9Sstevel@tonic-gate 	ohcip->ohci_frame_interval = Get_OpReg(hcr_frame_interval);
15367c478bd9Sstevel@tonic-gate 
15377c478bd9Sstevel@tonic-gate 	/*
15387c478bd9Sstevel@tonic-gate 	 * Initialize the FSLargestDataPacket value in the frame interval
15397c478bd9Sstevel@tonic-gate 	 * register. The controller compares the value of MaxPacketSize to
15407c478bd9Sstevel@tonic-gate 	 * this value to see if the entire packet may be sent out before
15417c478bd9Sstevel@tonic-gate 	 * the EOF.
15427c478bd9Sstevel@tonic-gate 	 */
15437c478bd9Sstevel@tonic-gate 	max_packet = ((((ohcip->ohci_frame_interval -
15447c478bd9Sstevel@tonic-gate 	    MAX_OVERHEAD) * 6) / 7) << HCR_FRME_FSMPS_SHFT);
15457c478bd9Sstevel@tonic-gate 
15467c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_frame_interval,
15477c478bd9Sstevel@tonic-gate 	    (max_packet | ohcip->ohci_frame_interval));
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 	/* Begin sending SOFs */
15507c478bd9Sstevel@tonic-gate 	curr_control = Get_OpReg(hcr_control);
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
15537c478bd9Sstevel@tonic-gate 	    "ohci_init_ctlr: curr_control=0x%x", curr_control);
15547c478bd9Sstevel@tonic-gate 
15557c478bd9Sstevel@tonic-gate 	/* Set the state to operational */
15567c478bd9Sstevel@tonic-gate 	curr_control = (curr_control &
15577c478bd9Sstevel@tonic-gate 	    (~HCR_CONTROL_HCFS)) | HCR_CONTROL_OPERAT;
15587c478bd9Sstevel@tonic-gate 
15597c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_control, curr_control);
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate 	ASSERT((Get_OpReg(hcr_control) &
15627c478bd9Sstevel@tonic-gate 	    HCR_CONTROL_HCFS) == HCR_CONTROL_OPERAT);
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 	/* Set host controller soft state to operational */
15657c478bd9Sstevel@tonic-gate 	ohcip->ohci_hc_soft_state = OHCI_CTLR_OPERATIONAL_STATE;
15667c478bd9Sstevel@tonic-gate 
15677c478bd9Sstevel@tonic-gate 	/* Get the number of clock ticks to wait */
15687c478bd9Sstevel@tonic-gate 	sof_time_wait = drv_usectohz(OHCI_MAX_SOF_TIMEWAIT * 1000000);
15697c478bd9Sstevel@tonic-gate 
15707c478bd9Sstevel@tonic-gate 	/* Clear ohci_sof_flag indicating waiting for SOF interrupt */
15717c478bd9Sstevel@tonic-gate 	ohcip->ohci_sof_flag = B_FALSE;
15727c478bd9Sstevel@tonic-gate 
15737c478bd9Sstevel@tonic-gate 	/* Enable the SOF interrupt */
15747c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_enable, HCR_INTR_SOF);
15757c478bd9Sstevel@tonic-gate 
15767c478bd9Sstevel@tonic-gate 	ASSERT(Get_OpReg(hcr_intr_enable) & HCR_INTR_SOF);
15777c478bd9Sstevel@tonic-gate 
15787c478bd9Sstevel@tonic-gate 	(void) cv_timedwait(&ohcip->ohci_SOF_cv,
15797c478bd9Sstevel@tonic-gate 	    &ohcip->ohci_int_mutex, ddi_get_lbolt() + sof_time_wait);
15807c478bd9Sstevel@tonic-gate 
15817c478bd9Sstevel@tonic-gate 	/* Wait for the SOF or timeout event */
15827c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_sof_flag == B_FALSE) {
15837c478bd9Sstevel@tonic-gate 
15847c478bd9Sstevel@tonic-gate 		/* Set host controller soft state to error */
15857c478bd9Sstevel@tonic-gate 		ohcip->ohci_hc_soft_state = OHCI_CTLR_ERROR_STATE;
15867c478bd9Sstevel@tonic-gate 
15877c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
15887c478bd9Sstevel@tonic-gate 		    "No SOF interrupts have been received, this USB OHCI host"
15897c478bd9Sstevel@tonic-gate 		    "controller is unusable");
15907c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
15917c478bd9Sstevel@tonic-gate 	}
15927c478bd9Sstevel@tonic-gate 
15937c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
15947c478bd9Sstevel@tonic-gate 	    "ohci_init_ctlr: SOF's have started");
15957c478bd9Sstevel@tonic-gate 
15967c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
15977c478bd9Sstevel@tonic-gate }
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate 
16007c478bd9Sstevel@tonic-gate /*
16017c478bd9Sstevel@tonic-gate  * ohci_init_hcca:
16027c478bd9Sstevel@tonic-gate  *
16037c478bd9Sstevel@tonic-gate  * Allocate the system memory and initialize Host Controller Communication
16047c478bd9Sstevel@tonic-gate  * Area (HCCA). The HCCA structure must be aligned to a 256-byte boundary.
16057c478bd9Sstevel@tonic-gate  */
16067c478bd9Sstevel@tonic-gate static int
16077c478bd9Sstevel@tonic-gate ohci_init_hcca(ohci_state_t	*ohcip)
16087c478bd9Sstevel@tonic-gate {
16097c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t	dev_attr;
16107c478bd9Sstevel@tonic-gate 	size_t			real_length;
16117c478bd9Sstevel@tonic-gate 	uint_t			mask, ccount;
16127c478bd9Sstevel@tonic-gate 	int			result;
16137c478bd9Sstevel@tonic-gate 	uintptr_t		addr;
16147c478bd9Sstevel@tonic-gate 
16157c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
16167c478bd9Sstevel@tonic-gate 
16177c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl, "ohci_init_hcca:");
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate 	/* The host controller will be little endian */
16207c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
16217c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_endian_flags  = DDI_STRUCTURE_LE_ACC;
16227c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
16237c478bd9Sstevel@tonic-gate 
16247c478bd9Sstevel@tonic-gate 	/* Byte alignment to HCCA alignment */
16257c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_align = OHCI_DMA_ATTR_HCCA_ALIGNMENT;
16267c478bd9Sstevel@tonic-gate 
16277c478bd9Sstevel@tonic-gate 	/* Create space for the HCCA block */
16287c478bd9Sstevel@tonic-gate 	if (ddi_dma_alloc_handle(ohcip->ohci_dip, &ohcip->ohci_dma_attr,
16297c478bd9Sstevel@tonic-gate 				DDI_DMA_SLEEP,
16307c478bd9Sstevel@tonic-gate 				0,
16317c478bd9Sstevel@tonic-gate 				&ohcip->ohci_hcca_dma_handle)
16327c478bd9Sstevel@tonic-gate 				!= DDI_SUCCESS) {
16337c478bd9Sstevel@tonic-gate 
16347c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
16357c478bd9Sstevel@tonic-gate 	}
16367c478bd9Sstevel@tonic-gate 
16377c478bd9Sstevel@tonic-gate 	if (ddi_dma_mem_alloc(ohcip->ohci_hcca_dma_handle,
16387c478bd9Sstevel@tonic-gate 				2 * sizeof (ohci_hcca_t),
16397c478bd9Sstevel@tonic-gate 				&dev_attr,
16407c478bd9Sstevel@tonic-gate 				DDI_DMA_CONSISTENT,
16417c478bd9Sstevel@tonic-gate 				DDI_DMA_SLEEP,
16427c478bd9Sstevel@tonic-gate 				0,
16437c478bd9Sstevel@tonic-gate 				(caddr_t *)&ohcip->ohci_hccap,
16447c478bd9Sstevel@tonic-gate 				&real_length,
16457c478bd9Sstevel@tonic-gate 				&ohcip->ohci_hcca_mem_handle)) {
16467c478bd9Sstevel@tonic-gate 
16477c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
16487c478bd9Sstevel@tonic-gate 	}
16497c478bd9Sstevel@tonic-gate 
16507c478bd9Sstevel@tonic-gate 	bzero((void *)ohcip->ohci_hccap, real_length);
16517c478bd9Sstevel@tonic-gate 
16527c478bd9Sstevel@tonic-gate 	/* Figure out the alignment requirements */
16537c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_HCCA, 0xFFFFFFFF);
16547c478bd9Sstevel@tonic-gate 
16557c478bd9Sstevel@tonic-gate 	/*
16567c478bd9Sstevel@tonic-gate 	 * Read the hcr_HCCA register until
16577c478bd9Sstevel@tonic-gate 	 * contenets are non-zero.
16587c478bd9Sstevel@tonic-gate 	 */
16597c478bd9Sstevel@tonic-gate 	mask = Get_OpReg(hcr_HCCA);
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 	while (mask == 0) {
16627c478bd9Sstevel@tonic-gate 		drv_usecwait(OHCI_TIMEWAIT);
16637c478bd9Sstevel@tonic-gate 		mask = Get_OpReg(hcr_HCCA);
16647c478bd9Sstevel@tonic-gate 	}
16657c478bd9Sstevel@tonic-gate 
16667c478bd9Sstevel@tonic-gate 	ASSERT(mask != 0);
16677c478bd9Sstevel@tonic-gate 
16687c478bd9Sstevel@tonic-gate 	addr = (uintptr_t)ohcip->ohci_hccap;
16697c478bd9Sstevel@tonic-gate 
16707c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
16717c478bd9Sstevel@tonic-gate 	    "ohci_init_hcca: addr=0x%lx, mask=0x%x", addr, mask);
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 	while (addr & (~mask)) {
16747c478bd9Sstevel@tonic-gate 		addr++;
16757c478bd9Sstevel@tonic-gate 	}
16767c478bd9Sstevel@tonic-gate 
16777c478bd9Sstevel@tonic-gate 	ohcip->ohci_hccap = (ohci_hcca_t *)addr;
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
16807c478bd9Sstevel@tonic-gate 	    "ohci_init_hcca: Real length %lu", real_length);
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
16837c478bd9Sstevel@tonic-gate 	    "ohci_init_hcca: virtual hcca 0x%p", (void *)ohcip->ohci_hccap);
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate 	/* Map the whole HCCA into the I/O address space */
16867c478bd9Sstevel@tonic-gate 	result = ddi_dma_addr_bind_handle(ohcip->ohci_hcca_dma_handle,
16877c478bd9Sstevel@tonic-gate 				NULL,
16887c478bd9Sstevel@tonic-gate 				(caddr_t)ohcip->ohci_hccap,
16897c478bd9Sstevel@tonic-gate 				real_length,
16907c478bd9Sstevel@tonic-gate 				DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
16917c478bd9Sstevel@tonic-gate 				DDI_DMA_SLEEP, NULL,
16927c478bd9Sstevel@tonic-gate 				&ohcip->ohci_hcca_cookie,
16937c478bd9Sstevel@tonic-gate 				&ccount);
16947c478bd9Sstevel@tonic-gate 
16957c478bd9Sstevel@tonic-gate 	if (result == DDI_DMA_MAPPED) {
16967c478bd9Sstevel@tonic-gate 		/* The cookie count should be 1 */
16977c478bd9Sstevel@tonic-gate 		if (ccount != 1) {
16987c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
16997c478bd9Sstevel@tonic-gate 			    "ohci_init_hcca: More than 1 cookie");
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
17027c478bd9Sstevel@tonic-gate 		}
17037c478bd9Sstevel@tonic-gate 	} else {
17047c478bd9Sstevel@tonic-gate 		ohci_decode_ddi_dma_addr_bind_handle_result(ohcip, result);
17057c478bd9Sstevel@tonic-gate 
17067c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
17077c478bd9Sstevel@tonic-gate 	}
17087c478bd9Sstevel@tonic-gate 
17096d9a41ffSqz 	/*
17106d9a41ffSqz 	 * DMA addresses for HCCA are bound
17116d9a41ffSqz 	 */
17126d9a41ffSqz 	ohcip->ohci_dma_addr_bind_flag |= OHCI_HCCA_DMA_BOUND;
17136d9a41ffSqz 
17147c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
17157c478bd9Sstevel@tonic-gate 	    "ohci_init_hcca: physical 0x%p",
17167c478bd9Sstevel@tonic-gate 	    (void *)(uintptr_t)ohcip->ohci_hcca_cookie.dmac_address);
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
17197c478bd9Sstevel@tonic-gate 	    "ohci_init_hcca: size %lu", ohcip->ohci_hcca_cookie.dmac_size);
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate 	/* Initialize the interrupt lists */
17227c478bd9Sstevel@tonic-gate 	ohci_build_interrupt_lattice(ohcip);
17237c478bd9Sstevel@tonic-gate 
17247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
17257c478bd9Sstevel@tonic-gate 	    "ohci_init_hcca: End");
17267c478bd9Sstevel@tonic-gate 
17277c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
17287c478bd9Sstevel@tonic-gate }
17297c478bd9Sstevel@tonic-gate 
17307c478bd9Sstevel@tonic-gate 
17317c478bd9Sstevel@tonic-gate /*
17327c478bd9Sstevel@tonic-gate  * ohci_build_interrupt_lattice:
17337c478bd9Sstevel@tonic-gate  *
17347c478bd9Sstevel@tonic-gate  * Construct the interrupt lattice tree using static Endpoint Descriptors
17357c478bd9Sstevel@tonic-gate  * (ED). This interrupt lattice tree will have total of 32 interrupt  ED
17367c478bd9Sstevel@tonic-gate  * lists and the Host Controller (HC) processes one interrupt ED list in
17377c478bd9Sstevel@tonic-gate  * every frame. The lower five bits of the current frame number indexes
17387c478bd9Sstevel@tonic-gate  * into an array of 32 interrupt Endpoint Descriptor lists found in the
17397c478bd9Sstevel@tonic-gate  * HCCA.
17407c478bd9Sstevel@tonic-gate  */
17417c478bd9Sstevel@tonic-gate static void
17427c478bd9Sstevel@tonic-gate ohci_build_interrupt_lattice(ohci_state_t	*ohcip)
17437c478bd9Sstevel@tonic-gate {
17447c478bd9Sstevel@tonic-gate 	ohci_ed_t	*list_array = ohcip->ohci_ed_pool_addr;
17457c478bd9Sstevel@tonic-gate 	int		half_list = NUM_INTR_ED_LISTS / 2;
17467c478bd9Sstevel@tonic-gate 	ohci_hcca_t	*hccap = ohcip->ohci_hccap;
17477c478bd9Sstevel@tonic-gate 	uintptr_t	addr;
17487c478bd9Sstevel@tonic-gate 	int		i;
17497c478bd9Sstevel@tonic-gate 
17507c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
17517c478bd9Sstevel@tonic-gate 	    "ohci_build_interrupt_lattice:");
17527c478bd9Sstevel@tonic-gate 
17537c478bd9Sstevel@tonic-gate 	/*
17547c478bd9Sstevel@tonic-gate 	 * Reserve the first 31 Endpoint Descriptor (ED) structures
17557c478bd9Sstevel@tonic-gate 	 * in the pool as static endpoints & these are required for
17567c478bd9Sstevel@tonic-gate 	 * constructing interrupt lattice tree.
17577c478bd9Sstevel@tonic-gate 	 */
17587c478bd9Sstevel@tonic-gate 	for (i = 0; i < NUM_STATIC_NODES; i++) {
17597c478bd9Sstevel@tonic-gate 		Set_ED(list_array[i].hced_ctrl, HC_EPT_sKip);
17607c478bd9Sstevel@tonic-gate 
17617c478bd9Sstevel@tonic-gate 		Set_ED(list_array[i].hced_state, HC_EPT_STATIC);
17627c478bd9Sstevel@tonic-gate 	}
17637c478bd9Sstevel@tonic-gate 
17647c478bd9Sstevel@tonic-gate 	/* Build the interrupt lattice tree */
17657c478bd9Sstevel@tonic-gate 	for (i = 0; i < half_list - 1; i++) {
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate 		/*
17687c478bd9Sstevel@tonic-gate 		 * The next  pointer in the host controller  endpoint
17697c478bd9Sstevel@tonic-gate 		 * descriptor must contain an iommu address. Calculate
17707c478bd9Sstevel@tonic-gate 		 * the offset into the cpu address and add this to the
17717c478bd9Sstevel@tonic-gate 		 * starting iommu address.
17727c478bd9Sstevel@tonic-gate 		 */
17737c478bd9Sstevel@tonic-gate 		addr = ohci_ed_cpu_to_iommu(ohcip, (ohci_ed_t *)&list_array[i]);
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 		Set_ED(list_array[2*i + 1].hced_next, addr);
17767c478bd9Sstevel@tonic-gate 		Set_ED(list_array[2*i + 2].hced_next, addr);
17777c478bd9Sstevel@tonic-gate 	}
17787c478bd9Sstevel@tonic-gate 
17797c478bd9Sstevel@tonic-gate 	/*
17807c478bd9Sstevel@tonic-gate 	 * Initialize the interrupt list in the HCCA so that it points
17817c478bd9Sstevel@tonic-gate 	 * to the bottom of the tree.
17827c478bd9Sstevel@tonic-gate 	 */
17837c478bd9Sstevel@tonic-gate 	for (i = 0; i < half_list; i++) {
17847c478bd9Sstevel@tonic-gate 		addr = ohci_ed_cpu_to_iommu(ohcip,
17857c478bd9Sstevel@tonic-gate 		    (ohci_ed_t *)&list_array[half_list - 1 + ohci_index[i]]);
17867c478bd9Sstevel@tonic-gate 
17877c478bd9Sstevel@tonic-gate 		ASSERT(Get_ED(list_array[half_list - 1 +
17887c478bd9Sstevel@tonic-gate 		    ohci_index[i]].hced_ctrl));
17897c478bd9Sstevel@tonic-gate 
17907c478bd9Sstevel@tonic-gate 		ASSERT(addr != 0);
17917c478bd9Sstevel@tonic-gate 
17927c478bd9Sstevel@tonic-gate 		Set_HCCA(hccap->HccaIntTble[i], addr);
17937c478bd9Sstevel@tonic-gate 		Set_HCCA(hccap->HccaIntTble[i + half_list], addr);
17947c478bd9Sstevel@tonic-gate 	}
17957c478bd9Sstevel@tonic-gate }
17967c478bd9Sstevel@tonic-gate 
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate /*
17997c478bd9Sstevel@tonic-gate  * ohci_take_control:
18007c478bd9Sstevel@tonic-gate  *
18017c478bd9Sstevel@tonic-gate  * Take control of the host controller. OpenHCI allows for optional support
18027c478bd9Sstevel@tonic-gate  * of legacy devices through the use of System Management Mode software and
18037c478bd9Sstevel@tonic-gate  * system Management interrupt hardware. See section 5.1.1.3 of the OpenHCI
18047c478bd9Sstevel@tonic-gate  * spec for more details.
18057c478bd9Sstevel@tonic-gate  */
18067c478bd9Sstevel@tonic-gate static int
18077c478bd9Sstevel@tonic-gate ohci_take_control(ohci_state_t	*ohcip)
18087c478bd9Sstevel@tonic-gate {
18097c478bd9Sstevel@tonic-gate #if defined(__x86)
18107c478bd9Sstevel@tonic-gate 	uint32_t hcr_control_val;
18117c478bd9Sstevel@tonic-gate 	uint32_t hcr_cmd_status_val;
18127c478bd9Sstevel@tonic-gate 	int wait;
18137c478bd9Sstevel@tonic-gate #endif	/* __x86 */
18147c478bd9Sstevel@tonic-gate 
18157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
18167c478bd9Sstevel@tonic-gate 	    "ohci_take_control:");
18177c478bd9Sstevel@tonic-gate 
18187c478bd9Sstevel@tonic-gate #if defined(__x86)
18197c478bd9Sstevel@tonic-gate 	/*
18207c478bd9Sstevel@tonic-gate 	 * On x86, we must tell the BIOS we want the controller,
18217c478bd9Sstevel@tonic-gate 	 * and wait for it to respond that we can have it.
18227c478bd9Sstevel@tonic-gate 	 */
18237c478bd9Sstevel@tonic-gate 	hcr_control_val = Get_OpReg(hcr_control);
18247c478bd9Sstevel@tonic-gate 	if ((hcr_control_val & HCR_CONTROL_IR) == 0) {
18257c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
18267c478bd9Sstevel@tonic-gate 		    "ohci_take_control: InterruptRouting off\n");
18277c478bd9Sstevel@tonic-gate 
18287c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
18297c478bd9Sstevel@tonic-gate 	}
18307c478bd9Sstevel@tonic-gate 
18317c478bd9Sstevel@tonic-gate 	/* attempt the OwnershipChange request */
18327c478bd9Sstevel@tonic-gate 	hcr_cmd_status_val = Get_OpReg(hcr_cmd_status);
18337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
18347c478bd9Sstevel@tonic-gate 	    "ohci_take_control: hcr_cmd_status: 0x%x\n",
18357c478bd9Sstevel@tonic-gate 	    hcr_cmd_status_val);
18367c478bd9Sstevel@tonic-gate 	hcr_cmd_status_val |= HCR_STATUS_OCR;
18377c478bd9Sstevel@tonic-gate 
18387c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_cmd_status, hcr_cmd_status_val);
18397c478bd9Sstevel@tonic-gate 
18407c478bd9Sstevel@tonic-gate 	/* now wait for 5 seconds for InterruptRouting to go away */
18417c478bd9Sstevel@tonic-gate 	for (wait = 0; wait < 5000; wait++) {
18427c478bd9Sstevel@tonic-gate 		if ((Get_OpReg(hcr_control) & HCR_CONTROL_IR) == 0)
18437c478bd9Sstevel@tonic-gate 			break;
18447c478bd9Sstevel@tonic-gate 		drv_usecwait(1000);
18457c478bd9Sstevel@tonic-gate 	}
18467c478bd9Sstevel@tonic-gate 	if (wait >= 5000) {
18477c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
18487c478bd9Sstevel@tonic-gate 		    "ohci_take_control: couldn't take control from BIOS\n");
18497c478bd9Sstevel@tonic-gate 
18507c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
18517c478bd9Sstevel@tonic-gate 	}
18527c478bd9Sstevel@tonic-gate #else	/* __x86 */
18537c478bd9Sstevel@tonic-gate 	/*
18547c478bd9Sstevel@tonic-gate 	 * On Sparc, there won't be  special System Management Mode
18557c478bd9Sstevel@tonic-gate 	 * hardware for legacy devices, while the x86 platforms may
18567c478bd9Sstevel@tonic-gate 	 * have to deal with  this. This  function may be  platform
18577c478bd9Sstevel@tonic-gate 	 * specific.
18587c478bd9Sstevel@tonic-gate 	 *
18597c478bd9Sstevel@tonic-gate 	 * The interrupt routing bit should not be set.
18607c478bd9Sstevel@tonic-gate 	 */
18617c478bd9Sstevel@tonic-gate 	if (Get_OpReg(hcr_control) & HCR_CONTROL_IR) {
18627c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
18637c478bd9Sstevel@tonic-gate 		    "ohci_take_control: Routing bit set");
18647c478bd9Sstevel@tonic-gate 
18657c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
18667c478bd9Sstevel@tonic-gate 	}
18677c478bd9Sstevel@tonic-gate #endif	/* __x86 */
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
18707c478bd9Sstevel@tonic-gate 	    "ohci_take_control: End");
18717c478bd9Sstevel@tonic-gate 
18727c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
18737c478bd9Sstevel@tonic-gate }
18747c478bd9Sstevel@tonic-gate 
18754610e4a0Sfrits /*
18764610e4a0Sfrits  * ohci_pm_support:
18774610e4a0Sfrits  *	always return success since PM has been quite reliable on ohci
18784610e4a0Sfrits  */
18794610e4a0Sfrits /*ARGSUSED*/
18804610e4a0Sfrits int
18814610e4a0Sfrits ohci_hcdi_pm_support(dev_info_t *dip)
18824610e4a0Sfrits {
18834610e4a0Sfrits 	return (USB_SUCCESS);
18844610e4a0Sfrits }
18857c478bd9Sstevel@tonic-gate 
18867c478bd9Sstevel@tonic-gate /*
18877c478bd9Sstevel@tonic-gate  * ohci_alloc_hcdi_ops:
18887c478bd9Sstevel@tonic-gate  *
18897c478bd9Sstevel@tonic-gate  * The HCDI interfaces or entry points are the software interfaces used by
18907c478bd9Sstevel@tonic-gate  * the Universal Serial Bus Driver  (USBA) to  access the services of the
18917c478bd9Sstevel@tonic-gate  * Host Controller Driver (HCD).  During HCD initialization, inform  USBA
18927c478bd9Sstevel@tonic-gate  * about all available HCDI interfaces or entry points.
18937c478bd9Sstevel@tonic-gate  */
18947c478bd9Sstevel@tonic-gate static usba_hcdi_ops_t *
18957c478bd9Sstevel@tonic-gate ohci_alloc_hcdi_ops(ohci_state_t	*ohcip)
18967c478bd9Sstevel@tonic-gate {
18977c478bd9Sstevel@tonic-gate 	usba_hcdi_ops_t			*usba_hcdi_ops;
18987c478bd9Sstevel@tonic-gate 
18997c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
19007c478bd9Sstevel@tonic-gate 	    "ohci_alloc_hcdi_ops:");
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate 	usba_hcdi_ops = usba_alloc_hcdi_ops();
19037c478bd9Sstevel@tonic-gate 
19047c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_ops_version = HCDI_OPS_VERSION;
19057c478bd9Sstevel@tonic-gate 
19064610e4a0Sfrits 	usba_hcdi_ops->usba_hcdi_pm_support = ohci_hcdi_pm_support;
19077c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_pipe_open = ohci_hcdi_pipe_open;
19087c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_pipe_close = ohci_hcdi_pipe_close;
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_pipe_reset = ohci_hcdi_pipe_reset;
19117c478bd9Sstevel@tonic-gate 
19127c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_pipe_ctrl_xfer = ohci_hcdi_pipe_ctrl_xfer;
19137c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_pipe_bulk_xfer = ohci_hcdi_pipe_bulk_xfer;
19147c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_pipe_intr_xfer = ohci_hcdi_pipe_intr_xfer;
19157c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_pipe_isoc_xfer = ohci_hcdi_pipe_isoc_xfer;
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_bulk_transfer_size =
19187c478bd9Sstevel@tonic-gate 					ohci_hcdi_bulk_transfer_size;
19197c478bd9Sstevel@tonic-gate 
19207c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_pipe_stop_intr_polling =
19217c478bd9Sstevel@tonic-gate 					ohci_hcdi_pipe_stop_intr_polling;
19227c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_pipe_stop_isoc_polling =
19237c478bd9Sstevel@tonic-gate 					ohci_hcdi_pipe_stop_isoc_polling;
19247c478bd9Sstevel@tonic-gate 
19257c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_get_current_frame_number =
19267c478bd9Sstevel@tonic-gate 					ohci_hcdi_get_current_frame_number;
19277c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_get_max_isoc_pkts =
19287c478bd9Sstevel@tonic-gate 					ohci_hcdi_get_max_isoc_pkts;
19297c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_console_input_init =
19307c478bd9Sstevel@tonic-gate 					ohci_hcdi_polled_input_init;
19317c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_console_input_enter =
19327c478bd9Sstevel@tonic-gate 					ohci_hcdi_polled_input_enter;
19337c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_console_read = ohci_hcdi_polled_read;
19347c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_console_input_exit =
19357c478bd9Sstevel@tonic-gate 					ohci_hcdi_polled_input_exit;
19367c478bd9Sstevel@tonic-gate 	usba_hcdi_ops->usba_hcdi_console_input_fini =
19377c478bd9Sstevel@tonic-gate 					ohci_hcdi_polled_input_fini;
19387c478bd9Sstevel@tonic-gate 
19397c478bd9Sstevel@tonic-gate 	return (usba_hcdi_ops);
19407c478bd9Sstevel@tonic-gate }
19417c478bd9Sstevel@tonic-gate 
19427c478bd9Sstevel@tonic-gate 
19437c478bd9Sstevel@tonic-gate /*
19447c478bd9Sstevel@tonic-gate  * Host Controller Driver (HCD) deinitialization functions
19457c478bd9Sstevel@tonic-gate  */
19467c478bd9Sstevel@tonic-gate 
19477c478bd9Sstevel@tonic-gate /*
19487c478bd9Sstevel@tonic-gate  * ohci_cleanup:
19497c478bd9Sstevel@tonic-gate  *
19507c478bd9Sstevel@tonic-gate  * Cleanup on attach failure or detach
19517c478bd9Sstevel@tonic-gate  */
19527c478bd9Sstevel@tonic-gate static int
19537c478bd9Sstevel@tonic-gate ohci_cleanup(ohci_state_t	*ohcip)
19547c478bd9Sstevel@tonic-gate {
19557c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
19567c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;
19577c478bd9Sstevel@tonic-gate 	ohci_td_t		*td;
19587c478bd9Sstevel@tonic-gate 	int			i, state, rval;
19597c478bd9Sstevel@tonic-gate 	int			flags = ohcip->ohci_flags;
19607c478bd9Sstevel@tonic-gate 
19617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl, "ohci_cleanup:");
19627c478bd9Sstevel@tonic-gate 
19637c478bd9Sstevel@tonic-gate 	if (flags & OHCI_RHREG) {
19647c478bd9Sstevel@tonic-gate 		/* Unload the root hub driver */
19657c478bd9Sstevel@tonic-gate 		if (ohci_unload_root_hub_driver(ohcip) != USB_SUCCESS) {
19667c478bd9Sstevel@tonic-gate 
19677c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
19687c478bd9Sstevel@tonic-gate 		}
19697c478bd9Sstevel@tonic-gate 	}
19707c478bd9Sstevel@tonic-gate 
19717c478bd9Sstevel@tonic-gate 	if (flags & OHCI_USBAREG) {
19727c478bd9Sstevel@tonic-gate 		/* Unregister this HCD instance with USBA */
19737c478bd9Sstevel@tonic-gate 		usba_hcdi_unregister(ohcip->ohci_dip);
19747c478bd9Sstevel@tonic-gate 	}
19757c478bd9Sstevel@tonic-gate 
19767c478bd9Sstevel@tonic-gate 	if (flags & OHCI_INTR) {
19777c478bd9Sstevel@tonic-gate 
19787c478bd9Sstevel@tonic-gate 		mutex_enter(&ohcip->ohci_int_mutex);
19797c478bd9Sstevel@tonic-gate 
19807c478bd9Sstevel@tonic-gate 		/* Disable all HC ED list processing */
19817c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control,
19827c478bd9Sstevel@tonic-gate 		    (Get_OpReg(hcr_control) & ~(HCR_CONTROL_CLE |
19837c478bd9Sstevel@tonic-gate 		    HCR_CONTROL_BLE | HCR_CONTROL_PLE | HCR_CONTROL_IE)));
19847c478bd9Sstevel@tonic-gate 
19857c478bd9Sstevel@tonic-gate 		/* Disable all HC interrupts */
19867c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_intr_disable,
19877c478bd9Sstevel@tonic-gate 		    (HCR_INTR_SO | HCR_INTR_WDH | HCR_INTR_RD | HCR_INTR_UE));
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 		/* Wait for the next SOF */
19907c478bd9Sstevel@tonic-gate 		(void) ohci_wait_for_sof(ohcip);
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate 		/* Disable Master and SOF interrupts */
19937c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_intr_disable, (HCR_INTR_MIE | HCR_INTR_SOF));
19947c478bd9Sstevel@tonic-gate 
19957c478bd9Sstevel@tonic-gate 		/* Set the Host Controller Functional State to Reset */
19967c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control, ((Get_OpReg(hcr_control) &
19977c478bd9Sstevel@tonic-gate 		    (~HCR_CONTROL_HCFS)) | HCR_CONTROL_RESET));
19987c478bd9Sstevel@tonic-gate 
19997c478bd9Sstevel@tonic-gate 		/* Wait for sometime */
20007c478bd9Sstevel@tonic-gate 		drv_usecwait(OHCI_TIMEWAIT);
20017c478bd9Sstevel@tonic-gate 
2002f806f48bShs 		/*
2003f806f48bShs 		 * Workaround for ULI1575 chipset. Following OHCI Operational
2004f806f48bShs 		 * Memory Registers are not cleared to their default value
2005f806f48bShs 		 * on reset. Explicitly set the registers to default value.
2006f806f48bShs 		 */
2007f806f48bShs 		if (ohcip->ohci_vendor_id == PCI_ULI1575_VENID &&
2008f806f48bShs 				ohcip->ohci_device_id == PCI_ULI1575_DEVID) {
2009f806f48bShs 			Set_OpReg(hcr_control, HCR_CONTROL_DEFAULT);
2010f806f48bShs 			Set_OpReg(hcr_intr_enable, HCR_INT_ENABLE_DEFAULT);
2011f806f48bShs 			Set_OpReg(hcr_HCCA, HCR_HCCA_DEFAULT);
2012f806f48bShs 			Set_OpReg(hcr_ctrl_head, HCR_CONTROL_HEAD_ED_DEFAULT);
2013f806f48bShs 			Set_OpReg(hcr_bulk_head, HCR_BULK_HEAD_ED_DEFAULT);
2014f806f48bShs 			Set_OpReg(hcr_frame_interval,
2015f806f48bShs 					HCR_FRAME_INTERVAL_DEFAULT);
2016f806f48bShs 			Set_OpReg(hcr_periodic_strt,
2017f806f48bShs 					HCR_PERIODIC_START_DEFAULT);
2018f806f48bShs 		}
2019f806f48bShs 
20207c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
20217c478bd9Sstevel@tonic-gate 
2022*9c75c6bfSgovinda 		ohci_rem_intrs(ohcip);
20237c478bd9Sstevel@tonic-gate 	}
20247c478bd9Sstevel@tonic-gate 
20257c478bd9Sstevel@tonic-gate 	/* Unmap the OHCI registers */
20267c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_regs_handle) {
20277c478bd9Sstevel@tonic-gate 		/* Reset the host controller */
20287c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_cmd_status, HCR_STATUS_RESET);
20297c478bd9Sstevel@tonic-gate 
20307c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&ohcip->ohci_regs_handle);
20317c478bd9Sstevel@tonic-gate 	}
20327c478bd9Sstevel@tonic-gate 
20337c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_config_handle) {
20347c478bd9Sstevel@tonic-gate 		pci_config_teardown(&ohcip->ohci_config_handle);
20357c478bd9Sstevel@tonic-gate 	}
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 	/* Free all the buffers */
20387c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_td_pool_addr && ohcip->ohci_td_pool_mem_handle) {
20397c478bd9Sstevel@tonic-gate 		for (i = 0; i < ohci_td_pool_size; i ++) {
20407c478bd9Sstevel@tonic-gate 			td = &ohcip->ohci_td_pool_addr[i];
20417c478bd9Sstevel@tonic-gate 			state = Get_TD(ohcip->ohci_td_pool_addr[i].hctd_state);
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 			if ((state != HC_TD_FREE) && (state != HC_TD_DUMMY) &&
20447c478bd9Sstevel@tonic-gate 			    (td->hctd_trans_wrapper)) {
20457c478bd9Sstevel@tonic-gate 
20467c478bd9Sstevel@tonic-gate 				mutex_enter(&ohcip->ohci_int_mutex);
20477c478bd9Sstevel@tonic-gate 
20487c478bd9Sstevel@tonic-gate 				tw = (ohci_trans_wrapper_t *)
20497c478bd9Sstevel@tonic-gate 					OHCI_LOOKUP_ID((uint32_t)
20507c478bd9Sstevel@tonic-gate 					Get_TD(td->hctd_trans_wrapper));
20517c478bd9Sstevel@tonic-gate 
20527c478bd9Sstevel@tonic-gate 				/* Obtain the pipe private structure */
20537c478bd9Sstevel@tonic-gate 				pp = tw->tw_pipe_private;
20547c478bd9Sstevel@tonic-gate 
20557c478bd9Sstevel@tonic-gate 				/* Stop the the transfer timer */
20567c478bd9Sstevel@tonic-gate 				ohci_stop_xfer_timer(ohcip, tw,
20577c478bd9Sstevel@tonic-gate 						OHCI_REMOVE_XFER_ALWAYS);
20587c478bd9Sstevel@tonic-gate 
20597c478bd9Sstevel@tonic-gate 				ohci_deallocate_tw_resources(ohcip, pp, tw);
20607c478bd9Sstevel@tonic-gate 
20617c478bd9Sstevel@tonic-gate 				mutex_exit(&ohcip->ohci_int_mutex);
20627c478bd9Sstevel@tonic-gate 			}
20637c478bd9Sstevel@tonic-gate 		}
20647c478bd9Sstevel@tonic-gate 
20657c478bd9Sstevel@tonic-gate 		/*
20667c478bd9Sstevel@tonic-gate 		 * If OHCI_TD_POOL_BOUND flag is set, then unbind
20677c478bd9Sstevel@tonic-gate 		 * the handle for TD pools.
20687c478bd9Sstevel@tonic-gate 		 */
20697c478bd9Sstevel@tonic-gate 		if ((ohcip->ohci_dma_addr_bind_flag &
20707c478bd9Sstevel@tonic-gate 		    OHCI_TD_POOL_BOUND) == OHCI_TD_POOL_BOUND) {
20717c478bd9Sstevel@tonic-gate 
20727c478bd9Sstevel@tonic-gate 			rval = ddi_dma_unbind_handle(
20737c478bd9Sstevel@tonic-gate 			    ohcip->ohci_td_pool_dma_handle);
20747c478bd9Sstevel@tonic-gate 
20757c478bd9Sstevel@tonic-gate 			ASSERT(rval == DDI_SUCCESS);
20767c478bd9Sstevel@tonic-gate 		}
20777c478bd9Sstevel@tonic-gate 		ddi_dma_mem_free(&ohcip->ohci_td_pool_mem_handle);
20787c478bd9Sstevel@tonic-gate 	}
20797c478bd9Sstevel@tonic-gate 
20807c478bd9Sstevel@tonic-gate 	/* Free the TD pool */
20817c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_td_pool_dma_handle) {
20827c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&ohcip->ohci_td_pool_dma_handle);
20837c478bd9Sstevel@tonic-gate 	}
20847c478bd9Sstevel@tonic-gate 
20857c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_ed_pool_addr && ohcip->ohci_ed_pool_mem_handle) {
20867c478bd9Sstevel@tonic-gate 		/*
20877c478bd9Sstevel@tonic-gate 		 * If OHCI_ED_POOL_BOUND flag is set, then unbind
20887c478bd9Sstevel@tonic-gate 		 * the handle for ED pools.
20897c478bd9Sstevel@tonic-gate 		 */
20907c478bd9Sstevel@tonic-gate 		if ((ohcip->ohci_dma_addr_bind_flag &
20917c478bd9Sstevel@tonic-gate 		    OHCI_ED_POOL_BOUND) == OHCI_ED_POOL_BOUND) {
20927c478bd9Sstevel@tonic-gate 
20937c478bd9Sstevel@tonic-gate 			rval = ddi_dma_unbind_handle(
20947c478bd9Sstevel@tonic-gate 			    ohcip->ohci_ed_pool_dma_handle);
20957c478bd9Sstevel@tonic-gate 
20967c478bd9Sstevel@tonic-gate 			ASSERT(rval == DDI_SUCCESS);
20977c478bd9Sstevel@tonic-gate 		}
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate 		ddi_dma_mem_free(&ohcip->ohci_ed_pool_mem_handle);
21007c478bd9Sstevel@tonic-gate 	}
21017c478bd9Sstevel@tonic-gate 
21027c478bd9Sstevel@tonic-gate 	/* Free the ED pool */
21037c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_ed_pool_dma_handle) {
21047c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&ohcip->ohci_ed_pool_dma_handle);
21057c478bd9Sstevel@tonic-gate 	}
21067c478bd9Sstevel@tonic-gate 
21077c478bd9Sstevel@tonic-gate 	/* Free the HCCA area */
21087c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_hccap && ohcip->ohci_hcca_mem_handle) {
21097c478bd9Sstevel@tonic-gate 		/*
21107c478bd9Sstevel@tonic-gate 		 * If OHCI_HCCA_DMA_BOUND flag is set, then unbind
21117c478bd9Sstevel@tonic-gate 		 * the handle for HCCA.
21127c478bd9Sstevel@tonic-gate 		 */
21137c478bd9Sstevel@tonic-gate 		if ((ohcip->ohci_dma_addr_bind_flag &
21147c478bd9Sstevel@tonic-gate 		    OHCI_HCCA_DMA_BOUND) == OHCI_HCCA_DMA_BOUND) {
21157c478bd9Sstevel@tonic-gate 
21167c478bd9Sstevel@tonic-gate 			rval = ddi_dma_unbind_handle(
21177c478bd9Sstevel@tonic-gate 			    ohcip->ohci_hcca_dma_handle);
21187c478bd9Sstevel@tonic-gate 
21197c478bd9Sstevel@tonic-gate 			ASSERT(rval == DDI_SUCCESS);
21207c478bd9Sstevel@tonic-gate 		}
21217c478bd9Sstevel@tonic-gate 
21227c478bd9Sstevel@tonic-gate 		ddi_dma_mem_free(&ohcip->ohci_hcca_mem_handle);
21237c478bd9Sstevel@tonic-gate 	}
21247c478bd9Sstevel@tonic-gate 
21257c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_hcca_dma_handle) {
21267c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&ohcip->ohci_hcca_dma_handle);
21277c478bd9Sstevel@tonic-gate 	}
21287c478bd9Sstevel@tonic-gate 
21297c478bd9Sstevel@tonic-gate 	if (flags & OHCI_INTR) {
21307c478bd9Sstevel@tonic-gate 
21317c478bd9Sstevel@tonic-gate 		/* Destroy the mutex */
21327c478bd9Sstevel@tonic-gate 		mutex_destroy(&ohcip->ohci_int_mutex);
21337c478bd9Sstevel@tonic-gate 
21347c478bd9Sstevel@tonic-gate 		/* Destroy the SOF condition varibale */
21357c478bd9Sstevel@tonic-gate 		cv_destroy(&ohcip->ohci_SOF_cv);
21367c478bd9Sstevel@tonic-gate 
21377c478bd9Sstevel@tonic-gate 		/* Destroy the serialize opens and closes semaphore */
21387c478bd9Sstevel@tonic-gate 		sema_destroy(&ohcip->ohci_ocsem);
21397c478bd9Sstevel@tonic-gate 	}
21407c478bd9Sstevel@tonic-gate 
21417c478bd9Sstevel@tonic-gate 	/* clean up kstat structs */
21427c478bd9Sstevel@tonic-gate 	ohci_destroy_stats(ohcip);
21437c478bd9Sstevel@tonic-gate 
21447c478bd9Sstevel@tonic-gate 	/* Free ohci hcdi ops */
21457c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_hcdi_ops) {
21467c478bd9Sstevel@tonic-gate 		usba_free_hcdi_ops(ohcip->ohci_hcdi_ops);
21477c478bd9Sstevel@tonic-gate 	}
21487c478bd9Sstevel@tonic-gate 
21497c478bd9Sstevel@tonic-gate 	if (flags & OHCI_ZALLOC) {
21507c478bd9Sstevel@tonic-gate 
21517c478bd9Sstevel@tonic-gate 		usb_free_log_hdl(ohcip->ohci_log_hdl);
21527c478bd9Sstevel@tonic-gate 
21537c478bd9Sstevel@tonic-gate 		/* Remove all properties that might have been created */
21547c478bd9Sstevel@tonic-gate 		ddi_prop_remove_all(ohcip->ohci_dip);
21557c478bd9Sstevel@tonic-gate 
21567c478bd9Sstevel@tonic-gate 		/* Free the soft state */
21577c478bd9Sstevel@tonic-gate 		ddi_soft_state_free(ohci_statep,
21587c478bd9Sstevel@tonic-gate 			ddi_get_instance(ohcip->ohci_dip));
21597c478bd9Sstevel@tonic-gate 	}
21607c478bd9Sstevel@tonic-gate 
21617c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
21627c478bd9Sstevel@tonic-gate }
21637c478bd9Sstevel@tonic-gate 
21647c478bd9Sstevel@tonic-gate 
2165*9c75c6bfSgovinda /*
2166*9c75c6bfSgovinda  * ohci_rem_intrs:
2167*9c75c6bfSgovinda  *
2168*9c75c6bfSgovinda  * Unregister FIXED or MSI interrupts
2169*9c75c6bfSgovinda  */
2170*9c75c6bfSgovinda static void
2171*9c75c6bfSgovinda ohci_rem_intrs(ohci_state_t	*ohcip)
2172*9c75c6bfSgovinda {
2173*9c75c6bfSgovinda 	int	i;
2174*9c75c6bfSgovinda 
2175*9c75c6bfSgovinda 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
2176*9c75c6bfSgovinda 	    "ohci_rem_intrs: interrupt type 0x%x", ohcip->ohci_intr_type);
2177*9c75c6bfSgovinda 
2178*9c75c6bfSgovinda 	/* Disable all interrupts */
2179*9c75c6bfSgovinda 	if (ohcip->ohci_intr_cap & DDI_INTR_FLAG_BLOCK) {
2180*9c75c6bfSgovinda 		(void) ddi_intr_block_disable(ohcip->ohci_htable,
2181*9c75c6bfSgovinda 		    ohcip->ohci_intr_cnt);
2182*9c75c6bfSgovinda 	} else {
2183*9c75c6bfSgovinda 		for (i = 0; i < ohcip->ohci_intr_cnt; i++) {
2184*9c75c6bfSgovinda 			(void) ddi_intr_disable(ohcip->ohci_htable[i]);
2185*9c75c6bfSgovinda 		}
2186*9c75c6bfSgovinda 	}
2187*9c75c6bfSgovinda 
2188*9c75c6bfSgovinda 	/* Call ddi_intr_remove_handler() */
2189*9c75c6bfSgovinda 	for (i = 0; i < ohcip->ohci_intr_cnt; i++) {
2190*9c75c6bfSgovinda 		(void) ddi_intr_remove_handler(ohcip->ohci_htable[i]);
2191*9c75c6bfSgovinda 		(void) ddi_intr_free(ohcip->ohci_htable[i]);
2192*9c75c6bfSgovinda 	}
2193*9c75c6bfSgovinda 
2194*9c75c6bfSgovinda 	kmem_free(ohcip->ohci_htable,
2195*9c75c6bfSgovinda 	    ohcip->ohci_intr_cnt * sizeof (ddi_intr_handle_t));
2196*9c75c6bfSgovinda }
2197*9c75c6bfSgovinda 
2198*9c75c6bfSgovinda 
21997c478bd9Sstevel@tonic-gate /*
22007c478bd9Sstevel@tonic-gate  * ohci_cpr_suspend
22017c478bd9Sstevel@tonic-gate  */
22027c478bd9Sstevel@tonic-gate static int
22037c478bd9Sstevel@tonic-gate ohci_cpr_suspend(ohci_state_t	*ohcip)
22047c478bd9Sstevel@tonic-gate {
22057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
22067c478bd9Sstevel@tonic-gate 	    "ohci_cpr_suspend:");
22077c478bd9Sstevel@tonic-gate 
22087c478bd9Sstevel@tonic-gate 	/* Call into the root hub and suspend it */
22097c478bd9Sstevel@tonic-gate 	if (usba_hubdi_detach(ohcip->ohci_dip, DDI_SUSPEND) != DDI_SUCCESS) {
22107c478bd9Sstevel@tonic-gate 
22117c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
22127c478bd9Sstevel@tonic-gate 	}
22137c478bd9Sstevel@tonic-gate 
22147c478bd9Sstevel@tonic-gate 	/* Only root hub's intr pipe should be open at this time */
22157c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
22167c478bd9Sstevel@tonic-gate 
22177c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_open_pipe_count > 1) {
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
22207c478bd9Sstevel@tonic-gate 		    "ohci_cpr_suspend: fails as open pipe count = %d",
22217c478bd9Sstevel@tonic-gate 		    ohcip->ohci_open_pipe_count);
22227c478bd9Sstevel@tonic-gate 
22237c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
22247c478bd9Sstevel@tonic-gate 
22257c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
22267c478bd9Sstevel@tonic-gate 	}
22277c478bd9Sstevel@tonic-gate 
22287c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
22297c478bd9Sstevel@tonic-gate 	    "ohci_cpr_suspend: Disable HC ED list processing");
22307c478bd9Sstevel@tonic-gate 
22317c478bd9Sstevel@tonic-gate 	/* Disable all HC ED list processing */
22327c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_control, (Get_OpReg(hcr_control) & ~(HCR_CONTROL_CLE |
22337c478bd9Sstevel@tonic-gate 	    HCR_CONTROL_BLE | HCR_CONTROL_PLE | HCR_CONTROL_IE)));
22347c478bd9Sstevel@tonic-gate 
22357c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
22367c478bd9Sstevel@tonic-gate 	    "ohci_cpr_suspend: Disable HC interrupts");
22377c478bd9Sstevel@tonic-gate 
22387c478bd9Sstevel@tonic-gate 	/* Disable all HC interrupts */
22397c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_disable, ~(HCR_INTR_MIE|HCR_INTR_SOF));
22407c478bd9Sstevel@tonic-gate 
22417c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
22427c478bd9Sstevel@tonic-gate 	    "ohci_cpr_suspend: Wait for the next SOF");
22437c478bd9Sstevel@tonic-gate 
22447c478bd9Sstevel@tonic-gate 	/* Wait for the next SOF */
22457c478bd9Sstevel@tonic-gate 	if (ohci_wait_for_sof(ohcip) != USB_SUCCESS) {
22467c478bd9Sstevel@tonic-gate 
22477c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
22487c478bd9Sstevel@tonic-gate 		    "ohci_cpr_suspend: ohci host controller suspend failed");
22497c478bd9Sstevel@tonic-gate 
22507c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
22517c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
22527c478bd9Sstevel@tonic-gate 	}
22537c478bd9Sstevel@tonic-gate 
22547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
22557c478bd9Sstevel@tonic-gate 	    "ohci_cpr_suspend: Disable Master interrupt");
22567c478bd9Sstevel@tonic-gate 
22577c478bd9Sstevel@tonic-gate 	/*
22587c478bd9Sstevel@tonic-gate 	 * Disable Master interrupt so that ohci driver don't
22597c478bd9Sstevel@tonic-gate 	 * get any ohci interrupts.
22607c478bd9Sstevel@tonic-gate 	 */
22617c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_disable, HCR_INTR_MIE);
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate 	/*
22647c478bd9Sstevel@tonic-gate 	 * Suspend the ohci host controller
22657c478bd9Sstevel@tonic-gate 	 * if usb keyboard is not connected.
22667c478bd9Sstevel@tonic-gate 	 */
22677c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_polled_kbd_count == 0) {
22687c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control, HCR_CONTROL_SUSPD);
22697c478bd9Sstevel@tonic-gate 	}
22707c478bd9Sstevel@tonic-gate 
22717c478bd9Sstevel@tonic-gate 	/* Set host controller soft state to suspend */
22727c478bd9Sstevel@tonic-gate 	ohcip->ohci_hc_soft_state = OHCI_CTLR_SUSPEND_STATE;
22737c478bd9Sstevel@tonic-gate 
22747c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
22757c478bd9Sstevel@tonic-gate 
22767c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
22777c478bd9Sstevel@tonic-gate }
22787c478bd9Sstevel@tonic-gate 
22797c478bd9Sstevel@tonic-gate 
22807c478bd9Sstevel@tonic-gate /*
22817c478bd9Sstevel@tonic-gate  * ohci_cpr_resume
22827c478bd9Sstevel@tonic-gate  */
22837c478bd9Sstevel@tonic-gate static int
22847c478bd9Sstevel@tonic-gate ohci_cpr_resume(ohci_state_t	*ohcip)
22857c478bd9Sstevel@tonic-gate {
22867c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
22877c478bd9Sstevel@tonic-gate 
22887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
22897c478bd9Sstevel@tonic-gate 	    "ohci_cpr_resume: Restart the controller");
22907c478bd9Sstevel@tonic-gate 
22917c478bd9Sstevel@tonic-gate 	/* Cleanup ohci specific information across cpr */
22927c478bd9Sstevel@tonic-gate 	ohci_cpr_cleanup(ohcip);
22937c478bd9Sstevel@tonic-gate 
22947c478bd9Sstevel@tonic-gate 	/* Restart the controller */
22957c478bd9Sstevel@tonic-gate 	if (ohci_init_ctlr(ohcip) != DDI_SUCCESS) {
22967c478bd9Sstevel@tonic-gate 
22977c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
22987c478bd9Sstevel@tonic-gate 		    "ohci_cpr_resume: ohci host controller resume failed ");
22997c478bd9Sstevel@tonic-gate 
23007c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
23017c478bd9Sstevel@tonic-gate 
23027c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
23037c478bd9Sstevel@tonic-gate 	}
23047c478bd9Sstevel@tonic-gate 
23057c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
23067c478bd9Sstevel@tonic-gate 
23077c478bd9Sstevel@tonic-gate 	/* Now resume the root hub */
23087c478bd9Sstevel@tonic-gate 	if (usba_hubdi_attach(ohcip->ohci_dip, DDI_RESUME) != DDI_SUCCESS) {
23097c478bd9Sstevel@tonic-gate 
23107c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
23117c478bd9Sstevel@tonic-gate 	}
23127c478bd9Sstevel@tonic-gate 
23137c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
23147c478bd9Sstevel@tonic-gate }
23157c478bd9Sstevel@tonic-gate 
23167c478bd9Sstevel@tonic-gate 
23177c478bd9Sstevel@tonic-gate /*
23187c478bd9Sstevel@tonic-gate  * HCDI entry points
23197c478bd9Sstevel@tonic-gate  *
23207c478bd9Sstevel@tonic-gate  * The Host Controller Driver Interfaces (HCDI) are the software interfaces
23217c478bd9Sstevel@tonic-gate  * between the Universal Serial Bus Layer (USBA) and the Host Controller
23227c478bd9Sstevel@tonic-gate  * Driver (HCD). The HCDI interfaces or entry points are subject to change.
23237c478bd9Sstevel@tonic-gate  */
23247c478bd9Sstevel@tonic-gate 
23257c478bd9Sstevel@tonic-gate /*
23267c478bd9Sstevel@tonic-gate  * ohci_hcdi_pipe_open:
23277c478bd9Sstevel@tonic-gate  *
23287c478bd9Sstevel@tonic-gate  * Member of HCD Ops structure and called during client specific pipe open
23297c478bd9Sstevel@tonic-gate  * Add the pipe to the data structure representing the device and allocate
23307c478bd9Sstevel@tonic-gate  * bandwidth for the pipe if it is a interrupt or isochronous endpoint.
23317c478bd9Sstevel@tonic-gate  */
23327c478bd9Sstevel@tonic-gate static int
23337c478bd9Sstevel@tonic-gate ohci_hcdi_pipe_open(
23347c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
23357c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
23367c478bd9Sstevel@tonic-gate {
23377c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
23387c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
23397c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*epdt = &ph->p_ep;
23407c478bd9Sstevel@tonic-gate 	int			rval, error = USB_SUCCESS;
23417c478bd9Sstevel@tonic-gate 	int			kmflag = (flags & USB_FLAGS_SLEEP) ?
23427c478bd9Sstevel@tonic-gate 				KM_SLEEP : KM_NOSLEEP;
23437c478bd9Sstevel@tonic-gate 	uint_t			node = 0;
23447c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;
23457c478bd9Sstevel@tonic-gate 
23467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
23477c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_open: addr = 0x%x, ep%d",
23487c478bd9Sstevel@tonic-gate 	    ph->p_usba_device->usb_addr,
23497c478bd9Sstevel@tonic-gate 	    epdt->bEndpointAddress & USB_EP_NUM_MASK);
23507c478bd9Sstevel@tonic-gate 
23517c478bd9Sstevel@tonic-gate 	sema_p(&ohcip->ohci_ocsem);
23527c478bd9Sstevel@tonic-gate 
23537c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
23547c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
23557c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
23567c478bd9Sstevel@tonic-gate 
23577c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
23587c478bd9Sstevel@tonic-gate 		sema_v(&ohcip->ohci_ocsem);
23597c478bd9Sstevel@tonic-gate 
23607c478bd9Sstevel@tonic-gate 		return (rval);
23617c478bd9Sstevel@tonic-gate 	}
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 	/*
23647c478bd9Sstevel@tonic-gate 	 * Check and handle root hub pipe open.
23657c478bd9Sstevel@tonic-gate 	 */
23667c478bd9Sstevel@tonic-gate 	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
23677c478bd9Sstevel@tonic-gate 
23687c478bd9Sstevel@tonic-gate 		mutex_enter(&ohcip->ohci_int_mutex);
23697c478bd9Sstevel@tonic-gate 		error = ohci_handle_root_hub_pipe_open(ph, flags);
23707c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
23717c478bd9Sstevel@tonic-gate 		sema_v(&ohcip->ohci_ocsem);
23727c478bd9Sstevel@tonic-gate 
23737c478bd9Sstevel@tonic-gate 		return (error);
23747c478bd9Sstevel@tonic-gate 	}
23757c478bd9Sstevel@tonic-gate 
23767c478bd9Sstevel@tonic-gate 	/*
23777c478bd9Sstevel@tonic-gate 	 * Opening of other pipes excluding root hub pipe are
23787c478bd9Sstevel@tonic-gate 	 * handled below. Check whether pipe is already opened.
23797c478bd9Sstevel@tonic-gate 	 */
23807c478bd9Sstevel@tonic-gate 	if (ph->p_hcd_private) {
23817c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
23827c478bd9Sstevel@tonic-gate 		    "ohci_hcdi_pipe_open: Pipe is already opened");
23837c478bd9Sstevel@tonic-gate 
23847c478bd9Sstevel@tonic-gate 		sema_v(&ohcip->ohci_ocsem);
23857c478bd9Sstevel@tonic-gate 
23867c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
23877c478bd9Sstevel@tonic-gate 	}
23887c478bd9Sstevel@tonic-gate 
23897c478bd9Sstevel@tonic-gate 	/*
23907c478bd9Sstevel@tonic-gate 	 * A portion of the bandwidth is reserved for the non-periodic
23917c478bd9Sstevel@tonic-gate 	 * transfers, i.e control and bulk transfers in each of one
23927c478bd9Sstevel@tonic-gate 	 * millisecond frame period & usually it will be 10% of frame
23937c478bd9Sstevel@tonic-gate 	 * period. Hence there is no need to check for the available
23947c478bd9Sstevel@tonic-gate 	 * bandwidth before adding the control or bulk endpoints.
23957c478bd9Sstevel@tonic-gate 	 *
23967c478bd9Sstevel@tonic-gate 	 * There is a need to check for the available bandwidth before
23977c478bd9Sstevel@tonic-gate 	 * adding the periodic transfers, i.e interrupt & isochronous,
23987c478bd9Sstevel@tonic-gate 	 * since all these periodic transfers are guaranteed transfers.
23997c478bd9Sstevel@tonic-gate 	 * Usually 90% of the total frame time is reserved for periodic
24007c478bd9Sstevel@tonic-gate 	 * transfers.
24017c478bd9Sstevel@tonic-gate 	 */
24027c478bd9Sstevel@tonic-gate 	if (OHCI_PERIODIC_ENDPOINT(epdt)) {
24037c478bd9Sstevel@tonic-gate 
24047c478bd9Sstevel@tonic-gate 		mutex_enter(&ohcip->ohci_int_mutex);
24057c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
24067c478bd9Sstevel@tonic-gate 
24077c478bd9Sstevel@tonic-gate 		error = ohci_allocate_bandwidth(ohcip, ph, &node);
24087c478bd9Sstevel@tonic-gate 
24097c478bd9Sstevel@tonic-gate 		if (error != USB_SUCCESS) {
24107c478bd9Sstevel@tonic-gate 
24117c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
24127c478bd9Sstevel@tonic-gate 			    "ohci_hcdi_pipe_open: Bandwidth allocation failed");
24137c478bd9Sstevel@tonic-gate 
24147c478bd9Sstevel@tonic-gate 			mutex_exit(&ph->p_mutex);
24157c478bd9Sstevel@tonic-gate 			mutex_exit(&ohcip->ohci_int_mutex);
24167c478bd9Sstevel@tonic-gate 			sema_v(&ohcip->ohci_ocsem);
24177c478bd9Sstevel@tonic-gate 
24187c478bd9Sstevel@tonic-gate 			return (error);
24197c478bd9Sstevel@tonic-gate 		}
24207c478bd9Sstevel@tonic-gate 
24217c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
24227c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
24237c478bd9Sstevel@tonic-gate 	}
24247c478bd9Sstevel@tonic-gate 
24257c478bd9Sstevel@tonic-gate 	/* Create the HCD pipe private structure */
24267c478bd9Sstevel@tonic-gate 	pp = kmem_zalloc(sizeof (ohci_pipe_private_t), kmflag);
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate 	/*
24297c478bd9Sstevel@tonic-gate 	 * Return failure if ohci pipe private
24307c478bd9Sstevel@tonic-gate 	 * structure allocation fails.
24317c478bd9Sstevel@tonic-gate 	 */
24327c478bd9Sstevel@tonic-gate 	if (pp == NULL) {
24337c478bd9Sstevel@tonic-gate 
24347c478bd9Sstevel@tonic-gate 		mutex_enter(&ohcip->ohci_int_mutex);
24357c478bd9Sstevel@tonic-gate 
24367c478bd9Sstevel@tonic-gate 		/* Deallocate bandwidth */
24377c478bd9Sstevel@tonic-gate 		if (OHCI_PERIODIC_ENDPOINT(epdt)) {
24387c478bd9Sstevel@tonic-gate 
24397c478bd9Sstevel@tonic-gate 			mutex_enter(&ph->p_mutex);
24407c478bd9Sstevel@tonic-gate 			ohci_deallocate_bandwidth(ohcip, ph);
24417c478bd9Sstevel@tonic-gate 			mutex_exit(&ph->p_mutex);
24427c478bd9Sstevel@tonic-gate 		}
24437c478bd9Sstevel@tonic-gate 
24447c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
24457c478bd9Sstevel@tonic-gate 		sema_v(&ohcip->ohci_ocsem);
24467c478bd9Sstevel@tonic-gate 
24477c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
24487c478bd9Sstevel@tonic-gate 	}
24497c478bd9Sstevel@tonic-gate 
24507c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
24517c478bd9Sstevel@tonic-gate 
24527c478bd9Sstevel@tonic-gate 	/* Store the node in the interrupt lattice */
24537c478bd9Sstevel@tonic-gate 	pp->pp_node = node;
24547c478bd9Sstevel@tonic-gate 
24557c478bd9Sstevel@tonic-gate 	/* Create prototype for xfer completion condition variable */
24567c478bd9Sstevel@tonic-gate 	cv_init(&pp->pp_xfer_cmpl_cv, NULL, CV_DRIVER, NULL);
24577c478bd9Sstevel@tonic-gate 
24587c478bd9Sstevel@tonic-gate 	/* Set the state of pipe as idle */
24597c478bd9Sstevel@tonic-gate 	pp->pp_state = OHCI_PIPE_STATE_IDLE;
24607c478bd9Sstevel@tonic-gate 
24617c478bd9Sstevel@tonic-gate 	/* Store a pointer to the pipe handle */
24627c478bd9Sstevel@tonic-gate 	pp->pp_pipe_handle = ph;
24637c478bd9Sstevel@tonic-gate 
24647c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
24657c478bd9Sstevel@tonic-gate 
24667c478bd9Sstevel@tonic-gate 	/* Store the pointer in the pipe handle */
24677c478bd9Sstevel@tonic-gate 	ph->p_hcd_private = (usb_opaque_t)pp;
24687c478bd9Sstevel@tonic-gate 
24697c478bd9Sstevel@tonic-gate 	/* Store a copy of the pipe policy */
24707c478bd9Sstevel@tonic-gate 	bcopy(&ph->p_policy, &pp->pp_policy, sizeof (usb_pipe_policy_t));
24717c478bd9Sstevel@tonic-gate 
24727c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
24737c478bd9Sstevel@tonic-gate 
24747c478bd9Sstevel@tonic-gate 	/* Allocate the host controller endpoint descriptor */
24757c478bd9Sstevel@tonic-gate 	pp->pp_ept = ohci_alloc_hc_ed(ohcip, ph);
24767c478bd9Sstevel@tonic-gate 
24777c478bd9Sstevel@tonic-gate 	if (pp->pp_ept == NULL) {
24787c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
24797c478bd9Sstevel@tonic-gate 		    "ohci_hcdi_pipe_open: ED allocation failed");
24807c478bd9Sstevel@tonic-gate 
24817c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
24827c478bd9Sstevel@tonic-gate 
24837c478bd9Sstevel@tonic-gate 		/* Deallocate bandwidth */
24847c478bd9Sstevel@tonic-gate 		if (OHCI_PERIODIC_ENDPOINT(epdt)) {
24857c478bd9Sstevel@tonic-gate 
24867c478bd9Sstevel@tonic-gate 			ohci_deallocate_bandwidth(ohcip, ph);
24877c478bd9Sstevel@tonic-gate 		}
24887c478bd9Sstevel@tonic-gate 
24897c478bd9Sstevel@tonic-gate 		/* Destroy the xfer completion condition varibale */
24907c478bd9Sstevel@tonic-gate 		cv_destroy(&pp->pp_xfer_cmpl_cv);
24917c478bd9Sstevel@tonic-gate 
24927c478bd9Sstevel@tonic-gate 		/*
24937c478bd9Sstevel@tonic-gate 		 * Deallocate the hcd private portion
24947c478bd9Sstevel@tonic-gate 		 * of the pipe handle.
24957c478bd9Sstevel@tonic-gate 		 */
24967c478bd9Sstevel@tonic-gate 		kmem_free(ph->p_hcd_private, sizeof (ohci_pipe_private_t));
24977c478bd9Sstevel@tonic-gate 
24987c478bd9Sstevel@tonic-gate 		/*
24997c478bd9Sstevel@tonic-gate 		 * Set the private structure in the
25007c478bd9Sstevel@tonic-gate 		 * pipe handle equal to NULL.
25017c478bd9Sstevel@tonic-gate 		 */
25027c478bd9Sstevel@tonic-gate 		ph->p_hcd_private = NULL;
25037c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
25067c478bd9Sstevel@tonic-gate 		sema_v(&ohcip->ohci_ocsem);
25077c478bd9Sstevel@tonic-gate 
25087c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
25097c478bd9Sstevel@tonic-gate 	}
25107c478bd9Sstevel@tonic-gate 
25117c478bd9Sstevel@tonic-gate 	/* Restore the data toggle information */
25127c478bd9Sstevel@tonic-gate 	ohci_restore_data_toggle(ohcip, ph);
25137c478bd9Sstevel@tonic-gate 
25147c478bd9Sstevel@tonic-gate 	/*
25157c478bd9Sstevel@tonic-gate 	 * Insert the endpoint onto the host controller's
25167c478bd9Sstevel@tonic-gate 	 * appropriate endpoint list. The host controller
25177c478bd9Sstevel@tonic-gate 	 * will not schedule this endpoint and will not have
25187c478bd9Sstevel@tonic-gate 	 * any TD's to process.
25197c478bd9Sstevel@tonic-gate 	 */
25207c478bd9Sstevel@tonic-gate 	ohci_insert_ed(ohcip, ph);
25217c478bd9Sstevel@tonic-gate 
25227c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
25237c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_open: ph = 0x%p", (void *)ph);
25247c478bd9Sstevel@tonic-gate 
25257c478bd9Sstevel@tonic-gate 	ohcip->ohci_open_pipe_count++;
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
25287c478bd9Sstevel@tonic-gate 
25297c478bd9Sstevel@tonic-gate 	sema_v(&ohcip->ohci_ocsem);
25307c478bd9Sstevel@tonic-gate 
25317c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
25327c478bd9Sstevel@tonic-gate }
25337c478bd9Sstevel@tonic-gate 
25347c478bd9Sstevel@tonic-gate 
25357c478bd9Sstevel@tonic-gate /*
25367c478bd9Sstevel@tonic-gate  * ohci_hcdi_pipe_close:
25377c478bd9Sstevel@tonic-gate  *
25387c478bd9Sstevel@tonic-gate  * Member of HCD Ops structure and called during the client  specific pipe
25397c478bd9Sstevel@tonic-gate  * close. Remove the pipe and the data structure representing the device.
25407c478bd9Sstevel@tonic-gate  * Deallocate  bandwidth for the pipe if it is a interrupt or isochronous
25417c478bd9Sstevel@tonic-gate  * endpoint.
25427c478bd9Sstevel@tonic-gate  */
25437c478bd9Sstevel@tonic-gate /* ARGSUSED */
25447c478bd9Sstevel@tonic-gate static int
25457c478bd9Sstevel@tonic-gate ohci_hcdi_pipe_close(
25467c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
25477c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
25487c478bd9Sstevel@tonic-gate {
25497c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
25507c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
25517c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
25527c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
25537c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
25547c478bd9Sstevel@tonic-gate 
25557c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
25567c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_close: addr = 0x%x, ep%d",
25577c478bd9Sstevel@tonic-gate 	    ph->p_usba_device->usb_addr,
25587c478bd9Sstevel@tonic-gate 	    eptd->bEndpointAddress & USB_EP_NUM_MASK);
25597c478bd9Sstevel@tonic-gate 
25607c478bd9Sstevel@tonic-gate 	sema_p(&ohcip->ohci_ocsem);
25617c478bd9Sstevel@tonic-gate 
25627c478bd9Sstevel@tonic-gate 	/* Check and handle root hub pipe close */
25637c478bd9Sstevel@tonic-gate 	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
25647c478bd9Sstevel@tonic-gate 
25657c478bd9Sstevel@tonic-gate 		mutex_enter(&ohcip->ohci_int_mutex);
25667c478bd9Sstevel@tonic-gate 		error = ohci_handle_root_hub_pipe_close(ph);
25677c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
25687c478bd9Sstevel@tonic-gate 		sema_v(&ohcip->ohci_ocsem);
25697c478bd9Sstevel@tonic-gate 
25707c478bd9Sstevel@tonic-gate 		return (error);
25717c478bd9Sstevel@tonic-gate 	}
25727c478bd9Sstevel@tonic-gate 
25737c478bd9Sstevel@tonic-gate 	ASSERT(ph->p_hcd_private != NULL);
25747c478bd9Sstevel@tonic-gate 
25757c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
25767c478bd9Sstevel@tonic-gate 
25777c478bd9Sstevel@tonic-gate 	/* Set pipe state to pipe close */
25787c478bd9Sstevel@tonic-gate 	pp->pp_state = OHCI_PIPE_STATE_CLOSE;
25797c478bd9Sstevel@tonic-gate 
25807c478bd9Sstevel@tonic-gate 	ohci_pipe_cleanup(ohcip, ph);
25817c478bd9Sstevel@tonic-gate 
25827c478bd9Sstevel@tonic-gate 	/*
25837c478bd9Sstevel@tonic-gate 	 * Remove the endoint descriptor from Host
25847c478bd9Sstevel@tonic-gate 	 * Controller's appropriate endpoint list.
25857c478bd9Sstevel@tonic-gate 	 */
25867c478bd9Sstevel@tonic-gate 	ohci_remove_ed(ohcip, pp);
25877c478bd9Sstevel@tonic-gate 
25887c478bd9Sstevel@tonic-gate 	/* Deallocate bandwidth */
25897c478bd9Sstevel@tonic-gate 	if (OHCI_PERIODIC_ENDPOINT(eptd)) {
25907c478bd9Sstevel@tonic-gate 
25917c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
25927c478bd9Sstevel@tonic-gate 		ohci_deallocate_bandwidth(ohcip, ph);
25937c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
25947c478bd9Sstevel@tonic-gate 	}
25957c478bd9Sstevel@tonic-gate 
25967c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
25977c478bd9Sstevel@tonic-gate 
25987c478bd9Sstevel@tonic-gate 	/* Destroy the xfer completion condition varibale */
25997c478bd9Sstevel@tonic-gate 	cv_destroy(&pp->pp_xfer_cmpl_cv);
26007c478bd9Sstevel@tonic-gate 
26017c478bd9Sstevel@tonic-gate 	/*
26027c478bd9Sstevel@tonic-gate 	 * Deallocate the hcd private portion
26037c478bd9Sstevel@tonic-gate 	 * of the pipe handle.
26047c478bd9Sstevel@tonic-gate 	 */
26057c478bd9Sstevel@tonic-gate 	kmem_free(ph->p_hcd_private, sizeof (ohci_pipe_private_t));
26067c478bd9Sstevel@tonic-gate 	ph->p_hcd_private = NULL;
26077c478bd9Sstevel@tonic-gate 
26087c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
26097c478bd9Sstevel@tonic-gate 
26107c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
26117c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_close: ph = 0x%p", (void *)ph);
26127c478bd9Sstevel@tonic-gate 
26137c478bd9Sstevel@tonic-gate 	ohcip->ohci_open_pipe_count--;
26147c478bd9Sstevel@tonic-gate 
26157c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
26167c478bd9Sstevel@tonic-gate 	sema_v(&ohcip->ohci_ocsem);
26177c478bd9Sstevel@tonic-gate 
26187c478bd9Sstevel@tonic-gate 	return (error);
26197c478bd9Sstevel@tonic-gate }
26207c478bd9Sstevel@tonic-gate 
26217c478bd9Sstevel@tonic-gate 
26227c478bd9Sstevel@tonic-gate /*
26237c478bd9Sstevel@tonic-gate  * ohci_hcdi_pipe_reset:
26247c478bd9Sstevel@tonic-gate  */
26257c478bd9Sstevel@tonic-gate /* ARGSUSED */
26267c478bd9Sstevel@tonic-gate static int
26277c478bd9Sstevel@tonic-gate ohci_hcdi_pipe_reset(
26287c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
26297c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
26307c478bd9Sstevel@tonic-gate {
26317c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
26327c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
26337c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
26347c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
26357c478bd9Sstevel@tonic-gate 
26367c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
26377c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_reset: ph = 0x%p ", (void *)ph);
26387c478bd9Sstevel@tonic-gate 
26397c478bd9Sstevel@tonic-gate 	/*
26407c478bd9Sstevel@tonic-gate 	 * Check and handle root hub pipe reset.
26417c478bd9Sstevel@tonic-gate 	 */
26427c478bd9Sstevel@tonic-gate 	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
26437c478bd9Sstevel@tonic-gate 
26447c478bd9Sstevel@tonic-gate 		error = ohci_handle_root_hub_pipe_reset(ph, usb_flags);
26457c478bd9Sstevel@tonic-gate 		return (error);
26467c478bd9Sstevel@tonic-gate 	}
26477c478bd9Sstevel@tonic-gate 
26487c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
26497c478bd9Sstevel@tonic-gate 
26507c478bd9Sstevel@tonic-gate 	/* Set pipe state to pipe reset */
26517c478bd9Sstevel@tonic-gate 	pp->pp_state = OHCI_PIPE_STATE_RESET;
26527c478bd9Sstevel@tonic-gate 
26537c478bd9Sstevel@tonic-gate 	ohci_pipe_cleanup(ohcip, ph);
26547c478bd9Sstevel@tonic-gate 
26557c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
26567c478bd9Sstevel@tonic-gate 
26577c478bd9Sstevel@tonic-gate 	return (error);
26587c478bd9Sstevel@tonic-gate }
26597c478bd9Sstevel@tonic-gate 
26607c478bd9Sstevel@tonic-gate 
26617c478bd9Sstevel@tonic-gate /*
26627c478bd9Sstevel@tonic-gate  * ohci_hcdi_pipe_ctrl_xfer:
26637c478bd9Sstevel@tonic-gate  */
26647c478bd9Sstevel@tonic-gate static int
26657c478bd9Sstevel@tonic-gate ohci_hcdi_pipe_ctrl_xfer(
26667c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
26677c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp,
26687c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
26697c478bd9Sstevel@tonic-gate {
26707c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
26717c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
26727c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
26737c478bd9Sstevel@tonic-gate 	int			rval;
26747c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
26757c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
26767c478bd9Sstevel@tonic-gate 
26777c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
26787c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_ctrl_xfer: ph = 0x%p reqp = 0x%p flags = 0x%x",
26797c478bd9Sstevel@tonic-gate 	    (void *)ph, ctrl_reqp, usb_flags);
26807c478bd9Sstevel@tonic-gate 
26817c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
26827c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
26837c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
26847c478bd9Sstevel@tonic-gate 
26857c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
26867c478bd9Sstevel@tonic-gate 
26877c478bd9Sstevel@tonic-gate 		return (rval);
26887c478bd9Sstevel@tonic-gate 	}
26897c478bd9Sstevel@tonic-gate 
26907c478bd9Sstevel@tonic-gate 	/*
26917c478bd9Sstevel@tonic-gate 	 * Check and handle root hub control request.
26927c478bd9Sstevel@tonic-gate 	 */
26937c478bd9Sstevel@tonic-gate 	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
26947c478bd9Sstevel@tonic-gate 
26957c478bd9Sstevel@tonic-gate 		error = ohci_handle_root_hub_request(ohcip, ph, ctrl_reqp);
26967c478bd9Sstevel@tonic-gate 
26977c478bd9Sstevel@tonic-gate 		return (error);
26987c478bd9Sstevel@tonic-gate 	}
26997c478bd9Sstevel@tonic-gate 
27007c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
27017c478bd9Sstevel@tonic-gate 
27027c478bd9Sstevel@tonic-gate 	/*
27037c478bd9Sstevel@tonic-gate 	 *  Check whether pipe is in halted state.
27047c478bd9Sstevel@tonic-gate 	 */
27057c478bd9Sstevel@tonic-gate 	if (pp->pp_state == OHCI_PIPE_STATE_ERROR) {
27067c478bd9Sstevel@tonic-gate 
27077c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
27087c478bd9Sstevel@tonic-gate 		    "ohci_hcdi_pipe_ctrl_xfer:"
27097c478bd9Sstevel@tonic-gate 		    "Pipe is in error state, need pipe reset to continue");
27107c478bd9Sstevel@tonic-gate 
27117c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
27127c478bd9Sstevel@tonic-gate 
27137c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
27147c478bd9Sstevel@tonic-gate 	}
27157c478bd9Sstevel@tonic-gate 
27167c478bd9Sstevel@tonic-gate 	/* Allocate a transfer wrapper */
27177c478bd9Sstevel@tonic-gate 	if ((tw = ohci_allocate_ctrl_resources(ohcip, pp, ctrl_reqp,
27187c478bd9Sstevel@tonic-gate 	    usb_flags)) == NULL) {
27197c478bd9Sstevel@tonic-gate 
27207c478bd9Sstevel@tonic-gate 		error = USB_NO_RESOURCES;
27217c478bd9Sstevel@tonic-gate 	} else {
27227c478bd9Sstevel@tonic-gate 		/* Insert the td's on the endpoint */
27237c478bd9Sstevel@tonic-gate 		ohci_insert_ctrl_req(ohcip, ph, ctrl_reqp, tw, usb_flags);
27247c478bd9Sstevel@tonic-gate 	}
27257c478bd9Sstevel@tonic-gate 
27267c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
27277c478bd9Sstevel@tonic-gate 
27287c478bd9Sstevel@tonic-gate 	return (error);
27297c478bd9Sstevel@tonic-gate }
27307c478bd9Sstevel@tonic-gate 
27317c478bd9Sstevel@tonic-gate 
27327c478bd9Sstevel@tonic-gate /*
27337c478bd9Sstevel@tonic-gate  * ohci_hcdi_bulk_transfer_size:
27347c478bd9Sstevel@tonic-gate  *
27357c478bd9Sstevel@tonic-gate  * Return maximum bulk transfer size
27367c478bd9Sstevel@tonic-gate  */
27377c478bd9Sstevel@tonic-gate 
27387c478bd9Sstevel@tonic-gate /* ARGSUSED */
27397c478bd9Sstevel@tonic-gate static int
27407c478bd9Sstevel@tonic-gate ohci_hcdi_bulk_transfer_size(
27417c478bd9Sstevel@tonic-gate 	usba_device_t	*usba_device,
27427c478bd9Sstevel@tonic-gate 	size_t		*size)
27437c478bd9Sstevel@tonic-gate {
27447c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip = ohci_obtain_state(
27457c478bd9Sstevel@tonic-gate 			    usba_device->usb_root_hub_dip);
27467c478bd9Sstevel@tonic-gate 	int		rval;
27477c478bd9Sstevel@tonic-gate 
27487c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
27497c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_bulk_transfer_size:");
27507c478bd9Sstevel@tonic-gate 
27517c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
27527c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
27537c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
27547c478bd9Sstevel@tonic-gate 
27557c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate 		return (rval);
27587c478bd9Sstevel@tonic-gate 	}
27597c478bd9Sstevel@tonic-gate 
27607c478bd9Sstevel@tonic-gate 	*size = OHCI_MAX_BULK_XFER_SIZE;
27617c478bd9Sstevel@tonic-gate 
27627c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
27637c478bd9Sstevel@tonic-gate }
27647c478bd9Sstevel@tonic-gate 
27657c478bd9Sstevel@tonic-gate 
27667c478bd9Sstevel@tonic-gate /*
27677c478bd9Sstevel@tonic-gate  * ohci_hcdi_pipe_bulk_xfer:
27687c478bd9Sstevel@tonic-gate  */
27697c478bd9Sstevel@tonic-gate static int
27707c478bd9Sstevel@tonic-gate ohci_hcdi_pipe_bulk_xfer(
27717c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
27727c478bd9Sstevel@tonic-gate 	usb_bulk_req_t		*bulk_reqp,
27737c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
27747c478bd9Sstevel@tonic-gate {
27757c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
27767c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
27777c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
27787c478bd9Sstevel@tonic-gate 	int			rval, error = USB_SUCCESS;
27797c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
27807c478bd9Sstevel@tonic-gate 
27817c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
27827c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_bulk_xfer: ph = 0x%p reqp = 0x%p flags = 0x%x",
27837c478bd9Sstevel@tonic-gate 	    (void *)ph, bulk_reqp, usb_flags);
27847c478bd9Sstevel@tonic-gate 
27857c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
27867c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
27877c478bd9Sstevel@tonic-gate 
27887c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
27897c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
27907c478bd9Sstevel@tonic-gate 
27917c478bd9Sstevel@tonic-gate 		return (rval);
27927c478bd9Sstevel@tonic-gate 	}
27937c478bd9Sstevel@tonic-gate 
27947c478bd9Sstevel@tonic-gate 	/*
27957c478bd9Sstevel@tonic-gate 	 *  Check whether pipe is in halted state.
27967c478bd9Sstevel@tonic-gate 	 */
27977c478bd9Sstevel@tonic-gate 	if (pp->pp_state == OHCI_PIPE_STATE_ERROR) {
27987c478bd9Sstevel@tonic-gate 
27997c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
28007c478bd9Sstevel@tonic-gate 		    "ohci_hcdi_pipe_bulk_xfer:"
28017c478bd9Sstevel@tonic-gate 		    "Pipe is in error state, need pipe reset to continue");
28027c478bd9Sstevel@tonic-gate 
28037c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
28047c478bd9Sstevel@tonic-gate 
28057c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
28067c478bd9Sstevel@tonic-gate 	}
28077c478bd9Sstevel@tonic-gate 
28087c478bd9Sstevel@tonic-gate 	/* Allocate a transfer wrapper */
28097c478bd9Sstevel@tonic-gate 	if ((tw = ohci_allocate_bulk_resources(ohcip, pp, bulk_reqp,
28107c478bd9Sstevel@tonic-gate 	    usb_flags)) == NULL) {
28117c478bd9Sstevel@tonic-gate 
28127c478bd9Sstevel@tonic-gate 		error = USB_NO_RESOURCES;
28137c478bd9Sstevel@tonic-gate 	} else {
28147c478bd9Sstevel@tonic-gate 		/* Add the TD into the Host Controller's bulk list */
28157c478bd9Sstevel@tonic-gate 		ohci_insert_bulk_req(ohcip, ph, bulk_reqp, tw, usb_flags);
28167c478bd9Sstevel@tonic-gate 	}
28177c478bd9Sstevel@tonic-gate 
28187c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
28197c478bd9Sstevel@tonic-gate 
28207c478bd9Sstevel@tonic-gate 	return (error);
28217c478bd9Sstevel@tonic-gate }
28227c478bd9Sstevel@tonic-gate 
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate /*
28257c478bd9Sstevel@tonic-gate  * ohci_hcdi_pipe_intr_xfer:
28267c478bd9Sstevel@tonic-gate  */
28277c478bd9Sstevel@tonic-gate static int
28287c478bd9Sstevel@tonic-gate ohci_hcdi_pipe_intr_xfer(
28297c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
28307c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*intr_reqp,
28317c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
28327c478bd9Sstevel@tonic-gate {
28337c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
28347c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
28357c478bd9Sstevel@tonic-gate 	int			pipe_dir, rval, error = USB_SUCCESS;
28367c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
28377c478bd9Sstevel@tonic-gate 
28387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
28397c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_intr_xfer: ph = 0x%p reqp = 0x%p flags = 0x%x",
28407c478bd9Sstevel@tonic-gate 	    (void *)ph, intr_reqp, usb_flags);
28417c478bd9Sstevel@tonic-gate 
28427c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
28437c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
28447c478bd9Sstevel@tonic-gate 
28457c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
28467c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
28477c478bd9Sstevel@tonic-gate 
28487c478bd9Sstevel@tonic-gate 		return (rval);
28497c478bd9Sstevel@tonic-gate 	}
28507c478bd9Sstevel@tonic-gate 
28517c478bd9Sstevel@tonic-gate 	/* Get the pipe direction */
28527c478bd9Sstevel@tonic-gate 	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
28537c478bd9Sstevel@tonic-gate 
28547c478bd9Sstevel@tonic-gate 	if (pipe_dir == USB_EP_DIR_IN) {
28557c478bd9Sstevel@tonic-gate 		error = ohci_start_periodic_pipe_polling(ohcip, ph,
28567c478bd9Sstevel@tonic-gate 		    (usb_opaque_t)intr_reqp, usb_flags);
28577c478bd9Sstevel@tonic-gate 	} else {
28587c478bd9Sstevel@tonic-gate 		/* Allocate transaction resources */
28597c478bd9Sstevel@tonic-gate 		if ((tw = ohci_allocate_intr_resources(ohcip, ph,
28607c478bd9Sstevel@tonic-gate 		    intr_reqp, usb_flags)) == NULL) {
28617c478bd9Sstevel@tonic-gate 			error = USB_NO_RESOURCES;
28627c478bd9Sstevel@tonic-gate 		} else {
28637c478bd9Sstevel@tonic-gate 			ohci_insert_intr_req(ohcip,
28647c478bd9Sstevel@tonic-gate 			    (ohci_pipe_private_t *)ph->p_hcd_private,
28657c478bd9Sstevel@tonic-gate 			    tw, usb_flags);
28667c478bd9Sstevel@tonic-gate 		}
28677c478bd9Sstevel@tonic-gate 	}
28687c478bd9Sstevel@tonic-gate 
28697c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
28707c478bd9Sstevel@tonic-gate 
28717c478bd9Sstevel@tonic-gate 	return (error);
28727c478bd9Sstevel@tonic-gate }
28737c478bd9Sstevel@tonic-gate 
28747c478bd9Sstevel@tonic-gate 
28757c478bd9Sstevel@tonic-gate /*
28767c478bd9Sstevel@tonic-gate  * ohci_hcdi_pipe_stop_intr_polling()
28777c478bd9Sstevel@tonic-gate  */
28787c478bd9Sstevel@tonic-gate static int
28797c478bd9Sstevel@tonic-gate ohci_hcdi_pipe_stop_intr_polling(
28807c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
28817c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
28827c478bd9Sstevel@tonic-gate {
28837c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
28847c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
28857c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
28867c478bd9Sstevel@tonic-gate 
28877c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
28887c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_stop_intr_polling: ph = 0x%p fl = 0x%x",
28897c478bd9Sstevel@tonic-gate 	    ph, flags);
28907c478bd9Sstevel@tonic-gate 
28917c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
28927c478bd9Sstevel@tonic-gate 
28937c478bd9Sstevel@tonic-gate 	error = ohci_stop_periodic_pipe_polling(ohcip, ph, flags);
28947c478bd9Sstevel@tonic-gate 
28957c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
28967c478bd9Sstevel@tonic-gate 
28977c478bd9Sstevel@tonic-gate 	return (error);
28987c478bd9Sstevel@tonic-gate }
28997c478bd9Sstevel@tonic-gate 
29007c478bd9Sstevel@tonic-gate 
29017c478bd9Sstevel@tonic-gate /*
29027c478bd9Sstevel@tonic-gate  * ohci_hcdi_get_current_frame_number:
29037c478bd9Sstevel@tonic-gate  *
29047c478bd9Sstevel@tonic-gate  * Return the current usb frame number
29057c478bd9Sstevel@tonic-gate  */
29067c478bd9Sstevel@tonic-gate static usb_frame_number_t
29077c478bd9Sstevel@tonic-gate ohci_hcdi_get_current_frame_number(usba_device_t	*usba_device)
29087c478bd9Sstevel@tonic-gate {
29097c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
29107c478bd9Sstevel@tonic-gate 				    usba_device->usb_root_hub_dip);
29117c478bd9Sstevel@tonic-gate 	usb_frame_number_t	frame_number;
29127c478bd9Sstevel@tonic-gate 	int			rval;
29137c478bd9Sstevel@tonic-gate 
29147c478bd9Sstevel@tonic-gate 	ohcip = ohci_obtain_state(usba_device->usb_root_hub_dip);
29157c478bd9Sstevel@tonic-gate 
29167c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
29177c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
29187c478bd9Sstevel@tonic-gate 
29197c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
29207c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
29217c478bd9Sstevel@tonic-gate 
29227c478bd9Sstevel@tonic-gate 		return (rval);
29237c478bd9Sstevel@tonic-gate 	}
29247c478bd9Sstevel@tonic-gate 
29257c478bd9Sstevel@tonic-gate 	frame_number = ohci_get_current_frame_number(ohcip);
29267c478bd9Sstevel@tonic-gate 
29277c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
29287c478bd9Sstevel@tonic-gate 
29297c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
29307c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_get_current_frame_number:"
29317c478bd9Sstevel@tonic-gate 	    "Current frame number 0x%llx", frame_number);
29327c478bd9Sstevel@tonic-gate 
29337c478bd9Sstevel@tonic-gate 	return (frame_number);
29347c478bd9Sstevel@tonic-gate }
29357c478bd9Sstevel@tonic-gate 
29367c478bd9Sstevel@tonic-gate 
29377c478bd9Sstevel@tonic-gate /*
29387c478bd9Sstevel@tonic-gate  * ohci_hcdi_get_max_isoc_pkts:
29397c478bd9Sstevel@tonic-gate  *
29407c478bd9Sstevel@tonic-gate  * Return maximum isochronous packets per usb isochronous request
29417c478bd9Sstevel@tonic-gate  */
29427c478bd9Sstevel@tonic-gate static uint_t
29437c478bd9Sstevel@tonic-gate ohci_hcdi_get_max_isoc_pkts(usba_device_t	*usba_device)
29447c478bd9Sstevel@tonic-gate {
29457c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
29467c478bd9Sstevel@tonic-gate 				    usba_device->usb_root_hub_dip);
29477c478bd9Sstevel@tonic-gate 	uint_t			max_isoc_pkts_per_request;
29487c478bd9Sstevel@tonic-gate 	int			rval;
29497c478bd9Sstevel@tonic-gate 
29507c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
29517c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
29527c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
29537c478bd9Sstevel@tonic-gate 
29547c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
29557c478bd9Sstevel@tonic-gate 
29567c478bd9Sstevel@tonic-gate 		return (rval);
29577c478bd9Sstevel@tonic-gate 	}
29587c478bd9Sstevel@tonic-gate 
29597c478bd9Sstevel@tonic-gate 	max_isoc_pkts_per_request = OHCI_MAX_ISOC_PKTS_PER_XFER;
29607c478bd9Sstevel@tonic-gate 
29617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
29627c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_get_max_isoc_pkts: maximum isochronous"
29637c478bd9Sstevel@tonic-gate 	    "packets per usb isochronous request = 0x%x",
29647c478bd9Sstevel@tonic-gate 	    max_isoc_pkts_per_request);
29657c478bd9Sstevel@tonic-gate 
29667c478bd9Sstevel@tonic-gate 	return (max_isoc_pkts_per_request);
29677c478bd9Sstevel@tonic-gate }
29687c478bd9Sstevel@tonic-gate 
29697c478bd9Sstevel@tonic-gate 
29707c478bd9Sstevel@tonic-gate /*
29717c478bd9Sstevel@tonic-gate  * ohci_hcdi_pipe_isoc_xfer:
29727c478bd9Sstevel@tonic-gate  */
29737c478bd9Sstevel@tonic-gate static int
29747c478bd9Sstevel@tonic-gate ohci_hcdi_pipe_isoc_xfer(
29757c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
29767c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_reqp,
29777c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
29787c478bd9Sstevel@tonic-gate {
29797c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
29807c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
29817c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
29827c478bd9Sstevel@tonic-gate 	int			pipe_dir, rval;
29837c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
29847c478bd9Sstevel@tonic-gate 
29857c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
29867c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_isoc_xfer: ph = 0x%p reqp = 0x%p flags = 0x%x",
29877c478bd9Sstevel@tonic-gate 	    (void *)ph, isoc_reqp, usb_flags);
29887c478bd9Sstevel@tonic-gate 
29897c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
29907c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
29917c478bd9Sstevel@tonic-gate 
29927c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
29937c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
29947c478bd9Sstevel@tonic-gate 
29957c478bd9Sstevel@tonic-gate 		return (rval);
29967c478bd9Sstevel@tonic-gate 	}
29977c478bd9Sstevel@tonic-gate 
29987c478bd9Sstevel@tonic-gate 	/* Get the isochronous pipe direction */
29997c478bd9Sstevel@tonic-gate 	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
30007c478bd9Sstevel@tonic-gate 
30017c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
30027c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_isoc_xfer: isoc_reqp = 0x%p, uf = 0x%x",
30037c478bd9Sstevel@tonic-gate 	    isoc_reqp, usb_flags);
30047c478bd9Sstevel@tonic-gate 
30057c478bd9Sstevel@tonic-gate 	if (pipe_dir == USB_EP_DIR_IN) {
30067c478bd9Sstevel@tonic-gate 		error = ohci_start_periodic_pipe_polling(ohcip, ph,
30077c478bd9Sstevel@tonic-gate 		    (usb_opaque_t)isoc_reqp, usb_flags);
30087c478bd9Sstevel@tonic-gate 	} else {
30097c478bd9Sstevel@tonic-gate 		/* Allocate transaction resources */
30107c478bd9Sstevel@tonic-gate 		if ((tw = ohci_allocate_isoc_resources(ohcip, ph,
30117c478bd9Sstevel@tonic-gate 		    isoc_reqp, usb_flags)) == NULL) {
30127c478bd9Sstevel@tonic-gate 			error = USB_NO_RESOURCES;
30137c478bd9Sstevel@tonic-gate 		} else {
30147c478bd9Sstevel@tonic-gate 			error = ohci_insert_isoc_req(ohcip,
30157c478bd9Sstevel@tonic-gate 			    (ohci_pipe_private_t *)ph->p_hcd_private,
30167c478bd9Sstevel@tonic-gate 			    tw, usb_flags);
30177c478bd9Sstevel@tonic-gate 		}
30187c478bd9Sstevel@tonic-gate 	}
30197c478bd9Sstevel@tonic-gate 
30207c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
30217c478bd9Sstevel@tonic-gate 
30227c478bd9Sstevel@tonic-gate 	return (error);
30237c478bd9Sstevel@tonic-gate }
30247c478bd9Sstevel@tonic-gate 
30257c478bd9Sstevel@tonic-gate 
30267c478bd9Sstevel@tonic-gate /*
30277c478bd9Sstevel@tonic-gate  * ohci_hcdi_pipe_stop_isoc_polling()
30287c478bd9Sstevel@tonic-gate  */
30297c478bd9Sstevel@tonic-gate static int
30307c478bd9Sstevel@tonic-gate ohci_hcdi_pipe_stop_isoc_polling(
30317c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
30327c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
30337c478bd9Sstevel@tonic-gate {
30347c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
30357c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
30367c478bd9Sstevel@tonic-gate 	int			rval, error = USB_SUCCESS;
30377c478bd9Sstevel@tonic-gate 
30387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ohcip->ohci_log_hdl,
30397c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_pipe_stop_isoc_polling: ph = 0x%p fl = 0x%x",
30407c478bd9Sstevel@tonic-gate 	    (void *)ph, flags);
30417c478bd9Sstevel@tonic-gate 
30427c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
30437c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
30447c478bd9Sstevel@tonic-gate 
30457c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
30467c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
30477c478bd9Sstevel@tonic-gate 		return (rval);
30487c478bd9Sstevel@tonic-gate 	}
30497c478bd9Sstevel@tonic-gate 
30507c478bd9Sstevel@tonic-gate 	error = ohci_stop_periodic_pipe_polling(ohcip, ph, flags);
30517c478bd9Sstevel@tonic-gate 
30527c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
30537c478bd9Sstevel@tonic-gate 	return (error);
30547c478bd9Sstevel@tonic-gate }
30557c478bd9Sstevel@tonic-gate 
30567c478bd9Sstevel@tonic-gate 
30577c478bd9Sstevel@tonic-gate /*
30587c478bd9Sstevel@tonic-gate  * Bandwidth Allocation functions
30597c478bd9Sstevel@tonic-gate  */
30607c478bd9Sstevel@tonic-gate 
30617c478bd9Sstevel@tonic-gate /*
30627c478bd9Sstevel@tonic-gate  * ohci_allocate_bandwidth:
30637c478bd9Sstevel@tonic-gate  *
30647c478bd9Sstevel@tonic-gate  * Figure out whether or not this interval may be supported. Return the index
30657c478bd9Sstevel@tonic-gate  * into the  lattice if it can be supported.  Return allocation failure if it
30667c478bd9Sstevel@tonic-gate  * can not be supported.
30677c478bd9Sstevel@tonic-gate  *
30687c478bd9Sstevel@tonic-gate  * The lattice structure looks like this with the bottom leaf actually
30697c478bd9Sstevel@tonic-gate  * being an array.  There is a total of 63 nodes in this tree.  The lattice tree
30707c478bd9Sstevel@tonic-gate  * itself is 0 based, while the bottom leaf array is 0 based.  The 0 bucket in
30717c478bd9Sstevel@tonic-gate  * the bottom leaf array is used to store the smalled allocated bandwidth of all
30727c478bd9Sstevel@tonic-gate  * the leaves.
30737c478bd9Sstevel@tonic-gate  *
30747c478bd9Sstevel@tonic-gate  *      0
30757c478bd9Sstevel@tonic-gate  *    1   2
30767c478bd9Sstevel@tonic-gate  *   3 4 5 6
30777c478bd9Sstevel@tonic-gate  *   ...
30787c478bd9Sstevel@tonic-gate  *  (32 33 ... 62 63)     <-- last row does not exist in lattice, but an array
30797c478bd9Sstevel@tonic-gate  *   0 1 2 3 ... 30 31
30807c478bd9Sstevel@tonic-gate  *
30817c478bd9Sstevel@tonic-gate  * We keep track of the bandwidth that each leaf uses.  First we search for the
30827c478bd9Sstevel@tonic-gate  * first leaf with the smallest used bandwidth.  Based on that leaf we find the
30837c478bd9Sstevel@tonic-gate  * parent node of that leaf based on the interval time.
30847c478bd9Sstevel@tonic-gate  *
30857c478bd9Sstevel@tonic-gate  * From the parent node, we find all the leafs of that subtree and update the
30867c478bd9Sstevel@tonic-gate  * additional bandwidth needed.  In order to balance the load the leaves are not
30877c478bd9Sstevel@tonic-gate  * executed directly from left to right, but scattered.  For a better picture
30887c478bd9Sstevel@tonic-gate  * refer to Section 3.3.2 in the OpenHCI 1.0 spec, there should be a figure
30897c478bd9Sstevel@tonic-gate  * showing the Interrupt ED Structure.
30907c478bd9Sstevel@tonic-gate  */
30917c478bd9Sstevel@tonic-gate static int
30927c478bd9Sstevel@tonic-gate ohci_allocate_bandwidth(
30937c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
30947c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
30957c478bd9Sstevel@tonic-gate 	uint_t			*node)
30967c478bd9Sstevel@tonic-gate {
30977c478bd9Sstevel@tonic-gate 	int			interval, error, i;
30987c478bd9Sstevel@tonic-gate 	uint_t			min, min_index, height;
30997c478bd9Sstevel@tonic-gate 	uint_t			leftmost, list, bandwidth;
31007c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*endpoint = &ph->p_ep;
31017c478bd9Sstevel@tonic-gate 
31027c478bd9Sstevel@tonic-gate 	/* This routine is protected by the ohci_int_mutex */
31037c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
31047c478bd9Sstevel@tonic-gate 
31057c478bd9Sstevel@tonic-gate 	/*
31067c478bd9Sstevel@tonic-gate 	 * Calculate the length in bytes of a transaction on this
31077c478bd9Sstevel@tonic-gate 	 * periodic endpoint.
31087c478bd9Sstevel@tonic-gate 	 */
31097c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_usba_device->usb_mutex);
31107c478bd9Sstevel@tonic-gate 	error = ohci_compute_total_bandwidth(
31117c478bd9Sstevel@tonic-gate 	    endpoint, ph->p_usba_device->usb_port_status, &bandwidth);
31127c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_usba_device->usb_mutex);
31137c478bd9Sstevel@tonic-gate 
31147c478bd9Sstevel@tonic-gate 	/*
31157c478bd9Sstevel@tonic-gate 	 * If length is zero, then, it means endpoint maximum packet
31167c478bd9Sstevel@tonic-gate 	 * supported is zero.  In that case, return failure without
31177c478bd9Sstevel@tonic-gate 	 * allocating any bandwidth.
31187c478bd9Sstevel@tonic-gate 	 */
31197c478bd9Sstevel@tonic-gate 	if (error != USB_SUCCESS) {
31207c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_BW, ohcip->ohci_log_hdl,
31217c478bd9Sstevel@tonic-gate 		    "ohci_allocate_bandwidth: Periodic endpoint with "
31227c478bd9Sstevel@tonic-gate 		    "zero endpoint maximum packet size is not supported");
31237c478bd9Sstevel@tonic-gate 
31247c478bd9Sstevel@tonic-gate 		return (USB_NOT_SUPPORTED);
31257c478bd9Sstevel@tonic-gate 	}
31267c478bd9Sstevel@tonic-gate 
31277c478bd9Sstevel@tonic-gate 	/*
31287c478bd9Sstevel@tonic-gate 	 * If the length in bytes plus the allocated bandwidth exceeds
31297c478bd9Sstevel@tonic-gate 	 * the maximum, return bandwidth allocation failure.
31307c478bd9Sstevel@tonic-gate 	 */
31317c478bd9Sstevel@tonic-gate 	if ((ohcip->ohci_periodic_minimum_bandwidth + bandwidth) >
31327c478bd9Sstevel@tonic-gate 	    (MAX_PERIODIC_BANDWIDTH)) {
31337c478bd9Sstevel@tonic-gate 
31347c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_BW, ohcip->ohci_log_hdl,
31357c478bd9Sstevel@tonic-gate 		    "ohci_allocate_bandwidth: Reached maximum "
31367c478bd9Sstevel@tonic-gate 		    "bandwidth value and cannot allocate bandwidth "
31377c478bd9Sstevel@tonic-gate 		    "for a given periodic endpoint");
31387c478bd9Sstevel@tonic-gate 
31397c478bd9Sstevel@tonic-gate 		return (USB_NO_BANDWIDTH);
31407c478bd9Sstevel@tonic-gate 	}
31417c478bd9Sstevel@tonic-gate 
31427c478bd9Sstevel@tonic-gate 	/* Adjust polling interval to be a power of 2 */
31437c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_usba_device->usb_mutex);
31447c478bd9Sstevel@tonic-gate 	interval = ohci_adjust_polling_interval(ohcip,
31457c478bd9Sstevel@tonic-gate 	    endpoint, ph->p_usba_device->usb_port_status);
31467c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_usba_device->usb_mutex);
31477c478bd9Sstevel@tonic-gate 
31487c478bd9Sstevel@tonic-gate 	/*
31497c478bd9Sstevel@tonic-gate 	 * If this interval can't be supported,
31507c478bd9Sstevel@tonic-gate 	 * return allocation failure.
31517c478bd9Sstevel@tonic-gate 	 */
31527c478bd9Sstevel@tonic-gate 	if (interval == USB_FAILURE) {
31537c478bd9Sstevel@tonic-gate 
31547c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
31557c478bd9Sstevel@tonic-gate 	}
31567c478bd9Sstevel@tonic-gate 
31577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_BW, ohcip->ohci_log_hdl,
31587c478bd9Sstevel@tonic-gate 	    "The new interval is %d", interval);
31597c478bd9Sstevel@tonic-gate 
31607c478bd9Sstevel@tonic-gate 	/* Find the leaf with the smallest allocated bandwidth */
31617c478bd9Sstevel@tonic-gate 	min_index = 0;
31627c478bd9Sstevel@tonic-gate 	min = ohcip->ohci_periodic_bandwidth[0];
31637c478bd9Sstevel@tonic-gate 
31647c478bd9Sstevel@tonic-gate 	for (i = 1; i < NUM_INTR_ED_LISTS; i++) {
31657c478bd9Sstevel@tonic-gate 		if (ohcip->ohci_periodic_bandwidth[i] < min) {
31667c478bd9Sstevel@tonic-gate 			min_index = i;
31677c478bd9Sstevel@tonic-gate 			min = ohcip->ohci_periodic_bandwidth[i];
31687c478bd9Sstevel@tonic-gate 		}
31697c478bd9Sstevel@tonic-gate 	}
31707c478bd9Sstevel@tonic-gate 
31717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_BW, ohcip->ohci_log_hdl,
31727c478bd9Sstevel@tonic-gate 	    "The leaf %d for minimal bandwidth %d", min_index, min);
31737c478bd9Sstevel@tonic-gate 
31747c478bd9Sstevel@tonic-gate 	/* Adjust min for the lattice */
31757c478bd9Sstevel@tonic-gate 	min_index = min_index + NUM_INTR_ED_LISTS - 1;
31767c478bd9Sstevel@tonic-gate 
31777c478bd9Sstevel@tonic-gate 	/*
31787c478bd9Sstevel@tonic-gate 	 * Find the index into the lattice given the
31797c478bd9Sstevel@tonic-gate 	 * leaf with the smallest allocated bandwidth.
31807c478bd9Sstevel@tonic-gate 	 */
31817c478bd9Sstevel@tonic-gate 	height = ohci_lattice_height(interval);
31827c478bd9Sstevel@tonic-gate 
31837c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_BW, ohcip->ohci_log_hdl,
31847c478bd9Sstevel@tonic-gate 	    "The height is %d", height);
31857c478bd9Sstevel@tonic-gate 
31867c478bd9Sstevel@tonic-gate 	*node = min_index;
31877c478bd9Sstevel@tonic-gate 
31887c478bd9Sstevel@tonic-gate 	for (i = 0; i < height; i++) {
31897c478bd9Sstevel@tonic-gate 		*node = ohci_lattice_parent(*node);
31907c478bd9Sstevel@tonic-gate 	}
31917c478bd9Sstevel@tonic-gate 
31927c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_BW, ohcip->ohci_log_hdl,
31937c478bd9Sstevel@tonic-gate 	    "Real node is %d", *node);
31947c478bd9Sstevel@tonic-gate 
31957c478bd9Sstevel@tonic-gate 	/*
31967c478bd9Sstevel@tonic-gate 	 * Find the leftmost leaf in the subtree
31977c478bd9Sstevel@tonic-gate 	 * specified by the node.
31987c478bd9Sstevel@tonic-gate 	 */
31997c478bd9Sstevel@tonic-gate 	leftmost = ohci_leftmost_leaf(*node, height);
32007c478bd9Sstevel@tonic-gate 
32017c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_BW, ohcip->ohci_log_hdl,
32027c478bd9Sstevel@tonic-gate 	    "Leftmost %d", leftmost);
32037c478bd9Sstevel@tonic-gate 
32047c478bd9Sstevel@tonic-gate 	for (i = 0; i < (NUM_INTR_ED_LISTS/interval); i++) {
32057c478bd9Sstevel@tonic-gate 		list = ohci_hcca_leaf_index(leftmost + i);
32067c478bd9Sstevel@tonic-gate 		if ((ohcip->ohci_periodic_bandwidth[list] +
32077c478bd9Sstevel@tonic-gate 		    bandwidth) > MAX_PERIODIC_BANDWIDTH) {
32087c478bd9Sstevel@tonic-gate 
32097c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_BW, ohcip->ohci_log_hdl,
32107c478bd9Sstevel@tonic-gate 			    "ohci_allocate_bandwidth: Reached maximum "
32117c478bd9Sstevel@tonic-gate 			    "bandwidth value and cannot allocate bandwidth "
32127c478bd9Sstevel@tonic-gate 			    "for periodic endpoint");
32137c478bd9Sstevel@tonic-gate 
32147c478bd9Sstevel@tonic-gate 			return (USB_NO_BANDWIDTH);
32157c478bd9Sstevel@tonic-gate 		}
32167c478bd9Sstevel@tonic-gate 	}
32177c478bd9Sstevel@tonic-gate 
32187c478bd9Sstevel@tonic-gate 	/*
32197c478bd9Sstevel@tonic-gate 	 * All the leaves for this node must be updated with the bandwidth.
32207c478bd9Sstevel@tonic-gate 	 */
32217c478bd9Sstevel@tonic-gate 	for (i = 0; i < (NUM_INTR_ED_LISTS/interval); i++) {
32227c478bd9Sstevel@tonic-gate 		list = ohci_hcca_leaf_index(leftmost + i);
32237c478bd9Sstevel@tonic-gate 		ohcip->ohci_periodic_bandwidth[list] += bandwidth;
32247c478bd9Sstevel@tonic-gate 	}
32257c478bd9Sstevel@tonic-gate 
32267c478bd9Sstevel@tonic-gate 	/* Find the leaf with the smallest allocated bandwidth */
32277c478bd9Sstevel@tonic-gate 	min_index = 0;
32287c478bd9Sstevel@tonic-gate 	min = ohcip->ohci_periodic_bandwidth[0];
32297c478bd9Sstevel@tonic-gate 
32307c478bd9Sstevel@tonic-gate 	for (i = 1; i < NUM_INTR_ED_LISTS; i++) {
32317c478bd9Sstevel@tonic-gate 		if (ohcip->ohci_periodic_bandwidth[i] < min) {
32327c478bd9Sstevel@tonic-gate 			min_index = i;
32337c478bd9Sstevel@tonic-gate 			min = ohcip->ohci_periodic_bandwidth[i];
32347c478bd9Sstevel@tonic-gate 		}
32357c478bd9Sstevel@tonic-gate 	}
32367c478bd9Sstevel@tonic-gate 
32377c478bd9Sstevel@tonic-gate 	/* Save the minimum for later use */
32387c478bd9Sstevel@tonic-gate 	ohcip->ohci_periodic_minimum_bandwidth = min;
32397c478bd9Sstevel@tonic-gate 
32407c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
32417c478bd9Sstevel@tonic-gate }
32427c478bd9Sstevel@tonic-gate 
32437c478bd9Sstevel@tonic-gate 
32447c478bd9Sstevel@tonic-gate /*
32457c478bd9Sstevel@tonic-gate  * ohci_deallocate_bandwidth:
32467c478bd9Sstevel@tonic-gate  *
32477c478bd9Sstevel@tonic-gate  * Deallocate bandwidth for the given node in the lattice and the length
32487c478bd9Sstevel@tonic-gate  * of transfer.
32497c478bd9Sstevel@tonic-gate  */
32507c478bd9Sstevel@tonic-gate static void
32517c478bd9Sstevel@tonic-gate ohci_deallocate_bandwidth(
32527c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
32537c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
32547c478bd9Sstevel@tonic-gate {
32557c478bd9Sstevel@tonic-gate 	uint_t			min, node, bandwidth;
32567c478bd9Sstevel@tonic-gate 	uint_t			height, leftmost, list;
32577c478bd9Sstevel@tonic-gate 	int			i, interval;
32587c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*endpoint = &ph->p_ep;
32597c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
32607c478bd9Sstevel@tonic-gate 
32617c478bd9Sstevel@tonic-gate 	/* This routine is protected by the ohci_int_mutex */
32627c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
32637c478bd9Sstevel@tonic-gate 
32647c478bd9Sstevel@tonic-gate 	/* Obtain the length */
32657c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_usba_device->usb_mutex);
32667c478bd9Sstevel@tonic-gate 	(void) ohci_compute_total_bandwidth(
32677c478bd9Sstevel@tonic-gate 	    endpoint, ph->p_usba_device->usb_port_status, &bandwidth);
32687c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_usba_device->usb_mutex);
32697c478bd9Sstevel@tonic-gate 
32707c478bd9Sstevel@tonic-gate 	/* Obtain the node */
32717c478bd9Sstevel@tonic-gate 	node = pp->pp_node;
32727c478bd9Sstevel@tonic-gate 
32737c478bd9Sstevel@tonic-gate 	/* Adjust polling interval to be a power of 2 */
32747c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_usba_device->usb_mutex);
32757c478bd9Sstevel@tonic-gate 	interval = ohci_adjust_polling_interval(ohcip,
32767c478bd9Sstevel@tonic-gate 	    endpoint, ph->p_usba_device->usb_port_status);
32777c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_usba_device->usb_mutex);
32787c478bd9Sstevel@tonic-gate 
32797c478bd9Sstevel@tonic-gate 	/* Find the height in the tree */
32807c478bd9Sstevel@tonic-gate 	height = ohci_lattice_height(interval);
32817c478bd9Sstevel@tonic-gate 
32827c478bd9Sstevel@tonic-gate 	/*
32837c478bd9Sstevel@tonic-gate 	 * Find the leftmost leaf in the subtree specified by the node
32847c478bd9Sstevel@tonic-gate 	 */
32857c478bd9Sstevel@tonic-gate 	leftmost = ohci_leftmost_leaf(node, height);
32867c478bd9Sstevel@tonic-gate 
32877c478bd9Sstevel@tonic-gate 	/* Delete the bandwith from the appropriate lists */
32887c478bd9Sstevel@tonic-gate 	for (i = 0; i < (NUM_INTR_ED_LISTS/interval); i++) {
32897c478bd9Sstevel@tonic-gate 		list = ohci_hcca_leaf_index(leftmost + i);
32907c478bd9Sstevel@tonic-gate 		ohcip->ohci_periodic_bandwidth[list] -= bandwidth;
32917c478bd9Sstevel@tonic-gate 	}
32927c478bd9Sstevel@tonic-gate 
32937c478bd9Sstevel@tonic-gate 	min = ohcip->ohci_periodic_bandwidth[0];
32947c478bd9Sstevel@tonic-gate 
32957c478bd9Sstevel@tonic-gate 	/* Recompute the minimum */
32967c478bd9Sstevel@tonic-gate 	for (i = 1; i < NUM_INTR_ED_LISTS; i++) {
32977c478bd9Sstevel@tonic-gate 		if (ohcip->ohci_periodic_bandwidth[i] < min) {
32987c478bd9Sstevel@tonic-gate 			min = ohcip->ohci_periodic_bandwidth[i];
32997c478bd9Sstevel@tonic-gate 		}
33007c478bd9Sstevel@tonic-gate 	}
33017c478bd9Sstevel@tonic-gate 
33027c478bd9Sstevel@tonic-gate 	/* Save the minimum for later use */
33037c478bd9Sstevel@tonic-gate 	ohcip->ohci_periodic_minimum_bandwidth = min;
33047c478bd9Sstevel@tonic-gate }
33057c478bd9Sstevel@tonic-gate 
33067c478bd9Sstevel@tonic-gate 
33077c478bd9Sstevel@tonic-gate /*
33087c478bd9Sstevel@tonic-gate  * ohci_compute_total_bandwidth:
33097c478bd9Sstevel@tonic-gate  *
33107c478bd9Sstevel@tonic-gate  * Given a periodic endpoint (interrupt or isochronous) determine the total
33117c478bd9Sstevel@tonic-gate  * bandwidth for one transaction. The OpenHCI host controller traverses the
33127c478bd9Sstevel@tonic-gate  * endpoint descriptor lists on a first-come-first-serve basis. When the HC
33137c478bd9Sstevel@tonic-gate  * services an endpoint, only a single transaction attempt is made. The  HC
33147c478bd9Sstevel@tonic-gate  * moves to the next Endpoint Descriptor after the first transaction attempt
33157c478bd9Sstevel@tonic-gate  * rather than finishing the entire Transfer Descriptor. Therefore, when  a
33167c478bd9Sstevel@tonic-gate  * Transfer Descriptor is inserted into the lattice, we will only count the
33177c478bd9Sstevel@tonic-gate  * number of bytes for one transaction.
33187c478bd9Sstevel@tonic-gate  *
33197c478bd9Sstevel@tonic-gate  * The following are the formulas used for  calculating bandwidth in  terms
33207c478bd9Sstevel@tonic-gate  * bytes and it is for the single USB full speed and low speed	transaction
33217c478bd9Sstevel@tonic-gate  * respectively. The protocol overheads will be different for each of  type
33227c478bd9Sstevel@tonic-gate  * of USB transfer and all these formulas & protocol overheads are  derived
33237c478bd9Sstevel@tonic-gate  * from the 5.9.3 section of USB Specification & with the help of Bandwidth
33247c478bd9Sstevel@tonic-gate  * Analysis white paper which is posted on the USB  developer forum.
33257c478bd9Sstevel@tonic-gate  *
33267c478bd9Sstevel@tonic-gate  * Full-Speed:
33277c478bd9Sstevel@tonic-gate  *		Protocol overhead  + ((MaxPacketSize * 7)/6 )  + Host_Delay
33287c478bd9Sstevel@tonic-gate  *
33297c478bd9Sstevel@tonic-gate  * Low-Speed:
33307c478bd9Sstevel@tonic-gate  *		Protocol overhead  + Hub LS overhead +
33317c478bd9Sstevel@tonic-gate  *		  (Low-Speed clock * ((MaxPacketSize * 7)/6 )) + Host_Delay
33327c478bd9Sstevel@tonic-gate  */
33337c478bd9Sstevel@tonic-gate static int
33347c478bd9Sstevel@tonic-gate ohci_compute_total_bandwidth(
33357c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*endpoint,
33367c478bd9Sstevel@tonic-gate 	usb_port_status_t	port_status,
33377c478bd9Sstevel@tonic-gate 	uint_t			*bandwidth)
33387c478bd9Sstevel@tonic-gate {
33397c478bd9Sstevel@tonic-gate 	ushort_t		maxpacketsize = endpoint->wMaxPacketSize;
33407c478bd9Sstevel@tonic-gate 
33417c478bd9Sstevel@tonic-gate 	/*
33427c478bd9Sstevel@tonic-gate 	 * If endpoint maximum packet is zero, then return immediately.
33437c478bd9Sstevel@tonic-gate 	 */
33447c478bd9Sstevel@tonic-gate 	if (maxpacketsize == 0) {
33457c478bd9Sstevel@tonic-gate 
33467c478bd9Sstevel@tonic-gate 		return (USB_NOT_SUPPORTED);
33477c478bd9Sstevel@tonic-gate 	}
33487c478bd9Sstevel@tonic-gate 
33497c478bd9Sstevel@tonic-gate 	/* Add Host Controller specific delay to required bandwidth */
33507c478bd9Sstevel@tonic-gate 	*bandwidth = HOST_CONTROLLER_DELAY;
33517c478bd9Sstevel@tonic-gate 
33527c478bd9Sstevel@tonic-gate 	/* Add bit-stuffing overhead */
33537c478bd9Sstevel@tonic-gate 	maxpacketsize = (ushort_t)((maxpacketsize * 7) / 6);
33547c478bd9Sstevel@tonic-gate 
33557c478bd9Sstevel@tonic-gate 	/* Low Speed interrupt transaction */
33567c478bd9Sstevel@tonic-gate 	if (port_status == USBA_LOW_SPEED_DEV) {
33577c478bd9Sstevel@tonic-gate 		/* Low Speed interrupt transaction */
33587c478bd9Sstevel@tonic-gate 		*bandwidth += (LOW_SPEED_PROTO_OVERHEAD +
33597c478bd9Sstevel@tonic-gate 		    HUB_LOW_SPEED_PROTO_OVERHEAD +
33607c478bd9Sstevel@tonic-gate 		    (LOW_SPEED_CLOCK * maxpacketsize));
33617c478bd9Sstevel@tonic-gate 	} else {
33627c478bd9Sstevel@tonic-gate 		/* Full Speed transaction */
33637c478bd9Sstevel@tonic-gate 		*bandwidth += maxpacketsize;
33647c478bd9Sstevel@tonic-gate 
33657c478bd9Sstevel@tonic-gate 		if ((endpoint->bmAttributes &
33667c478bd9Sstevel@tonic-gate 		    USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) {
33677c478bd9Sstevel@tonic-gate 			/* Full Speed interrupt transaction */
33687c478bd9Sstevel@tonic-gate 			*bandwidth += FS_NON_ISOC_PROTO_OVERHEAD;
33697c478bd9Sstevel@tonic-gate 		} else {
33707c478bd9Sstevel@tonic-gate 			/* Isochronous and input transaction */
33717c478bd9Sstevel@tonic-gate 			if ((endpoint->bEndpointAddress &
33727c478bd9Sstevel@tonic-gate 			    USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
33737c478bd9Sstevel@tonic-gate 				*bandwidth += FS_ISOC_INPUT_PROTO_OVERHEAD;
33747c478bd9Sstevel@tonic-gate 			} else {
33757c478bd9Sstevel@tonic-gate 				/* Isochronous and output transaction */
33767c478bd9Sstevel@tonic-gate 				*bandwidth += FS_ISOC_OUTPUT_PROTO_OVERHEAD;
33777c478bd9Sstevel@tonic-gate 			}
33787c478bd9Sstevel@tonic-gate 		}
33797c478bd9Sstevel@tonic-gate 	}
33807c478bd9Sstevel@tonic-gate 
33817c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
33827c478bd9Sstevel@tonic-gate }
33837c478bd9Sstevel@tonic-gate 
33847c478bd9Sstevel@tonic-gate 
33857c478bd9Sstevel@tonic-gate /*
33867c478bd9Sstevel@tonic-gate  * ohci_adjust_polling_interval:
33877c478bd9Sstevel@tonic-gate  */
33887c478bd9Sstevel@tonic-gate static int
33897c478bd9Sstevel@tonic-gate ohci_adjust_polling_interval(
33907c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
33917c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*endpoint,
33927c478bd9Sstevel@tonic-gate 	usb_port_status_t	port_status)
33937c478bd9Sstevel@tonic-gate {
33947c478bd9Sstevel@tonic-gate 	uint_t			interval;
33957c478bd9Sstevel@tonic-gate 	int			i = 0;
33967c478bd9Sstevel@tonic-gate 
33977c478bd9Sstevel@tonic-gate 	/*
33987c478bd9Sstevel@tonic-gate 	 * Get the polling interval from the endpoint descriptor
33997c478bd9Sstevel@tonic-gate 	 */
34007c478bd9Sstevel@tonic-gate 	interval = endpoint->bInterval;
34017c478bd9Sstevel@tonic-gate 
34027c478bd9Sstevel@tonic-gate 	/*
34037c478bd9Sstevel@tonic-gate 	 * The bInterval value in the endpoint descriptor can range
34047c478bd9Sstevel@tonic-gate 	 * from 1 to 255ms. The interrupt lattice has 32 leaf nodes,
34057c478bd9Sstevel@tonic-gate 	 * and the host controller cycles through these nodes every
34067c478bd9Sstevel@tonic-gate 	 * 32ms. The longest polling  interval that the  controller
34077c478bd9Sstevel@tonic-gate 	 * supports is 32ms.
34087c478bd9Sstevel@tonic-gate 	 */
34097c478bd9Sstevel@tonic-gate 
34107c478bd9Sstevel@tonic-gate 	/*
34117c478bd9Sstevel@tonic-gate 	 * Return an error if the polling interval is less than 1ms
34127c478bd9Sstevel@tonic-gate 	 * and greater than 255ms
34137c478bd9Sstevel@tonic-gate 	 */
34147c478bd9Sstevel@tonic-gate 	if ((interval < MIN_POLL_INTERVAL) ||
34157c478bd9Sstevel@tonic-gate 	    (interval > MAX_POLL_INTERVAL)) {
34167c478bd9Sstevel@tonic-gate 
34177c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
34187c478bd9Sstevel@tonic-gate 		    "ohci_adjust_polling_interval: "
34197c478bd9Sstevel@tonic-gate 		    "Endpoint's poll interval must be between %d and %d ms",
34207c478bd9Sstevel@tonic-gate 		    MIN_POLL_INTERVAL, MAX_POLL_INTERVAL);
34217c478bd9Sstevel@tonic-gate 
34227c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
34237c478bd9Sstevel@tonic-gate 	}
34247c478bd9Sstevel@tonic-gate 
34257c478bd9Sstevel@tonic-gate 	/*
34267c478bd9Sstevel@tonic-gate 	 * According USB Specifications, a  full-speed endpoint can
34277c478bd9Sstevel@tonic-gate 	 * specify a desired polling interval 1ms to 255ms and a low
34287c478bd9Sstevel@tonic-gate 	 * speed  endpoints are limited to  specifying only 10ms to
34297c478bd9Sstevel@tonic-gate 	 * 255ms. But some old keyboards & mice uses polling interval
34307c478bd9Sstevel@tonic-gate 	 * of 8ms. For compatibility  purpose, we are using polling
34317c478bd9Sstevel@tonic-gate 	 * interval between 8ms & 255ms for low speed endpoints. But
34327c478bd9Sstevel@tonic-gate 	 * ohci driver will reject the any low speed endpoints which
34337c478bd9Sstevel@tonic-gate 	 * request polling interval less than 8ms.
34347c478bd9Sstevel@tonic-gate 	 */
34357c478bd9Sstevel@tonic-gate 	if ((port_status == USBA_LOW_SPEED_DEV) &&
34367c478bd9Sstevel@tonic-gate 	    (interval < MIN_LOW_SPEED_POLL_INTERVAL)) {
34377c478bd9Sstevel@tonic-gate 
34387c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(PRINT_MASK_BW, ohcip->ohci_log_hdl,
34397c478bd9Sstevel@tonic-gate 		    "ohci_adjust_polling_interval: "
34407c478bd9Sstevel@tonic-gate 		    "Low speed endpoint's poll interval of %d ms "
34417c478bd9Sstevel@tonic-gate 		    "is below threshold.  Rounding up to %d ms",
34427c478bd9Sstevel@tonic-gate 		    interval, MIN_LOW_SPEED_POLL_INTERVAL);
34437c478bd9Sstevel@tonic-gate 
34447c478bd9Sstevel@tonic-gate 		interval = MIN_LOW_SPEED_POLL_INTERVAL;
34457c478bd9Sstevel@tonic-gate 	}
34467c478bd9Sstevel@tonic-gate 
34477c478bd9Sstevel@tonic-gate 	/*
34487c478bd9Sstevel@tonic-gate 	 * If polling interval is greater than 32ms,
34497c478bd9Sstevel@tonic-gate 	 * adjust polling interval equal to 32ms.
34507c478bd9Sstevel@tonic-gate 	 */
34517c478bd9Sstevel@tonic-gate 	if (interval > NUM_INTR_ED_LISTS) {
34527c478bd9Sstevel@tonic-gate 		interval = NUM_INTR_ED_LISTS;
34537c478bd9Sstevel@tonic-gate 	}
34547c478bd9Sstevel@tonic-gate 
34557c478bd9Sstevel@tonic-gate 	/*
34567c478bd9Sstevel@tonic-gate 	 * Find the nearest power of 2 that'sless
34577c478bd9Sstevel@tonic-gate 	 * than interval.
34587c478bd9Sstevel@tonic-gate 	 */
34597c478bd9Sstevel@tonic-gate 	while ((ohci_pow_2(i)) <= interval) {
34607c478bd9Sstevel@tonic-gate 		i++;
34617c478bd9Sstevel@tonic-gate 	}
34627c478bd9Sstevel@tonic-gate 
34637c478bd9Sstevel@tonic-gate 	return (ohci_pow_2((i - 1)));
34647c478bd9Sstevel@tonic-gate }
34657c478bd9Sstevel@tonic-gate 
34667c478bd9Sstevel@tonic-gate 
34677c478bd9Sstevel@tonic-gate /*
34687c478bd9Sstevel@tonic-gate  * ohci_lattice_height:
34697c478bd9Sstevel@tonic-gate  *
34707c478bd9Sstevel@tonic-gate  * Given the requested bandwidth, find the height in the tree at which the
34717c478bd9Sstevel@tonic-gate  * nodes for this bandwidth fall.  The height is measured as the number of
34727c478bd9Sstevel@tonic-gate  * nodes from the leaf to the level specified by bandwidth The root of the
34737c478bd9Sstevel@tonic-gate  * tree is at height TREE_HEIGHT.
34747c478bd9Sstevel@tonic-gate  */
34757c478bd9Sstevel@tonic-gate static uint_t
34767c478bd9Sstevel@tonic-gate ohci_lattice_height(uint_t interval)
34777c478bd9Sstevel@tonic-gate {
34787c478bd9Sstevel@tonic-gate 	return (TREE_HEIGHT - (ohci_log_2(interval)));
34797c478bd9Sstevel@tonic-gate }
34807c478bd9Sstevel@tonic-gate 
34817c478bd9Sstevel@tonic-gate 
34827c478bd9Sstevel@tonic-gate /*
34837c478bd9Sstevel@tonic-gate  * ohci_lattice_parent:
34847c478bd9Sstevel@tonic-gate  */
34857c478bd9Sstevel@tonic-gate static uint_t
34867c478bd9Sstevel@tonic-gate ohci_lattice_parent(uint_t node)
34877c478bd9Sstevel@tonic-gate {
34887c478bd9Sstevel@tonic-gate 	if ((node % 2) == 0) {
34897c478bd9Sstevel@tonic-gate 		return ((node/2) - 1);
34907c478bd9Sstevel@tonic-gate 	} else {
34917c478bd9Sstevel@tonic-gate 		return ((node + 1)/2 - 1);
34927c478bd9Sstevel@tonic-gate 	}
34937c478bd9Sstevel@tonic-gate }
34947c478bd9Sstevel@tonic-gate 
34957c478bd9Sstevel@tonic-gate 
34967c478bd9Sstevel@tonic-gate /*
34977c478bd9Sstevel@tonic-gate  * ohci_leftmost_leaf:
34987c478bd9Sstevel@tonic-gate  *
34997c478bd9Sstevel@tonic-gate  * Find the leftmost leaf in the subtree specified by the node. Height refers
35007c478bd9Sstevel@tonic-gate  * to number of nodes from the bottom of the tree to the node,	including the
35017c478bd9Sstevel@tonic-gate  * node.
35027c478bd9Sstevel@tonic-gate  *
35037c478bd9Sstevel@tonic-gate  * The formula for a zero based tree is:
35047c478bd9Sstevel@tonic-gate  *     2^H * Node + 2^H - 1
35057c478bd9Sstevel@tonic-gate  * The leaf of the tree is an array, convert the number for the array.
35067c478bd9Sstevel@tonic-gate  *     Subtract the size of nodes not in the array
35077c478bd9Sstevel@tonic-gate  *     2^H * Node + 2^H - 1 - (NUM_INTR_ED_LIST - 1) =
35087c478bd9Sstevel@tonic-gate  *     2^H * Node + 2^H - NUM_INTR_ED_LIST =
35097c478bd9Sstevel@tonic-gate  *     2^H * (Node + 1) - NUM_INTR_ED_LIST
35107c478bd9Sstevel@tonic-gate  *         0
35117c478bd9Sstevel@tonic-gate  *       1   2
35127c478bd9Sstevel@tonic-gate  *      0 1 2 3
35137c478bd9Sstevel@tonic-gate  */
35147c478bd9Sstevel@tonic-gate static uint_t
35157c478bd9Sstevel@tonic-gate ohci_leftmost_leaf(
35167c478bd9Sstevel@tonic-gate 	uint_t	node,
35177c478bd9Sstevel@tonic-gate 	uint_t	height)
35187c478bd9Sstevel@tonic-gate {
35197c478bd9Sstevel@tonic-gate 	return ((ohci_pow_2(height) * (node + 1)) - NUM_INTR_ED_LISTS);
35207c478bd9Sstevel@tonic-gate }
35217c478bd9Sstevel@tonic-gate 
35227c478bd9Sstevel@tonic-gate /*
35237c478bd9Sstevel@tonic-gate  * ohci_hcca_intr_index:
35247c478bd9Sstevel@tonic-gate  *
35257c478bd9Sstevel@tonic-gate  * Given a node in the lattice, find the index for the hcca interrupt table
35267c478bd9Sstevel@tonic-gate  */
35277c478bd9Sstevel@tonic-gate static uint_t
35287c478bd9Sstevel@tonic-gate ohci_hcca_intr_index(uint_t node)
35297c478bd9Sstevel@tonic-gate {
35307c478bd9Sstevel@tonic-gate 	/*
35317c478bd9Sstevel@tonic-gate 	 * Adjust the node to the array representing
35327c478bd9Sstevel@tonic-gate 	 * the bottom of the tree.
35337c478bd9Sstevel@tonic-gate 	 */
35347c478bd9Sstevel@tonic-gate 	node = node - NUM_STATIC_NODES;
35357c478bd9Sstevel@tonic-gate 
35367c478bd9Sstevel@tonic-gate 	if ((node % 2) == 0) {
35377c478bd9Sstevel@tonic-gate 		return (ohci_index[node / 2]);
35387c478bd9Sstevel@tonic-gate 	} else {
35397c478bd9Sstevel@tonic-gate 		return (ohci_index[node / 2] + (NUM_INTR_ED_LISTS / 2));
35407c478bd9Sstevel@tonic-gate 	}
35417c478bd9Sstevel@tonic-gate }
35427c478bd9Sstevel@tonic-gate 
35437c478bd9Sstevel@tonic-gate /*
35447c478bd9Sstevel@tonic-gate  * ohci_hcca_leaf_index:
35457c478bd9Sstevel@tonic-gate  *
35467c478bd9Sstevel@tonic-gate  * Given a node in the bottom leaf array of the lattice, find the index
35477c478bd9Sstevel@tonic-gate  * for the hcca interrupt table
35487c478bd9Sstevel@tonic-gate  */
35497c478bd9Sstevel@tonic-gate static uint_t
35507c478bd9Sstevel@tonic-gate ohci_hcca_leaf_index(uint_t leaf)
35517c478bd9Sstevel@tonic-gate {
35527c478bd9Sstevel@tonic-gate 	if ((leaf % 2) == 0) {
35537c478bd9Sstevel@tonic-gate 		return (ohci_index[leaf / 2]);
35547c478bd9Sstevel@tonic-gate 	} else {
35557c478bd9Sstevel@tonic-gate 		return (ohci_index[leaf / 2] + (NUM_INTR_ED_LISTS / 2));
35567c478bd9Sstevel@tonic-gate 	}
35577c478bd9Sstevel@tonic-gate }
35587c478bd9Sstevel@tonic-gate 
35597c478bd9Sstevel@tonic-gate /*
35607c478bd9Sstevel@tonic-gate  * ohci_pow_2:
35617c478bd9Sstevel@tonic-gate  *
35627c478bd9Sstevel@tonic-gate  * Compute 2 to the power
35637c478bd9Sstevel@tonic-gate  */
35647c478bd9Sstevel@tonic-gate static uint_t
35657c478bd9Sstevel@tonic-gate ohci_pow_2(uint_t x)
35667c478bd9Sstevel@tonic-gate {
35677c478bd9Sstevel@tonic-gate 	if (x == 0) {
35687c478bd9Sstevel@tonic-gate 		return (1);
35697c478bd9Sstevel@tonic-gate 	} else {
35707c478bd9Sstevel@tonic-gate 		return (2 << (x - 1));
35717c478bd9Sstevel@tonic-gate 	}
35727c478bd9Sstevel@tonic-gate }
35737c478bd9Sstevel@tonic-gate 
35747c478bd9Sstevel@tonic-gate 
35757c478bd9Sstevel@tonic-gate /*
35767c478bd9Sstevel@tonic-gate  * ohci_log_2:
35777c478bd9Sstevel@tonic-gate  *
35787c478bd9Sstevel@tonic-gate  * Compute log base 2 of x
35797c478bd9Sstevel@tonic-gate  */
35807c478bd9Sstevel@tonic-gate static uint_t
35817c478bd9Sstevel@tonic-gate ohci_log_2(uint_t x)
35827c478bd9Sstevel@tonic-gate {
35837c478bd9Sstevel@tonic-gate 	int i = 0;
35847c478bd9Sstevel@tonic-gate 
35857c478bd9Sstevel@tonic-gate 	while (x != 1) {
35867c478bd9Sstevel@tonic-gate 		x = x >> 1;
35877c478bd9Sstevel@tonic-gate 		i++;
35887c478bd9Sstevel@tonic-gate 	}
35897c478bd9Sstevel@tonic-gate 
35907c478bd9Sstevel@tonic-gate 	return (i);
35917c478bd9Sstevel@tonic-gate }
35927c478bd9Sstevel@tonic-gate 
35937c478bd9Sstevel@tonic-gate 
35947c478bd9Sstevel@tonic-gate /*
35957c478bd9Sstevel@tonic-gate  * Endpoint Descriptor (ED) manipulations functions
35967c478bd9Sstevel@tonic-gate  */
35977c478bd9Sstevel@tonic-gate 
35987c478bd9Sstevel@tonic-gate /*
35997c478bd9Sstevel@tonic-gate  * ohci_alloc_hc_ed:
36007c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
36017c478bd9Sstevel@tonic-gate  *
36027c478bd9Sstevel@tonic-gate  * Allocate an endpoint descriptor (ED)
36037c478bd9Sstevel@tonic-gate  */
36047c478bd9Sstevel@tonic-gate ohci_ed_t *
36057c478bd9Sstevel@tonic-gate ohci_alloc_hc_ed(
36067c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
36077c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
36087c478bd9Sstevel@tonic-gate {
36097c478bd9Sstevel@tonic-gate 	int			i, state;
36107c478bd9Sstevel@tonic-gate 	ohci_ed_t		*hc_ed;
36117c478bd9Sstevel@tonic-gate 
36127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
36137c478bd9Sstevel@tonic-gate 	    "ohci_alloc_hc_ed: ph = 0x%p", (void *)ph);
36147c478bd9Sstevel@tonic-gate 
36157c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
36167c478bd9Sstevel@tonic-gate 
36177c478bd9Sstevel@tonic-gate 	/*
36187c478bd9Sstevel@tonic-gate 	 * The first 31 endpoints in the Endpoint Descriptor (ED)
36197c478bd9Sstevel@tonic-gate 	 * buffer pool are reserved for building interrupt lattice
36207c478bd9Sstevel@tonic-gate 	 * tree. Search for a blank endpoint descriptor in the ED
36217c478bd9Sstevel@tonic-gate 	 * buffer pool.
36227c478bd9Sstevel@tonic-gate 	 */
36237c478bd9Sstevel@tonic-gate 	for (i = NUM_STATIC_NODES; i < ohci_ed_pool_size; i ++) {
36247c478bd9Sstevel@tonic-gate 		state = Get_ED(ohcip->ohci_ed_pool_addr[i].hced_state);
36257c478bd9Sstevel@tonic-gate 
36267c478bd9Sstevel@tonic-gate 		if (state == HC_EPT_FREE) {
36277c478bd9Sstevel@tonic-gate 			break;
36287c478bd9Sstevel@tonic-gate 		}
36297c478bd9Sstevel@tonic-gate 	}
36307c478bd9Sstevel@tonic-gate 
36317c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
36327c478bd9Sstevel@tonic-gate 	    "ohci_alloc_hc_ed: Allocated %d", i);
36337c478bd9Sstevel@tonic-gate 
36347c478bd9Sstevel@tonic-gate 	if (i == ohci_ed_pool_size) {
36357c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
36367c478bd9Sstevel@tonic-gate 		    "ohci_alloc_hc_ed: ED exhausted");
36377c478bd9Sstevel@tonic-gate 
36387c478bd9Sstevel@tonic-gate 		return (NULL);
36397c478bd9Sstevel@tonic-gate 	} else {
36407c478bd9Sstevel@tonic-gate 
36417c478bd9Sstevel@tonic-gate 		hc_ed = &ohcip->ohci_ed_pool_addr[i];
36427c478bd9Sstevel@tonic-gate 
36437c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
36447c478bd9Sstevel@tonic-gate 		    "ohci_alloc_hc_ed: Allocated address 0x%p", (void *)hc_ed);
36457c478bd9Sstevel@tonic-gate 
36467c478bd9Sstevel@tonic-gate 		ohci_print_ed(ohcip, hc_ed);
36477c478bd9Sstevel@tonic-gate 
36487c478bd9Sstevel@tonic-gate 		/* Unpack the endpoint descriptor into a control field */
36497c478bd9Sstevel@tonic-gate 		if (ph) {
36507c478bd9Sstevel@tonic-gate 			if ((ohci_initialize_dummy(ohcip,
36517c478bd9Sstevel@tonic-gate 			    hc_ed)) == USB_NO_RESOURCES) {
36527c478bd9Sstevel@tonic-gate 				bzero((void *)hc_ed, sizeof (ohci_ed_t));
36537c478bd9Sstevel@tonic-gate 				Set_ED(hc_ed->hced_state, HC_EPT_FREE);
36547c478bd9Sstevel@tonic-gate 				return (NULL);
36557c478bd9Sstevel@tonic-gate 			}
36567c478bd9Sstevel@tonic-gate 
36577c478bd9Sstevel@tonic-gate 			Set_ED(hc_ed->hced_prev, NULL);
36587c478bd9Sstevel@tonic-gate 			Set_ED(hc_ed->hced_next, NULL);
36597c478bd9Sstevel@tonic-gate 
36607c478bd9Sstevel@tonic-gate 			/* Change ED's state Active */
36617c478bd9Sstevel@tonic-gate 			Set_ED(hc_ed->hced_state, HC_EPT_ACTIVE);
36627c478bd9Sstevel@tonic-gate 
36637c478bd9Sstevel@tonic-gate 			Set_ED(hc_ed->hced_ctrl,
36647c478bd9Sstevel@tonic-gate 			    ohci_unpack_endpoint(ohcip, ph));
36657c478bd9Sstevel@tonic-gate 		} else {
36667c478bd9Sstevel@tonic-gate 			Set_ED(hc_ed->hced_ctrl, HC_EPT_sKip);
36677c478bd9Sstevel@tonic-gate 
36687c478bd9Sstevel@tonic-gate 			/* Change ED's state Static */
36697c478bd9Sstevel@tonic-gate 			Set_ED(hc_ed->hced_state, HC_EPT_STATIC);
36707c478bd9Sstevel@tonic-gate 		}
36717c478bd9Sstevel@tonic-gate 
36727c478bd9Sstevel@tonic-gate 		return (hc_ed);
36737c478bd9Sstevel@tonic-gate 	}
36747c478bd9Sstevel@tonic-gate }
36757c478bd9Sstevel@tonic-gate 
36767c478bd9Sstevel@tonic-gate 
36777c478bd9Sstevel@tonic-gate /*
36787c478bd9Sstevel@tonic-gate  * ohci_unpack_endpoint:
36797c478bd9Sstevel@tonic-gate  *
36807c478bd9Sstevel@tonic-gate  * Unpack the information in the pipe handle and create the first byte
36817c478bd9Sstevel@tonic-gate  * of the Host Controller's (HC) Endpoint Descriptor (ED).
36827c478bd9Sstevel@tonic-gate  */
36837c478bd9Sstevel@tonic-gate static uint_t
36847c478bd9Sstevel@tonic-gate ohci_unpack_endpoint(
36857c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
36867c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
36877c478bd9Sstevel@tonic-gate {
36887c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*endpoint = &ph->p_ep;
36897c478bd9Sstevel@tonic-gate 	uint_t			maxpacketsize, addr, ctrl = 0;
36907c478bd9Sstevel@tonic-gate 
36917c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
36927c478bd9Sstevel@tonic-gate 	    "ohci_unpack_endpoint:");
36937c478bd9Sstevel@tonic-gate 
36947c478bd9Sstevel@tonic-gate 	ctrl = ph->p_usba_device->usb_addr;
36957c478bd9Sstevel@tonic-gate 
36967c478bd9Sstevel@tonic-gate 	addr = endpoint->bEndpointAddress;
36977c478bd9Sstevel@tonic-gate 
36987c478bd9Sstevel@tonic-gate 	/* Assign the endpoint's address */
36997c478bd9Sstevel@tonic-gate 	ctrl = ctrl | ((addr & USB_EP_NUM_MASK) << HC_EPT_EP_SHFT);
37007c478bd9Sstevel@tonic-gate 
37017c478bd9Sstevel@tonic-gate 	/*
37027c478bd9Sstevel@tonic-gate 	 * Assign the direction. If the endpoint is a control endpoint,
37037c478bd9Sstevel@tonic-gate 	 * the direction is assigned by the Transfer Descriptor (TD).
37047c478bd9Sstevel@tonic-gate 	 */
37057c478bd9Sstevel@tonic-gate 	if ((endpoint->bmAttributes &
37067c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_MASK) != USB_EP_ATTR_CONTROL) {
37077c478bd9Sstevel@tonic-gate 		if (addr & USB_EP_DIR_MASK) {
37087c478bd9Sstevel@tonic-gate 			/* The direction is IN */
37097c478bd9Sstevel@tonic-gate 			ctrl = ctrl | HC_EPT_DF_IN;
37107c478bd9Sstevel@tonic-gate 		} else {
37117c478bd9Sstevel@tonic-gate 			/* The direction is OUT */
37127c478bd9Sstevel@tonic-gate 			ctrl = ctrl | HC_EPT_DF_OUT;
37137c478bd9Sstevel@tonic-gate 		}
37147c478bd9Sstevel@tonic-gate 	}
37157c478bd9Sstevel@tonic-gate 
37167c478bd9Sstevel@tonic-gate 	/* Assign the speed */
37177c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_usba_device->usb_mutex);
37187c478bd9Sstevel@tonic-gate 	if (ph->p_usba_device->usb_port_status == USBA_LOW_SPEED_DEV) {
37197c478bd9Sstevel@tonic-gate 		ctrl = ctrl | HC_EPT_Speed;
37207c478bd9Sstevel@tonic-gate 	}
37217c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_usba_device->usb_mutex);
37227c478bd9Sstevel@tonic-gate 
37237c478bd9Sstevel@tonic-gate 	/* Assign the format */
37247c478bd9Sstevel@tonic-gate 	if ((endpoint->bmAttributes &
37257c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH) {
37267c478bd9Sstevel@tonic-gate 		ctrl = ctrl | HC_EPT_Format;
37277c478bd9Sstevel@tonic-gate 	}
37287c478bd9Sstevel@tonic-gate 
37297c478bd9Sstevel@tonic-gate 	maxpacketsize = endpoint->wMaxPacketSize;
37307c478bd9Sstevel@tonic-gate 	maxpacketsize = maxpacketsize << HC_EPT_MAXPKTSZ;
37317c478bd9Sstevel@tonic-gate 	ctrl = ctrl | (maxpacketsize & HC_EPT_MPS);
37327c478bd9Sstevel@tonic-gate 
37337c478bd9Sstevel@tonic-gate 	return (ctrl);
37347c478bd9Sstevel@tonic-gate }
37357c478bd9Sstevel@tonic-gate 
37367c478bd9Sstevel@tonic-gate 
37377c478bd9Sstevel@tonic-gate /*
37387c478bd9Sstevel@tonic-gate  * ohci_insert_ed:
37397c478bd9Sstevel@tonic-gate  *
37407c478bd9Sstevel@tonic-gate  * Add the Endpoint Descriptor (ED) into the Host Controller's
37417c478bd9Sstevel@tonic-gate  * (HC) appropriate endpoint list.
37427c478bd9Sstevel@tonic-gate  */
37437c478bd9Sstevel@tonic-gate static void
37447c478bd9Sstevel@tonic-gate ohci_insert_ed(
37457c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
37467c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
37477c478bd9Sstevel@tonic-gate {
37487c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
37497c478bd9Sstevel@tonic-gate 
37507c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
37517c478bd9Sstevel@tonic-gate 	    "ohci_insert_ed:");
37527c478bd9Sstevel@tonic-gate 
37537c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
37547c478bd9Sstevel@tonic-gate 
37557c478bd9Sstevel@tonic-gate 	switch (ph->p_ep.bmAttributes & USB_EP_ATTR_MASK) {
37567c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
37577c478bd9Sstevel@tonic-gate 		ohci_insert_ctrl_ed(ohcip, pp);
37587c478bd9Sstevel@tonic-gate 		break;
37597c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
37607c478bd9Sstevel@tonic-gate 		ohci_insert_bulk_ed(ohcip, pp);
37617c478bd9Sstevel@tonic-gate 		break;
37627c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
37637c478bd9Sstevel@tonic-gate 		ohci_insert_intr_ed(ohcip, pp);
37647c478bd9Sstevel@tonic-gate 		break;
37657c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
37667c478bd9Sstevel@tonic-gate 		ohci_insert_isoc_ed(ohcip, pp);
37677c478bd9Sstevel@tonic-gate 		break;
37687c478bd9Sstevel@tonic-gate 	}
37697c478bd9Sstevel@tonic-gate }
37707c478bd9Sstevel@tonic-gate 
37717c478bd9Sstevel@tonic-gate 
37727c478bd9Sstevel@tonic-gate /*
37737c478bd9Sstevel@tonic-gate  * ohci_insert_ctrl_ed:
37747c478bd9Sstevel@tonic-gate  *
37757c478bd9Sstevel@tonic-gate  * Insert a control endpoint into the Host Controller's (HC)
37767c478bd9Sstevel@tonic-gate  * control endpoint list.
37777c478bd9Sstevel@tonic-gate  */
37787c478bd9Sstevel@tonic-gate static void
37797c478bd9Sstevel@tonic-gate ohci_insert_ctrl_ed(
37807c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
37817c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
37827c478bd9Sstevel@tonic-gate {
37837c478bd9Sstevel@tonic-gate 	ohci_ed_t	*ept = pp->pp_ept;
37847c478bd9Sstevel@tonic-gate 	ohci_ed_t	*prev_ept;
37857c478bd9Sstevel@tonic-gate 
37867c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
37877c478bd9Sstevel@tonic-gate 	    "ohci_insert_ctrl_ed:");
37887c478bd9Sstevel@tonic-gate 
37897c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
37907c478bd9Sstevel@tonic-gate 
37917c478bd9Sstevel@tonic-gate 	/* Obtain a ptr to the head of the list */
37927c478bd9Sstevel@tonic-gate 	if (Get_OpReg(hcr_ctrl_head)) {
37937c478bd9Sstevel@tonic-gate 		prev_ept = ohci_ed_iommu_to_cpu(ohcip,
37947c478bd9Sstevel@tonic-gate 		    Get_OpReg(hcr_ctrl_head));
37957c478bd9Sstevel@tonic-gate 
37967c478bd9Sstevel@tonic-gate 		/* Set up the backwards pointer */
37977c478bd9Sstevel@tonic-gate 		Set_ED(prev_ept->hced_prev, ohci_ed_cpu_to_iommu(ohcip, ept));
37987c478bd9Sstevel@tonic-gate 	}
37997c478bd9Sstevel@tonic-gate 
38007c478bd9Sstevel@tonic-gate 	/* The new endpoint points to the head of the list */
38017c478bd9Sstevel@tonic-gate 	Set_ED(ept->hced_next, Get_OpReg(hcr_ctrl_head));
38027c478bd9Sstevel@tonic-gate 
38037c478bd9Sstevel@tonic-gate 	/* Set the head ptr to the new endpoint */
38047c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_ctrl_head, ohci_ed_cpu_to_iommu(ohcip, ept));
38057c478bd9Sstevel@tonic-gate 
38067c478bd9Sstevel@tonic-gate 	/*
38077c478bd9Sstevel@tonic-gate 	 * Enable Control list processing if control open
38087c478bd9Sstevel@tonic-gate 	 * pipe count is zero.
38097c478bd9Sstevel@tonic-gate 	 */
38107c478bd9Sstevel@tonic-gate 	if (!ohcip->ohci_open_ctrl_pipe_count) {
38117c478bd9Sstevel@tonic-gate 		/* Start Control list processing */
38127c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control,
38137c478bd9Sstevel@tonic-gate 		    (Get_OpReg(hcr_control) | HCR_CONTROL_CLE));
38147c478bd9Sstevel@tonic-gate 	}
38157c478bd9Sstevel@tonic-gate 
38167c478bd9Sstevel@tonic-gate 	ohcip->ohci_open_ctrl_pipe_count++;
38177c478bd9Sstevel@tonic-gate }
38187c478bd9Sstevel@tonic-gate 
38197c478bd9Sstevel@tonic-gate 
38207c478bd9Sstevel@tonic-gate /*
38217c478bd9Sstevel@tonic-gate  * ohci_insert_bulk_ed:
38227c478bd9Sstevel@tonic-gate  *
38237c478bd9Sstevel@tonic-gate  * Insert a bulk endpoint into the Host Controller's (HC) bulk endpoint list.
38247c478bd9Sstevel@tonic-gate  */
38257c478bd9Sstevel@tonic-gate static void
38267c478bd9Sstevel@tonic-gate ohci_insert_bulk_ed(
38277c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
38287c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
38297c478bd9Sstevel@tonic-gate {
38307c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept;
38317c478bd9Sstevel@tonic-gate 	ohci_ed_t		*prev_ept;
38327c478bd9Sstevel@tonic-gate 
38337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
38347c478bd9Sstevel@tonic-gate 	    "ohci_insert_bulk_ed:");
38357c478bd9Sstevel@tonic-gate 
38367c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
38377c478bd9Sstevel@tonic-gate 
38387c478bd9Sstevel@tonic-gate 	/* Obtain a ptr to the head of the Bulk list */
38397c478bd9Sstevel@tonic-gate 	if (Get_OpReg(hcr_bulk_head)) {
38407c478bd9Sstevel@tonic-gate 		prev_ept = ohci_ed_iommu_to_cpu(ohcip,
38417c478bd9Sstevel@tonic-gate 		    Get_OpReg(hcr_bulk_head));
38427c478bd9Sstevel@tonic-gate 
38437c478bd9Sstevel@tonic-gate 		/* Set up the backwards pointer */
38447c478bd9Sstevel@tonic-gate 		Set_ED(prev_ept->hced_prev, ohci_ed_cpu_to_iommu(ohcip, ept));
38457c478bd9Sstevel@tonic-gate 	}
38467c478bd9Sstevel@tonic-gate 
38477c478bd9Sstevel@tonic-gate 	/* The new endpoint points to the head of the Bulk list */
38487c478bd9Sstevel@tonic-gate 	Set_ED(ept->hced_next, Get_OpReg(hcr_bulk_head));
38497c478bd9Sstevel@tonic-gate 
38507c478bd9Sstevel@tonic-gate 	/* Set the Bulk head ptr to the new endpoint */
38517c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_bulk_head, ohci_ed_cpu_to_iommu(ohcip, ept));
38527c478bd9Sstevel@tonic-gate 
38537c478bd9Sstevel@tonic-gate 	/*
38547c478bd9Sstevel@tonic-gate 	 * Enable Bulk list processing if bulk open pipe
38557c478bd9Sstevel@tonic-gate 	 * count is zero.
38567c478bd9Sstevel@tonic-gate 	 */
38577c478bd9Sstevel@tonic-gate 	if (!ohcip->ohci_open_bulk_pipe_count) {
38587c478bd9Sstevel@tonic-gate 		/* Start Bulk list processing */
38597c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control,
38607c478bd9Sstevel@tonic-gate 		    (Get_OpReg(hcr_control) | HCR_CONTROL_BLE));
38617c478bd9Sstevel@tonic-gate 	}
38627c478bd9Sstevel@tonic-gate 
38637c478bd9Sstevel@tonic-gate 	ohcip->ohci_open_bulk_pipe_count++;
38647c478bd9Sstevel@tonic-gate }
38657c478bd9Sstevel@tonic-gate 
38667c478bd9Sstevel@tonic-gate 
38677c478bd9Sstevel@tonic-gate /*
38687c478bd9Sstevel@tonic-gate  * ohci_insert_intr_ed:
38697c478bd9Sstevel@tonic-gate  *
38707c478bd9Sstevel@tonic-gate  * Insert a interrupt endpoint into the Host Controller's (HC) interrupt
38717c478bd9Sstevel@tonic-gate  * lattice tree.
38727c478bd9Sstevel@tonic-gate  */
38737c478bd9Sstevel@tonic-gate static void
38747c478bd9Sstevel@tonic-gate ohci_insert_intr_ed(
38757c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
38767c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
38777c478bd9Sstevel@tonic-gate {
38787c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept;
38797c478bd9Sstevel@tonic-gate 	ohci_ed_t		*next_lattice_ept, *lattice_ept;
38807c478bd9Sstevel@tonic-gate 	uint_t			node;
38817c478bd9Sstevel@tonic-gate 
38827c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
38837c478bd9Sstevel@tonic-gate 
38847c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
38857c478bd9Sstevel@tonic-gate 	    "ohci_insert_intr_ed:");
38867c478bd9Sstevel@tonic-gate 
38877c478bd9Sstevel@tonic-gate 	/*
38887c478bd9Sstevel@tonic-gate 	 * The appropriate node was found
38897c478bd9Sstevel@tonic-gate 	 * during the opening of the pipe.
38907c478bd9Sstevel@tonic-gate 	 */
38917c478bd9Sstevel@tonic-gate 	node = pp->pp_node;
38927c478bd9Sstevel@tonic-gate 
38937c478bd9Sstevel@tonic-gate 	if (node >= NUM_STATIC_NODES) {
38947c478bd9Sstevel@tonic-gate 		/* Get the hcca interrupt table index */
38957c478bd9Sstevel@tonic-gate 		node = ohci_hcca_intr_index(node);
38967c478bd9Sstevel@tonic-gate 
38977c478bd9Sstevel@tonic-gate 		/* Get the first endpoint on the list */
38987c478bd9Sstevel@tonic-gate 		next_lattice_ept = ohci_ed_iommu_to_cpu(ohcip,
38997c478bd9Sstevel@tonic-gate 		    Get_HCCA(ohcip->ohci_hccap->HccaIntTble[node]));
39007c478bd9Sstevel@tonic-gate 
39017c478bd9Sstevel@tonic-gate 		/* Update this endpoint to point to it */
39027c478bd9Sstevel@tonic-gate 		Set_ED(ept->hced_next,
39037c478bd9Sstevel@tonic-gate 		    ohci_ed_cpu_to_iommu(ohcip, next_lattice_ept));
39047c478bd9Sstevel@tonic-gate 
39057c478bd9Sstevel@tonic-gate 		/* Put this endpoint at the head of the list */
39067c478bd9Sstevel@tonic-gate 		Set_HCCA(ohcip->ohci_hccap->HccaIntTble[node],
39077c478bd9Sstevel@tonic-gate 		    ohci_ed_cpu_to_iommu(ohcip, ept));
39087c478bd9Sstevel@tonic-gate 
39097c478bd9Sstevel@tonic-gate 		/* The previous pointer is NULL */
39107c478bd9Sstevel@tonic-gate 		Set_ED(ept->hced_prev, NULL);
39117c478bd9Sstevel@tonic-gate 
39127c478bd9Sstevel@tonic-gate 		/* Update the previous pointer of ept->hced_next */
39137c478bd9Sstevel@tonic-gate 		if (Get_ED(next_lattice_ept->hced_state) != HC_EPT_STATIC) {
39147c478bd9Sstevel@tonic-gate 			Set_ED(next_lattice_ept->hced_prev,
39157c478bd9Sstevel@tonic-gate 			    ohci_ed_cpu_to_iommu(ohcip, ept));
39167c478bd9Sstevel@tonic-gate 		}
39177c478bd9Sstevel@tonic-gate 	} else {
39187c478bd9Sstevel@tonic-gate 		/* Find the lattice endpoint */
39197c478bd9Sstevel@tonic-gate 		lattice_ept = &ohcip->ohci_ed_pool_addr[node];
39207c478bd9Sstevel@tonic-gate 
39217c478bd9Sstevel@tonic-gate 		/* Find the next lattice endpoint */
39227c478bd9Sstevel@tonic-gate 		next_lattice_ept = ohci_ed_iommu_to_cpu(
39237c478bd9Sstevel@tonic-gate 		    ohcip, Get_ED(lattice_ept->hced_next));
39247c478bd9Sstevel@tonic-gate 
39257c478bd9Sstevel@tonic-gate 		/*
39267c478bd9Sstevel@tonic-gate 		 * Update this endpoint to point to the next one in the
39277c478bd9Sstevel@tonic-gate 		 * lattice.
39287c478bd9Sstevel@tonic-gate 		 */
39297c478bd9Sstevel@tonic-gate 		Set_ED(ept->hced_next, Get_ED(lattice_ept->hced_next));
39307c478bd9Sstevel@tonic-gate 
39317c478bd9Sstevel@tonic-gate 		/* Insert this endpoint into the lattice */
39327c478bd9Sstevel@tonic-gate 		Set_ED(lattice_ept->hced_next,
39337c478bd9Sstevel@tonic-gate 		    ohci_ed_cpu_to_iommu(ohcip, ept));
39347c478bd9Sstevel@tonic-gate 
39357c478bd9Sstevel@tonic-gate 		/* Update the previous pointer */
39367c478bd9Sstevel@tonic-gate 		Set_ED(ept->hced_prev,
39377c478bd9Sstevel@tonic-gate 		    ohci_ed_cpu_to_iommu(ohcip, lattice_ept));
39387c478bd9Sstevel@tonic-gate 
39397c478bd9Sstevel@tonic-gate 		/* Update the previous pointer of ept->hced_next */
39407c478bd9Sstevel@tonic-gate 		if ((next_lattice_ept) &&
39417c478bd9Sstevel@tonic-gate 		    (Get_ED(next_lattice_ept->hced_state) != HC_EPT_STATIC)) {
39427c478bd9Sstevel@tonic-gate 
39437c478bd9Sstevel@tonic-gate 			Set_ED(next_lattice_ept->hced_prev,
39447c478bd9Sstevel@tonic-gate 			    ohci_ed_cpu_to_iommu(ohcip, ept));
39457c478bd9Sstevel@tonic-gate 		}
39467c478bd9Sstevel@tonic-gate 	}
39477c478bd9Sstevel@tonic-gate 
39487c478bd9Sstevel@tonic-gate 	/*
39497c478bd9Sstevel@tonic-gate 	 * Enable periodic list processing if periodic (interrupt
39507c478bd9Sstevel@tonic-gate 	 * and isochronous) open pipe count is zero.
39517c478bd9Sstevel@tonic-gate 	 */
39527c478bd9Sstevel@tonic-gate 	if (!ohcip->ohci_open_periodic_pipe_count) {
39537c478bd9Sstevel@tonic-gate 		ASSERT(!ohcip->ohci_open_isoch_pipe_count);
39547c478bd9Sstevel@tonic-gate 
39557c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control,
39567c478bd9Sstevel@tonic-gate 		    (Get_OpReg(hcr_control) | HCR_CONTROL_PLE));
39577c478bd9Sstevel@tonic-gate 	}
39587c478bd9Sstevel@tonic-gate 
39597c478bd9Sstevel@tonic-gate 	ohcip->ohci_open_periodic_pipe_count++;
39607c478bd9Sstevel@tonic-gate }
39617c478bd9Sstevel@tonic-gate 
39627c478bd9Sstevel@tonic-gate 
39637c478bd9Sstevel@tonic-gate /*
39647c478bd9Sstevel@tonic-gate  * ohci_insert_isoc_ed:
39657c478bd9Sstevel@tonic-gate  *
39667c478bd9Sstevel@tonic-gate  * Insert a isochronous endpoint into the Host Controller's (HC) interrupt
39677c478bd9Sstevel@tonic-gate  * lattice tree. A isochronous endpoint will be inserted at the end of the
39687c478bd9Sstevel@tonic-gate  * 1ms interrupt endpoint list.
39697c478bd9Sstevel@tonic-gate  */
39707c478bd9Sstevel@tonic-gate static void
39717c478bd9Sstevel@tonic-gate ohci_insert_isoc_ed(
39727c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
39737c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
39747c478bd9Sstevel@tonic-gate {
39757c478bd9Sstevel@tonic-gate 	ohci_ed_t		*next_lattice_ept, *lattice_ept;
39767c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept;
39777c478bd9Sstevel@tonic-gate 	uint_t			node;
39787c478bd9Sstevel@tonic-gate 
39797c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
39807c478bd9Sstevel@tonic-gate 
39817c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
39827c478bd9Sstevel@tonic-gate 	    "ohci_insert_isoc_ed:");
39837c478bd9Sstevel@tonic-gate 
39847c478bd9Sstevel@tonic-gate 	/*
39857c478bd9Sstevel@tonic-gate 	 * The appropriate node was found during the opening of the pipe.
39867c478bd9Sstevel@tonic-gate 	 * This  node must be root of the interrupt lattice tree.
39877c478bd9Sstevel@tonic-gate 	 */
39887c478bd9Sstevel@tonic-gate 	node = pp->pp_node;
39897c478bd9Sstevel@tonic-gate 
39907c478bd9Sstevel@tonic-gate 	ASSERT(node == 0);
39917c478bd9Sstevel@tonic-gate 
39927c478bd9Sstevel@tonic-gate 	/* Find the 1ms interrupt lattice endpoint */
39937c478bd9Sstevel@tonic-gate 	lattice_ept = &ohcip->ohci_ed_pool_addr[node];
39947c478bd9Sstevel@tonic-gate 
39957c478bd9Sstevel@tonic-gate 	/* Find the next lattice endpoint */
39967c478bd9Sstevel@tonic-gate 	next_lattice_ept = ohci_ed_iommu_to_cpu(
39977c478bd9Sstevel@tonic-gate 	    ohcip, Get_ED(lattice_ept->hced_next));
39987c478bd9Sstevel@tonic-gate 
39997c478bd9Sstevel@tonic-gate 	while (next_lattice_ept) {
40007c478bd9Sstevel@tonic-gate 		lattice_ept = next_lattice_ept;
40017c478bd9Sstevel@tonic-gate 
40027c478bd9Sstevel@tonic-gate 		/* Find the next lattice endpoint */
40037c478bd9Sstevel@tonic-gate 		next_lattice_ept = ohci_ed_iommu_to_cpu(
40047c478bd9Sstevel@tonic-gate 		    ohcip, Get_ED(lattice_ept->hced_next));
40057c478bd9Sstevel@tonic-gate 	}
40067c478bd9Sstevel@tonic-gate 
40077c478bd9Sstevel@tonic-gate 	/* The next pointer is NULL */
40087c478bd9Sstevel@tonic-gate 	Set_ED(ept->hced_next, NULL);
40097c478bd9Sstevel@tonic-gate 
40107c478bd9Sstevel@tonic-gate 	/* Update the previous pointer */
40117c478bd9Sstevel@tonic-gate 	Set_ED(ept->hced_prev, ohci_ed_cpu_to_iommu(ohcip, lattice_ept));
40127c478bd9Sstevel@tonic-gate 
40137c478bd9Sstevel@tonic-gate 	/* Insert this endpoint into the lattice */
40147c478bd9Sstevel@tonic-gate 	Set_ED(lattice_ept->hced_next, ohci_ed_cpu_to_iommu(ohcip, ept));
40157c478bd9Sstevel@tonic-gate 
40167c478bd9Sstevel@tonic-gate 	/*
40177c478bd9Sstevel@tonic-gate 	 * Enable periodic and isoch lists processing if isoch
40187c478bd9Sstevel@tonic-gate 	 * open pipe count is zero.
40197c478bd9Sstevel@tonic-gate 	 */
40207c478bd9Sstevel@tonic-gate 	if (!ohcip->ohci_open_isoch_pipe_count) {
40217c478bd9Sstevel@tonic-gate 
40227c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control, (Get_OpReg(hcr_control) |
40237c478bd9Sstevel@tonic-gate 		    HCR_CONTROL_PLE | HCR_CONTROL_IE));
40247c478bd9Sstevel@tonic-gate 	}
40257c478bd9Sstevel@tonic-gate 
40267c478bd9Sstevel@tonic-gate 	ohcip->ohci_open_periodic_pipe_count++;
40277c478bd9Sstevel@tonic-gate 	ohcip->ohci_open_isoch_pipe_count++;
40287c478bd9Sstevel@tonic-gate }
40297c478bd9Sstevel@tonic-gate 
40307c478bd9Sstevel@tonic-gate 
40317c478bd9Sstevel@tonic-gate /*
40327c478bd9Sstevel@tonic-gate  * ohci_modify_sKip_bit:
40337c478bd9Sstevel@tonic-gate  *
40347c478bd9Sstevel@tonic-gate  * Modify the sKip bit on the Host Controller (HC) Endpoint Descriptor (ED).
40357c478bd9Sstevel@tonic-gate  */
40367c478bd9Sstevel@tonic-gate static void
40377c478bd9Sstevel@tonic-gate ohci_modify_sKip_bit(
40387c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
40397c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
40407c478bd9Sstevel@tonic-gate 	skip_bit_t		action,
40417c478bd9Sstevel@tonic-gate 	usb_flags_t		flag)
40427c478bd9Sstevel@tonic-gate {
40437c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept;
40447c478bd9Sstevel@tonic-gate 
40457c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
40467c478bd9Sstevel@tonic-gate 	    "ohci_modify_sKip_bit: action = 0x%x flag = 0x%x",
40477c478bd9Sstevel@tonic-gate 	    action, flag);
40487c478bd9Sstevel@tonic-gate 
40497c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
40507c478bd9Sstevel@tonic-gate 
40517c478bd9Sstevel@tonic-gate 	if (action == CLEAR_sKip) {
40527c478bd9Sstevel@tonic-gate 		/*
40537c478bd9Sstevel@tonic-gate 		 * If the skip bit is to be cleared, just clear it.
40547c478bd9Sstevel@tonic-gate 		 * there shouldn't be any race condition problems.
40557c478bd9Sstevel@tonic-gate 		 * If the host controller reads the bit before the
40567c478bd9Sstevel@tonic-gate 		 * driver has a chance to set the bit, the bit will
40577c478bd9Sstevel@tonic-gate 		 * be reread on the next frame.
40587c478bd9Sstevel@tonic-gate 		 */
40597c478bd9Sstevel@tonic-gate 		Set_ED(ept->hced_ctrl, (Get_ED(ept->hced_ctrl) & ~HC_EPT_sKip));
40607c478bd9Sstevel@tonic-gate 	} else {
40617c478bd9Sstevel@tonic-gate 		/* Sync ED and TD pool */
40627c478bd9Sstevel@tonic-gate 		if (flag & OHCI_FLAGS_DMA_SYNC) {
40637c478bd9Sstevel@tonic-gate 			Sync_ED_TD_Pool(ohcip);
40647c478bd9Sstevel@tonic-gate 		}
40657c478bd9Sstevel@tonic-gate 
40667c478bd9Sstevel@tonic-gate 		/* Check Halt or Skip bit is already set */
40677c478bd9Sstevel@tonic-gate 		if ((Get_ED(ept->hced_headp) & HC_EPT_Halt) ||
40687c478bd9Sstevel@tonic-gate 		    (Get_ED(ept->hced_ctrl) & HC_EPT_sKip)) {
40697c478bd9Sstevel@tonic-gate 
40707c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
40717c478bd9Sstevel@tonic-gate 			    "ohci_modify_sKip_bit: "
40727c478bd9Sstevel@tonic-gate 			    "Halt or Skip bit is already set");
40737c478bd9Sstevel@tonic-gate 		} else {
40747c478bd9Sstevel@tonic-gate 			/*
40757c478bd9Sstevel@tonic-gate 			 * The action is to set the skip bit.  In order to
40767c478bd9Sstevel@tonic-gate 			 * be sure that the HCD has seen the sKip bit, wait
40777c478bd9Sstevel@tonic-gate 			 * for the next start of frame.
40787c478bd9Sstevel@tonic-gate 			 */
40797c478bd9Sstevel@tonic-gate 			Set_ED(ept->hced_ctrl,
40807c478bd9Sstevel@tonic-gate 			    (Get_ED(ept->hced_ctrl) | HC_EPT_sKip));
40817c478bd9Sstevel@tonic-gate 
40827c478bd9Sstevel@tonic-gate 			if (flag & OHCI_FLAGS_SLEEP) {
40837c478bd9Sstevel@tonic-gate 				/* Wait for the next SOF */
40847c478bd9Sstevel@tonic-gate 				(void) ohci_wait_for_sof(ohcip);
40857c478bd9Sstevel@tonic-gate 
40867c478bd9Sstevel@tonic-gate 				/* Sync ED and TD pool */
40877c478bd9Sstevel@tonic-gate 				if (flag & OHCI_FLAGS_DMA_SYNC) {
40887c478bd9Sstevel@tonic-gate 					Sync_ED_TD_Pool(ohcip);
40897c478bd9Sstevel@tonic-gate 				}
40907c478bd9Sstevel@tonic-gate 			}
40917c478bd9Sstevel@tonic-gate 		}
40927c478bd9Sstevel@tonic-gate 	}
40937c478bd9Sstevel@tonic-gate }
40947c478bd9Sstevel@tonic-gate 
40957c478bd9Sstevel@tonic-gate 
40967c478bd9Sstevel@tonic-gate /*
40977c478bd9Sstevel@tonic-gate  * ohci_remove_ed:
40987c478bd9Sstevel@tonic-gate  *
40997c478bd9Sstevel@tonic-gate  * Remove the Endpoint Descriptor (ED) from the Host Controller's appropriate
41007c478bd9Sstevel@tonic-gate  * endpoint list.
41017c478bd9Sstevel@tonic-gate  */
41027c478bd9Sstevel@tonic-gate static void
41037c478bd9Sstevel@tonic-gate ohci_remove_ed(
41047c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
41057c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
41067c478bd9Sstevel@tonic-gate {
41077c478bd9Sstevel@tonic-gate 	uchar_t			attributes;
41087c478bd9Sstevel@tonic-gate 
41097c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
41107c478bd9Sstevel@tonic-gate 
41117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
41127c478bd9Sstevel@tonic-gate 	    "ohci_remove_ed:");
41137c478bd9Sstevel@tonic-gate 
41147c478bd9Sstevel@tonic-gate 	attributes = pp->pp_pipe_handle->p_ep.bmAttributes & USB_EP_ATTR_MASK;
41157c478bd9Sstevel@tonic-gate 
41167c478bd9Sstevel@tonic-gate 	switch (attributes) {
41177c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
41187c478bd9Sstevel@tonic-gate 		ohci_remove_ctrl_ed(ohcip, pp);
41197c478bd9Sstevel@tonic-gate 		break;
41207c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
41217c478bd9Sstevel@tonic-gate 		ohci_remove_bulk_ed(ohcip, pp);
41227c478bd9Sstevel@tonic-gate 		break;
41237c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
41247c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
41257c478bd9Sstevel@tonic-gate 		ohci_remove_periodic_ed(ohcip, pp);
41267c478bd9Sstevel@tonic-gate 		break;
41277c478bd9Sstevel@tonic-gate 	}
41287c478bd9Sstevel@tonic-gate }
41297c478bd9Sstevel@tonic-gate 
41307c478bd9Sstevel@tonic-gate 
41317c478bd9Sstevel@tonic-gate /*
41327c478bd9Sstevel@tonic-gate  * ohci_remove_ctrl_ed:
41337c478bd9Sstevel@tonic-gate  *
41347c478bd9Sstevel@tonic-gate  * Remove a control Endpoint Descriptor (ED) from the Host Controller's (HC)
41357c478bd9Sstevel@tonic-gate  * control endpoint list.
41367c478bd9Sstevel@tonic-gate  */
41377c478bd9Sstevel@tonic-gate static void
41387c478bd9Sstevel@tonic-gate ohci_remove_ctrl_ed(
41397c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
41407c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
41417c478bd9Sstevel@tonic-gate {
41427c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept; /* ept to be removed */
41437c478bd9Sstevel@tonic-gate 
41447c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
41457c478bd9Sstevel@tonic-gate 	    "ohci_remove_ctrl_ed:");
41467c478bd9Sstevel@tonic-gate 
41477c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
41487c478bd9Sstevel@tonic-gate 
41497c478bd9Sstevel@tonic-gate 	/* The control list should already be stopped */
41507c478bd9Sstevel@tonic-gate 	ASSERT(!(Get_OpReg(hcr_control) & HCR_CONTROL_CLE));
41517c478bd9Sstevel@tonic-gate 
41527c478bd9Sstevel@tonic-gate 	ohcip->ohci_open_ctrl_pipe_count--;
41537c478bd9Sstevel@tonic-gate 
41547c478bd9Sstevel@tonic-gate 	/* Detach the endpoint from the list that it's on */
41557c478bd9Sstevel@tonic-gate 	ohci_detach_ed_from_list(ohcip, ept, USB_EP_ATTR_CONTROL);
41567c478bd9Sstevel@tonic-gate 
41577c478bd9Sstevel@tonic-gate 	/*
41587c478bd9Sstevel@tonic-gate 	 * If next endpoint pointed by endpoint to be removed is not NULL
41597c478bd9Sstevel@tonic-gate 	 * then set current control pointer to the next endpoint pointed by
41607c478bd9Sstevel@tonic-gate 	 * endpoint to be removed. Otherwise set current control pointer to
41617c478bd9Sstevel@tonic-gate 	 * the beginning of the control list.
41627c478bd9Sstevel@tonic-gate 	 */
41637c478bd9Sstevel@tonic-gate 	if (Get_ED(ept->hced_next)) {
41647c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_ctrl_curr, Get_ED(ept->hced_next));
41657c478bd9Sstevel@tonic-gate 	} else {
41667c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_ctrl_curr, Get_OpReg(hcr_ctrl_head));
41677c478bd9Sstevel@tonic-gate 	}
41687c478bd9Sstevel@tonic-gate 
41697c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_open_ctrl_pipe_count) {
41707c478bd9Sstevel@tonic-gate 		ASSERT(Get_OpReg(hcr_ctrl_head));
41717c478bd9Sstevel@tonic-gate 
41727c478bd9Sstevel@tonic-gate 		/* Reenable the control list */
41737c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control,
41747c478bd9Sstevel@tonic-gate 		    (Get_OpReg(hcr_control) | HCR_CONTROL_CLE));
41757c478bd9Sstevel@tonic-gate 	}
41767c478bd9Sstevel@tonic-gate 
41777c478bd9Sstevel@tonic-gate 	ohci_insert_ed_on_reclaim_list(ohcip, pp);
41787c478bd9Sstevel@tonic-gate }
41797c478bd9Sstevel@tonic-gate 
41807c478bd9Sstevel@tonic-gate 
41817c478bd9Sstevel@tonic-gate /*
41827c478bd9Sstevel@tonic-gate  * ohci_remove_bulk_ed:
41837c478bd9Sstevel@tonic-gate  *
41847c478bd9Sstevel@tonic-gate  * Remove free the  bulk Endpoint Descriptor (ED) from the Host Controller's
41857c478bd9Sstevel@tonic-gate  * (HC) bulk endpoint list.
41867c478bd9Sstevel@tonic-gate  */
41877c478bd9Sstevel@tonic-gate static void
41887c478bd9Sstevel@tonic-gate ohci_remove_bulk_ed(
41897c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
41907c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
41917c478bd9Sstevel@tonic-gate {
41927c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept;	/* ept to be removed */
41937c478bd9Sstevel@tonic-gate 
41947c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
41957c478bd9Sstevel@tonic-gate 	    "ohci_remove_bulk_ed:");
41967c478bd9Sstevel@tonic-gate 
41977c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
41987c478bd9Sstevel@tonic-gate 
41997c478bd9Sstevel@tonic-gate 	/* The bulk list should already be stopped */
42007c478bd9Sstevel@tonic-gate 	ASSERT(!(Get_OpReg(hcr_control) & HCR_CONTROL_BLE));
42017c478bd9Sstevel@tonic-gate 
42027c478bd9Sstevel@tonic-gate 	ohcip->ohci_open_bulk_pipe_count--;
42037c478bd9Sstevel@tonic-gate 
42047c478bd9Sstevel@tonic-gate 	/* Detach the endpoint from the bulk list */
42057c478bd9Sstevel@tonic-gate 	ohci_detach_ed_from_list(ohcip, ept, USB_EP_ATTR_BULK);
42067c478bd9Sstevel@tonic-gate 
42077c478bd9Sstevel@tonic-gate 	/*
42087c478bd9Sstevel@tonic-gate 	 * If next endpoint pointed by endpoint to be removed is not NULL
42097c478bd9Sstevel@tonic-gate 	 * then set current bulk pointer to the next endpoint pointed by
42107c478bd9Sstevel@tonic-gate 	 * endpoint to be removed. Otherwise set current bulk pointer to
42117c478bd9Sstevel@tonic-gate 	 * the beginning of the bulk list.
42127c478bd9Sstevel@tonic-gate 	 */
42137c478bd9Sstevel@tonic-gate 	if (Get_ED(ept->hced_next)) {
42147c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_bulk_curr, Get_ED(ept->hced_next));
42157c478bd9Sstevel@tonic-gate 	} else {
42167c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_bulk_curr, Get_OpReg(hcr_bulk_head));
42177c478bd9Sstevel@tonic-gate 	}
42187c478bd9Sstevel@tonic-gate 
42197c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_open_bulk_pipe_count) {
42207c478bd9Sstevel@tonic-gate 		ASSERT(Get_OpReg(hcr_bulk_head));
42217c478bd9Sstevel@tonic-gate 
42227c478bd9Sstevel@tonic-gate 		/* Re-enable the bulk list */
42237c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control,
42247c478bd9Sstevel@tonic-gate 		    (Get_OpReg(hcr_control) | HCR_CONTROL_BLE));
42257c478bd9Sstevel@tonic-gate 	}
42267c478bd9Sstevel@tonic-gate 
42277c478bd9Sstevel@tonic-gate 	ohci_insert_ed_on_reclaim_list(ohcip, pp);
42287c478bd9Sstevel@tonic-gate }
42297c478bd9Sstevel@tonic-gate 
42307c478bd9Sstevel@tonic-gate 
42317c478bd9Sstevel@tonic-gate /*
42327c478bd9Sstevel@tonic-gate  * ohci_remove_periodic_ed:
42337c478bd9Sstevel@tonic-gate  *
42347c478bd9Sstevel@tonic-gate  * Set up an periodic endpoint to be removed from the Host Controller's (HC)
42357c478bd9Sstevel@tonic-gate  * interrupt lattice tree. The Endpoint Descriptor (ED) will be freed in the
42367c478bd9Sstevel@tonic-gate  * interrupt handler.
42377c478bd9Sstevel@tonic-gate  */
42387c478bd9Sstevel@tonic-gate static void
42397c478bd9Sstevel@tonic-gate ohci_remove_periodic_ed(
42407c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
42417c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
42427c478bd9Sstevel@tonic-gate {
42437c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept;	/* ept to be removed */
42447c478bd9Sstevel@tonic-gate 	uint_t			ept_type;
42457c478bd9Sstevel@tonic-gate 
42467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
42477c478bd9Sstevel@tonic-gate 	    "ohci_remove_periodic_ed:");
42487c478bd9Sstevel@tonic-gate 
42497c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
42507c478bd9Sstevel@tonic-gate 
42517c478bd9Sstevel@tonic-gate 	ASSERT((Get_ED(ept->hced_tailp) & HC_EPT_TD_TAIL) ==
42527c478bd9Sstevel@tonic-gate 	    (Get_ED(ept->hced_headp) & HC_EPT_TD_HEAD));
42537c478bd9Sstevel@tonic-gate 
42547c478bd9Sstevel@tonic-gate 	ohcip->ohci_open_periodic_pipe_count--;
42557c478bd9Sstevel@tonic-gate 
42567c478bd9Sstevel@tonic-gate 	ept_type = pp->pp_pipe_handle->
42577c478bd9Sstevel@tonic-gate 			p_ep.bmAttributes & USB_EP_ATTR_MASK;
42587c478bd9Sstevel@tonic-gate 
42597c478bd9Sstevel@tonic-gate 	if (ept_type == USB_EP_ATTR_ISOCH) {
42607c478bd9Sstevel@tonic-gate 		ohcip->ohci_open_isoch_pipe_count--;
42617c478bd9Sstevel@tonic-gate 	}
42627c478bd9Sstevel@tonic-gate 
42637c478bd9Sstevel@tonic-gate 	/* Store the node number */
42647c478bd9Sstevel@tonic-gate 	Set_ED(ept->hced_node, pp->pp_node);
42657c478bd9Sstevel@tonic-gate 
42667c478bd9Sstevel@tonic-gate 	/* Remove the endpoint from interrupt lattice tree */
42677c478bd9Sstevel@tonic-gate 	ohci_detach_ed_from_list(ohcip, ept, ept_type);
42687c478bd9Sstevel@tonic-gate 
42697c478bd9Sstevel@tonic-gate 	/*
42707c478bd9Sstevel@tonic-gate 	 * Disable isoch list processing if isoch open pipe count
42717c478bd9Sstevel@tonic-gate 	 * is zero.
42727c478bd9Sstevel@tonic-gate 	 */
42737c478bd9Sstevel@tonic-gate 	if (!ohcip->ohci_open_isoch_pipe_count) {
42747c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control,
42757c478bd9Sstevel@tonic-gate 		    (Get_OpReg(hcr_control) & ~(HCR_CONTROL_IE)));
42767c478bd9Sstevel@tonic-gate 	}
42777c478bd9Sstevel@tonic-gate 
42787c478bd9Sstevel@tonic-gate 	/*
42797c478bd9Sstevel@tonic-gate 	 * Disable periodic list processing if periodic (interrupt
42807c478bd9Sstevel@tonic-gate 	 * and isochrous) open pipe count is zero.
42817c478bd9Sstevel@tonic-gate 	 */
42827c478bd9Sstevel@tonic-gate 	if (!ohcip->ohci_open_periodic_pipe_count) {
42837c478bd9Sstevel@tonic-gate 		ASSERT(!ohcip->ohci_open_isoch_pipe_count);
42847c478bd9Sstevel@tonic-gate 
42857c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_control,
42867c478bd9Sstevel@tonic-gate 		    (Get_OpReg(hcr_control) & ~(HCR_CONTROL_PLE)));
42877c478bd9Sstevel@tonic-gate 	}
42887c478bd9Sstevel@tonic-gate 
42897c478bd9Sstevel@tonic-gate 	ohci_insert_ed_on_reclaim_list(ohcip, pp);
42907c478bd9Sstevel@tonic-gate }
42917c478bd9Sstevel@tonic-gate 
42927c478bd9Sstevel@tonic-gate 
42937c478bd9Sstevel@tonic-gate /*
42947c478bd9Sstevel@tonic-gate  * ohci_detach_ed_from_list:
42957c478bd9Sstevel@tonic-gate  *
42967c478bd9Sstevel@tonic-gate  * Remove the Endpoint Descriptor (ED) from the appropriate Host Controller's
42977c478bd9Sstevel@tonic-gate  * (HC) endpoint list.
42987c478bd9Sstevel@tonic-gate  */
42997c478bd9Sstevel@tonic-gate static void
43007c478bd9Sstevel@tonic-gate ohci_detach_ed_from_list(
43017c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
43027c478bd9Sstevel@tonic-gate 	ohci_ed_t	*ept,
43037c478bd9Sstevel@tonic-gate 	uint_t		ept_type)
43047c478bd9Sstevel@tonic-gate {
43057c478bd9Sstevel@tonic-gate 	ohci_ed_t	*prev_ept;	/* Previous endpoint */
43067c478bd9Sstevel@tonic-gate 	ohci_ed_t	*next_ept;	/* Endpoint after one to be removed */
43077c478bd9Sstevel@tonic-gate 	uint_t		node;
43087c478bd9Sstevel@tonic-gate 
43097c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
43107c478bd9Sstevel@tonic-gate 	    "ohci_detach_ed_from_list:");
43117c478bd9Sstevel@tonic-gate 
43127c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
43137c478bd9Sstevel@tonic-gate 
43147c478bd9Sstevel@tonic-gate 	prev_ept = ohci_ed_iommu_to_cpu(ohcip, Get_ED(ept->hced_prev));
43157c478bd9Sstevel@tonic-gate 	next_ept = ohci_ed_iommu_to_cpu(ohcip, Get_ED(ept->hced_next));
43167c478bd9Sstevel@tonic-gate 
43177c478bd9Sstevel@tonic-gate 	/*
43187c478bd9Sstevel@tonic-gate 	 * If there is no previous endpoint, then this
43197c478bd9Sstevel@tonic-gate 	 * endpoint is at the head of the endpoint list.
43207c478bd9Sstevel@tonic-gate 	 */
43217c478bd9Sstevel@tonic-gate 	if (prev_ept == NULL) {
43227c478bd9Sstevel@tonic-gate 		if (next_ept) {
43237c478bd9Sstevel@tonic-gate 			/*
43247c478bd9Sstevel@tonic-gate 			 * If this endpoint is the first element of the
43257c478bd9Sstevel@tonic-gate 			 * list and there is more  than one endpoint on
43267c478bd9Sstevel@tonic-gate 			 * the list then perform specific actions based
43277c478bd9Sstevel@tonic-gate 			 * on the type of endpoint list.
43287c478bd9Sstevel@tonic-gate 			 */
43297c478bd9Sstevel@tonic-gate 			switch (ept_type) {
43307c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_CONTROL:
43317c478bd9Sstevel@tonic-gate 				/* Set the head of list to next ept */
43327c478bd9Sstevel@tonic-gate 				Set_OpReg(hcr_ctrl_head,
43337c478bd9Sstevel@tonic-gate 				    Get_ED(ept->hced_next));
43347c478bd9Sstevel@tonic-gate 
43357c478bd9Sstevel@tonic-gate 				/* Clear prev ptr of  next endpoint */
43367c478bd9Sstevel@tonic-gate 				Set_ED(next_ept->hced_prev,  NULL);
43377c478bd9Sstevel@tonic-gate 				break;
43387c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_BULK:
43397c478bd9Sstevel@tonic-gate 				/* Set the head of list to next ept */
43407c478bd9Sstevel@tonic-gate 				Set_OpReg(hcr_bulk_head,
43417c478bd9Sstevel@tonic-gate 				    Get_ED(ept->hced_next));
43427c478bd9Sstevel@tonic-gate 
43437c478bd9Sstevel@tonic-gate 				/* Clear prev ptr of  next endpoint */
43447c478bd9Sstevel@tonic-gate 				Set_ED(next_ept->hced_prev, NULL);
43457c478bd9Sstevel@tonic-gate 				break;
43467c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_INTR:
43477c478bd9Sstevel@tonic-gate 				/*
43487c478bd9Sstevel@tonic-gate 				 * HCCA area should point
43497c478bd9Sstevel@tonic-gate 				 * directly to this ept.
43507c478bd9Sstevel@tonic-gate 				 */
43517c478bd9Sstevel@tonic-gate 				ASSERT(Get_ED(ept->hced_node) >=
43527c478bd9Sstevel@tonic-gate 				    NUM_STATIC_NODES);
43537c478bd9Sstevel@tonic-gate 
43547c478bd9Sstevel@tonic-gate 				/* Get the hcca interrupt table index */
43557c478bd9Sstevel@tonic-gate 				node = ohci_hcca_intr_index(
43567c478bd9Sstevel@tonic-gate 				    Get_ED(ept->hced_node));
43577c478bd9Sstevel@tonic-gate 
43587c478bd9Sstevel@tonic-gate 				/*
43597c478bd9Sstevel@tonic-gate 				 * Delete the ept from the
43607c478bd9Sstevel@tonic-gate 				 * bottom of the tree.
43617c478bd9Sstevel@tonic-gate 				 */
43627c478bd9Sstevel@tonic-gate 				Set_HCCA(ohcip->ohci_hccap->
43637c478bd9Sstevel@tonic-gate 				    HccaIntTble[node], Get_ED(ept->hced_next));
43647c478bd9Sstevel@tonic-gate 
43657c478bd9Sstevel@tonic-gate 				/*
43667c478bd9Sstevel@tonic-gate 				 * Update the previous pointer
43677c478bd9Sstevel@tonic-gate 				 * of ept->hced_next
43687c478bd9Sstevel@tonic-gate 				 */
43697c478bd9Sstevel@tonic-gate 				if (Get_ED(next_ept->hced_state) !=
43707c478bd9Sstevel@tonic-gate 				    HC_EPT_STATIC) {
43717c478bd9Sstevel@tonic-gate 
43727c478bd9Sstevel@tonic-gate 					Set_ED(next_ept->hced_prev, NULL);
43737c478bd9Sstevel@tonic-gate 				}
43747c478bd9Sstevel@tonic-gate 
43757c478bd9Sstevel@tonic-gate 				break;
43767c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_ISOCH:
43777c478bd9Sstevel@tonic-gate 			default:
43787c478bd9Sstevel@tonic-gate 				break;
43797c478bd9Sstevel@tonic-gate 			}
43807c478bd9Sstevel@tonic-gate 		} else {
43817c478bd9Sstevel@tonic-gate 			/*
43827c478bd9Sstevel@tonic-gate 			 * If there was only one element on the list
43837c478bd9Sstevel@tonic-gate 			 * perform specific actions based on the type
43847c478bd9Sstevel@tonic-gate 			 * of the list.
43857c478bd9Sstevel@tonic-gate 			 */
43867c478bd9Sstevel@tonic-gate 			switch (ept_type) {
43877c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_CONTROL:
43887c478bd9Sstevel@tonic-gate 				/* Set the head to NULL */
43897c478bd9Sstevel@tonic-gate 				Set_OpReg(hcr_ctrl_head, NULL);
43907c478bd9Sstevel@tonic-gate 				break;
43917c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_BULK:
43927c478bd9Sstevel@tonic-gate 				/* Set the head to NULL */
43937c478bd9Sstevel@tonic-gate 				Set_OpReg(hcr_bulk_head, NULL);
43947c478bd9Sstevel@tonic-gate 				break;
43957c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_INTR:
43967c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_ISOCH:
43977c478bd9Sstevel@tonic-gate 			default:
43987c478bd9Sstevel@tonic-gate 				break;
43997c478bd9Sstevel@tonic-gate 			}
44007c478bd9Sstevel@tonic-gate 		}
44017c478bd9Sstevel@tonic-gate 	} else {
44027c478bd9Sstevel@tonic-gate 		/* The previous ept points to the next one */
44037c478bd9Sstevel@tonic-gate 		Set_ED(prev_ept->hced_next, Get_ED(ept->hced_next));
44047c478bd9Sstevel@tonic-gate 
44057c478bd9Sstevel@tonic-gate 		/*
44067c478bd9Sstevel@tonic-gate 		 * Set the previous ptr of the next_ept to prev_ept
44077c478bd9Sstevel@tonic-gate 		 * if this isn't the last endpoint on the list
44087c478bd9Sstevel@tonic-gate 		 */
44097c478bd9Sstevel@tonic-gate 		if ((next_ept) &&
44107c478bd9Sstevel@tonic-gate 		    (Get_ED(next_ept->hced_state) != HC_EPT_STATIC)) {
44117c478bd9Sstevel@tonic-gate 
44127c478bd9Sstevel@tonic-gate 			/* Set the previous ptr of the next one */
44137c478bd9Sstevel@tonic-gate 			Set_ED(next_ept->hced_prev, Get_ED(ept->hced_prev));
44147c478bd9Sstevel@tonic-gate 		}
44157c478bd9Sstevel@tonic-gate 	}
44167c478bd9Sstevel@tonic-gate }
44177c478bd9Sstevel@tonic-gate 
44187c478bd9Sstevel@tonic-gate 
44197c478bd9Sstevel@tonic-gate /*
44207c478bd9Sstevel@tonic-gate  * ohci_insert_ed_on_reclaim_list:
44217c478bd9Sstevel@tonic-gate  *
44227c478bd9Sstevel@tonic-gate  * Insert Endpoint onto the reclaim list
44237c478bd9Sstevel@tonic-gate  */
44247c478bd9Sstevel@tonic-gate static void
44257c478bd9Sstevel@tonic-gate ohci_insert_ed_on_reclaim_list(
44267c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
44277c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
44287c478bd9Sstevel@tonic-gate {
44297c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept; /* ept to be removed */
44307c478bd9Sstevel@tonic-gate 	ohci_ed_t		*next_ept, *prev_ept;
44317c478bd9Sstevel@tonic-gate 	usb_frame_number_t	frame_number;
44327c478bd9Sstevel@tonic-gate 
44337c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
44347c478bd9Sstevel@tonic-gate 
44357c478bd9Sstevel@tonic-gate 	/*
44367c478bd9Sstevel@tonic-gate 	 * Read current usb frame number and add appropriate number of
44377c478bd9Sstevel@tonic-gate 	 * usb frames needs to wait before reclaiming current endpoint.
44387c478bd9Sstevel@tonic-gate 	 */
44397c478bd9Sstevel@tonic-gate 	frame_number =
44407c478bd9Sstevel@tonic-gate 	    ohci_get_current_frame_number(ohcip) + MAX_SOF_WAIT_COUNT;
44417c478bd9Sstevel@tonic-gate 
44427c478bd9Sstevel@tonic-gate 	/* Store 32bit ID */
44437c478bd9Sstevel@tonic-gate 	Set_ED(ept->hced_reclaim_frame,
44447c478bd9Sstevel@tonic-gate 	    ((uint32_t)(OHCI_GET_ID((void *)(uintptr_t)frame_number))));
44457c478bd9Sstevel@tonic-gate 
44467c478bd9Sstevel@tonic-gate 	/* Insert the endpoint onto the reclaimation list */
44477c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_reclaim_list) {
44487c478bd9Sstevel@tonic-gate 		next_ept = ohcip->ohci_reclaim_list;
44497c478bd9Sstevel@tonic-gate 
44507c478bd9Sstevel@tonic-gate 		while (next_ept) {
44517c478bd9Sstevel@tonic-gate 			prev_ept = next_ept;
44527c478bd9Sstevel@tonic-gate 			next_ept = ohci_ed_iommu_to_cpu(ohcip,
44537c478bd9Sstevel@tonic-gate 			    Get_ED(next_ept->hced_reclaim_next));
44547c478bd9Sstevel@tonic-gate 		}
44557c478bd9Sstevel@tonic-gate 
44567c478bd9Sstevel@tonic-gate 		Set_ED(prev_ept->hced_reclaim_next,
44577c478bd9Sstevel@tonic-gate 		    ohci_ed_cpu_to_iommu(ohcip, ept));
44587c478bd9Sstevel@tonic-gate 	} else {
44597c478bd9Sstevel@tonic-gate 		ohcip->ohci_reclaim_list = ept;
44607c478bd9Sstevel@tonic-gate 	}
44617c478bd9Sstevel@tonic-gate 
44627c478bd9Sstevel@tonic-gate 	ASSERT(Get_ED(ept->hced_reclaim_next) == NULL);
44637c478bd9Sstevel@tonic-gate 
44647c478bd9Sstevel@tonic-gate 	/* Enable the SOF interrupt */
44657c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_enable, HCR_INTR_SOF);
44667c478bd9Sstevel@tonic-gate }
44677c478bd9Sstevel@tonic-gate 
44687c478bd9Sstevel@tonic-gate 
44697c478bd9Sstevel@tonic-gate /*
44707c478bd9Sstevel@tonic-gate  * ohci_deallocate_ed:
44717c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
44727c478bd9Sstevel@tonic-gate  *
44737c478bd9Sstevel@tonic-gate  * Deallocate a Host Controller's (HC) Endpoint Descriptor (ED).
44747c478bd9Sstevel@tonic-gate  */
44757c478bd9Sstevel@tonic-gate void
44767c478bd9Sstevel@tonic-gate ohci_deallocate_ed(
44777c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
44787c478bd9Sstevel@tonic-gate 	ohci_ed_t	*old_ed)
44797c478bd9Sstevel@tonic-gate {
44807c478bd9Sstevel@tonic-gate 	ohci_td_t	*dummy_td;
44817c478bd9Sstevel@tonic-gate 
44827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
44837c478bd9Sstevel@tonic-gate 	    "ohci_deallocate_ed:");
44847c478bd9Sstevel@tonic-gate 
44857c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
44867c478bd9Sstevel@tonic-gate 
44877c478bd9Sstevel@tonic-gate 	dummy_td = ohci_td_iommu_to_cpu(ohcip, Get_ED(old_ed->hced_headp));
44887c478bd9Sstevel@tonic-gate 
44897c478bd9Sstevel@tonic-gate 	if (dummy_td) {
44907c478bd9Sstevel@tonic-gate 
44917c478bd9Sstevel@tonic-gate 		ASSERT(Get_TD(dummy_td->hctd_state) == HC_TD_DUMMY);
44927c478bd9Sstevel@tonic-gate 		ohci_deallocate_td(ohcip, dummy_td);
44937c478bd9Sstevel@tonic-gate 	}
44947c478bd9Sstevel@tonic-gate 
44957c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
44967c478bd9Sstevel@tonic-gate 	    "ohci_deallocate_ed: Deallocated 0x%p", (void *)old_ed);
44977c478bd9Sstevel@tonic-gate 
44987c478bd9Sstevel@tonic-gate 	bzero((void *)old_ed, sizeof (ohci_ed_t));
44997c478bd9Sstevel@tonic-gate 	Set_ED(old_ed->hced_state, HC_EPT_FREE);
45007c478bd9Sstevel@tonic-gate }
45017c478bd9Sstevel@tonic-gate 
45027c478bd9Sstevel@tonic-gate 
45037c478bd9Sstevel@tonic-gate /*
45047c478bd9Sstevel@tonic-gate  * ohci_ed_cpu_to_iommu:
45057c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
45067c478bd9Sstevel@tonic-gate  *
45077c478bd9Sstevel@tonic-gate  * This function converts for the given Endpoint Descriptor (ED) CPU address
45087c478bd9Sstevel@tonic-gate  * to IO address.
45097c478bd9Sstevel@tonic-gate  */
45107c478bd9Sstevel@tonic-gate uint32_t
45117c478bd9Sstevel@tonic-gate ohci_ed_cpu_to_iommu(
45127c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
45137c478bd9Sstevel@tonic-gate 	ohci_ed_t	*addr)
45147c478bd9Sstevel@tonic-gate {
45157c478bd9Sstevel@tonic-gate 	uint32_t	ed;
45167c478bd9Sstevel@tonic-gate 
45177c478bd9Sstevel@tonic-gate 	ed = (uint32_t)ohcip->ohci_ed_pool_cookie.dmac_address +
45187c478bd9Sstevel@tonic-gate 	    (uint32_t)((uintptr_t)addr - (uintptr_t)(ohcip->ohci_ed_pool_addr));
45197c478bd9Sstevel@tonic-gate 
45207c478bd9Sstevel@tonic-gate 	ASSERT(ed >= ohcip->ohci_ed_pool_cookie.dmac_address);
45217c478bd9Sstevel@tonic-gate 	ASSERT(ed <= ohcip->ohci_ed_pool_cookie.dmac_address +
45227c478bd9Sstevel@tonic-gate 	    sizeof (ohci_ed_t) * ohci_ed_pool_size);
45237c478bd9Sstevel@tonic-gate 
45247c478bd9Sstevel@tonic-gate 	return (ed);
45257c478bd9Sstevel@tonic-gate }
45267c478bd9Sstevel@tonic-gate 
45277c478bd9Sstevel@tonic-gate 
45287c478bd9Sstevel@tonic-gate /*
45297c478bd9Sstevel@tonic-gate  * ohci_ed_iommu_to_cpu:
45307c478bd9Sstevel@tonic-gate  *
45317c478bd9Sstevel@tonic-gate  * This function converts for the given Endpoint Descriptor (ED) IO address
45327c478bd9Sstevel@tonic-gate  * to CPU address.
45337c478bd9Sstevel@tonic-gate  */
45347c478bd9Sstevel@tonic-gate static ohci_ed_t *
45357c478bd9Sstevel@tonic-gate ohci_ed_iommu_to_cpu(
45367c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
45377c478bd9Sstevel@tonic-gate 	uintptr_t	addr)
45387c478bd9Sstevel@tonic-gate {
45397c478bd9Sstevel@tonic-gate 	ohci_ed_t	*ed;
45407c478bd9Sstevel@tonic-gate 
45417c478bd9Sstevel@tonic-gate 	if (addr == NULL) {
45427c478bd9Sstevel@tonic-gate 
45437c478bd9Sstevel@tonic-gate 		return (NULL);
45447c478bd9Sstevel@tonic-gate 	}
45457c478bd9Sstevel@tonic-gate 
45467c478bd9Sstevel@tonic-gate 	ed = (ohci_ed_t *)((uintptr_t)
45477c478bd9Sstevel@tonic-gate 	    (addr - ohcip->ohci_ed_pool_cookie.dmac_address) +
45487c478bd9Sstevel@tonic-gate 	    (uintptr_t)ohcip->ohci_ed_pool_addr);
45497c478bd9Sstevel@tonic-gate 
45507c478bd9Sstevel@tonic-gate 	ASSERT(ed >= ohcip->ohci_ed_pool_addr);
45517c478bd9Sstevel@tonic-gate 	ASSERT((uintptr_t)ed <= (uintptr_t)ohcip->ohci_ed_pool_addr +
45527c478bd9Sstevel@tonic-gate 	    (uintptr_t)(sizeof (ohci_ed_t) * ohci_ed_pool_size));
45537c478bd9Sstevel@tonic-gate 
45547c478bd9Sstevel@tonic-gate 	return (ed);
45557c478bd9Sstevel@tonic-gate }
45567c478bd9Sstevel@tonic-gate 
45577c478bd9Sstevel@tonic-gate 
45587c478bd9Sstevel@tonic-gate /*
45597c478bd9Sstevel@tonic-gate  * Transfer Descriptor manipulations functions
45607c478bd9Sstevel@tonic-gate  */
45617c478bd9Sstevel@tonic-gate 
45627c478bd9Sstevel@tonic-gate /*
45637c478bd9Sstevel@tonic-gate  * ohci_initialize_dummy:
45647c478bd9Sstevel@tonic-gate  *
45657c478bd9Sstevel@tonic-gate  * An Endpoint Descriptor (ED) has a  dummy Transfer Descriptor (TD) on the
45667c478bd9Sstevel@tonic-gate  * end of its TD list. Initially, both the head and tail pointers of the ED
45677c478bd9Sstevel@tonic-gate  * point to the dummy TD.
45687c478bd9Sstevel@tonic-gate  */
45697c478bd9Sstevel@tonic-gate static int
45707c478bd9Sstevel@tonic-gate ohci_initialize_dummy(
45717c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
45727c478bd9Sstevel@tonic-gate 	ohci_ed_t	*ept)
45737c478bd9Sstevel@tonic-gate {
45747c478bd9Sstevel@tonic-gate 	ohci_td_t *dummy;
45757c478bd9Sstevel@tonic-gate 
45767c478bd9Sstevel@tonic-gate 	/* Obtain a  dummy TD */
45777c478bd9Sstevel@tonic-gate 	dummy = ohci_allocate_td_from_pool(ohcip);
45787c478bd9Sstevel@tonic-gate 
45797c478bd9Sstevel@tonic-gate 	if (dummy == NULL) {
45807c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
45817c478bd9Sstevel@tonic-gate 	}
45827c478bd9Sstevel@tonic-gate 
45837c478bd9Sstevel@tonic-gate 	/*
45847c478bd9Sstevel@tonic-gate 	 * Both the head and tail pointers of an ED point
45857c478bd9Sstevel@tonic-gate 	 * to this new dummy TD.
45867c478bd9Sstevel@tonic-gate 	 */
45877c478bd9Sstevel@tonic-gate 	Set_ED(ept->hced_headp, (ohci_td_cpu_to_iommu(ohcip, dummy)));
45887c478bd9Sstevel@tonic-gate 	Set_ED(ept->hced_tailp, (ohci_td_cpu_to_iommu(ohcip, dummy)));
45897c478bd9Sstevel@tonic-gate 
45907c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
45917c478bd9Sstevel@tonic-gate }
45927c478bd9Sstevel@tonic-gate 
45937c478bd9Sstevel@tonic-gate /*
45947c478bd9Sstevel@tonic-gate  * ohci_allocate_ctrl_resources:
45957c478bd9Sstevel@tonic-gate  *
45967c478bd9Sstevel@tonic-gate  * Calculates the number of tds necessary for a ctrl transfer, and allocates
45977c478bd9Sstevel@tonic-gate  * all the resources necessary.
45987c478bd9Sstevel@tonic-gate  *
45997c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise TW.
46007c478bd9Sstevel@tonic-gate  */
46017c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t *
46027c478bd9Sstevel@tonic-gate ohci_allocate_ctrl_resources(
46037c478bd9Sstevel@tonic-gate 	ohci_state_t 		*ohcip,
46047c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
46057c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp,
46067c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
46077c478bd9Sstevel@tonic-gate {
46087c478bd9Sstevel@tonic-gate 	size_t 			td_count = 2;
46097c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
46107c478bd9Sstevel@tonic-gate 
46117c478bd9Sstevel@tonic-gate 	/* Add one more td for data phase */
46127c478bd9Sstevel@tonic-gate 	if (ctrl_reqp->ctrl_wLength) {
46137c478bd9Sstevel@tonic-gate 		td_count++;
46147c478bd9Sstevel@tonic-gate 	}
46157c478bd9Sstevel@tonic-gate 
46167c478bd9Sstevel@tonic-gate 	tw = ohci_allocate_tw_resources(ohcip, pp,
46177c478bd9Sstevel@tonic-gate 	    ctrl_reqp->ctrl_wLength + SETUP_SIZE,
46187c478bd9Sstevel@tonic-gate 	    usb_flags, td_count);
46197c478bd9Sstevel@tonic-gate 
46207c478bd9Sstevel@tonic-gate 	return (tw);
46217c478bd9Sstevel@tonic-gate }
46227c478bd9Sstevel@tonic-gate 
46237c478bd9Sstevel@tonic-gate /*
46247c478bd9Sstevel@tonic-gate  * ohci_insert_ctrl_req:
46257c478bd9Sstevel@tonic-gate  *
46267c478bd9Sstevel@tonic-gate  * Create a Transfer Descriptor (TD) and a data buffer for a control endpoint.
46277c478bd9Sstevel@tonic-gate  */
46287c478bd9Sstevel@tonic-gate /* ARGSUSED */
46297c478bd9Sstevel@tonic-gate static void
46307c478bd9Sstevel@tonic-gate ohci_insert_ctrl_req(
46317c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
46327c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
46337c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp,
46347c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
46357c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
46367c478bd9Sstevel@tonic-gate {
46377c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
46387c478bd9Sstevel@tonic-gate 	uchar_t			bmRequestType = ctrl_reqp->ctrl_bmRequestType;
46397c478bd9Sstevel@tonic-gate 	uchar_t			bRequest = ctrl_reqp->ctrl_bRequest;
46407c478bd9Sstevel@tonic-gate 	uint16_t		wValue = ctrl_reqp->ctrl_wValue;
46417c478bd9Sstevel@tonic-gate 	uint16_t		wIndex = ctrl_reqp->ctrl_wIndex;
46427c478bd9Sstevel@tonic-gate 	uint16_t		wLength = ctrl_reqp->ctrl_wLength;
46437c478bd9Sstevel@tonic-gate 	mblk_t			*data = ctrl_reqp->ctrl_data;
46447c478bd9Sstevel@tonic-gate 	uint32_t		ctrl = 0;
46457c478bd9Sstevel@tonic-gate 	int			sdata;
46467c478bd9Sstevel@tonic-gate 
46477c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
46487c478bd9Sstevel@tonic-gate 	    "ohci_insert_ctrl_req:");
46497c478bd9Sstevel@tonic-gate 
46507c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
46517c478bd9Sstevel@tonic-gate 
46527c478bd9Sstevel@tonic-gate 	/*
46537c478bd9Sstevel@tonic-gate 	 * Save current control request pointer and timeout values
46547c478bd9Sstevel@tonic-gate 	 * in transfer wrapper.
46557c478bd9Sstevel@tonic-gate 	 */
46567c478bd9Sstevel@tonic-gate 	tw->tw_curr_xfer_reqp = (usb_opaque_t)ctrl_reqp;
46577c478bd9Sstevel@tonic-gate 	tw->tw_timeout = ctrl_reqp->ctrl_timeout ?
46587c478bd9Sstevel@tonic-gate 	    ctrl_reqp->ctrl_timeout : OHCI_DEFAULT_XFER_TIMEOUT;
46597c478bd9Sstevel@tonic-gate 
46607c478bd9Sstevel@tonic-gate 	/*
46617c478bd9Sstevel@tonic-gate 	 * Initialize the callback and any callback data for when
46627c478bd9Sstevel@tonic-gate 	 * the td completes.
46637c478bd9Sstevel@tonic-gate 	 */
46647c478bd9Sstevel@tonic-gate 	tw->tw_handle_td = ohci_handle_ctrl_td;
46657c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value = NULL;
46667c478bd9Sstevel@tonic-gate 
46677c478bd9Sstevel@tonic-gate 	/* Create the first four bytes of the setup packet */
46687c478bd9Sstevel@tonic-gate 	sdata = (bmRequestType << 24) | (bRequest << 16) |
46697c478bd9Sstevel@tonic-gate 	    (((wValue >> 8) | (wValue << 8)) & 0x0000FFFF);
46707c478bd9Sstevel@tonic-gate 
46717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
46727c478bd9Sstevel@tonic-gate 	    "ohci_create_setup_pkt: sdata = 0x%x", sdata);
46737c478bd9Sstevel@tonic-gate 
46747c478bd9Sstevel@tonic-gate 	ddi_put32(tw->tw_accesshandle, (uint_t *)tw->tw_buf, sdata);
46757c478bd9Sstevel@tonic-gate 
46767c478bd9Sstevel@tonic-gate 	/* Create the second four bytes */
46777c478bd9Sstevel@tonic-gate 	sdata = (uint32_t)(((((wIndex >> 8) |
46787c478bd9Sstevel@tonic-gate 	    (wIndex << 8)) << 16) & 0xFFFF0000) |
46797c478bd9Sstevel@tonic-gate 	    (((wLength >> 8) | (wLength << 8)) & 0x0000FFFF));
46807c478bd9Sstevel@tonic-gate 
46817c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
46827c478bd9Sstevel@tonic-gate 	    "ohci_create_setup_pkt: sdata = 0x%x", sdata);
46837c478bd9Sstevel@tonic-gate 
46847c478bd9Sstevel@tonic-gate 	ddi_put32(tw->tw_accesshandle,
46857c478bd9Sstevel@tonic-gate 	    (uint_t *)(tw->tw_buf + sizeof (uint_t)), sdata);
46867c478bd9Sstevel@tonic-gate 
46877c478bd9Sstevel@tonic-gate 	ctrl = HC_TD_SETUP|HC_TD_MS_DT|HC_TD_DT_0|HC_TD_6I;
46887c478bd9Sstevel@tonic-gate 
46897c478bd9Sstevel@tonic-gate 	/*
46907c478bd9Sstevel@tonic-gate 	 * The TD's are placed on the ED one at a time.
46917c478bd9Sstevel@tonic-gate 	 * Once this TD is placed on the done list, the
46927c478bd9Sstevel@tonic-gate 	 * data or status phase TD will be enqueued.
46937c478bd9Sstevel@tonic-gate 	 */
46947c478bd9Sstevel@tonic-gate 	(void) ohci_insert_hc_td(ohcip, ctrl,
46957c478bd9Sstevel@tonic-gate 	    tw->tw_cookie.dmac_address, SETUP_SIZE,
46967c478bd9Sstevel@tonic-gate 	    OHCI_CTRL_SETUP_PHASE, pp, tw);
46977c478bd9Sstevel@tonic-gate 
46987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
46997c478bd9Sstevel@tonic-gate 	    "Create_setup: pp 0x%p", (void *)pp);
47007c478bd9Sstevel@tonic-gate 
47017c478bd9Sstevel@tonic-gate 	/*
47027c478bd9Sstevel@tonic-gate 	 * If this control transfer has a data phase, record the
47037c478bd9Sstevel@tonic-gate 	 * direction. If the data phase is an OUT transaction,
47047c478bd9Sstevel@tonic-gate 	 * copy the data into the buffer of the transfer wrapper.
47057c478bd9Sstevel@tonic-gate 	 */
47067c478bd9Sstevel@tonic-gate 	if (wLength != 0) {
47077c478bd9Sstevel@tonic-gate 		/* There is a data stage.  Find the direction */
47087c478bd9Sstevel@tonic-gate 		if (bmRequestType & USB_DEV_REQ_DEV_TO_HOST) {
47097c478bd9Sstevel@tonic-gate 			tw->tw_direction = HC_TD_IN;
47107c478bd9Sstevel@tonic-gate 		} else {
47117c478bd9Sstevel@tonic-gate 			tw->tw_direction = HC_TD_OUT;
47127c478bd9Sstevel@tonic-gate 
47137c478bd9Sstevel@tonic-gate 			/* Copy the data into the message */
47147c478bd9Sstevel@tonic-gate 			ddi_rep_put8(tw->tw_accesshandle, data->b_rptr,
47157c478bd9Sstevel@tonic-gate 			    (uint8_t *)(tw->tw_buf + SETUP_SIZE),
47167c478bd9Sstevel@tonic-gate 			    wLength, DDI_DEV_AUTOINCR);
47177c478bd9Sstevel@tonic-gate 
47187c478bd9Sstevel@tonic-gate 		}
47197c478bd9Sstevel@tonic-gate 
47207c478bd9Sstevel@tonic-gate 		ctrl = (ctrl_reqp->ctrl_attributes & USB_ATTRS_SHORT_XFER_OK) ?
47217c478bd9Sstevel@tonic-gate 			HC_TD_R : 0;
47227c478bd9Sstevel@tonic-gate 
47237c478bd9Sstevel@tonic-gate 		/*
47247c478bd9Sstevel@tonic-gate 		 * There is a data stage.
47257c478bd9Sstevel@tonic-gate 		 * Find the direction.
47267c478bd9Sstevel@tonic-gate 		 */
47277c478bd9Sstevel@tonic-gate 		if (tw->tw_direction == HC_TD_IN) {
47287c478bd9Sstevel@tonic-gate 			ctrl = ctrl|HC_TD_IN|HC_TD_MS_DT|HC_TD_DT_1|HC_TD_6I;
47297c478bd9Sstevel@tonic-gate 		} else {
47307c478bd9Sstevel@tonic-gate 			ctrl = ctrl|HC_TD_OUT|HC_TD_MS_DT|HC_TD_DT_1|HC_TD_6I;
47317c478bd9Sstevel@tonic-gate 		}
47327c478bd9Sstevel@tonic-gate 
47337c478bd9Sstevel@tonic-gate 		/*
47347c478bd9Sstevel@tonic-gate 		 * Create the TD.  If this is an OUT transaction,
47357c478bd9Sstevel@tonic-gate 		 * the data is already in the buffer of the TW.
47367c478bd9Sstevel@tonic-gate 		 */
47377c478bd9Sstevel@tonic-gate 		(void) ohci_insert_hc_td(ohcip, ctrl,
47387c478bd9Sstevel@tonic-gate 		    tw->tw_cookie.dmac_address + SETUP_SIZE,
47397c478bd9Sstevel@tonic-gate 		    tw->tw_length - SETUP_SIZE, OHCI_CTRL_DATA_PHASE,
47407c478bd9Sstevel@tonic-gate 		    pp, tw);
47417c478bd9Sstevel@tonic-gate 
47427c478bd9Sstevel@tonic-gate 		/*
47437c478bd9Sstevel@tonic-gate 		 * The direction of the STATUS TD depends on
47447c478bd9Sstevel@tonic-gate 		 * the direction of the transfer.
47457c478bd9Sstevel@tonic-gate 		 */
47467c478bd9Sstevel@tonic-gate 		if (tw->tw_direction == HC_TD_IN) {
47477c478bd9Sstevel@tonic-gate 			ctrl = HC_TD_OUT|HC_TD_MS_DT|HC_TD_DT_1|HC_TD_1I;
47487c478bd9Sstevel@tonic-gate 		} else {
47497c478bd9Sstevel@tonic-gate 			ctrl = HC_TD_IN|HC_TD_MS_DT|HC_TD_DT_1|HC_TD_1I;
47507c478bd9Sstevel@tonic-gate 		}
47517c478bd9Sstevel@tonic-gate 	} else {
47527c478bd9Sstevel@tonic-gate 		ctrl = HC_TD_IN|HC_TD_MS_DT|HC_TD_DT_1|HC_TD_1I;
47537c478bd9Sstevel@tonic-gate 	}
47547c478bd9Sstevel@tonic-gate 
47557c478bd9Sstevel@tonic-gate 	/* Status stage */
47567c478bd9Sstevel@tonic-gate 	(void) ohci_insert_hc_td(ohcip, ctrl, NULL,
47577c478bd9Sstevel@tonic-gate 	    0, OHCI_CTRL_STATUS_PHASE, pp, tw);
47587c478bd9Sstevel@tonic-gate 
47597c478bd9Sstevel@tonic-gate 	/* Indicate that the control list is filled */
47607c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_cmd_status, HCR_STATUS_CLF);
47617c478bd9Sstevel@tonic-gate 
47627c478bd9Sstevel@tonic-gate 	/* Start the timer for this control transfer */
47637c478bd9Sstevel@tonic-gate 	ohci_start_xfer_timer(ohcip, pp, tw);
47647c478bd9Sstevel@tonic-gate }
47657c478bd9Sstevel@tonic-gate 
47667c478bd9Sstevel@tonic-gate /*
47677c478bd9Sstevel@tonic-gate  * ohci_allocate_bulk_resources:
47687c478bd9Sstevel@tonic-gate  *
47697c478bd9Sstevel@tonic-gate  * Calculates the number of tds necessary for a ctrl transfer, and allocates
47707c478bd9Sstevel@tonic-gate  * all the resources necessary.
47717c478bd9Sstevel@tonic-gate  *
47727c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise TW.
47737c478bd9Sstevel@tonic-gate  */
47747c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t *
47757c478bd9Sstevel@tonic-gate ohci_allocate_bulk_resources(
47767c478bd9Sstevel@tonic-gate 	ohci_state_t 		*ohcip,
47777c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
47787c478bd9Sstevel@tonic-gate 	usb_bulk_req_t		*bulk_reqp,
47797c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
47807c478bd9Sstevel@tonic-gate {
47817c478bd9Sstevel@tonic-gate 	size_t 			td_count = 0;
47827c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
47837c478bd9Sstevel@tonic-gate 
47847c478bd9Sstevel@tonic-gate 	/* Check the size of bulk request */
47857c478bd9Sstevel@tonic-gate 	if (bulk_reqp->bulk_len > OHCI_MAX_BULK_XFER_SIZE) {
47867c478bd9Sstevel@tonic-gate 
47877c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
47887c478bd9Sstevel@tonic-gate 		    "ohci_allocate_bulk_resources: Bulk request size 0x%x is "
47897c478bd9Sstevel@tonic-gate 		    "more than 0x%x", bulk_reqp->bulk_len,
47907c478bd9Sstevel@tonic-gate 		    OHCI_MAX_BULK_XFER_SIZE);
47917c478bd9Sstevel@tonic-gate 
47927c478bd9Sstevel@tonic-gate 		return (NULL);
47937c478bd9Sstevel@tonic-gate 	}
47947c478bd9Sstevel@tonic-gate 
47957c478bd9Sstevel@tonic-gate 	/* Get the required bulk packet size */
47967c478bd9Sstevel@tonic-gate 	td_count = bulk_reqp->bulk_len / OHCI_MAX_TD_XFER_SIZE;
47977c478bd9Sstevel@tonic-gate 	if (bulk_reqp->bulk_len % OHCI_MAX_TD_XFER_SIZE) {
47987c478bd9Sstevel@tonic-gate 		td_count++;
47997c478bd9Sstevel@tonic-gate 	}
48007c478bd9Sstevel@tonic-gate 
48017c478bd9Sstevel@tonic-gate 	tw = ohci_allocate_tw_resources(ohcip, pp, bulk_reqp->bulk_len,
48027c478bd9Sstevel@tonic-gate 	    usb_flags, td_count);
48037c478bd9Sstevel@tonic-gate 
48047c478bd9Sstevel@tonic-gate 	return (tw);
48057c478bd9Sstevel@tonic-gate }
48067c478bd9Sstevel@tonic-gate 
48077c478bd9Sstevel@tonic-gate /*
48087c478bd9Sstevel@tonic-gate  * ohci_insert_bulk_req:
48097c478bd9Sstevel@tonic-gate  *
48107c478bd9Sstevel@tonic-gate  * Create a Transfer Descriptor (TD) and a data buffer for a bulk
48117c478bd9Sstevel@tonic-gate  * endpoint.
48127c478bd9Sstevel@tonic-gate  */
48137c478bd9Sstevel@tonic-gate /* ARGSUSED */
48147c478bd9Sstevel@tonic-gate static void
48157c478bd9Sstevel@tonic-gate ohci_insert_bulk_req(
48167c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
48177c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
48187c478bd9Sstevel@tonic-gate 	usb_bulk_req_t		*bulk_reqp,
48197c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
48207c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
48217c478bd9Sstevel@tonic-gate {
48227c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
48237c478bd9Sstevel@tonic-gate 	uint_t			bulk_pkt_size, count;
48247c478bd9Sstevel@tonic-gate 	size_t			residue = 0, len = 0;
48257c478bd9Sstevel@tonic-gate 	uint32_t		ctrl = 0;
48267c478bd9Sstevel@tonic-gate 	int			pipe_dir;
48277c478bd9Sstevel@tonic-gate 
48287c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
48297c478bd9Sstevel@tonic-gate 	    "ohci_insert_bulk_req: bulk_reqp = 0x%p flags = 0x%x",
48307c478bd9Sstevel@tonic-gate 	    bulk_reqp, flags);
48317c478bd9Sstevel@tonic-gate 
48327c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
48337c478bd9Sstevel@tonic-gate 
48347c478bd9Sstevel@tonic-gate 	/* Get the bulk pipe direction */
48357c478bd9Sstevel@tonic-gate 	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
48367c478bd9Sstevel@tonic-gate 
48377c478bd9Sstevel@tonic-gate 	/* Get the required bulk packet size */
48387c478bd9Sstevel@tonic-gate 	bulk_pkt_size = min(bulk_reqp->bulk_len, OHCI_MAX_TD_XFER_SIZE);
48397c478bd9Sstevel@tonic-gate 
48407c478bd9Sstevel@tonic-gate 	residue = tw->tw_length % bulk_pkt_size;
48417c478bd9Sstevel@tonic-gate 
48427c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
48437c478bd9Sstevel@tonic-gate 	    "ohci_insert_bulk_req: bulk_pkt_size = %d", bulk_pkt_size);
48447c478bd9Sstevel@tonic-gate 
48457c478bd9Sstevel@tonic-gate 	/*
48467c478bd9Sstevel@tonic-gate 	 * Save current bulk request pointer and timeout values
48477c478bd9Sstevel@tonic-gate 	 * in transfer wrapper.
48487c478bd9Sstevel@tonic-gate 	 */
48497c478bd9Sstevel@tonic-gate 	tw->tw_curr_xfer_reqp = (usb_opaque_t)bulk_reqp;
48507c478bd9Sstevel@tonic-gate 	tw->tw_timeout = bulk_reqp->bulk_timeout;
48517c478bd9Sstevel@tonic-gate 
48527c478bd9Sstevel@tonic-gate 	/*
48537c478bd9Sstevel@tonic-gate 	 * Initialize the callback and any callback
48547c478bd9Sstevel@tonic-gate 	 * data required when the td completes.
48557c478bd9Sstevel@tonic-gate 	 */
48567c478bd9Sstevel@tonic-gate 	tw->tw_handle_td = ohci_handle_bulk_td;
48577c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value = NULL;
48587c478bd9Sstevel@tonic-gate 
48597c478bd9Sstevel@tonic-gate 	tw->tw_direction =
48607c478bd9Sstevel@tonic-gate 	    (pipe_dir == USB_EP_DIR_OUT) ? HC_TD_OUT : HC_TD_IN;
48617c478bd9Sstevel@tonic-gate 
48627c478bd9Sstevel@tonic-gate 	if (tw->tw_direction == HC_TD_OUT) {
48637c478bd9Sstevel@tonic-gate 
48647c478bd9Sstevel@tonic-gate 		ASSERT(bulk_reqp->bulk_data != NULL);
48657c478bd9Sstevel@tonic-gate 
48667c478bd9Sstevel@tonic-gate 		/* Copy the data into the message */
48677c478bd9Sstevel@tonic-gate 		ddi_rep_put8(tw->tw_accesshandle,
48687c478bd9Sstevel@tonic-gate 		    bulk_reqp->bulk_data->b_rptr, (uint8_t *)tw->tw_buf,
48697c478bd9Sstevel@tonic-gate 		    bulk_reqp->bulk_len, DDI_DEV_AUTOINCR);
48707c478bd9Sstevel@tonic-gate 	}
48717c478bd9Sstevel@tonic-gate 
48727c478bd9Sstevel@tonic-gate 	ctrl = tw->tw_direction|HC_TD_DT_0|HC_TD_6I;
48737c478bd9Sstevel@tonic-gate 
48747c478bd9Sstevel@tonic-gate 	/* Insert all the bulk TDs */
48757c478bd9Sstevel@tonic-gate 	for (count = 0; count < tw->tw_num_tds; count++) {
48767c478bd9Sstevel@tonic-gate 
48777c478bd9Sstevel@tonic-gate 		/* Check for last td */
48787c478bd9Sstevel@tonic-gate 		if (count == (tw->tw_num_tds - 1)) {
48797c478bd9Sstevel@tonic-gate 
48807c478bd9Sstevel@tonic-gate 			ctrl = ((ctrl & ~HC_TD_DI) | HC_TD_1I);
48817c478bd9Sstevel@tonic-gate 
48827c478bd9Sstevel@tonic-gate 			/* Check for inserting residue data */
48837c478bd9Sstevel@tonic-gate 			if (residue) {
48847c478bd9Sstevel@tonic-gate 				bulk_pkt_size = residue;
48857c478bd9Sstevel@tonic-gate 			}
48867c478bd9Sstevel@tonic-gate 
48877c478bd9Sstevel@tonic-gate 			/*
48887c478bd9Sstevel@tonic-gate 			 * Only set the round bit on the last TD, to ensure
48897c478bd9Sstevel@tonic-gate 			 * the controller will always HALT the ED in case of
48907c478bd9Sstevel@tonic-gate 			 * a short transfer.
48917c478bd9Sstevel@tonic-gate 			 */
48927c478bd9Sstevel@tonic-gate 			if (bulk_reqp->bulk_attributes &
48937c478bd9Sstevel@tonic-gate 			    USB_ATTRS_SHORT_XFER_OK) {
48947c478bd9Sstevel@tonic-gate 				ctrl |= HC_TD_R;
48957c478bd9Sstevel@tonic-gate 			}
48967c478bd9Sstevel@tonic-gate 		}
48977c478bd9Sstevel@tonic-gate 
48987c478bd9Sstevel@tonic-gate 		/* Insert the TD onto the endpoint */
48997c478bd9Sstevel@tonic-gate 		(void) ohci_insert_hc_td(ohcip, ctrl,
49007c478bd9Sstevel@tonic-gate 		    tw->tw_cookie.dmac_address + len,
49017c478bd9Sstevel@tonic-gate 		    bulk_pkt_size, 0, pp, tw);
49027c478bd9Sstevel@tonic-gate 
49037c478bd9Sstevel@tonic-gate 		len = len + bulk_pkt_size;
49047c478bd9Sstevel@tonic-gate 	}
49057c478bd9Sstevel@tonic-gate 
49067c478bd9Sstevel@tonic-gate 	/* Indicate that the bulk list is filled */
49077c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_cmd_status, HCR_STATUS_BLF);
49087c478bd9Sstevel@tonic-gate 
49097c478bd9Sstevel@tonic-gate 	/* Start the timer for this bulk transfer */
49107c478bd9Sstevel@tonic-gate 	ohci_start_xfer_timer(ohcip, pp, tw);
49117c478bd9Sstevel@tonic-gate }
49127c478bd9Sstevel@tonic-gate 
49137c478bd9Sstevel@tonic-gate 
49147c478bd9Sstevel@tonic-gate /*
49157c478bd9Sstevel@tonic-gate  * ohci_start_periodic_pipe_polling:
49167c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
49177c478bd9Sstevel@tonic-gate  */
49187c478bd9Sstevel@tonic-gate int
49197c478bd9Sstevel@tonic-gate ohci_start_periodic_pipe_polling(
49207c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
49217c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
49227c478bd9Sstevel@tonic-gate 	usb_opaque_t		periodic_in_reqp,
49237c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
49247c478bd9Sstevel@tonic-gate {
49257c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
49267c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
49277c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
49287c478bd9Sstevel@tonic-gate 
49297c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
49307c478bd9Sstevel@tonic-gate 	    "ohci_start_periodic_pipe_polling: ep%d",
49317c478bd9Sstevel@tonic-gate 	    ph->p_ep.bEndpointAddress & USB_EP_NUM_MASK);
49327c478bd9Sstevel@tonic-gate 
49337c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
49347c478bd9Sstevel@tonic-gate 
49357c478bd9Sstevel@tonic-gate 	/*
49367c478bd9Sstevel@tonic-gate 	 * Check and handle start polling on root hub interrupt pipe.
49377c478bd9Sstevel@tonic-gate 	 */
49387c478bd9Sstevel@tonic-gate 	if ((ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) &&
49397c478bd9Sstevel@tonic-gate 	    ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
49407c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_INTR)) {
49417c478bd9Sstevel@tonic-gate 
49427c478bd9Sstevel@tonic-gate 		error = ohci_handle_root_hub_pipe_start_intr_polling(ph,
49437c478bd9Sstevel@tonic-gate 		    (usb_intr_req_t *)periodic_in_reqp, flags);
49447c478bd9Sstevel@tonic-gate 
49457c478bd9Sstevel@tonic-gate 		return (error);
49467c478bd9Sstevel@tonic-gate 	}
49477c478bd9Sstevel@tonic-gate 
49487c478bd9Sstevel@tonic-gate 	switch (pp->pp_state) {
49497c478bd9Sstevel@tonic-gate 	case OHCI_PIPE_STATE_IDLE:
49507c478bd9Sstevel@tonic-gate 		/* Save the Original client's Periodic IN request */
49517c478bd9Sstevel@tonic-gate 		pp->pp_client_periodic_in_reqp = periodic_in_reqp;
49527c478bd9Sstevel@tonic-gate 
49537c478bd9Sstevel@tonic-gate 		/*
49547c478bd9Sstevel@tonic-gate 		 * This pipe is uninitialized or if a valid TD is
49557c478bd9Sstevel@tonic-gate 		 * not found then insert a TD on the interrupt or
49567c478bd9Sstevel@tonic-gate 		 * isochronous IN endpoint.
49577c478bd9Sstevel@tonic-gate 		 */
49587c478bd9Sstevel@tonic-gate 		error = ohci_start_pipe_polling(ohcip, ph, flags);
49597c478bd9Sstevel@tonic-gate 
49607c478bd9Sstevel@tonic-gate 		if (error != USB_SUCCESS) {
49617c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
49627c478bd9Sstevel@tonic-gate 			    "ohci_start_periodic_pipe_polling: "
49637c478bd9Sstevel@tonic-gate 			    "Start polling failed");
49647c478bd9Sstevel@tonic-gate 
49657c478bd9Sstevel@tonic-gate 			pp->pp_client_periodic_in_reqp = NULL;
49667c478bd9Sstevel@tonic-gate 
49677c478bd9Sstevel@tonic-gate 			return (error);
49687c478bd9Sstevel@tonic-gate 		}
49697c478bd9Sstevel@tonic-gate 
49707c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
49717c478bd9Sstevel@tonic-gate 		    "ohci_start_periodic_pipe_polling: PP = 0x%p", pp);
49727c478bd9Sstevel@tonic-gate 
49737c478bd9Sstevel@tonic-gate 		ASSERT((pp->pp_tw_head != NULL) && (pp->pp_tw_tail != NULL));
49747c478bd9Sstevel@tonic-gate 
49757c478bd9Sstevel@tonic-gate 		break;
49767c478bd9Sstevel@tonic-gate 	case OHCI_PIPE_STATE_ACTIVE:
49777c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
49787c478bd9Sstevel@tonic-gate 		    "ohci_start_periodic_pipe_polling: "
49797c478bd9Sstevel@tonic-gate 		    "Polling is already in progress");
49807c478bd9Sstevel@tonic-gate 
49817c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
49827c478bd9Sstevel@tonic-gate 		break;
49837c478bd9Sstevel@tonic-gate 	case OHCI_PIPE_STATE_ERROR:
49847c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
49857c478bd9Sstevel@tonic-gate 		    "ohci_start_periodic_pipe_polling: "
49867c478bd9Sstevel@tonic-gate 		    "Pipe is halted and perform reset before restart polling");
49877c478bd9Sstevel@tonic-gate 
49887c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
49897c478bd9Sstevel@tonic-gate 		break;
49907c478bd9Sstevel@tonic-gate 	default:
49917c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
49927c478bd9Sstevel@tonic-gate 		    "ohci_start_periodic_pipe_polling: Undefined state");
49937c478bd9Sstevel@tonic-gate 
49947c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
49957c478bd9Sstevel@tonic-gate 		break;
49967c478bd9Sstevel@tonic-gate 	}
49977c478bd9Sstevel@tonic-gate 
49987c478bd9Sstevel@tonic-gate 	return (error);
49997c478bd9Sstevel@tonic-gate }
50007c478bd9Sstevel@tonic-gate 
50017c478bd9Sstevel@tonic-gate 
50027c478bd9Sstevel@tonic-gate /*
50037c478bd9Sstevel@tonic-gate  * ohci_start_pipe_polling:
50047c478bd9Sstevel@tonic-gate  *
50057c478bd9Sstevel@tonic-gate  * Insert the number of periodic requests corresponding to polling
50067c478bd9Sstevel@tonic-gate  * interval as calculated during pipe open.
50077c478bd9Sstevel@tonic-gate  */
50087c478bd9Sstevel@tonic-gate static int
50097c478bd9Sstevel@tonic-gate ohci_start_pipe_polling(
50107c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
50117c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
50127c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
50137c478bd9Sstevel@tonic-gate {
50147c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
50157c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
50167c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw_list, *tw;
50177c478bd9Sstevel@tonic-gate 	int			i, total_tws;
50187c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
50197c478bd9Sstevel@tonic-gate 
50207c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
50217c478bd9Sstevel@tonic-gate 	    "ohci_start_pipe_polling:");
50227c478bd9Sstevel@tonic-gate 
50237c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
50247c478bd9Sstevel@tonic-gate 
50257c478bd9Sstevel@tonic-gate 	/*
50267c478bd9Sstevel@tonic-gate 	 * For the start polling, pp_max_periodic_req_cnt will be zero
50277c478bd9Sstevel@tonic-gate 	 * and for the restart polling request, it will be non zero.
50287c478bd9Sstevel@tonic-gate 	 *
50297c478bd9Sstevel@tonic-gate 	 * In case of start polling request, find out number of requests
50307c478bd9Sstevel@tonic-gate 	 * required for the Interrupt IN endpoints corresponding to the
50317c478bd9Sstevel@tonic-gate 	 * endpoint polling interval. For Isochronous IN endpoints, it is
50327c478bd9Sstevel@tonic-gate 	 * always fixed since its polling interval will be one ms.
50337c478bd9Sstevel@tonic-gate 	 */
50347c478bd9Sstevel@tonic-gate 	if (pp->pp_max_periodic_req_cnt == 0) {
50357c478bd9Sstevel@tonic-gate 
50367c478bd9Sstevel@tonic-gate 		ohci_set_periodic_pipe_polling(ohcip, ph);
50377c478bd9Sstevel@tonic-gate 	}
50387c478bd9Sstevel@tonic-gate 
50397c478bd9Sstevel@tonic-gate 	ASSERT(pp->pp_max_periodic_req_cnt != 0);
50407c478bd9Sstevel@tonic-gate 
50417c478bd9Sstevel@tonic-gate 	/* Allocate all the necessary resources for the IN transfer */
50427c478bd9Sstevel@tonic-gate 	tw_list = NULL;
50437c478bd9Sstevel@tonic-gate 	total_tws = pp->pp_max_periodic_req_cnt - pp->pp_cur_periodic_req_cnt;
50447c478bd9Sstevel@tonic-gate 	for (i = 0; i < total_tws; i++) {
50457c478bd9Sstevel@tonic-gate 		switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
50467c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
50477c478bd9Sstevel@tonic-gate 			tw = ohci_allocate_intr_resources(
50487c478bd9Sstevel@tonic-gate 				ohcip, ph, NULL, flags);
50497c478bd9Sstevel@tonic-gate 			break;
50507c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
50517c478bd9Sstevel@tonic-gate 			tw = ohci_allocate_isoc_resources(
50527c478bd9Sstevel@tonic-gate 				ohcip, ph, NULL, flags);
50537c478bd9Sstevel@tonic-gate 			break;
50547c478bd9Sstevel@tonic-gate 		}
50557c478bd9Sstevel@tonic-gate 		if (tw == NULL) {
50567c478bd9Sstevel@tonic-gate 			error = USB_NO_RESOURCES;
50577c478bd9Sstevel@tonic-gate 			/* There are not enough resources, deallocate the TWs */
50587c478bd9Sstevel@tonic-gate 			tw = tw_list;
50597c478bd9Sstevel@tonic-gate 			while (tw != NULL) {
50607c478bd9Sstevel@tonic-gate 				tw_list = tw->tw_next;
50617c478bd9Sstevel@tonic-gate 				ohci_deallocate_periodic_in_resource(
50627c478bd9Sstevel@tonic-gate 					ohcip, pp, tw);
50637c478bd9Sstevel@tonic-gate 				ohci_deallocate_tw_resources(ohcip, pp, tw);
50647c478bd9Sstevel@tonic-gate 				tw = tw_list;
50657c478bd9Sstevel@tonic-gate 			}
50667c478bd9Sstevel@tonic-gate 			return (error);
50677c478bd9Sstevel@tonic-gate 		} else {
50687c478bd9Sstevel@tonic-gate 			if (tw_list == NULL) {
50697c478bd9Sstevel@tonic-gate 				tw_list = tw;
50707c478bd9Sstevel@tonic-gate 			}
50717c478bd9Sstevel@tonic-gate 		}
50727c478bd9Sstevel@tonic-gate 	}
50737c478bd9Sstevel@tonic-gate 
50747c478bd9Sstevel@tonic-gate 	i = 0;
50757c478bd9Sstevel@tonic-gate 	while (pp->pp_cur_periodic_req_cnt < pp->pp_max_periodic_req_cnt) {
50767c478bd9Sstevel@tonic-gate 
50777c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
50787c478bd9Sstevel@tonic-gate 		    "ohci_start_pipe_polling: max = %d curr = %d tw = %p:",
50797c478bd9Sstevel@tonic-gate 		    pp->pp_max_periodic_req_cnt, pp->pp_cur_periodic_req_cnt,
50807c478bd9Sstevel@tonic-gate 		    tw_list);
50817c478bd9Sstevel@tonic-gate 
50827c478bd9Sstevel@tonic-gate 		tw = tw_list;
50837c478bd9Sstevel@tonic-gate 		tw_list = tw->tw_next;
50847c478bd9Sstevel@tonic-gate 
50857c478bd9Sstevel@tonic-gate 		switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
50867c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
50877c478bd9Sstevel@tonic-gate 			ohci_insert_intr_req(ohcip, pp, tw, flags);
50887c478bd9Sstevel@tonic-gate 			break;
50897c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
50907c478bd9Sstevel@tonic-gate 			error = ohci_insert_isoc_req(ohcip, pp, tw, flags);
50917c478bd9Sstevel@tonic-gate 			break;
50927c478bd9Sstevel@tonic-gate 		}
50937c478bd9Sstevel@tonic-gate 		if (error == USB_SUCCESS) {
50947c478bd9Sstevel@tonic-gate 			pp->pp_cur_periodic_req_cnt++;
50957c478bd9Sstevel@tonic-gate 		} else {
50967c478bd9Sstevel@tonic-gate 			/*
50977c478bd9Sstevel@tonic-gate 			 * Deallocate the remaining tw
50987c478bd9Sstevel@tonic-gate 			 * The current tw should have already been deallocated
50997c478bd9Sstevel@tonic-gate 			 */
51007c478bd9Sstevel@tonic-gate 			tw = tw_list;
51017c478bd9Sstevel@tonic-gate 			while (tw != NULL) {
51027c478bd9Sstevel@tonic-gate 				tw_list = tw->tw_next;
51037c478bd9Sstevel@tonic-gate 				ohci_deallocate_periodic_in_resource(
51047c478bd9Sstevel@tonic-gate 					ohcip, pp, tw);
51057c478bd9Sstevel@tonic-gate 				ohci_deallocate_tw_resources(ohcip, pp, tw);
51067c478bd9Sstevel@tonic-gate 				tw = tw_list;
51077c478bd9Sstevel@tonic-gate 			}
51087c478bd9Sstevel@tonic-gate 			/*
51097c478bd9Sstevel@tonic-gate 			 * If this is the first req return an error.
51107c478bd9Sstevel@tonic-gate 			 * Otherwise return success.
51117c478bd9Sstevel@tonic-gate 			 */
51127c478bd9Sstevel@tonic-gate 			if (i != 0) {
51137c478bd9Sstevel@tonic-gate 				error = USB_SUCCESS;
51147c478bd9Sstevel@tonic-gate 			}
51157c478bd9Sstevel@tonic-gate 
51167c478bd9Sstevel@tonic-gate 			break;
51177c478bd9Sstevel@tonic-gate 		}
51187c478bd9Sstevel@tonic-gate 		i++;
51197c478bd9Sstevel@tonic-gate 	}
51207c478bd9Sstevel@tonic-gate 
51217c478bd9Sstevel@tonic-gate 	return (error);
51227c478bd9Sstevel@tonic-gate }
51237c478bd9Sstevel@tonic-gate 
51247c478bd9Sstevel@tonic-gate 
51257c478bd9Sstevel@tonic-gate /*
51267c478bd9Sstevel@tonic-gate  * ohci_set_periodic_pipe_polling:
51277c478bd9Sstevel@tonic-gate  *
51287c478bd9Sstevel@tonic-gate  * Calculate the number of periodic requests needed corresponding to the
51297c478bd9Sstevel@tonic-gate  * interrupt/isochronous IN endpoints polling interval. Table below gives
51307c478bd9Sstevel@tonic-gate  * the number of periodic requests needed for the interrupt/isochronous
51317c478bd9Sstevel@tonic-gate  * IN endpoints according to endpoint polling interval.
51327c478bd9Sstevel@tonic-gate  *
51337c478bd9Sstevel@tonic-gate  * Polling interval		Number of periodic requests
51347c478bd9Sstevel@tonic-gate  *
51357c478bd9Sstevel@tonic-gate  * 1ms				4
51367c478bd9Sstevel@tonic-gate  * 2ms				2
51377c478bd9Sstevel@tonic-gate  * 4ms to 32ms			1
51387c478bd9Sstevel@tonic-gate  */
51397c478bd9Sstevel@tonic-gate static void
51407c478bd9Sstevel@tonic-gate ohci_set_periodic_pipe_polling(
51417c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
51427c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
51437c478bd9Sstevel@tonic-gate {
51447c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
51457c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*endpoint = &ph->p_ep;
51467c478bd9Sstevel@tonic-gate 	uchar_t			ep_attr = endpoint->bmAttributes;
51477c478bd9Sstevel@tonic-gate 	uint_t			interval;
51487c478bd9Sstevel@tonic-gate 
51497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
51507c478bd9Sstevel@tonic-gate 	    "ohci_set_periodic_pipe_polling:");
51517c478bd9Sstevel@tonic-gate 
51527c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
51537c478bd9Sstevel@tonic-gate 
51547c478bd9Sstevel@tonic-gate 	pp->pp_cur_periodic_req_cnt = 0;
51557c478bd9Sstevel@tonic-gate 
51567c478bd9Sstevel@tonic-gate 	/*
51577c478bd9Sstevel@tonic-gate 	 * Check usb flag whether USB_FLAGS_ONE_TIME_POLL flag is
51587c478bd9Sstevel@tonic-gate 	 * set and if so, set pp->pp_max_periodic_req_cnt to one.
51597c478bd9Sstevel@tonic-gate 	 */
51607c478bd9Sstevel@tonic-gate 	if (((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) &&
51617c478bd9Sstevel@tonic-gate 	    (pp->pp_client_periodic_in_reqp)) {
51627c478bd9Sstevel@tonic-gate 		usb_intr_req_t *intr_reqp =
51637c478bd9Sstevel@tonic-gate 		    (usb_intr_req_t *)pp->pp_client_periodic_in_reqp;
51647c478bd9Sstevel@tonic-gate 
51657c478bd9Sstevel@tonic-gate 		if (intr_reqp->intr_attributes &
51667c478bd9Sstevel@tonic-gate 		    USB_ATTRS_ONE_XFER) {
51677c478bd9Sstevel@tonic-gate 
51687c478bd9Sstevel@tonic-gate 			pp->pp_max_periodic_req_cnt = INTR_XMS_REQS;
51697c478bd9Sstevel@tonic-gate 
51707c478bd9Sstevel@tonic-gate 			return;
51717c478bd9Sstevel@tonic-gate 		}
51727c478bd9Sstevel@tonic-gate 	}
51737c478bd9Sstevel@tonic-gate 
51747c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_usba_device->usb_mutex);
51757c478bd9Sstevel@tonic-gate 
51767c478bd9Sstevel@tonic-gate 	/*
51777c478bd9Sstevel@tonic-gate 	 * The ohci_adjust_polling_interval function will not fail
51787c478bd9Sstevel@tonic-gate 	 * at this instance since bandwidth allocation is already
51797c478bd9Sstevel@tonic-gate 	 * done. Here we are getting only the periodic interval.
51807c478bd9Sstevel@tonic-gate 	 */
51817c478bd9Sstevel@tonic-gate 	interval = ohci_adjust_polling_interval(ohcip, endpoint,
51827c478bd9Sstevel@tonic-gate 		ph->p_usba_device->usb_port_status);
51837c478bd9Sstevel@tonic-gate 
51847c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_usba_device->usb_mutex);
51857c478bd9Sstevel@tonic-gate 
51867c478bd9Sstevel@tonic-gate 	switch (interval) {
51877c478bd9Sstevel@tonic-gate 	case INTR_1MS_POLL:
51887c478bd9Sstevel@tonic-gate 		pp->pp_max_periodic_req_cnt = INTR_1MS_REQS;
51897c478bd9Sstevel@tonic-gate 		break;
51907c478bd9Sstevel@tonic-gate 	case INTR_2MS_POLL:
51917c478bd9Sstevel@tonic-gate 		pp->pp_max_periodic_req_cnt = INTR_2MS_REQS;
51927c478bd9Sstevel@tonic-gate 		break;
51937c478bd9Sstevel@tonic-gate 	default:
51947c478bd9Sstevel@tonic-gate 		pp->pp_max_periodic_req_cnt = INTR_XMS_REQS;
51957c478bd9Sstevel@tonic-gate 		break;
51967c478bd9Sstevel@tonic-gate 	}
51977c478bd9Sstevel@tonic-gate 
51987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
51997c478bd9Sstevel@tonic-gate 	    "ohci_set_periodic_pipe_polling: Max periodic requests = %d",
52007c478bd9Sstevel@tonic-gate 	    pp->pp_max_periodic_req_cnt);
52017c478bd9Sstevel@tonic-gate }
52027c478bd9Sstevel@tonic-gate 
52037c478bd9Sstevel@tonic-gate /*
52047c478bd9Sstevel@tonic-gate  * ohci_allocate_intr_resources:
52057c478bd9Sstevel@tonic-gate  *
52067c478bd9Sstevel@tonic-gate  * Calculates the number of tds necessary for a intr transfer, and allocates
52077c478bd9Sstevel@tonic-gate  * all the necessary resources.
52087c478bd9Sstevel@tonic-gate  *
52097c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise TW.
52107c478bd9Sstevel@tonic-gate  */
52117c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t *
52127c478bd9Sstevel@tonic-gate ohci_allocate_intr_resources(
52137c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
52147c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
52157c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*intr_reqp,
52167c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
52177c478bd9Sstevel@tonic-gate {
52187c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
52197c478bd9Sstevel@tonic-gate 	int			pipe_dir;
52207c478bd9Sstevel@tonic-gate 	size_t 			td_count = 1;
52217c478bd9Sstevel@tonic-gate 	size_t			tw_length;
52227c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
52237c478bd9Sstevel@tonic-gate 
52247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
52257c478bd9Sstevel@tonic-gate 	    "ohci_allocate_intr_resources:");
52267c478bd9Sstevel@tonic-gate 
52277c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
52287c478bd9Sstevel@tonic-gate 
52297c478bd9Sstevel@tonic-gate 	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
52307c478bd9Sstevel@tonic-gate 
52317c478bd9Sstevel@tonic-gate 	/* Get the length of interrupt transfer & alloc data */
52327c478bd9Sstevel@tonic-gate 	if (intr_reqp) {
52337c478bd9Sstevel@tonic-gate 		tw_length = intr_reqp->intr_len;
52347c478bd9Sstevel@tonic-gate 	} else {
52357c478bd9Sstevel@tonic-gate 		ASSERT(pipe_dir == USB_EP_DIR_IN);
52367c478bd9Sstevel@tonic-gate 		tw_length = (pp->pp_client_periodic_in_reqp) ?
52377c478bd9Sstevel@tonic-gate 		    (((usb_intr_req_t *)pp->
52387c478bd9Sstevel@tonic-gate 		    pp_client_periodic_in_reqp)->intr_len) :
52397c478bd9Sstevel@tonic-gate 		    ph->p_ep.wMaxPacketSize;
52407c478bd9Sstevel@tonic-gate 	}
52417c478bd9Sstevel@tonic-gate 
52427c478bd9Sstevel@tonic-gate 	/* Check the size of interrupt request */
52437c478bd9Sstevel@tonic-gate 	if (tw_length > OHCI_MAX_TD_XFER_SIZE) {
52447c478bd9Sstevel@tonic-gate 
52457c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
52467c478bd9Sstevel@tonic-gate 		    "ohci_allocate_intr_resources: Intr request size 0x%lx is "
52477c478bd9Sstevel@tonic-gate 		    "more than 0x%x", tw_length, OHCI_MAX_TD_XFER_SIZE);
52487c478bd9Sstevel@tonic-gate 
52497c478bd9Sstevel@tonic-gate 		return (NULL);
52507c478bd9Sstevel@tonic-gate 	}
52517c478bd9Sstevel@tonic-gate 
52527c478bd9Sstevel@tonic-gate 	if ((tw = ohci_allocate_tw_resources(ohcip, pp, tw_length,
52537c478bd9Sstevel@tonic-gate 	    flags, td_count)) == NULL) {
52547c478bd9Sstevel@tonic-gate 
52557c478bd9Sstevel@tonic-gate 		return (NULL);
52567c478bd9Sstevel@tonic-gate 	}
52577c478bd9Sstevel@tonic-gate 
52587c478bd9Sstevel@tonic-gate 	if (pipe_dir == USB_EP_DIR_IN) {
52597c478bd9Sstevel@tonic-gate 		if (ohci_allocate_periodic_in_resource(ohcip, pp, tw, flags) !=
52607c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
52617c478bd9Sstevel@tonic-gate 
52627c478bd9Sstevel@tonic-gate 			ohci_deallocate_tw_resources(ohcip, pp, tw);
52637c478bd9Sstevel@tonic-gate 			return (NULL);
52647c478bd9Sstevel@tonic-gate 		}
52657c478bd9Sstevel@tonic-gate 		tw->tw_direction = HC_TD_IN;
52667c478bd9Sstevel@tonic-gate 	} else {
52677c478bd9Sstevel@tonic-gate 		ASSERT(intr_reqp->intr_data != NULL);
52687c478bd9Sstevel@tonic-gate 
52697c478bd9Sstevel@tonic-gate 		/* Copy the data into the message */
52707c478bd9Sstevel@tonic-gate 		ddi_rep_put8(tw->tw_accesshandle,
52717c478bd9Sstevel@tonic-gate 		    intr_reqp->intr_data->b_rptr, (uint8_t *)tw->tw_buf,
52727c478bd9Sstevel@tonic-gate 		    intr_reqp->intr_len, DDI_DEV_AUTOINCR);
52737c478bd9Sstevel@tonic-gate 
52747c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = (usb_opaque_t)intr_reqp;
52757c478bd9Sstevel@tonic-gate 		tw->tw_direction = HC_TD_OUT;
52767c478bd9Sstevel@tonic-gate 	}
52777c478bd9Sstevel@tonic-gate 
52787c478bd9Sstevel@tonic-gate 	if (intr_reqp) {
52797c478bd9Sstevel@tonic-gate 		tw->tw_timeout = intr_reqp->intr_timeout;
52807c478bd9Sstevel@tonic-gate 	}
52817c478bd9Sstevel@tonic-gate 
52827c478bd9Sstevel@tonic-gate 	/*
52837c478bd9Sstevel@tonic-gate 	 * Initialize the callback and any callback
52847c478bd9Sstevel@tonic-gate 	 * data required when the td completes.
52857c478bd9Sstevel@tonic-gate 	 */
52867c478bd9Sstevel@tonic-gate 	tw->tw_handle_td = ohci_handle_intr_td;
52877c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value = NULL;
52887c478bd9Sstevel@tonic-gate 
52897c478bd9Sstevel@tonic-gate 	return (tw);
52907c478bd9Sstevel@tonic-gate }
52917c478bd9Sstevel@tonic-gate 
52927c478bd9Sstevel@tonic-gate /*
52937c478bd9Sstevel@tonic-gate  * ohci_insert_intr_req:
52947c478bd9Sstevel@tonic-gate  *
52957c478bd9Sstevel@tonic-gate  * Insert an Interrupt request into the Host Controller's periodic list.
52967c478bd9Sstevel@tonic-gate  */
52977c478bd9Sstevel@tonic-gate /* ARGSUSED */
52987c478bd9Sstevel@tonic-gate static void
52997c478bd9Sstevel@tonic-gate ohci_insert_intr_req(
53007c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
53017c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
53027c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
53037c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
53047c478bd9Sstevel@tonic-gate {
53057c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp = NULL;
53067c478bd9Sstevel@tonic-gate 	uint_t			ctrl = 0;
53077c478bd9Sstevel@tonic-gate 
53087c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
53097c478bd9Sstevel@tonic-gate 
53107c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_curr_xfer_reqp != NULL);
53117c478bd9Sstevel@tonic-gate 
53127c478bd9Sstevel@tonic-gate 	/* Get the current interrupt request pointer */
53137c478bd9Sstevel@tonic-gate 	curr_intr_reqp = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
53147c478bd9Sstevel@tonic-gate 
53157c478bd9Sstevel@tonic-gate 	ctrl = tw->tw_direction | HC_TD_DT_0 | HC_TD_1I;
53167c478bd9Sstevel@tonic-gate 
53177c478bd9Sstevel@tonic-gate 	if (curr_intr_reqp->intr_attributes & USB_ATTRS_SHORT_XFER_OK) {
53187c478bd9Sstevel@tonic-gate 		ctrl |= HC_TD_R;
53197c478bd9Sstevel@tonic-gate 	}
53207c478bd9Sstevel@tonic-gate 
53217c478bd9Sstevel@tonic-gate 	/* Insert another interrupt TD */
53227c478bd9Sstevel@tonic-gate 	(void) ohci_insert_hc_td(ohcip, ctrl,
53237c478bd9Sstevel@tonic-gate 	    tw->tw_cookie.dmac_address, tw->tw_length, 0, pp, tw);
53247c478bd9Sstevel@tonic-gate 
53257c478bd9Sstevel@tonic-gate 	/* Start the timer for this Interrupt transfer */
53267c478bd9Sstevel@tonic-gate 	ohci_start_xfer_timer(ohcip, pp, tw);
53277c478bd9Sstevel@tonic-gate }
53287c478bd9Sstevel@tonic-gate 
53297c478bd9Sstevel@tonic-gate 
53307c478bd9Sstevel@tonic-gate /*
53317c478bd9Sstevel@tonic-gate  * ohci_stop_periodic_pipe_polling:
53327c478bd9Sstevel@tonic-gate  */
53337c478bd9Sstevel@tonic-gate /* ARGSUSED */
53347c478bd9Sstevel@tonic-gate static int
53357c478bd9Sstevel@tonic-gate ohci_stop_periodic_pipe_polling(
53367c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
53377c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
53387c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
53397c478bd9Sstevel@tonic-gate {
53407c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
53417c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
53427c478bd9Sstevel@tonic-gate 
53437c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
53447c478bd9Sstevel@tonic-gate 	    "ohci_stop_periodic_pipe_polling: Flags = 0x%x", flags);
53457c478bd9Sstevel@tonic-gate 
53467c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
53477c478bd9Sstevel@tonic-gate 
53487c478bd9Sstevel@tonic-gate 	/*
53497c478bd9Sstevel@tonic-gate 	 * Check and handle stop polling on root hub interrupt pipe.
53507c478bd9Sstevel@tonic-gate 	 */
53517c478bd9Sstevel@tonic-gate 	if ((ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) &&
53527c478bd9Sstevel@tonic-gate 	    ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
53537c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_INTR)) {
53547c478bd9Sstevel@tonic-gate 
53557c478bd9Sstevel@tonic-gate 		ohci_handle_root_hub_pipe_stop_intr_polling(
53567c478bd9Sstevel@tonic-gate 					ph, flags);
53577c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
53587c478bd9Sstevel@tonic-gate 	}
53597c478bd9Sstevel@tonic-gate 
53607c478bd9Sstevel@tonic-gate 	if (pp->pp_state != OHCI_PIPE_STATE_ACTIVE) {
53617c478bd9Sstevel@tonic-gate 
53627c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
53637c478bd9Sstevel@tonic-gate 		    "ohci_stop_periodic_pipe_polling: Polling already stopped");
53647c478bd9Sstevel@tonic-gate 
53657c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
53667c478bd9Sstevel@tonic-gate 	}
53677c478bd9Sstevel@tonic-gate 
53687c478bd9Sstevel@tonic-gate 	/* Set pipe state to pipe stop polling */
53697c478bd9Sstevel@tonic-gate 	pp->pp_state = OHCI_PIPE_STATE_STOP_POLLING;
53707c478bd9Sstevel@tonic-gate 
53717c478bd9Sstevel@tonic-gate 	ohci_pipe_cleanup(ohcip, ph);
53727c478bd9Sstevel@tonic-gate 
53737c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
53747c478bd9Sstevel@tonic-gate }
53757c478bd9Sstevel@tonic-gate 
53767c478bd9Sstevel@tonic-gate /*
53777c478bd9Sstevel@tonic-gate  * ohci_allocate_isoc_resources:
53787c478bd9Sstevel@tonic-gate  *
53797c478bd9Sstevel@tonic-gate  * Calculates the number of tds necessary for a intr transfer, and allocates
53807c478bd9Sstevel@tonic-gate  * all the necessary resources.
53817c478bd9Sstevel@tonic-gate  *
53827c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise TW.
53837c478bd9Sstevel@tonic-gate  */
53847c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t *
53857c478bd9Sstevel@tonic-gate ohci_allocate_isoc_resources(
53867c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
53877c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
53887c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_reqp,
53897c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
53907c478bd9Sstevel@tonic-gate {
53917c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
53927c478bd9Sstevel@tonic-gate 	int			pipe_dir;
53937c478bd9Sstevel@tonic-gate 	uint_t			max_pkt_size = ph->p_ep.wMaxPacketSize;
53947c478bd9Sstevel@tonic-gate 	uint_t			max_isoc_xfer_size;
53957c478bd9Sstevel@tonic-gate 	usb_isoc_pkt_descr_t	*isoc_pkt_descr;
53967c478bd9Sstevel@tonic-gate 	ushort_t		isoc_pkt_count;
53977c478bd9Sstevel@tonic-gate 	size_t 			count, td_count;
53987c478bd9Sstevel@tonic-gate 	size_t			tw_length;
53997c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
54007c478bd9Sstevel@tonic-gate 
54017c478bd9Sstevel@tonic-gate 
54027c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
54037c478bd9Sstevel@tonic-gate 	    "ohci_allocate_isoc_resources: flags = ox%x", flags);
54047c478bd9Sstevel@tonic-gate 
54057c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
54067c478bd9Sstevel@tonic-gate 
54077c478bd9Sstevel@tonic-gate 	/*
54087c478bd9Sstevel@tonic-gate 	 *  Check whether pipe is in halted state.
54097c478bd9Sstevel@tonic-gate 	 */
54107c478bd9Sstevel@tonic-gate 	if (pp->pp_state == OHCI_PIPE_STATE_ERROR) {
54117c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
54127c478bd9Sstevel@tonic-gate 		    "ohci_allocate_isoc_resources:"
54137c478bd9Sstevel@tonic-gate 		    "Pipe is in error state, need pipe reset to continue");
54147c478bd9Sstevel@tonic-gate 
54157c478bd9Sstevel@tonic-gate 		return (NULL);
54167c478bd9Sstevel@tonic-gate 	}
54177c478bd9Sstevel@tonic-gate 
54187c478bd9Sstevel@tonic-gate 	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
54197c478bd9Sstevel@tonic-gate 
54207c478bd9Sstevel@tonic-gate 	/* Calculate the maximum isochronous transfer size */
54217c478bd9Sstevel@tonic-gate 	max_isoc_xfer_size = OHCI_MAX_ISOC_PKTS_PER_XFER * max_pkt_size;
54227c478bd9Sstevel@tonic-gate 
54237c478bd9Sstevel@tonic-gate 	if (isoc_reqp) {
54247c478bd9Sstevel@tonic-gate 		isoc_pkt_descr = isoc_reqp->isoc_pkt_descr;
54257c478bd9Sstevel@tonic-gate 		isoc_pkt_count = isoc_reqp->isoc_pkts_count;
54267c478bd9Sstevel@tonic-gate 	} else {
54277c478bd9Sstevel@tonic-gate 		isoc_pkt_descr = ((usb_isoc_req_t *)
54287c478bd9Sstevel@tonic-gate 		    pp->pp_client_periodic_in_reqp)->isoc_pkt_descr;
54297c478bd9Sstevel@tonic-gate 
54307c478bd9Sstevel@tonic-gate 		isoc_pkt_count = ((usb_isoc_req_t *)
54317c478bd9Sstevel@tonic-gate 		    pp->pp_client_periodic_in_reqp)->isoc_pkts_count;
54327c478bd9Sstevel@tonic-gate 	}
54337c478bd9Sstevel@tonic-gate 
54347c478bd9Sstevel@tonic-gate 	/*
54357c478bd9Sstevel@tonic-gate 	 * For isochronous IN pipe, get value of number of isochronous
54367c478bd9Sstevel@tonic-gate 	 * packets per usb isochronous request
54377c478bd9Sstevel@tonic-gate 	 */
54387c478bd9Sstevel@tonic-gate 	if (pipe_dir == USB_EP_DIR_IN) {
54397c478bd9Sstevel@tonic-gate 		for (count = 0, tw_length = 0;
54407c478bd9Sstevel@tonic-gate 		    count < isoc_pkt_count; count++) {
54417c478bd9Sstevel@tonic-gate 			tw_length += isoc_pkt_descr->isoc_pkt_length;
54427c478bd9Sstevel@tonic-gate 			isoc_pkt_descr++;
54437c478bd9Sstevel@tonic-gate 		}
54447c478bd9Sstevel@tonic-gate 	} else {
54457c478bd9Sstevel@tonic-gate 		ASSERT(isoc_reqp != NULL);
54467c478bd9Sstevel@tonic-gate 		tw_length = isoc_reqp->isoc_data->b_wptr -
54477c478bd9Sstevel@tonic-gate 		    isoc_reqp->isoc_data->b_rptr;
54487c478bd9Sstevel@tonic-gate 	}
54497c478bd9Sstevel@tonic-gate 
54507c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
54517c478bd9Sstevel@tonic-gate 	    "ohci_allocate_isoc_resources: length = 0x%lx", tw_length);
54527c478bd9Sstevel@tonic-gate 
54537c478bd9Sstevel@tonic-gate 	/* Check the size of isochronous request */
54547c478bd9Sstevel@tonic-gate 	if (tw_length > max_isoc_xfer_size) {
54557c478bd9Sstevel@tonic-gate 
54567c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
54577c478bd9Sstevel@tonic-gate 		    "ohci_allocate_isoc_resources: Maximum isoc request"
54587c478bd9Sstevel@tonic-gate 		    "size 0x%x Given isoc request size 0x%lx",
54597c478bd9Sstevel@tonic-gate 		    max_isoc_xfer_size, tw_length);
54607c478bd9Sstevel@tonic-gate 
54617c478bd9Sstevel@tonic-gate 		return (NULL);
54627c478bd9Sstevel@tonic-gate 	}
54637c478bd9Sstevel@tonic-gate 
54647c478bd9Sstevel@tonic-gate 	/*
54657c478bd9Sstevel@tonic-gate 	 * Each isochronous TD can hold data upto eight isochronous
54667c478bd9Sstevel@tonic-gate 	 * data packets. Calculate the number of isochronous TDs needs
54677c478bd9Sstevel@tonic-gate 	 * to be insert to complete current isochronous request.
54687c478bd9Sstevel@tonic-gate 	 */
54697c478bd9Sstevel@tonic-gate 	td_count = isoc_pkt_count / OHCI_ISOC_PKTS_PER_TD;
54707c478bd9Sstevel@tonic-gate 
54717c478bd9Sstevel@tonic-gate 	if (isoc_pkt_count % OHCI_ISOC_PKTS_PER_TD) {
54727c478bd9Sstevel@tonic-gate 		td_count++;
54737c478bd9Sstevel@tonic-gate 	}
54747c478bd9Sstevel@tonic-gate 
54757c478bd9Sstevel@tonic-gate 	if ((tw = ohci_allocate_tw_resources(ohcip, pp, tw_length,
54767c478bd9Sstevel@tonic-gate 	    flags, td_count)) == NULL) {
54777c478bd9Sstevel@tonic-gate 		return (NULL);
54787c478bd9Sstevel@tonic-gate 	}
54797c478bd9Sstevel@tonic-gate 
54807c478bd9Sstevel@tonic-gate 	if (pipe_dir == USB_EP_DIR_IN) {
54817c478bd9Sstevel@tonic-gate 		if (ohci_allocate_periodic_in_resource(ohcip, pp, tw, flags) !=
54827c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
54837c478bd9Sstevel@tonic-gate 
54847c478bd9Sstevel@tonic-gate 			ohci_deallocate_tw_resources(ohcip, pp, tw);
54857c478bd9Sstevel@tonic-gate 			return (NULL);
54867c478bd9Sstevel@tonic-gate 		}
54877c478bd9Sstevel@tonic-gate 		tw->tw_direction = HC_TD_IN;
54887c478bd9Sstevel@tonic-gate 	} else {
54897c478bd9Sstevel@tonic-gate 		if (tw->tw_length) {
54907c478bd9Sstevel@tonic-gate 			ASSERT(isoc_reqp->isoc_data != NULL);
54917c478bd9Sstevel@tonic-gate 
54927c478bd9Sstevel@tonic-gate 			/* Copy the data into the message */
54937c478bd9Sstevel@tonic-gate 			ddi_rep_put8(tw->tw_accesshandle,
54947c478bd9Sstevel@tonic-gate 			    isoc_reqp->isoc_data->b_rptr,
54957c478bd9Sstevel@tonic-gate 			    (uint8_t *)tw->tw_buf, tw->tw_length,
54967c478bd9Sstevel@tonic-gate 			    DDI_DEV_AUTOINCR);
54977c478bd9Sstevel@tonic-gate 		}
54987c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = (usb_opaque_t)isoc_reqp;
54997c478bd9Sstevel@tonic-gate 		tw->tw_direction = HC_TD_OUT;
55007c478bd9Sstevel@tonic-gate 	}
55017c478bd9Sstevel@tonic-gate 
55027c478bd9Sstevel@tonic-gate 	/*
55037c478bd9Sstevel@tonic-gate 	 * Initialize the callback and any callback
55047c478bd9Sstevel@tonic-gate 	 * data required when the td completes.
55057c478bd9Sstevel@tonic-gate 	 */
55067c478bd9Sstevel@tonic-gate 	tw->tw_handle_td = ohci_handle_isoc_td;
55077c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value = NULL;
55087c478bd9Sstevel@tonic-gate 
55097c478bd9Sstevel@tonic-gate 	return (tw);
55107c478bd9Sstevel@tonic-gate }
55117c478bd9Sstevel@tonic-gate 
55127c478bd9Sstevel@tonic-gate /*
55137c478bd9Sstevel@tonic-gate  * ohci_insert_isoc_req:
55147c478bd9Sstevel@tonic-gate  *
55157c478bd9Sstevel@tonic-gate  * Insert an isochronous request into the Host Controller's
55167c478bd9Sstevel@tonic-gate  * isochronous list.  If there is an error is will appropriately
55177c478bd9Sstevel@tonic-gate  * deallocate the unused resources.
55187c478bd9Sstevel@tonic-gate  */
55197c478bd9Sstevel@tonic-gate static int
55207c478bd9Sstevel@tonic-gate ohci_insert_isoc_req(
55217c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
55227c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
55237c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
55247c478bd9Sstevel@tonic-gate 	uint_t			flags)
55257c478bd9Sstevel@tonic-gate {
55267c478bd9Sstevel@tonic-gate 	size_t			curr_isoc_xfer_offset, curr_isoc_xfer_len;
55277c478bd9Sstevel@tonic-gate 	uint_t			isoc_pkts, residue, count;
55287c478bd9Sstevel@tonic-gate 	uint_t			i, ctrl, frame_count;
55297c478bd9Sstevel@tonic-gate 	uint_t			error = USB_SUCCESS;
55307c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*curr_isoc_reqp;
55317c478bd9Sstevel@tonic-gate 	usb_isoc_pkt_descr_t	*curr_isoc_pkt_descr;
55327c478bd9Sstevel@tonic-gate 
55337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
55347c478bd9Sstevel@tonic-gate 	    "ohci_insert_isoc_req: flags = 0x%x", flags);
55357c478bd9Sstevel@tonic-gate 
55367c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
55377c478bd9Sstevel@tonic-gate 
55387c478bd9Sstevel@tonic-gate 	/*
55397c478bd9Sstevel@tonic-gate 	 * Get the current isochronous request and packet
55407c478bd9Sstevel@tonic-gate 	 * descriptor pointers.
55417c478bd9Sstevel@tonic-gate 	 */
55427c478bd9Sstevel@tonic-gate 	curr_isoc_reqp = (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
55437c478bd9Sstevel@tonic-gate 	curr_isoc_pkt_descr = curr_isoc_reqp->isoc_pkt_descr;
55447c478bd9Sstevel@tonic-gate 
55457c478bd9Sstevel@tonic-gate 	ASSERT(curr_isoc_reqp != NULL);
55467c478bd9Sstevel@tonic-gate 	ASSERT(curr_isoc_reqp->isoc_pkt_descr != NULL);
55477c478bd9Sstevel@tonic-gate 
55487c478bd9Sstevel@tonic-gate 	/*
55497c478bd9Sstevel@tonic-gate 	 * Save address of first usb isochronous packet descriptor.
55507c478bd9Sstevel@tonic-gate 	 */
55517c478bd9Sstevel@tonic-gate 	tw->tw_curr_isoc_pktp = curr_isoc_reqp->isoc_pkt_descr;
55527c478bd9Sstevel@tonic-gate 
55537c478bd9Sstevel@tonic-gate 	/* Insert all the isochronous TDs */
55547c478bd9Sstevel@tonic-gate 	for (count = 0, curr_isoc_xfer_offset = 0,
55557c478bd9Sstevel@tonic-gate 	    isoc_pkts = 0; count < tw->tw_num_tds; count++) {
55567c478bd9Sstevel@tonic-gate 
55577c478bd9Sstevel@tonic-gate 		residue = curr_isoc_reqp->isoc_pkts_count - isoc_pkts;
55587c478bd9Sstevel@tonic-gate 
55597c478bd9Sstevel@tonic-gate 		/* Check for inserting residue data */
55607c478bd9Sstevel@tonic-gate 		if ((count == (tw->tw_num_tds - 1)) &&
55617c478bd9Sstevel@tonic-gate 		    (residue < OHCI_ISOC_PKTS_PER_TD)) {
55627c478bd9Sstevel@tonic-gate 			frame_count = residue;
55637c478bd9Sstevel@tonic-gate 		} else {
55647c478bd9Sstevel@tonic-gate 			frame_count = OHCI_ISOC_PKTS_PER_TD;
55657c478bd9Sstevel@tonic-gate 		}
55667c478bd9Sstevel@tonic-gate 
55677c478bd9Sstevel@tonic-gate 		curr_isoc_pkt_descr = tw->tw_curr_isoc_pktp;
55687c478bd9Sstevel@tonic-gate 
55697c478bd9Sstevel@tonic-gate 		/*
55707c478bd9Sstevel@tonic-gate 		 * Calculate length of isochronous transfer
55717c478bd9Sstevel@tonic-gate 		 * for the current TD.
55727c478bd9Sstevel@tonic-gate 		 */
55737c478bd9Sstevel@tonic-gate 		for (i = 0, curr_isoc_xfer_len = 0;
55747c478bd9Sstevel@tonic-gate 		    i < frame_count; i++, curr_isoc_pkt_descr++) {
55757c478bd9Sstevel@tonic-gate 			curr_isoc_xfer_len +=
55767c478bd9Sstevel@tonic-gate 			    curr_isoc_pkt_descr->isoc_pkt_length;
55777c478bd9Sstevel@tonic-gate 		}
55787c478bd9Sstevel@tonic-gate 
55797c478bd9Sstevel@tonic-gate 		/*
55807c478bd9Sstevel@tonic-gate 		 * Programm td control field by checking whether this
55817c478bd9Sstevel@tonic-gate 		 * is last td.
55827c478bd9Sstevel@tonic-gate 		 */
55837c478bd9Sstevel@tonic-gate 		if (count == (tw->tw_num_tds - 1)) {
55847c478bd9Sstevel@tonic-gate 			ctrl = ((((frame_count - 1) << HC_ITD_FC_SHIFT) &
55857c478bd9Sstevel@tonic-gate 			    HC_ITD_FC) | HC_TD_DT_0 | HC_TD_0I);
55867c478bd9Sstevel@tonic-gate 		} else {
55877c478bd9Sstevel@tonic-gate 			ctrl = ((((frame_count - 1) << HC_ITD_FC_SHIFT) &
55887c478bd9Sstevel@tonic-gate 			    HC_ITD_FC) | HC_TD_DT_0 | HC_TD_6I);
55897c478bd9Sstevel@tonic-gate 		}
55907c478bd9Sstevel@tonic-gate 
55917c478bd9Sstevel@tonic-gate 		/* Insert the TD into the endpoint */
55927c478bd9Sstevel@tonic-gate 		if ((error = ohci_insert_hc_td(ohcip, ctrl,
55937c478bd9Sstevel@tonic-gate 		    tw->tw_cookie.dmac_address + curr_isoc_xfer_offset,
55947c478bd9Sstevel@tonic-gate 		    curr_isoc_xfer_len, 0, pp, tw)) !=
55957c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
55967c478bd9Sstevel@tonic-gate 			tw->tw_num_tds = count;
55977c478bd9Sstevel@tonic-gate 			tw->tw_length  = curr_isoc_xfer_offset;
55987c478bd9Sstevel@tonic-gate 			break;
55997c478bd9Sstevel@tonic-gate 		}
56007c478bd9Sstevel@tonic-gate 
56017c478bd9Sstevel@tonic-gate 		isoc_pkts += frame_count;
56027c478bd9Sstevel@tonic-gate 		tw->tw_curr_isoc_pktp += frame_count;
56037c478bd9Sstevel@tonic-gate 		curr_isoc_xfer_offset += curr_isoc_xfer_len;
56047c478bd9Sstevel@tonic-gate 	}
56057c478bd9Sstevel@tonic-gate 
56067c478bd9Sstevel@tonic-gate 	if (error != USB_SUCCESS) {
56077c478bd9Sstevel@tonic-gate 		/* Free periodic in resources */
56087c478bd9Sstevel@tonic-gate 		if (tw->tw_direction == USB_EP_DIR_IN) {
56097c478bd9Sstevel@tonic-gate 			ohci_deallocate_periodic_in_resource(ohcip, pp, tw);
56107c478bd9Sstevel@tonic-gate 		}
56117c478bd9Sstevel@tonic-gate 
56127c478bd9Sstevel@tonic-gate 		/* Free all resources if IN or if count == 0(for both IN/OUT) */
56137c478bd9Sstevel@tonic-gate 		if (tw->tw_direction == USB_EP_DIR_IN || count == 0) {
56147c478bd9Sstevel@tonic-gate 
56157c478bd9Sstevel@tonic-gate 			ohci_deallocate_tw_resources(ohcip, pp, tw);
56167c478bd9Sstevel@tonic-gate 
56177c478bd9Sstevel@tonic-gate 			if (pp->pp_cur_periodic_req_cnt) {
56187c478bd9Sstevel@tonic-gate 				/*
56197c478bd9Sstevel@tonic-gate 				 * Set pipe state to stop polling and
56207c478bd9Sstevel@tonic-gate 				 * error to no resource. Don't insert
56217c478bd9Sstevel@tonic-gate 				 * any more isochronous polling requests.
56227c478bd9Sstevel@tonic-gate 				 */
56237c478bd9Sstevel@tonic-gate 				pp->pp_state = OHCI_PIPE_STATE_STOP_POLLING;
56247c478bd9Sstevel@tonic-gate 				pp->pp_error = error;
56257c478bd9Sstevel@tonic-gate 			} else {
56267c478bd9Sstevel@tonic-gate 				/* Set periodic in pipe state to idle */
56277c478bd9Sstevel@tonic-gate 				pp->pp_state = OHCI_PIPE_STATE_IDLE;
56287c478bd9Sstevel@tonic-gate 			}
56297c478bd9Sstevel@tonic-gate 		}
56307c478bd9Sstevel@tonic-gate 	} else {
56317c478bd9Sstevel@tonic-gate 
56327c478bd9Sstevel@tonic-gate 		/*
56337c478bd9Sstevel@tonic-gate 		 * Reset back to the address of first usb isochronous
56347c478bd9Sstevel@tonic-gate 		 * packet descriptor.
56357c478bd9Sstevel@tonic-gate 		 */
56367c478bd9Sstevel@tonic-gate 		tw->tw_curr_isoc_pktp = curr_isoc_reqp->isoc_pkt_descr;
56377c478bd9Sstevel@tonic-gate 
56387c478bd9Sstevel@tonic-gate 		/* Reset the CONTINUE flag */
56397c478bd9Sstevel@tonic-gate 		pp->pp_flag &= ~OHCI_ISOC_XFER_CONTINUE;
56407c478bd9Sstevel@tonic-gate 	}
56417c478bd9Sstevel@tonic-gate 
56427c478bd9Sstevel@tonic-gate 	return (error);
56437c478bd9Sstevel@tonic-gate }
56447c478bd9Sstevel@tonic-gate 
56457c478bd9Sstevel@tonic-gate 
56467c478bd9Sstevel@tonic-gate /*
56477c478bd9Sstevel@tonic-gate  * ohci_insert_hc_td:
56487c478bd9Sstevel@tonic-gate  *
56497c478bd9Sstevel@tonic-gate  * Insert a Transfer Descriptor (TD) on an Endpoint Descriptor (ED).
56507c478bd9Sstevel@tonic-gate  * Always returns USB_SUCCESS, except for ISOCH.
56517c478bd9Sstevel@tonic-gate  */
56527c478bd9Sstevel@tonic-gate static int
56537c478bd9Sstevel@tonic-gate ohci_insert_hc_td(
56547c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
56557c478bd9Sstevel@tonic-gate 	uint_t			hctd_ctrl,
56567c478bd9Sstevel@tonic-gate 	uint32_t		hctd_iommu_cbp,
56577c478bd9Sstevel@tonic-gate 	size_t			hctd_length,
56587c478bd9Sstevel@tonic-gate 	uint32_t		hctd_ctrl_phase,
56597c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
56607c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
56617c478bd9Sstevel@tonic-gate {
56627c478bd9Sstevel@tonic-gate 	ohci_td_t		*new_dummy;
56637c478bd9Sstevel@tonic-gate 	ohci_td_t		*cpu_current_dummy;
56647c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept;
56657c478bd9Sstevel@tonic-gate 	int			error;
56667c478bd9Sstevel@tonic-gate 
56677c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
56687c478bd9Sstevel@tonic-gate 
56697c478bd9Sstevel@tonic-gate 	/* Retrieve preallocated td from the TW */
56707c478bd9Sstevel@tonic-gate 	new_dummy = tw->tw_hctd_free_list;
56717c478bd9Sstevel@tonic-gate 
56727c478bd9Sstevel@tonic-gate 	ASSERT(new_dummy != NULL);
56737c478bd9Sstevel@tonic-gate 
56747c478bd9Sstevel@tonic-gate 	tw->tw_hctd_free_list = ohci_td_iommu_to_cpu(ohcip,
56757c478bd9Sstevel@tonic-gate 	    Get_TD(new_dummy->hctd_tw_next_td));
56767c478bd9Sstevel@tonic-gate 	Set_TD(new_dummy->hctd_tw_next_td, NULL);
56777c478bd9Sstevel@tonic-gate 
56787c478bd9Sstevel@tonic-gate 	/* Fill in the current dummy */
56797c478bd9Sstevel@tonic-gate 	cpu_current_dummy = (ohci_td_t *)
56807c478bd9Sstevel@tonic-gate 	    (ohci_td_iommu_to_cpu(ohcip, Get_ED(ept->hced_tailp)));
56817c478bd9Sstevel@tonic-gate 
56827c478bd9Sstevel@tonic-gate 	/*
56837c478bd9Sstevel@tonic-gate 	 * Fill in the current dummy td and
56847c478bd9Sstevel@tonic-gate 	 * add the new dummy to the end.
56857c478bd9Sstevel@tonic-gate 	 */
56867c478bd9Sstevel@tonic-gate 	ohci_fill_in_td(ohcip, cpu_current_dummy, new_dummy,
56877c478bd9Sstevel@tonic-gate 	    hctd_ctrl, hctd_iommu_cbp, hctd_length, hctd_ctrl_phase, pp, tw);
56887c478bd9Sstevel@tonic-gate 
56897c478bd9Sstevel@tonic-gate 	/*
56907c478bd9Sstevel@tonic-gate 	 * If this is an isochronous TD, first write proper
56917c478bd9Sstevel@tonic-gate 	 * starting usb frame number in which this TD must
56927c478bd9Sstevel@tonic-gate 	 * can be processed. After writing the frame number
56937c478bd9Sstevel@tonic-gate 	 * insert this TD into the ED's list.
56947c478bd9Sstevel@tonic-gate 	 */
56957c478bd9Sstevel@tonic-gate 	if ((pp->pp_pipe_handle->p_ep.bmAttributes &
56967c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH) {
56977c478bd9Sstevel@tonic-gate 
56987c478bd9Sstevel@tonic-gate 		error = ohci_insert_td_with_frame_number(
56997c478bd9Sstevel@tonic-gate 		    ohcip, pp, tw, cpu_current_dummy, new_dummy);
57007c478bd9Sstevel@tonic-gate 
57017c478bd9Sstevel@tonic-gate 		if (error != USB_SUCCESS) {
57027c478bd9Sstevel@tonic-gate 			/* Reset the current dummy back to a dummy */
57037c478bd9Sstevel@tonic-gate 			bzero((char *)cpu_current_dummy, sizeof (ohci_td_t));
57047c478bd9Sstevel@tonic-gate 			Set_TD(cpu_current_dummy->hctd_state, HC_TD_DUMMY);
57057c478bd9Sstevel@tonic-gate 
57067c478bd9Sstevel@tonic-gate 			/* return the new dummy back to the free list */
57077c478bd9Sstevel@tonic-gate 			bzero((char *)new_dummy, sizeof (ohci_td_t));
57087c478bd9Sstevel@tonic-gate 			Set_TD(new_dummy->hctd_state, HC_TD_DUMMY);
57097c478bd9Sstevel@tonic-gate 			if (tw->tw_hctd_free_list != NULL) {
57107c478bd9Sstevel@tonic-gate 				Set_TD(new_dummy->hctd_tw_next_td,
57117c478bd9Sstevel@tonic-gate 				    ohci_td_cpu_to_iommu(ohcip,
57127c478bd9Sstevel@tonic-gate 					tw->tw_hctd_free_list));
57137c478bd9Sstevel@tonic-gate 			}
57147c478bd9Sstevel@tonic-gate 			tw->tw_hctd_free_list = new_dummy;
57157c478bd9Sstevel@tonic-gate 
57167c478bd9Sstevel@tonic-gate 			return (error);
57177c478bd9Sstevel@tonic-gate 		}
57187c478bd9Sstevel@tonic-gate 	} else {
57197c478bd9Sstevel@tonic-gate 		/*
57207c478bd9Sstevel@tonic-gate 		 * For control, bulk and interrupt TD, just
57217c478bd9Sstevel@tonic-gate 		 * add the new dummy to the ED's list. When
57227c478bd9Sstevel@tonic-gate 		 * this occurs, the Host Controller ill see
57237c478bd9Sstevel@tonic-gate 		 * the newly filled in dummy TD.
57247c478bd9Sstevel@tonic-gate 		 */
57257c478bd9Sstevel@tonic-gate 		Set_ED(ept->hced_tailp,
57267c478bd9Sstevel@tonic-gate 		    (ohci_td_cpu_to_iommu(ohcip, new_dummy)));
57277c478bd9Sstevel@tonic-gate 	}
57287c478bd9Sstevel@tonic-gate 
57297c478bd9Sstevel@tonic-gate 	/* Insert this td onto the tw */
57307c478bd9Sstevel@tonic-gate 	ohci_insert_td_on_tw(ohcip, tw, cpu_current_dummy);
57317c478bd9Sstevel@tonic-gate 
57327c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
57337c478bd9Sstevel@tonic-gate }
57347c478bd9Sstevel@tonic-gate 
57357c478bd9Sstevel@tonic-gate 
57367c478bd9Sstevel@tonic-gate /*
57377c478bd9Sstevel@tonic-gate  * ohci_allocate_td_from_pool:
57387c478bd9Sstevel@tonic-gate  *
57397c478bd9Sstevel@tonic-gate  * Allocate a Transfer Descriptor (TD) from the TD buffer pool.
57407c478bd9Sstevel@tonic-gate  */
57417c478bd9Sstevel@tonic-gate static ohci_td_t *
57427c478bd9Sstevel@tonic-gate ohci_allocate_td_from_pool(ohci_state_t	*ohcip)
57437c478bd9Sstevel@tonic-gate {
57447c478bd9Sstevel@tonic-gate 	int				i, state;
57457c478bd9Sstevel@tonic-gate 	ohci_td_t			*td;
57467c478bd9Sstevel@tonic-gate 
57477c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
57487c478bd9Sstevel@tonic-gate 
57497c478bd9Sstevel@tonic-gate 	/*
57507c478bd9Sstevel@tonic-gate 	 * Search for a blank Transfer Descriptor (TD)
57517c478bd9Sstevel@tonic-gate 	 * in the TD buffer pool.
57527c478bd9Sstevel@tonic-gate 	 */
57537c478bd9Sstevel@tonic-gate 	for (i = 0; i < ohci_td_pool_size; i ++) {
57547c478bd9Sstevel@tonic-gate 		state = Get_TD(ohcip->ohci_td_pool_addr[i].hctd_state);
57557c478bd9Sstevel@tonic-gate 		if (state == HC_TD_FREE) {
57567c478bd9Sstevel@tonic-gate 			break;
57577c478bd9Sstevel@tonic-gate 		}
57587c478bd9Sstevel@tonic-gate 	}
57597c478bd9Sstevel@tonic-gate 
57607c478bd9Sstevel@tonic-gate 	if (i >= ohci_td_pool_size) {
57617c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
57627c478bd9Sstevel@tonic-gate 		    "ohci_allocate_td_from_pool: TD exhausted");
57637c478bd9Sstevel@tonic-gate 
57647c478bd9Sstevel@tonic-gate 		return (NULL);
57657c478bd9Sstevel@tonic-gate 	}
57667c478bd9Sstevel@tonic-gate 
57677c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
57687c478bd9Sstevel@tonic-gate 	    "ohci_allocate_td_from_pool: Allocated %d", i);
57697c478bd9Sstevel@tonic-gate 
57707c478bd9Sstevel@tonic-gate 	/* Create a new dummy for the end of the TD list */
57717c478bd9Sstevel@tonic-gate 	td = &ohcip->ohci_td_pool_addr[i];
57727c478bd9Sstevel@tonic-gate 
57737c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
57747c478bd9Sstevel@tonic-gate 	    "ohci_allocate_td_from_pool: td 0x%p", (void *)td);
57757c478bd9Sstevel@tonic-gate 
57767c478bd9Sstevel@tonic-gate 	/* Mark the newly allocated TD as a dummy */
57777c478bd9Sstevel@tonic-gate 	Set_TD(td->hctd_state, HC_TD_DUMMY);
57787c478bd9Sstevel@tonic-gate 
57797c478bd9Sstevel@tonic-gate 	return (td);
57807c478bd9Sstevel@tonic-gate }
57817c478bd9Sstevel@tonic-gate 
57827c478bd9Sstevel@tonic-gate /*
57837c478bd9Sstevel@tonic-gate  * ohci_fill_in_td:
57847c478bd9Sstevel@tonic-gate  *
57857c478bd9Sstevel@tonic-gate  * Fill in the fields of a Transfer Descriptor (TD).
57867c478bd9Sstevel@tonic-gate  */
57877c478bd9Sstevel@tonic-gate static void
57887c478bd9Sstevel@tonic-gate ohci_fill_in_td(
57897c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
57907c478bd9Sstevel@tonic-gate 	ohci_td_t		*td,
57917c478bd9Sstevel@tonic-gate 	ohci_td_t		*new_dummy,
57927c478bd9Sstevel@tonic-gate 	uint_t			hctd_ctrl,
57937c478bd9Sstevel@tonic-gate 	uint32_t		hctd_iommu_cbp,
57947c478bd9Sstevel@tonic-gate 	size_t			hctd_length,
57957c478bd9Sstevel@tonic-gate 	uint32_t		hctd_ctrl_phase,
57967c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
57977c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
57987c478bd9Sstevel@tonic-gate {
57997c478bd9Sstevel@tonic-gate 	/* Assert that the td to be filled in is a dummy */
58007c478bd9Sstevel@tonic-gate 	ASSERT(Get_TD(td->hctd_state) == HC_TD_DUMMY);
58017c478bd9Sstevel@tonic-gate 
58027c478bd9Sstevel@tonic-gate 	/* Change TD's state Active */
58037c478bd9Sstevel@tonic-gate 	Set_TD(td->hctd_state, HC_TD_ACTIVE);
58047c478bd9Sstevel@tonic-gate 
58057c478bd9Sstevel@tonic-gate 	/*
58067c478bd9Sstevel@tonic-gate 	 * If this is an isochronous TD, update the special itd
58077c478bd9Sstevel@tonic-gate 	 * portions. Otherwise, just update the control field.
58087c478bd9Sstevel@tonic-gate 	 */
58097c478bd9Sstevel@tonic-gate 	if ((pp->pp_pipe_handle->p_ep.bmAttributes &
58107c478bd9Sstevel@tonic-gate 		USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH) {
58117c478bd9Sstevel@tonic-gate 		ohci_init_itd(ohcip, pp, tw, hctd_ctrl, hctd_iommu_cbp, td);
58127c478bd9Sstevel@tonic-gate 	} else {
58137c478bd9Sstevel@tonic-gate 		/* Update the dummy with control information */
58147c478bd9Sstevel@tonic-gate 		Set_TD(td->hctd_ctrl, (hctd_ctrl | HC_TD_CC_NA));
58157c478bd9Sstevel@tonic-gate 
58167c478bd9Sstevel@tonic-gate 		/* Update the beginning of the buffer */
58177c478bd9Sstevel@tonic-gate 		Set_TD(td->hctd_cbp, hctd_iommu_cbp);
58187c478bd9Sstevel@tonic-gate 	}
58197c478bd9Sstevel@tonic-gate 
58207c478bd9Sstevel@tonic-gate 	/* The current dummy now points to the new dummy */
58217c478bd9Sstevel@tonic-gate 	Set_TD(td->hctd_next_td, (ohci_td_cpu_to_iommu(ohcip, new_dummy)));
58227c478bd9Sstevel@tonic-gate 
58237c478bd9Sstevel@tonic-gate 	/* Fill in the end of the buffer */
58247c478bd9Sstevel@tonic-gate 	if (hctd_length == 0) {
58257c478bd9Sstevel@tonic-gate 		ASSERT(Get_TD(td->hctd_cbp) == 0);
58267c478bd9Sstevel@tonic-gate 		ASSERT(hctd_iommu_cbp == 0);
58277c478bd9Sstevel@tonic-gate 		Set_TD(td->hctd_buf_end, 0);
58287c478bd9Sstevel@tonic-gate 	} else {
58297c478bd9Sstevel@tonic-gate 		Set_TD(td->hctd_buf_end,
58307c478bd9Sstevel@tonic-gate 		hctd_iommu_cbp + hctd_length - 1);
58317c478bd9Sstevel@tonic-gate 	}
58327c478bd9Sstevel@tonic-gate 
58337c478bd9Sstevel@tonic-gate 	/*
58347c478bd9Sstevel@tonic-gate 	 * For Control transfer, hctd_ctrl_phase is a valid field.
58357c478bd9Sstevel@tonic-gate 	 */
58367c478bd9Sstevel@tonic-gate 	if (hctd_ctrl_phase) {
58377c478bd9Sstevel@tonic-gate 		Set_TD(td->hctd_ctrl_phase, hctd_ctrl_phase);
58387c478bd9Sstevel@tonic-gate 	}
58397c478bd9Sstevel@tonic-gate 
58407c478bd9Sstevel@tonic-gate 	/* Print the td */
58417c478bd9Sstevel@tonic-gate 	ohci_print_td(ohcip, td);
58427c478bd9Sstevel@tonic-gate 
58437c478bd9Sstevel@tonic-gate 	/* Fill in the wrapper portion of the TD */
58447c478bd9Sstevel@tonic-gate 
58457c478bd9Sstevel@tonic-gate 	/* Set the transfer wrapper */
58467c478bd9Sstevel@tonic-gate 	ASSERT(tw != NULL);
58477c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_id != NULL);
58487c478bd9Sstevel@tonic-gate 
58497c478bd9Sstevel@tonic-gate 	Set_TD(td->hctd_trans_wrapper, (uint32_t)tw->tw_id);
58507c478bd9Sstevel@tonic-gate 	Set_TD(td->hctd_tw_next_td, NULL);
58517c478bd9Sstevel@tonic-gate }
58527c478bd9Sstevel@tonic-gate 
58537c478bd9Sstevel@tonic-gate 
58547c478bd9Sstevel@tonic-gate /*
58557c478bd9Sstevel@tonic-gate  * ohci_init_itd:
58567c478bd9Sstevel@tonic-gate  *
58577c478bd9Sstevel@tonic-gate  * Initialize the Isochronous portion of the Transfer Descriptor (TD).
58587c478bd9Sstevel@tonic-gate  */
58597c478bd9Sstevel@tonic-gate /* ARGSUSED */
58607c478bd9Sstevel@tonic-gate static void
58617c478bd9Sstevel@tonic-gate ohci_init_itd(
58627c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
58637c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
58647c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
58657c478bd9Sstevel@tonic-gate 	uint_t			hctd_ctrl,
58667c478bd9Sstevel@tonic-gate 	uint32_t		hctd_iommu_cbp,
58677c478bd9Sstevel@tonic-gate 	ohci_td_t		*td)
58687c478bd9Sstevel@tonic-gate {
58697c478bd9Sstevel@tonic-gate 	uint32_t		offset, offset_addr;
58707c478bd9Sstevel@tonic-gate 	uint_t			buf, fc, toggle, flag;
58717c478bd9Sstevel@tonic-gate 	usb_isoc_pkt_descr_t	*temp_pkt_descr;
58727c478bd9Sstevel@tonic-gate 	int			i;
58737c478bd9Sstevel@tonic-gate 
58747c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
58757c478bd9Sstevel@tonic-gate 
58767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
58777c478bd9Sstevel@tonic-gate 	    "ohci_init_itd: ctrl = 0x%x", hctd_ctrl);
58787c478bd9Sstevel@tonic-gate 
58797c478bd9Sstevel@tonic-gate 	/*
58807c478bd9Sstevel@tonic-gate 	 * Write control information except starting
58817c478bd9Sstevel@tonic-gate 	 * usb frame number.
58827c478bd9Sstevel@tonic-gate 	 */
58837c478bd9Sstevel@tonic-gate 	Set_TD(td->hctd_ctrl, (hctd_ctrl | HC_TD_CC_NA));
58847c478bd9Sstevel@tonic-gate 
58857c478bd9Sstevel@tonic-gate 	/*
58867c478bd9Sstevel@tonic-gate 	 * For an isochronous transfer, the hctd_cbp contains,
58877c478bd9Sstevel@tonic-gate 	 * the 4k page, and not the actual start of the buffer.
58887c478bd9Sstevel@tonic-gate 	 */
58897c478bd9Sstevel@tonic-gate 	Set_TD(td->hctd_cbp, ((uint32_t)hctd_iommu_cbp & HC_ITD_PAGE_MASK));
58907c478bd9Sstevel@tonic-gate 
58917c478bd9Sstevel@tonic-gate 	fc = (hctd_ctrl & HC_ITD_FC) >> HC_ITD_FC_SHIFT;
58927c478bd9Sstevel@tonic-gate 	toggle = 0;
58937c478bd9Sstevel@tonic-gate 	buf = hctd_iommu_cbp;
58947c478bd9Sstevel@tonic-gate 
58957c478bd9Sstevel@tonic-gate 	/*
58967c478bd9Sstevel@tonic-gate 	 * Get the address of first isochronous data packet
58977c478bd9Sstevel@tonic-gate 	 * for the current isochronous TD.
58987c478bd9Sstevel@tonic-gate 	 */
58997c478bd9Sstevel@tonic-gate 	temp_pkt_descr =  tw->tw_curr_isoc_pktp;
59007c478bd9Sstevel@tonic-gate 
59017c478bd9Sstevel@tonic-gate 	/* The offsets are actually offsets into the page */
59027c478bd9Sstevel@tonic-gate 	for (i = 0; i <= fc; i++) {
59037c478bd9Sstevel@tonic-gate 		offset_addr = (uint32_t)((buf &
59047c478bd9Sstevel@tonic-gate 		    HC_ITD_OFFSET_ADDR) | (HC_TD_CC_NA >> HC_ITD_CC_SHIFT));
59057c478bd9Sstevel@tonic-gate 
59067c478bd9Sstevel@tonic-gate 		flag =	((hctd_iommu_cbp &
59077c478bd9Sstevel@tonic-gate 		    HC_ITD_PAGE_MASK) ^ (buf & HC_ITD_PAGE_MASK));
59087c478bd9Sstevel@tonic-gate 
59097c478bd9Sstevel@tonic-gate 		if (flag) {
59107c478bd9Sstevel@tonic-gate 			offset_addr |= HC_ITD_4KBOUNDARY_CROSS;
59117c478bd9Sstevel@tonic-gate 		}
59127c478bd9Sstevel@tonic-gate 
59137c478bd9Sstevel@tonic-gate 		if (toggle) {
59147c478bd9Sstevel@tonic-gate 			offset = (uint32_t)((offset_addr <<
59157c478bd9Sstevel@tonic-gate 			    HC_ITD_OFFSET_SHIFT) & HC_ITD_ODD_OFFSET);
59167c478bd9Sstevel@tonic-gate 
59177c478bd9Sstevel@tonic-gate 			Set_TD(td->hctd_offsets[i / 2],
59187c478bd9Sstevel@tonic-gate 			    Get_TD(td->hctd_offsets[i / 2]) | offset);
59197c478bd9Sstevel@tonic-gate 			toggle = 0;
59207c478bd9Sstevel@tonic-gate 		} else {
59217c478bd9Sstevel@tonic-gate 			offset = (uint32_t)(offset_addr & HC_ITD_EVEN_OFFSET);
59227c478bd9Sstevel@tonic-gate 
59237c478bd9Sstevel@tonic-gate 			Set_TD(td->hctd_offsets[i / 2],
59247c478bd9Sstevel@tonic-gate 			    Get_TD(td->hctd_offsets[i / 2]) | offset);
59257c478bd9Sstevel@tonic-gate 			toggle = 1;
59267c478bd9Sstevel@tonic-gate 		}
59277c478bd9Sstevel@tonic-gate 
59287c478bd9Sstevel@tonic-gate 		buf = (uint32_t)(buf + temp_pkt_descr->isoc_pkt_length);
59297c478bd9Sstevel@tonic-gate 		temp_pkt_descr++;
59307c478bd9Sstevel@tonic-gate 	}
59317c478bd9Sstevel@tonic-gate }
59327c478bd9Sstevel@tonic-gate 
59337c478bd9Sstevel@tonic-gate 
59347c478bd9Sstevel@tonic-gate /*
59357c478bd9Sstevel@tonic-gate  * ohci_insert_td_with_frame_number:
59367c478bd9Sstevel@tonic-gate  *
59377c478bd9Sstevel@tonic-gate  * Insert current isochronous TD into the ED's list. with proper
59387c478bd9Sstevel@tonic-gate  * usb frame number in which this TD can be processed.
59397c478bd9Sstevel@tonic-gate  */
59407c478bd9Sstevel@tonic-gate static int
59417c478bd9Sstevel@tonic-gate ohci_insert_td_with_frame_number(
59427c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
59437c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
59447c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
59457c478bd9Sstevel@tonic-gate 	ohci_td_t		*current_td,
59467c478bd9Sstevel@tonic-gate 	ohci_td_t		*dummy_td)
59477c478bd9Sstevel@tonic-gate {
59487c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_reqp =
59497c478bd9Sstevel@tonic-gate 				    (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
59507c478bd9Sstevel@tonic-gate 	usb_frame_number_t	current_frame_number, start_frame_number;
59517c478bd9Sstevel@tonic-gate 	uint_t			ddic, ctrl, isoc_pkts;
59527c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept;
59537c478bd9Sstevel@tonic-gate 
59547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
59557c478bd9Sstevel@tonic-gate 	    "ohci_insert_td_with_frame_number:"
59567c478bd9Sstevel@tonic-gate 	    "isoc flags 0x%x", isoc_reqp->isoc_attributes);
59577c478bd9Sstevel@tonic-gate 
59587c478bd9Sstevel@tonic-gate 	/* Get the TD ctrl information */
59597c478bd9Sstevel@tonic-gate 	isoc_pkts = ((Get_TD(current_td->hctd_ctrl) &
59607c478bd9Sstevel@tonic-gate 	    HC_ITD_FC) >> HC_ITD_FC_SHIFT) + 1;
59617c478bd9Sstevel@tonic-gate 
59627c478bd9Sstevel@tonic-gate 	/*
59637c478bd9Sstevel@tonic-gate 	 * Enter critical, while programming the usb frame number
59647c478bd9Sstevel@tonic-gate 	 * and inserting current isochronous TD into the ED's list.
59657c478bd9Sstevel@tonic-gate 	 */
59667c478bd9Sstevel@tonic-gate 	ddic = ddi_enter_critical();
59677c478bd9Sstevel@tonic-gate 
59687c478bd9Sstevel@tonic-gate 	/* Get the current frame number */
59697c478bd9Sstevel@tonic-gate 	current_frame_number = ohci_get_current_frame_number(ohcip);
59707c478bd9Sstevel@tonic-gate 
59717c478bd9Sstevel@tonic-gate 	/* Check the given isochronous flags */
59727c478bd9Sstevel@tonic-gate 	switch (isoc_reqp->isoc_attributes &
59737c478bd9Sstevel@tonic-gate 	    (USB_ATTRS_ISOC_START_FRAME | USB_ATTRS_ISOC_XFER_ASAP)) {
59747c478bd9Sstevel@tonic-gate 	case USB_ATTRS_ISOC_START_FRAME:
59757c478bd9Sstevel@tonic-gate 		/* Starting frame number is specified */
59767c478bd9Sstevel@tonic-gate 		if (pp->pp_flag & OHCI_ISOC_XFER_CONTINUE) {
59777c478bd9Sstevel@tonic-gate 			/* Get the starting usb frame number */
59787c478bd9Sstevel@tonic-gate 			start_frame_number = pp->pp_next_frame_number;
59797c478bd9Sstevel@tonic-gate 		} else {
59807c478bd9Sstevel@tonic-gate 			/* Check for the Starting usb frame number */
59817c478bd9Sstevel@tonic-gate 			if ((isoc_reqp->isoc_frame_no == 0) ||
59827c478bd9Sstevel@tonic-gate 			    ((isoc_reqp->isoc_frame_no +
59837c478bd9Sstevel@tonic-gate 			    isoc_reqp->isoc_pkts_count) <
59847c478bd9Sstevel@tonic-gate 			    current_frame_number)) {
59857c478bd9Sstevel@tonic-gate 
59867c478bd9Sstevel@tonic-gate 				/* Exit the critical */
59877c478bd9Sstevel@tonic-gate 				ddi_exit_critical(ddic);
59887c478bd9Sstevel@tonic-gate 
59897c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_LISTS,
59907c478bd9Sstevel@tonic-gate 				    ohcip->ohci_log_hdl,
59917c478bd9Sstevel@tonic-gate 				    "ohci_insert_td_with_frame_number:"
59927c478bd9Sstevel@tonic-gate 				    "Invalid starting frame number");
59937c478bd9Sstevel@tonic-gate 
59947c478bd9Sstevel@tonic-gate 				return (USB_INVALID_START_FRAME);
59957c478bd9Sstevel@tonic-gate 			}
59967c478bd9Sstevel@tonic-gate 
59977c478bd9Sstevel@tonic-gate 			/* Get the starting usb frame number */
59987c478bd9Sstevel@tonic-gate 			start_frame_number = isoc_reqp->isoc_frame_no;
59997c478bd9Sstevel@tonic-gate 
60007c478bd9Sstevel@tonic-gate 			pp->pp_next_frame_number = 0;
60017c478bd9Sstevel@tonic-gate 		}
60027c478bd9Sstevel@tonic-gate 		break;
60037c478bd9Sstevel@tonic-gate 	case USB_ATTRS_ISOC_XFER_ASAP:
60047c478bd9Sstevel@tonic-gate 		/* ohci has to specify starting frame number */
60057c478bd9Sstevel@tonic-gate 		if ((pp->pp_next_frame_number) &&
60067c478bd9Sstevel@tonic-gate 		    (pp->pp_next_frame_number > current_frame_number)) {
60077c478bd9Sstevel@tonic-gate 			/*
60087c478bd9Sstevel@tonic-gate 			 * Get the next usb frame number.
60097c478bd9Sstevel@tonic-gate 			 */
60107c478bd9Sstevel@tonic-gate 			start_frame_number = pp->pp_next_frame_number;
60117c478bd9Sstevel@tonic-gate 		} else {
60127c478bd9Sstevel@tonic-gate 			/*
60137c478bd9Sstevel@tonic-gate 			 * Add appropriate offset to the current usb
60147c478bd9Sstevel@tonic-gate 			 * frame number and use it as a starting frame
60157c478bd9Sstevel@tonic-gate 			 * number.
60167c478bd9Sstevel@tonic-gate 			 */
60177c478bd9Sstevel@tonic-gate 			start_frame_number =
60187c478bd9Sstevel@tonic-gate 			    current_frame_number + OHCI_FRAME_OFFSET;
60197c478bd9Sstevel@tonic-gate 		}
60207c478bd9Sstevel@tonic-gate 
60217c478bd9Sstevel@tonic-gate 		if (!(pp->pp_flag & OHCI_ISOC_XFER_CONTINUE)) {
60227c478bd9Sstevel@tonic-gate 			isoc_reqp->isoc_frame_no = start_frame_number;
60237c478bd9Sstevel@tonic-gate 		}
60247c478bd9Sstevel@tonic-gate 		break;
60257c478bd9Sstevel@tonic-gate 	default:
60267c478bd9Sstevel@tonic-gate 		/* Exit the critical */
60277c478bd9Sstevel@tonic-gate 		ddi_exit_critical(ddic);
60287c478bd9Sstevel@tonic-gate 
60297c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
60307c478bd9Sstevel@tonic-gate 		    "ohci_insert_td_with_frame_number: Either starting "
60317c478bd9Sstevel@tonic-gate 		    "frame number or ASAP flags are not set, attrs = 0x%x",
60327c478bd9Sstevel@tonic-gate 		    isoc_reqp->isoc_attributes);
60337c478bd9Sstevel@tonic-gate 
60347c478bd9Sstevel@tonic-gate 		return (USB_NO_FRAME_NUMBER);
60357c478bd9Sstevel@tonic-gate 	}
60367c478bd9Sstevel@tonic-gate 
60377c478bd9Sstevel@tonic-gate 	/* Get the TD ctrl information */
60387c478bd9Sstevel@tonic-gate 	ctrl = Get_TD(current_td->hctd_ctrl) & (~(HC_ITD_SF));
60397c478bd9Sstevel@tonic-gate 
60407c478bd9Sstevel@tonic-gate 	/* Set the frame number field */
60417c478bd9Sstevel@tonic-gate 	Set_TD(current_td->hctd_ctrl, ctrl | (start_frame_number & HC_ITD_SF));
60427c478bd9Sstevel@tonic-gate 
60437c478bd9Sstevel@tonic-gate 	/*
60447c478bd9Sstevel@tonic-gate 	 * Add the new dummy to the ED's list. When this occurs,
60457c478bd9Sstevel@tonic-gate 	 * the Host Controller will see newly filled in dummy TD.
60467c478bd9Sstevel@tonic-gate 	 */
60477c478bd9Sstevel@tonic-gate 	Set_ED(ept->hced_tailp, (ohci_td_cpu_to_iommu(ohcip, dummy_td)));
60487c478bd9Sstevel@tonic-gate 
60497c478bd9Sstevel@tonic-gate 	/* Exit the critical */
60507c478bd9Sstevel@tonic-gate 	ddi_exit_critical(ddic);
60517c478bd9Sstevel@tonic-gate 
60527c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
60537c478bd9Sstevel@tonic-gate 	    "ohci_insert_td_with_frame_number:"
60547c478bd9Sstevel@tonic-gate 	    "current frame number 0x%llx start frame number 0x%llx",
60557c478bd9Sstevel@tonic-gate 	    current_frame_number, start_frame_number);
60567c478bd9Sstevel@tonic-gate 
60577c478bd9Sstevel@tonic-gate 	/*
60587c478bd9Sstevel@tonic-gate 	 * Increment this saved frame number by current number
60597c478bd9Sstevel@tonic-gate 	 * of data packets needs to be transfer.
60607c478bd9Sstevel@tonic-gate 	 */
60617c478bd9Sstevel@tonic-gate 	pp->pp_next_frame_number = start_frame_number + isoc_pkts;
60627c478bd9Sstevel@tonic-gate 
60637c478bd9Sstevel@tonic-gate 	/*
60647c478bd9Sstevel@tonic-gate 	 * Set OHCI_ISOC_XFER_CONTINUE flag in order to send other
60657c478bd9Sstevel@tonic-gate 	 * isochronous packets,  part of the current isoch request
60667c478bd9Sstevel@tonic-gate 	 * in the subsequent frames.
60677c478bd9Sstevel@tonic-gate 	 */
60687c478bd9Sstevel@tonic-gate 	pp->pp_flag |= OHCI_ISOC_XFER_CONTINUE;
60697c478bd9Sstevel@tonic-gate 
60707c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
60717c478bd9Sstevel@tonic-gate }
60727c478bd9Sstevel@tonic-gate 
60737c478bd9Sstevel@tonic-gate 
60747c478bd9Sstevel@tonic-gate /*
60757c478bd9Sstevel@tonic-gate  * ohci_insert_td_on_tw:
60767c478bd9Sstevel@tonic-gate  *
60777c478bd9Sstevel@tonic-gate  * The transfer wrapper keeps a list of all Transfer Descriptors (TD) that
60787c478bd9Sstevel@tonic-gate  * are allocated for this transfer. Insert a TD  onto this list. The  list
60797c478bd9Sstevel@tonic-gate  * of TD's does not include the dummy TD that is at the end of the list of
60807c478bd9Sstevel@tonic-gate  * TD's for the endpoint.
60817c478bd9Sstevel@tonic-gate  */
60827c478bd9Sstevel@tonic-gate static void
60837c478bd9Sstevel@tonic-gate ohci_insert_td_on_tw(
60847c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
60857c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
60867c478bd9Sstevel@tonic-gate 	ohci_td_t		*td)
60877c478bd9Sstevel@tonic-gate {
60887c478bd9Sstevel@tonic-gate 	/*
60897c478bd9Sstevel@tonic-gate 	 * Set the next pointer to NULL because
60907c478bd9Sstevel@tonic-gate 	 * this is the last TD on list.
60917c478bd9Sstevel@tonic-gate 	 */
60927c478bd9Sstevel@tonic-gate 	Set_TD(td->hctd_tw_next_td, NULL);
60937c478bd9Sstevel@tonic-gate 
60947c478bd9Sstevel@tonic-gate 	if (tw->tw_hctd_head == NULL) {
60957c478bd9Sstevel@tonic-gate 		ASSERT(tw->tw_hctd_tail == NULL);
60967c478bd9Sstevel@tonic-gate 		tw->tw_hctd_head = td;
60977c478bd9Sstevel@tonic-gate 		tw->tw_hctd_tail = td;
60987c478bd9Sstevel@tonic-gate 	} else {
60997c478bd9Sstevel@tonic-gate 		ohci_td_t *dummy = (ohci_td_t *)tw->tw_hctd_tail;
61007c478bd9Sstevel@tonic-gate 
61017c478bd9Sstevel@tonic-gate 		ASSERT(dummy != NULL);
61027c478bd9Sstevel@tonic-gate 		ASSERT(dummy != td);
61037c478bd9Sstevel@tonic-gate 		ASSERT(Get_TD(td->hctd_state) != HC_TD_DUMMY);
61047c478bd9Sstevel@tonic-gate 
61057c478bd9Sstevel@tonic-gate 		/* Add the td to the end of the list */
61067c478bd9Sstevel@tonic-gate 		Set_TD(dummy->hctd_tw_next_td,
61077c478bd9Sstevel@tonic-gate 		    ohci_td_cpu_to_iommu(ohcip, td));
61087c478bd9Sstevel@tonic-gate 
61097c478bd9Sstevel@tonic-gate 		tw->tw_hctd_tail = td;
61107c478bd9Sstevel@tonic-gate 
61117c478bd9Sstevel@tonic-gate 		ASSERT(Get_TD(td->hctd_tw_next_td) == NULL);
61127c478bd9Sstevel@tonic-gate 	}
61137c478bd9Sstevel@tonic-gate }
61147c478bd9Sstevel@tonic-gate 
61157c478bd9Sstevel@tonic-gate 
61167c478bd9Sstevel@tonic-gate /*
61177c478bd9Sstevel@tonic-gate  * ohci_traverse_tds:
61187c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
61197c478bd9Sstevel@tonic-gate  *
61207c478bd9Sstevel@tonic-gate  * Traverse the list of TD's for an endpoint.  Since the endpoint is marked
61217c478bd9Sstevel@tonic-gate  * as sKipped,	the Host Controller (HC) is no longer accessing these TD's.
61227c478bd9Sstevel@tonic-gate  * Remove all the TD's that are attached to the endpoint.
61237c478bd9Sstevel@tonic-gate  */
61247c478bd9Sstevel@tonic-gate void
61257c478bd9Sstevel@tonic-gate ohci_traverse_tds(
61267c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
61277c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
61287c478bd9Sstevel@tonic-gate {
61297c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
61307c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept;
61317c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;
61327c478bd9Sstevel@tonic-gate 	uint32_t		addr;
61337c478bd9Sstevel@tonic-gate 	ohci_td_t		*tailp, *headp, *next;
61347c478bd9Sstevel@tonic-gate 
61357c478bd9Sstevel@tonic-gate 	pp = (ohci_pipe_private_t *)ph->p_hcd_private;
61367c478bd9Sstevel@tonic-gate 	ept = pp->pp_ept;
61377c478bd9Sstevel@tonic-gate 
61387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
61397c478bd9Sstevel@tonic-gate 	    "ohci_traverse_tds: ph = 0x%p ept = 0x%p",
61407c478bd9Sstevel@tonic-gate 	    (void *)ph, (void *)ept);
61417c478bd9Sstevel@tonic-gate 
61427c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
61437c478bd9Sstevel@tonic-gate 
61447c478bd9Sstevel@tonic-gate 	addr = Get_ED(ept->hced_headp) & (uint32_t)HC_EPT_TD_HEAD;
61457c478bd9Sstevel@tonic-gate 
61467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
61477c478bd9Sstevel@tonic-gate 	    "ohci_traverse_tds: addr (head) = 0x%x", addr);
61487c478bd9Sstevel@tonic-gate 
61497c478bd9Sstevel@tonic-gate 	headp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip, addr));
61507c478bd9Sstevel@tonic-gate 
61517c478bd9Sstevel@tonic-gate 	addr = Get_ED(ept->hced_tailp) & (uint32_t)HC_EPT_TD_TAIL;
61527c478bd9Sstevel@tonic-gate 
61537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
61547c478bd9Sstevel@tonic-gate 	    "ohci_traverse_tds: addr (tail) = 0x%x", addr);
61557c478bd9Sstevel@tonic-gate 
61567c478bd9Sstevel@tonic-gate 	tailp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip, addr));
61577c478bd9Sstevel@tonic-gate 
61587c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
61597c478bd9Sstevel@tonic-gate 	    "ohci_traverse_tds: cpu head = 0x%p cpu tail = 0x%p",
61607c478bd9Sstevel@tonic-gate 	    (void *)headp, (void *)tailp);
61617c478bd9Sstevel@tonic-gate 
61627c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
61637c478bd9Sstevel@tonic-gate 	    "ohci_traverse_tds: iommu head = 0x%x iommu tail = 0x%x",
61647c478bd9Sstevel@tonic-gate 	    ohci_td_cpu_to_iommu(ohcip, headp),
61657c478bd9Sstevel@tonic-gate 	    ohci_td_cpu_to_iommu(ohcip, tailp));
61667c478bd9Sstevel@tonic-gate 
61677c478bd9Sstevel@tonic-gate 	/*
61687c478bd9Sstevel@tonic-gate 	 * Traverse the list of TD's that are currently on the endpoint.
61697c478bd9Sstevel@tonic-gate 	 * These TD's have not been processed and will not be processed
61707c478bd9Sstevel@tonic-gate 	 * because the endpoint processing is stopped.
61717c478bd9Sstevel@tonic-gate 	 */
61727c478bd9Sstevel@tonic-gate 	while (headp != tailp) {
61737c478bd9Sstevel@tonic-gate 		next = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip,
61747c478bd9Sstevel@tonic-gate 		    (Get_TD(headp->hctd_next_td) & HC_EPT_TD_TAIL)));
61757c478bd9Sstevel@tonic-gate 
61767c478bd9Sstevel@tonic-gate 		tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
61777c478bd9Sstevel@tonic-gate 		    (uint32_t)Get_TD(headp->hctd_trans_wrapper));
61787c478bd9Sstevel@tonic-gate 
61797c478bd9Sstevel@tonic-gate 		/* Stop the the transfer timer */
61807c478bd9Sstevel@tonic-gate 		ohci_stop_xfer_timer(ohcip, tw, OHCI_REMOVE_XFER_ALWAYS);
61817c478bd9Sstevel@tonic-gate 
61827c478bd9Sstevel@tonic-gate 		ohci_deallocate_td(ohcip, headp);
61837c478bd9Sstevel@tonic-gate 		headp = next;
61847c478bd9Sstevel@tonic-gate 	}
61857c478bd9Sstevel@tonic-gate 
61867c478bd9Sstevel@tonic-gate 	/* Both head and tail pointers must be same */
61877c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
61887c478bd9Sstevel@tonic-gate 	    "ohci_traverse_tds: head = 0x%p tail = 0x%p",
61897c478bd9Sstevel@tonic-gate 	    (void *)headp, (void *)tailp);
61907c478bd9Sstevel@tonic-gate 
61917c478bd9Sstevel@tonic-gate 	/* Update the pointer in the endpoint descriptor */
61927c478bd9Sstevel@tonic-gate 	Set_ED(ept->hced_headp, (ohci_td_cpu_to_iommu(ohcip, headp)));
61937c478bd9Sstevel@tonic-gate 
61947c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
61957c478bd9Sstevel@tonic-gate 	    "ohci_traverse_tds: new head = 0x%x",
61967c478bd9Sstevel@tonic-gate 	    (ohci_td_cpu_to_iommu(ohcip, headp)));
61977c478bd9Sstevel@tonic-gate 
61987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
61997c478bd9Sstevel@tonic-gate 	    "ohci_traverse_tds: tailp = 0x%x headp = 0x%x",
62007c478bd9Sstevel@tonic-gate 	    (Get_ED(ept->hced_tailp) & HC_EPT_TD_TAIL),
62017c478bd9Sstevel@tonic-gate 	    (Get_ED(ept->hced_headp) & HC_EPT_TD_HEAD));
62027c478bd9Sstevel@tonic-gate 
62037c478bd9Sstevel@tonic-gate 	ASSERT((Get_ED(ept->hced_tailp) & HC_EPT_TD_TAIL) ==
62047c478bd9Sstevel@tonic-gate 	    (Get_ED(ept->hced_headp) & HC_EPT_TD_HEAD));
62057c478bd9Sstevel@tonic-gate }
62067c478bd9Sstevel@tonic-gate 
62077c478bd9Sstevel@tonic-gate 
62087c478bd9Sstevel@tonic-gate /*
62097c478bd9Sstevel@tonic-gate  * ohci_done_list_tds:
62107c478bd9Sstevel@tonic-gate  *
62117c478bd9Sstevel@tonic-gate  * There may be TD's on the done list that have not been processed yet. Walk
62127c478bd9Sstevel@tonic-gate  * through these TD's and mark them as RECLAIM. All the mappings for the  TD
62137c478bd9Sstevel@tonic-gate  * will be torn down, so the interrupt handle is alerted of this fact through
62147c478bd9Sstevel@tonic-gate  * the RECLAIM flag.
62157c478bd9Sstevel@tonic-gate  */
62167c478bd9Sstevel@tonic-gate static void
62177c478bd9Sstevel@tonic-gate ohci_done_list_tds(
62187c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
62197c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
62207c478bd9Sstevel@tonic-gate {
62217c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
62227c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*head_tw = pp->pp_tw_head;
62237c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*next_tw;
62247c478bd9Sstevel@tonic-gate 	ohci_td_t		*head_td, *next_td;
62257c478bd9Sstevel@tonic-gate 
62267c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
62277c478bd9Sstevel@tonic-gate 
62287c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
62297c478bd9Sstevel@tonic-gate 	    "ohci_done_list_tds:");
62307c478bd9Sstevel@tonic-gate 
62317c478bd9Sstevel@tonic-gate 	/* Process the transfer wrappers for this pipe */
62327c478bd9Sstevel@tonic-gate 	next_tw = head_tw;
62337c478bd9Sstevel@tonic-gate 	while (next_tw) {
62347c478bd9Sstevel@tonic-gate 		head_td = (ohci_td_t *)next_tw->tw_hctd_head;
62357c478bd9Sstevel@tonic-gate 		next_td = head_td;
62367c478bd9Sstevel@tonic-gate 
62377c478bd9Sstevel@tonic-gate 		if (head_td) {
62387c478bd9Sstevel@tonic-gate 			/*
62397c478bd9Sstevel@tonic-gate 			 * Walk through each TD for this transfer
62407c478bd9Sstevel@tonic-gate 			 * wrapper. If a TD still exists, then it
62417c478bd9Sstevel@tonic-gate 			 * is currently on the done list.
62427c478bd9Sstevel@tonic-gate 			 */
62437c478bd9Sstevel@tonic-gate 			while (next_td) {
62447c478bd9Sstevel@tonic-gate 
62457c478bd9Sstevel@tonic-gate 				/* To free TD, set TD state to RECLAIM */
62467c478bd9Sstevel@tonic-gate 				Set_TD(next_td->hctd_state, HC_TD_RECLAIM);
62477c478bd9Sstevel@tonic-gate 
62487c478bd9Sstevel@tonic-gate 				Set_TD(next_td->hctd_trans_wrapper, NULL);
62497c478bd9Sstevel@tonic-gate 
62507c478bd9Sstevel@tonic-gate 				next_td = ohci_td_iommu_to_cpu(ohcip,
62517c478bd9Sstevel@tonic-gate 				    Get_TD(next_td->hctd_tw_next_td));
62527c478bd9Sstevel@tonic-gate 			}
62537c478bd9Sstevel@tonic-gate 		}
62547c478bd9Sstevel@tonic-gate 
62557c478bd9Sstevel@tonic-gate 		/* Stop the the transfer timer */
62567c478bd9Sstevel@tonic-gate 		ohci_stop_xfer_timer(ohcip, next_tw, OHCI_REMOVE_XFER_ALWAYS);
62577c478bd9Sstevel@tonic-gate 
62587c478bd9Sstevel@tonic-gate 		next_tw = next_tw->tw_next;
62597c478bd9Sstevel@tonic-gate 	}
62607c478bd9Sstevel@tonic-gate }
62617c478bd9Sstevel@tonic-gate 
62627c478bd9Sstevel@tonic-gate 
62637c478bd9Sstevel@tonic-gate /*
62647c478bd9Sstevel@tonic-gate  * ohci_deallocate_td:
62657c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
62667c478bd9Sstevel@tonic-gate  *
62677c478bd9Sstevel@tonic-gate  * Deallocate a Host Controller's (HC) Transfer Descriptor (TD).
62687c478bd9Sstevel@tonic-gate  */
62697c478bd9Sstevel@tonic-gate void
62707c478bd9Sstevel@tonic-gate ohci_deallocate_td(
62717c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
62727c478bd9Sstevel@tonic-gate 	ohci_td_t	*old_td)
62737c478bd9Sstevel@tonic-gate {
62747c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
62757c478bd9Sstevel@tonic-gate 
62767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
62777c478bd9Sstevel@tonic-gate 	    "ohci_deallocate_td: old_td = 0x%p", (void *)old_td);
62787c478bd9Sstevel@tonic-gate 
62797c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
62807c478bd9Sstevel@tonic-gate 
62817c478bd9Sstevel@tonic-gate 	/*
62827c478bd9Sstevel@tonic-gate 	 * Obtain the transaction wrapper and tw will be
62837c478bd9Sstevel@tonic-gate 	 * NULL for the dummy and for the reclaim TD's.
62847c478bd9Sstevel@tonic-gate 	 */
62857c478bd9Sstevel@tonic-gate 	if ((Get_TD(old_td->hctd_state) == HC_TD_DUMMY) ||
62867c478bd9Sstevel@tonic-gate 		(Get_TD(old_td->hctd_state) == HC_TD_RECLAIM)) {
62877c478bd9Sstevel@tonic-gate 		tw = (ohci_trans_wrapper_t *)((uintptr_t)
62887c478bd9Sstevel@tonic-gate 		Get_TD(old_td->hctd_trans_wrapper));
62897c478bd9Sstevel@tonic-gate 		ASSERT(tw == NULL);
62907c478bd9Sstevel@tonic-gate 	} else {
62917c478bd9Sstevel@tonic-gate 		tw = (ohci_trans_wrapper_t *)
62927c478bd9Sstevel@tonic-gate 		OHCI_LOOKUP_ID((uint32_t)
62937c478bd9Sstevel@tonic-gate 		Get_TD(old_td->hctd_trans_wrapper));
62947c478bd9Sstevel@tonic-gate 		ASSERT(tw != NULL);
62957c478bd9Sstevel@tonic-gate 	}
62967c478bd9Sstevel@tonic-gate 
62977c478bd9Sstevel@tonic-gate 	/*
62987c478bd9Sstevel@tonic-gate 	 * If this TD should be reclaimed, don't try to access its
62997c478bd9Sstevel@tonic-gate 	 * transfer wrapper.
63007c478bd9Sstevel@tonic-gate 	 */
63017c478bd9Sstevel@tonic-gate 	if ((Get_TD(old_td->hctd_state) != HC_TD_RECLAIM) && tw) {
63027c478bd9Sstevel@tonic-gate 		ohci_td_t	*td = (ohci_td_t *)tw->tw_hctd_head;
63037c478bd9Sstevel@tonic-gate 		ohci_td_t	*test;
63047c478bd9Sstevel@tonic-gate 
63057c478bd9Sstevel@tonic-gate 		/*
63067c478bd9Sstevel@tonic-gate 		 * Take this TD off the transfer wrapper's list since
63077c478bd9Sstevel@tonic-gate 		 * the pipe is FIFO, this must be the first TD on the
63087c478bd9Sstevel@tonic-gate 		 * list.
63097c478bd9Sstevel@tonic-gate 		 */
63107c478bd9Sstevel@tonic-gate 		ASSERT((ohci_td_t *)tw->tw_hctd_head == old_td);
63117c478bd9Sstevel@tonic-gate 
63127c478bd9Sstevel@tonic-gate 		tw->tw_hctd_head =
63137c478bd9Sstevel@tonic-gate 		    ohci_td_iommu_to_cpu(ohcip, Get_TD(td->hctd_tw_next_td));
63147c478bd9Sstevel@tonic-gate 
63157c478bd9Sstevel@tonic-gate 		if (tw->tw_hctd_head) {
63167c478bd9Sstevel@tonic-gate 			test = (ohci_td_t *)tw->tw_hctd_head;
63177c478bd9Sstevel@tonic-gate 			ASSERT(Get_TD(test->hctd_state) != HC_TD_DUMMY);
63187c478bd9Sstevel@tonic-gate 		}
63197c478bd9Sstevel@tonic-gate 
63207c478bd9Sstevel@tonic-gate 		/*
63217c478bd9Sstevel@tonic-gate 		 * If the head becomes NULL, then there are no more
63227c478bd9Sstevel@tonic-gate 		 * active TD's for this transfer wrapper. Also	set
63237c478bd9Sstevel@tonic-gate 		 * the tail to NULL.
63247c478bd9Sstevel@tonic-gate 		 */
63257c478bd9Sstevel@tonic-gate 		if (tw->tw_hctd_head == NULL) {
63267c478bd9Sstevel@tonic-gate 			tw->tw_hctd_tail = NULL;
63277c478bd9Sstevel@tonic-gate 		} else {
63287c478bd9Sstevel@tonic-gate 			/*
63297c478bd9Sstevel@tonic-gate 			 * If this is the last td on the list, make
63307c478bd9Sstevel@tonic-gate 			 * sure it doesn't point to yet another td.
63317c478bd9Sstevel@tonic-gate 			 */
63327c478bd9Sstevel@tonic-gate 			if (tw->tw_hctd_head == tw->tw_hctd_tail) {
63337c478bd9Sstevel@tonic-gate 				td = (ohci_td_t *)tw->tw_hctd_head;
63347c478bd9Sstevel@tonic-gate 
63357c478bd9Sstevel@tonic-gate 				ASSERT(Get_TD(td->hctd_tw_next_td) == NULL);
63367c478bd9Sstevel@tonic-gate 			}
63377c478bd9Sstevel@tonic-gate 		}
63387c478bd9Sstevel@tonic-gate 	}
63397c478bd9Sstevel@tonic-gate 
63407c478bd9Sstevel@tonic-gate 	bzero((void *)old_td, sizeof (ohci_td_t));
63417c478bd9Sstevel@tonic-gate 	Set_TD(old_td->hctd_state, HC_TD_FREE);
63427c478bd9Sstevel@tonic-gate 
63437c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
63447c478bd9Sstevel@tonic-gate 	    "ohci_deallocate_td: td 0x%p", (void *)old_td);
63457c478bd9Sstevel@tonic-gate }
63467c478bd9Sstevel@tonic-gate 
63477c478bd9Sstevel@tonic-gate 
63487c478bd9Sstevel@tonic-gate /*
63497c478bd9Sstevel@tonic-gate  * ohci_td_cpu_to_iommu:
63507c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
63517c478bd9Sstevel@tonic-gate  *
63527c478bd9Sstevel@tonic-gate  * This function converts for the given Transfer Descriptor (TD) CPU address
63537c478bd9Sstevel@tonic-gate  * to IO address.
63547c478bd9Sstevel@tonic-gate  */
63557c478bd9Sstevel@tonic-gate uint32_t
63567c478bd9Sstevel@tonic-gate ohci_td_cpu_to_iommu(
63577c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
63587c478bd9Sstevel@tonic-gate 	ohci_td_t	*addr)
63597c478bd9Sstevel@tonic-gate {
63607c478bd9Sstevel@tonic-gate 	uint32_t	td;
63617c478bd9Sstevel@tonic-gate 
63627c478bd9Sstevel@tonic-gate 	td  = (uint32_t)ohcip->ohci_td_pool_cookie.dmac_address +
63637c478bd9Sstevel@tonic-gate 	    (uint32_t)((uintptr_t)addr - (uintptr_t)(ohcip->ohci_td_pool_addr));
63647c478bd9Sstevel@tonic-gate 
63657c478bd9Sstevel@tonic-gate 	ASSERT((ohcip->ohci_td_pool_cookie.dmac_address +
63667c478bd9Sstevel@tonic-gate 	    (uint32_t) (sizeof (ohci_td_t) *
63677c478bd9Sstevel@tonic-gate 	    (addr - ohcip->ohci_td_pool_addr))) ==
63687c478bd9Sstevel@tonic-gate 	    (ohcip->ohci_td_pool_cookie.dmac_address +
63697c478bd9Sstevel@tonic-gate 	    (uint32_t)((uintptr_t)addr - (uintptr_t)
63707c478bd9Sstevel@tonic-gate 	    (ohcip->ohci_td_pool_addr))));
63717c478bd9Sstevel@tonic-gate 
63727c478bd9Sstevel@tonic-gate 	ASSERT(td >= ohcip->ohci_td_pool_cookie.dmac_address);
63737c478bd9Sstevel@tonic-gate 	ASSERT(td <= ohcip->ohci_td_pool_cookie.dmac_address +
63747c478bd9Sstevel@tonic-gate 	    sizeof (ohci_td_t) * ohci_td_pool_size);
63757c478bd9Sstevel@tonic-gate 
63767c478bd9Sstevel@tonic-gate 	return (td);
63777c478bd9Sstevel@tonic-gate }
63787c478bd9Sstevel@tonic-gate 
63797c478bd9Sstevel@tonic-gate 
63807c478bd9Sstevel@tonic-gate /*
63817c478bd9Sstevel@tonic-gate  * ohci_td_iommu_to_cpu:
63827c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
63837c478bd9Sstevel@tonic-gate  *
63847c478bd9Sstevel@tonic-gate  * This function converts for the given Transfer Descriptor (TD) IO address
63857c478bd9Sstevel@tonic-gate  * to CPU address.
63867c478bd9Sstevel@tonic-gate  */
63877c478bd9Sstevel@tonic-gate ohci_td_t *
63887c478bd9Sstevel@tonic-gate ohci_td_iommu_to_cpu(
63897c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
63907c478bd9Sstevel@tonic-gate 	uintptr_t	addr)
63917c478bd9Sstevel@tonic-gate {
63927c478bd9Sstevel@tonic-gate 	ohci_td_t	*td;
63937c478bd9Sstevel@tonic-gate 
63947c478bd9Sstevel@tonic-gate 	if (addr == NULL) {
63957c478bd9Sstevel@tonic-gate 
63967c478bd9Sstevel@tonic-gate 		return (NULL);
63977c478bd9Sstevel@tonic-gate 	}
63987c478bd9Sstevel@tonic-gate 
63997c478bd9Sstevel@tonic-gate 	td = (ohci_td_t *)((uintptr_t)
64007c478bd9Sstevel@tonic-gate 	    (addr - ohcip->ohci_td_pool_cookie.dmac_address) +
64017c478bd9Sstevel@tonic-gate 	    (uintptr_t)ohcip->ohci_td_pool_addr);
64027c478bd9Sstevel@tonic-gate 
64037c478bd9Sstevel@tonic-gate 	ASSERT(td >= ohcip->ohci_td_pool_addr);
64047c478bd9Sstevel@tonic-gate 	ASSERT((uintptr_t)td <= (uintptr_t)ohcip->ohci_td_pool_addr +
64057c478bd9Sstevel@tonic-gate 	    (uintptr_t)(sizeof (ohci_td_t) * ohci_td_pool_size));
64067c478bd9Sstevel@tonic-gate 
64077c478bd9Sstevel@tonic-gate 	return (td);
64087c478bd9Sstevel@tonic-gate }
64097c478bd9Sstevel@tonic-gate 
64107c478bd9Sstevel@tonic-gate /*
64117c478bd9Sstevel@tonic-gate  * ohci_allocate_tds_for_tw:
64127c478bd9Sstevel@tonic-gate  *
64137c478bd9Sstevel@tonic-gate  * Allocate n Transfer Descriptors (TD) from the TD buffer pool and places it
64147c478bd9Sstevel@tonic-gate  * into the TW.
64157c478bd9Sstevel@tonic-gate  *
64167c478bd9Sstevel@tonic-gate  * Returns USB_NO_RESOURCES if it was not able to allocate all the requested TD
64177c478bd9Sstevel@tonic-gate  * otherwise USB_SUCCESS.
64187c478bd9Sstevel@tonic-gate  */
64197c478bd9Sstevel@tonic-gate static int
64207c478bd9Sstevel@tonic-gate ohci_allocate_tds_for_tw(
64217c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
64227c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
64237c478bd9Sstevel@tonic-gate 	size_t			td_count)
64247c478bd9Sstevel@tonic-gate {
64257c478bd9Sstevel@tonic-gate 	ohci_td_t		*td;
64267c478bd9Sstevel@tonic-gate 	uint32_t		td_addr;
64277c478bd9Sstevel@tonic-gate 	int			i;
64287c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
64297c478bd9Sstevel@tonic-gate 
64307c478bd9Sstevel@tonic-gate 	for (i = 0; i < td_count; i++) {
64317c478bd9Sstevel@tonic-gate 		td = ohci_allocate_td_from_pool(ohcip);
64327c478bd9Sstevel@tonic-gate 		if (td == NULL) {
64337c478bd9Sstevel@tonic-gate 			error = USB_NO_RESOURCES;
64347c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
64357c478bd9Sstevel@tonic-gate 			    "ohci_allocate_tds_for_tw: "
64367c478bd9Sstevel@tonic-gate 			    "Unable to allocate %lu TDs",
64377c478bd9Sstevel@tonic-gate 			    td_count);
64387c478bd9Sstevel@tonic-gate 			break;
64397c478bd9Sstevel@tonic-gate 		}
64407c478bd9Sstevel@tonic-gate 		if (tw->tw_hctd_free_list != NULL) {
64417c478bd9Sstevel@tonic-gate 			td_addr = ohci_td_cpu_to_iommu(ohcip,
64427c478bd9Sstevel@tonic-gate 			    tw->tw_hctd_free_list);
64437c478bd9Sstevel@tonic-gate 			Set_TD(td->hctd_tw_next_td, td_addr);
64447c478bd9Sstevel@tonic-gate 		}
64457c478bd9Sstevel@tonic-gate 		tw->tw_hctd_free_list = td;
64467c478bd9Sstevel@tonic-gate 	}
64477c478bd9Sstevel@tonic-gate 
64487c478bd9Sstevel@tonic-gate 	return (error);
64497c478bd9Sstevel@tonic-gate }
64507c478bd9Sstevel@tonic-gate 
64517c478bd9Sstevel@tonic-gate /*
64527c478bd9Sstevel@tonic-gate  * ohci_allocate_tw_resources:
64537c478bd9Sstevel@tonic-gate  *
64547c478bd9Sstevel@tonic-gate  * Allocate a Transaction Wrapper (TW) and n Transfer Descriptors (TD)
64557c478bd9Sstevel@tonic-gate  * from the TD buffer pool and places it into the TW.  It does an all
64567c478bd9Sstevel@tonic-gate  * or nothing transaction.
64577c478bd9Sstevel@tonic-gate  *
64587c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise TW.
64597c478bd9Sstevel@tonic-gate  */
64607c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t *
64617c478bd9Sstevel@tonic-gate ohci_allocate_tw_resources(
64627c478bd9Sstevel@tonic-gate 	ohci_state_t 		*ohcip,
64637c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
64647c478bd9Sstevel@tonic-gate 	size_t			tw_length,
64657c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags,
64667c478bd9Sstevel@tonic-gate 	size_t 			td_count)
64677c478bd9Sstevel@tonic-gate {
64687c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
64697c478bd9Sstevel@tonic-gate 
64707c478bd9Sstevel@tonic-gate 	tw = ohci_create_transfer_wrapper(ohcip, pp, tw_length, usb_flags);
64717c478bd9Sstevel@tonic-gate 
64727c478bd9Sstevel@tonic-gate 	if (tw == NULL) {
64737c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
64747c478bd9Sstevel@tonic-gate 		    "ohci_allocate_tw_resources: Unable to allocate TW");
64757c478bd9Sstevel@tonic-gate 	} else {
64767c478bd9Sstevel@tonic-gate 		if (ohci_allocate_tds_for_tw(ohcip, tw, td_count) ==
64777c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
64787c478bd9Sstevel@tonic-gate 			tw->tw_num_tds = td_count;
64797c478bd9Sstevel@tonic-gate 		} else {
64807c478bd9Sstevel@tonic-gate 			ohci_deallocate_tw_resources(ohcip, pp, tw);
64817c478bd9Sstevel@tonic-gate 			tw = NULL;
64827c478bd9Sstevel@tonic-gate 		}
64837c478bd9Sstevel@tonic-gate 	}
64847c478bd9Sstevel@tonic-gate 
64857c478bd9Sstevel@tonic-gate 	return (tw);
64867c478bd9Sstevel@tonic-gate }
64877c478bd9Sstevel@tonic-gate 
64887c478bd9Sstevel@tonic-gate /*
64897c478bd9Sstevel@tonic-gate  * ohci_free_tw_tds_resources:
64907c478bd9Sstevel@tonic-gate  *
64917c478bd9Sstevel@tonic-gate  * Free all allocated resources for Transaction Wrapper (TW).
64927c478bd9Sstevel@tonic-gate  * Does not free the TW itself.
64937c478bd9Sstevel@tonic-gate  */
64947c478bd9Sstevel@tonic-gate static void
64957c478bd9Sstevel@tonic-gate ohci_free_tw_tds_resources(
64967c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
64977c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
64987c478bd9Sstevel@tonic-gate {
64997c478bd9Sstevel@tonic-gate 	ohci_td_t		*td;
65007c478bd9Sstevel@tonic-gate 	ohci_td_t		*temp_td;
65017c478bd9Sstevel@tonic-gate 
65027c478bd9Sstevel@tonic-gate 	td = tw->tw_hctd_free_list;
65037c478bd9Sstevel@tonic-gate 	while (td != NULL) {
65047c478bd9Sstevel@tonic-gate 		/* Save the pointer to the next td before destroying it */
65057c478bd9Sstevel@tonic-gate 		temp_td = ohci_td_iommu_to_cpu(ohcip,
65067c478bd9Sstevel@tonic-gate 		    Get_TD(td->hctd_tw_next_td));
65077c478bd9Sstevel@tonic-gate 		ohci_deallocate_td(ohcip, td);
65087c478bd9Sstevel@tonic-gate 		td = temp_td;
65097c478bd9Sstevel@tonic-gate 	}
65107c478bd9Sstevel@tonic-gate 	tw->tw_hctd_free_list = NULL;
65117c478bd9Sstevel@tonic-gate }
65127c478bd9Sstevel@tonic-gate 
65137c478bd9Sstevel@tonic-gate 
65147c478bd9Sstevel@tonic-gate /*
65157c478bd9Sstevel@tonic-gate  * Transfer Wrapper functions
65167c478bd9Sstevel@tonic-gate  *
65177c478bd9Sstevel@tonic-gate  * ohci_create_transfer_wrapper:
65187c478bd9Sstevel@tonic-gate  *
65197c478bd9Sstevel@tonic-gate  * Create a Transaction Wrapper (TW) and this involves the allocating of DMA
65207c478bd9Sstevel@tonic-gate  * resources.
65217c478bd9Sstevel@tonic-gate  */
65227c478bd9Sstevel@tonic-gate static ohci_trans_wrapper_t *
65237c478bd9Sstevel@tonic-gate ohci_create_transfer_wrapper(
65247c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
65257c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
65267c478bd9Sstevel@tonic-gate 	size_t			length,
65277c478bd9Sstevel@tonic-gate 	uint_t			usb_flags)
65287c478bd9Sstevel@tonic-gate {
65297c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t	dev_attr;
65307c478bd9Sstevel@tonic-gate 	int			result;
65317c478bd9Sstevel@tonic-gate 	size_t			real_length;
65327c478bd9Sstevel@tonic-gate 	uint_t			ccount;	/* Cookie count */
65337c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
65347c478bd9Sstevel@tonic-gate 
65357c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
65367c478bd9Sstevel@tonic-gate 	    "ohci_create_transfer_wrapper: length = 0x%lx flags = 0x%x",
65377c478bd9Sstevel@tonic-gate 	    length, usb_flags);
65387c478bd9Sstevel@tonic-gate 
65397c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
65407c478bd9Sstevel@tonic-gate 
65417c478bd9Sstevel@tonic-gate 	/* Allocate space for the transfer wrapper */
65427c478bd9Sstevel@tonic-gate 	tw = kmem_zalloc(sizeof (ohci_trans_wrapper_t), KM_NOSLEEP);
65437c478bd9Sstevel@tonic-gate 
65447c478bd9Sstevel@tonic-gate 	if (tw == NULL) {
65457c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC,  ohcip->ohci_log_hdl,
65467c478bd9Sstevel@tonic-gate 		    "ohci_create_transfer_wrapper: kmem_zalloc failed");
65477c478bd9Sstevel@tonic-gate 
65487c478bd9Sstevel@tonic-gate 		return (NULL);
65497c478bd9Sstevel@tonic-gate 	}
65507c478bd9Sstevel@tonic-gate 
65517c478bd9Sstevel@tonic-gate 	/*  Force the required 4K restrictive alignment */
65527c478bd9Sstevel@tonic-gate 	ohcip->ohci_dma_attr.dma_attr_align = OHCI_DMA_ATTR_ALIGNMENT;
65537c478bd9Sstevel@tonic-gate 
65547c478bd9Sstevel@tonic-gate 	/* Allocate the DMA handle */
65557c478bd9Sstevel@tonic-gate 	result = ddi_dma_alloc_handle(ohcip->ohci_dip,
65567c478bd9Sstevel@tonic-gate 	    &ohcip->ohci_dma_attr, DDI_DMA_DONTWAIT, 0, &tw->tw_dmahandle);
65577c478bd9Sstevel@tonic-gate 
65587c478bd9Sstevel@tonic-gate 	if (result != DDI_SUCCESS) {
65597c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
65607c478bd9Sstevel@tonic-gate 		    "ohci_create_transfer_wrapper: Alloc handle failed");
65617c478bd9Sstevel@tonic-gate 
65627c478bd9Sstevel@tonic-gate 		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
65637c478bd9Sstevel@tonic-gate 
65647c478bd9Sstevel@tonic-gate 		return (NULL);
65657c478bd9Sstevel@tonic-gate 	}
65667c478bd9Sstevel@tonic-gate 
65677c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
65687c478bd9Sstevel@tonic-gate 
65697c478bd9Sstevel@tonic-gate 	/* The host controller will be little endian */
65707c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_endian_flags  = DDI_STRUCTURE_BE_ACC;
65717c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
65727c478bd9Sstevel@tonic-gate 
65737c478bd9Sstevel@tonic-gate 	/* Allocate the memory */
65747c478bd9Sstevel@tonic-gate 	result = ddi_dma_mem_alloc(tw->tw_dmahandle, length,
65757c478bd9Sstevel@tonic-gate 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
65767c478bd9Sstevel@tonic-gate 	    (caddr_t *)&tw->tw_buf, &real_length, &tw->tw_accesshandle);
65777c478bd9Sstevel@tonic-gate 
65787c478bd9Sstevel@tonic-gate 	if (result != DDI_SUCCESS) {
65797c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
65807c478bd9Sstevel@tonic-gate 		    "ohci_create_transfer_wrapper: dma_mem_alloc fail");
65817c478bd9Sstevel@tonic-gate 
65827c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&tw->tw_dmahandle);
65837c478bd9Sstevel@tonic-gate 		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
65847c478bd9Sstevel@tonic-gate 
65857c478bd9Sstevel@tonic-gate 		return (NULL);
65867c478bd9Sstevel@tonic-gate 	}
65877c478bd9Sstevel@tonic-gate 
65887c478bd9Sstevel@tonic-gate 	ASSERT(real_length >= length);
65897c478bd9Sstevel@tonic-gate 
65907c478bd9Sstevel@tonic-gate 	/* Bind the handle */
65917c478bd9Sstevel@tonic-gate 	result = ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
65927c478bd9Sstevel@tonic-gate 	    (caddr_t)tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
65937c478bd9Sstevel@tonic-gate 	    DDI_DMA_DONTWAIT, NULL, &tw->tw_cookie, &ccount);
65947c478bd9Sstevel@tonic-gate 
65957c478bd9Sstevel@tonic-gate 	if (result == DDI_DMA_MAPPED) {
65967c478bd9Sstevel@tonic-gate 		/* The cookie count should be 1 */
65977c478bd9Sstevel@tonic-gate 		if (ccount != 1) {
65987c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
65997c478bd9Sstevel@tonic-gate 			    "ohci_create_transfer_wrapper: More than 1 cookie");
66007c478bd9Sstevel@tonic-gate 
66017c478bd9Sstevel@tonic-gate 			result = ddi_dma_unbind_handle(tw->tw_dmahandle);
66027c478bd9Sstevel@tonic-gate 			ASSERT(result == DDI_SUCCESS);
66037c478bd9Sstevel@tonic-gate 
66047c478bd9Sstevel@tonic-gate 			ddi_dma_mem_free(&tw->tw_accesshandle);
66057c478bd9Sstevel@tonic-gate 			ddi_dma_free_handle(&tw->tw_dmahandle);
66067c478bd9Sstevel@tonic-gate 			kmem_free(tw, sizeof (ohci_trans_wrapper_t));
66077c478bd9Sstevel@tonic-gate 
66087c478bd9Sstevel@tonic-gate 			return (NULL);
66097c478bd9Sstevel@tonic-gate 		}
66107c478bd9Sstevel@tonic-gate 	} else {
66117c478bd9Sstevel@tonic-gate 		ohci_decode_ddi_dma_addr_bind_handle_result(ohcip, result);
66127c478bd9Sstevel@tonic-gate 
66137c478bd9Sstevel@tonic-gate 		ddi_dma_mem_free(&tw->tw_accesshandle);
66147c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&tw->tw_dmahandle);
66157c478bd9Sstevel@tonic-gate 		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
66167c478bd9Sstevel@tonic-gate 
66177c478bd9Sstevel@tonic-gate 		return (NULL);
66187c478bd9Sstevel@tonic-gate 	}
66197c478bd9Sstevel@tonic-gate 
66207c478bd9Sstevel@tonic-gate 	/*
66217c478bd9Sstevel@tonic-gate 	 * Only allow one wrapper to be added at a time. Insert the
66227c478bd9Sstevel@tonic-gate 	 * new transaction wrapper into the list for this pipe.
66237c478bd9Sstevel@tonic-gate 	 */
66247c478bd9Sstevel@tonic-gate 	if (pp->pp_tw_head == NULL) {
66257c478bd9Sstevel@tonic-gate 		pp->pp_tw_head = tw;
66267c478bd9Sstevel@tonic-gate 		pp->pp_tw_tail = tw;
66277c478bd9Sstevel@tonic-gate 	} else {
66287c478bd9Sstevel@tonic-gate 		pp->pp_tw_tail->tw_next = tw;
66297c478bd9Sstevel@tonic-gate 		pp->pp_tw_tail = tw;
66307c478bd9Sstevel@tonic-gate 	}
66317c478bd9Sstevel@tonic-gate 
66327c478bd9Sstevel@tonic-gate 	/* Store the transfer length */
66337c478bd9Sstevel@tonic-gate 	tw->tw_length = length;
66347c478bd9Sstevel@tonic-gate 
66357c478bd9Sstevel@tonic-gate 	/* Store a back pointer to the pipe private structure */
66367c478bd9Sstevel@tonic-gate 	tw->tw_pipe_private = pp;
66377c478bd9Sstevel@tonic-gate 
66387c478bd9Sstevel@tonic-gate 	/* Store the transfer type - synchronous or asynchronous */
66397c478bd9Sstevel@tonic-gate 	tw->tw_flags = usb_flags;
66407c478bd9Sstevel@tonic-gate 
66417c478bd9Sstevel@tonic-gate 	/* Get and Store 32bit ID */
66427c478bd9Sstevel@tonic-gate 	tw->tw_id = OHCI_GET_ID((void *)tw);
66437c478bd9Sstevel@tonic-gate 
66447c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_id != NULL);
66457c478bd9Sstevel@tonic-gate 
66467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
66477c478bd9Sstevel@tonic-gate 	    "ohci_create_transfer_wrapper: tw = 0x%p", tw);
66487c478bd9Sstevel@tonic-gate 
66497c478bd9Sstevel@tonic-gate 	return (tw);
66507c478bd9Sstevel@tonic-gate }
66517c478bd9Sstevel@tonic-gate 
66527c478bd9Sstevel@tonic-gate 
66537c478bd9Sstevel@tonic-gate /*
66547c478bd9Sstevel@tonic-gate  * ohci_start_xfer_timer:
66557c478bd9Sstevel@tonic-gate  *
66567c478bd9Sstevel@tonic-gate  * Start the timer for the control, bulk and for one time interrupt
66577c478bd9Sstevel@tonic-gate  * transfers.
66587c478bd9Sstevel@tonic-gate  */
66597c478bd9Sstevel@tonic-gate /* ARGSUSED */
66607c478bd9Sstevel@tonic-gate static void
66617c478bd9Sstevel@tonic-gate ohci_start_xfer_timer(
66627c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
66637c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
66647c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
66657c478bd9Sstevel@tonic-gate {
66667c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS,  ohcip->ohci_log_hdl,
66677c478bd9Sstevel@tonic-gate 	    "ohci_start_xfer_timer: tw = 0x%p", tw);
66687c478bd9Sstevel@tonic-gate 
66697c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
66707c478bd9Sstevel@tonic-gate 
66717c478bd9Sstevel@tonic-gate 	/*
66727c478bd9Sstevel@tonic-gate 	 * The timeout handling is done only for control, bulk and for
66737c478bd9Sstevel@tonic-gate 	 * one time Interrupt transfers.
66747c478bd9Sstevel@tonic-gate 	 *
66757c478bd9Sstevel@tonic-gate 	 * NOTE: If timeout is zero; Assume infinite timeout and don't
66767c478bd9Sstevel@tonic-gate 	 * insert this transfer on the timeout list.
66777c478bd9Sstevel@tonic-gate 	 */
66787c478bd9Sstevel@tonic-gate 	if (tw->tw_timeout) {
66797c478bd9Sstevel@tonic-gate 		/*
66807c478bd9Sstevel@tonic-gate 		 * Increase timeout value by one second and this extra one
66817c478bd9Sstevel@tonic-gate 		 * second is used to halt the endpoint if given transfer
66827c478bd9Sstevel@tonic-gate 		 * times out.
66837c478bd9Sstevel@tonic-gate 		 */
66847c478bd9Sstevel@tonic-gate 		tw->tw_timeout++;
66857c478bd9Sstevel@tonic-gate 
66867c478bd9Sstevel@tonic-gate 		/*
66877c478bd9Sstevel@tonic-gate 		 * Add this transfer wrapper into the transfer timeout list.
66887c478bd9Sstevel@tonic-gate 		 */
66897c478bd9Sstevel@tonic-gate 		if (ohcip->ohci_timeout_list) {
66907c478bd9Sstevel@tonic-gate 			tw->tw_timeout_next = ohcip->ohci_timeout_list;
66917c478bd9Sstevel@tonic-gate 		}
66927c478bd9Sstevel@tonic-gate 
66937c478bd9Sstevel@tonic-gate 		ohcip->ohci_timeout_list = tw;
66947c478bd9Sstevel@tonic-gate 		ohci_start_timer(ohcip);
66957c478bd9Sstevel@tonic-gate 	}
66967c478bd9Sstevel@tonic-gate }
66977c478bd9Sstevel@tonic-gate 
66987c478bd9Sstevel@tonic-gate 
66997c478bd9Sstevel@tonic-gate /*
67007c478bd9Sstevel@tonic-gate  * ohci_stop_xfer_timer:
67017c478bd9Sstevel@tonic-gate  *
67027c478bd9Sstevel@tonic-gate  * Start the timer for the control, bulk and for one time interrupt
67037c478bd9Sstevel@tonic-gate  * transfers.
67047c478bd9Sstevel@tonic-gate  */
67057c478bd9Sstevel@tonic-gate void
67067c478bd9Sstevel@tonic-gate ohci_stop_xfer_timer(
67077c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
67087c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
67097c478bd9Sstevel@tonic-gate 	uint_t			flag)
67107c478bd9Sstevel@tonic-gate {
67117c478bd9Sstevel@tonic-gate 	timeout_id_t		timer_id;
67127c478bd9Sstevel@tonic-gate 
67137c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS,  ohcip->ohci_log_hdl,
67147c478bd9Sstevel@tonic-gate 	    "ohci_stop_xfer_timer: tw = 0x%p", tw);
67157c478bd9Sstevel@tonic-gate 
67167c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
67177c478bd9Sstevel@tonic-gate 
67187c478bd9Sstevel@tonic-gate 	/*
67197c478bd9Sstevel@tonic-gate 	 * The timeout handling is done only for control, bulk
67207c478bd9Sstevel@tonic-gate 	 * and for one time Interrupt transfers.
67217c478bd9Sstevel@tonic-gate 	 */
67227c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_timeout_list == NULL) {
67237c478bd9Sstevel@tonic-gate 		return;
67247c478bd9Sstevel@tonic-gate 	}
67257c478bd9Sstevel@tonic-gate 
67267c478bd9Sstevel@tonic-gate 	switch (flag) {
67277c478bd9Sstevel@tonic-gate 	case OHCI_REMOVE_XFER_IFLAST:
67287c478bd9Sstevel@tonic-gate 		if (tw->tw_hctd_head != tw->tw_hctd_tail) {
67297c478bd9Sstevel@tonic-gate 			break;
67307c478bd9Sstevel@tonic-gate 		}
67317c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
67327c478bd9Sstevel@tonic-gate 	case OHCI_REMOVE_XFER_ALWAYS:
67337c478bd9Sstevel@tonic-gate 		ohci_remove_tw_from_timeout_list(ohcip, tw);
67347c478bd9Sstevel@tonic-gate 
67357c478bd9Sstevel@tonic-gate 		if ((ohcip->ohci_timeout_list == NULL) &&
67367c478bd9Sstevel@tonic-gate 		    (ohcip->ohci_timer_id)) {
67377c478bd9Sstevel@tonic-gate 
67387c478bd9Sstevel@tonic-gate 			timer_id = ohcip->ohci_timer_id;
67397c478bd9Sstevel@tonic-gate 
67407c478bd9Sstevel@tonic-gate 			/* Reset the timer id to zero */
67417c478bd9Sstevel@tonic-gate 			ohcip->ohci_timer_id = 0;
67427c478bd9Sstevel@tonic-gate 
67437c478bd9Sstevel@tonic-gate 			mutex_exit(&ohcip->ohci_int_mutex);
67447c478bd9Sstevel@tonic-gate 
67457c478bd9Sstevel@tonic-gate 			(void) untimeout(timer_id);
67467c478bd9Sstevel@tonic-gate 
67477c478bd9Sstevel@tonic-gate 			mutex_enter(&ohcip->ohci_int_mutex);
67487c478bd9Sstevel@tonic-gate 		}
67497c478bd9Sstevel@tonic-gate 		break;
67507c478bd9Sstevel@tonic-gate 	default:
67517c478bd9Sstevel@tonic-gate 		break;
67527c478bd9Sstevel@tonic-gate 	}
67537c478bd9Sstevel@tonic-gate }
67547c478bd9Sstevel@tonic-gate 
67557c478bd9Sstevel@tonic-gate 
67567c478bd9Sstevel@tonic-gate /*
67577c478bd9Sstevel@tonic-gate  * ohci_xfer_timeout_handler:
67587c478bd9Sstevel@tonic-gate  *
67597c478bd9Sstevel@tonic-gate  * Control or bulk transfer timeout handler.
67607c478bd9Sstevel@tonic-gate  */
67617c478bd9Sstevel@tonic-gate static void
67627c478bd9Sstevel@tonic-gate ohci_xfer_timeout_handler(void *arg)
67637c478bd9Sstevel@tonic-gate {
67647c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = (ohci_state_t *)arg;
67657c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*exp_xfer_list_head = NULL;
67667c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*exp_xfer_list_tail = NULL;
67677c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw, *next;
67687c478bd9Sstevel@tonic-gate 	ohci_td_t		*td;
67697c478bd9Sstevel@tonic-gate 	usb_flags_t		flags;
67707c478bd9Sstevel@tonic-gate 
67717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS,  ohcip->ohci_log_hdl,
67727c478bd9Sstevel@tonic-gate 	    "ohci_xfer_timeout_handler: ohcip = 0x%p", ohcip);
67737c478bd9Sstevel@tonic-gate 
67747c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
67757c478bd9Sstevel@tonic-gate 
67767c478bd9Sstevel@tonic-gate 	/* Set the required flags */
67777c478bd9Sstevel@tonic-gate 	flags = OHCI_FLAGS_NOSLEEP | OHCI_FLAGS_DMA_SYNC;
67787c478bd9Sstevel@tonic-gate 
67797c478bd9Sstevel@tonic-gate 	/*
67807c478bd9Sstevel@tonic-gate 	 * Check whether still timeout handler is valid.
67817c478bd9Sstevel@tonic-gate 	 */
67827c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_timer_id) {
67837c478bd9Sstevel@tonic-gate 
67847c478bd9Sstevel@tonic-gate 		/* Reset the timer id to zero */
67857c478bd9Sstevel@tonic-gate 		ohcip->ohci_timer_id = 0;
67867c478bd9Sstevel@tonic-gate 	} else {
67877c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
67887c478bd9Sstevel@tonic-gate 
67897c478bd9Sstevel@tonic-gate 		return;
67907c478bd9Sstevel@tonic-gate 	}
67917c478bd9Sstevel@tonic-gate 
67927c478bd9Sstevel@tonic-gate 	/* Get the transfer timeout list head */
67937c478bd9Sstevel@tonic-gate 	tw = ohcip->ohci_timeout_list;
67947c478bd9Sstevel@tonic-gate 
67957c478bd9Sstevel@tonic-gate 	/*
67967c478bd9Sstevel@tonic-gate 	 * Process ohci timeout list and look whether the timer
67977c478bd9Sstevel@tonic-gate 	 * has expired for any transfers. Create a temporary list
67987c478bd9Sstevel@tonic-gate 	 * of expired transfers and process them later.
67997c478bd9Sstevel@tonic-gate 	 */
68007c478bd9Sstevel@tonic-gate 	while (tw) {
68017c478bd9Sstevel@tonic-gate 		/* Get the transfer on the timeout list */
68027c478bd9Sstevel@tonic-gate 		next = tw->tw_timeout_next;
68037c478bd9Sstevel@tonic-gate 
68047c478bd9Sstevel@tonic-gate 		tw->tw_timeout--;
68057c478bd9Sstevel@tonic-gate 
68067c478bd9Sstevel@tonic-gate 		/*
68077c478bd9Sstevel@tonic-gate 		 * Set the sKip bit to stop all transactions on
68087c478bd9Sstevel@tonic-gate 		 * this pipe
68097c478bd9Sstevel@tonic-gate 		 */
68107c478bd9Sstevel@tonic-gate 		if (tw->tw_timeout == 1) {
68117c478bd9Sstevel@tonic-gate 			ohci_modify_sKip_bit(ohcip,
68127c478bd9Sstevel@tonic-gate 			    tw->tw_pipe_private, SET_sKip, flags);
68137c478bd9Sstevel@tonic-gate 
68147c478bd9Sstevel@tonic-gate 			/* Reset dma sync flag */
68157c478bd9Sstevel@tonic-gate 			flags &= ~OHCI_FLAGS_DMA_SYNC;
68167c478bd9Sstevel@tonic-gate 		}
68177c478bd9Sstevel@tonic-gate 
68187c478bd9Sstevel@tonic-gate 		/* Remove tw from the timeout list */
68197c478bd9Sstevel@tonic-gate 		if (tw->tw_timeout <= 0) {
68207c478bd9Sstevel@tonic-gate 
68217c478bd9Sstevel@tonic-gate 			ohci_remove_tw_from_timeout_list(ohcip, tw);
68227c478bd9Sstevel@tonic-gate 
68237c478bd9Sstevel@tonic-gate 			/* Add tw to the end of expire list */
68247c478bd9Sstevel@tonic-gate 			if (exp_xfer_list_head) {
68257c478bd9Sstevel@tonic-gate 				exp_xfer_list_tail->tw_timeout_next = tw;
68267c478bd9Sstevel@tonic-gate 			} else {
68277c478bd9Sstevel@tonic-gate 				exp_xfer_list_head = tw;
68287c478bd9Sstevel@tonic-gate 			}
68297c478bd9Sstevel@tonic-gate 			exp_xfer_list_tail = tw;
68307c478bd9Sstevel@tonic-gate 			tw->tw_timeout_next = NULL;
68317c478bd9Sstevel@tonic-gate 		}
68327c478bd9Sstevel@tonic-gate 
68337c478bd9Sstevel@tonic-gate 		tw = next;
68347c478bd9Sstevel@tonic-gate 	}
68357c478bd9Sstevel@tonic-gate 
68367c478bd9Sstevel@tonic-gate 	/* Get the expired transfer timeout list head */
68377c478bd9Sstevel@tonic-gate 	tw = exp_xfer_list_head;
68387c478bd9Sstevel@tonic-gate 
68397c478bd9Sstevel@tonic-gate 	if (tw && (flags & OHCI_FLAGS_DMA_SYNC)) {
68407c478bd9Sstevel@tonic-gate 		/* Sync ED and TD pool */
68417c478bd9Sstevel@tonic-gate 		Sync_ED_TD_Pool(ohcip);
68427c478bd9Sstevel@tonic-gate 	}
68437c478bd9Sstevel@tonic-gate 
68447c478bd9Sstevel@tonic-gate 	/*
68457c478bd9Sstevel@tonic-gate 	 * Process the expired transfers by notifing the corrsponding
68467c478bd9Sstevel@tonic-gate 	 * client driver through the exception callback.
68477c478bd9Sstevel@tonic-gate 	 */
68487c478bd9Sstevel@tonic-gate 	while (tw) {
68497c478bd9Sstevel@tonic-gate 		/* Get the transfer on the expired transfer timeout list */
68507c478bd9Sstevel@tonic-gate 		next = tw->tw_timeout_next;
68517c478bd9Sstevel@tonic-gate 
68527c478bd9Sstevel@tonic-gate 		td = tw->tw_hctd_head;
68537c478bd9Sstevel@tonic-gate 
68547c478bd9Sstevel@tonic-gate 		while (td) {
68557c478bd9Sstevel@tonic-gate 			/* Set TD state to TIMEOUT */
68567c478bd9Sstevel@tonic-gate 			Set_TD(td->hctd_state, HC_TD_TIMEOUT);
68577c478bd9Sstevel@tonic-gate 
68587c478bd9Sstevel@tonic-gate 			/* Get the next TD from the wrapper */
68597c478bd9Sstevel@tonic-gate 			td = ohci_td_iommu_to_cpu(ohcip,
68607c478bd9Sstevel@tonic-gate 			    Get_TD(td->hctd_tw_next_td));
68617c478bd9Sstevel@tonic-gate 		}
68627c478bd9Sstevel@tonic-gate 
68637c478bd9Sstevel@tonic-gate 		ohci_handle_error(ohcip, tw->tw_hctd_head, USB_CR_TIMEOUT);
68647c478bd9Sstevel@tonic-gate 
68657c478bd9Sstevel@tonic-gate 		tw = next;
68667c478bd9Sstevel@tonic-gate 	}
68677c478bd9Sstevel@tonic-gate 
68687c478bd9Sstevel@tonic-gate 	ohci_start_timer(ohcip);
68697c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
68707c478bd9Sstevel@tonic-gate }
68717c478bd9Sstevel@tonic-gate 
68727c478bd9Sstevel@tonic-gate 
68737c478bd9Sstevel@tonic-gate /*
68747c478bd9Sstevel@tonic-gate  * ohci_remove_tw_from_timeout_list:
68757c478bd9Sstevel@tonic-gate  *
68767c478bd9Sstevel@tonic-gate  * Remove Control or bulk transfer from the timeout list.
68777c478bd9Sstevel@tonic-gate  */
68787c478bd9Sstevel@tonic-gate static void
68797c478bd9Sstevel@tonic-gate ohci_remove_tw_from_timeout_list(
68807c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
68817c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
68827c478bd9Sstevel@tonic-gate {
68837c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*prev, *next;
68847c478bd9Sstevel@tonic-gate 
68857c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS,  ohcip->ohci_log_hdl,
68867c478bd9Sstevel@tonic-gate 	    "ohci_remove_tw_from_timeout_list: tw = 0x%p", tw);
68877c478bd9Sstevel@tonic-gate 
68887c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
68897c478bd9Sstevel@tonic-gate 
68907c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_timeout_list == tw) {
68917c478bd9Sstevel@tonic-gate 		ohcip->ohci_timeout_list = tw->tw_timeout_next;
68927c478bd9Sstevel@tonic-gate 	} else {
68937c478bd9Sstevel@tonic-gate 		prev = ohcip->ohci_timeout_list;
68947c478bd9Sstevel@tonic-gate 		next = prev->tw_timeout_next;
68957c478bd9Sstevel@tonic-gate 
68967c478bd9Sstevel@tonic-gate 		while (next && (next != tw)) {
68977c478bd9Sstevel@tonic-gate 			prev = next;
68987c478bd9Sstevel@tonic-gate 			next = next->tw_timeout_next;
68997c478bd9Sstevel@tonic-gate 		}
69007c478bd9Sstevel@tonic-gate 
69017c478bd9Sstevel@tonic-gate 		if (next == tw) {
69027c478bd9Sstevel@tonic-gate 			prev->tw_timeout_next = next->tw_timeout_next;
69037c478bd9Sstevel@tonic-gate 		}
69047c478bd9Sstevel@tonic-gate 	}
69057c478bd9Sstevel@tonic-gate 
69067c478bd9Sstevel@tonic-gate 	/* Reset the xfer timeout */
69077c478bd9Sstevel@tonic-gate 	tw->tw_timeout_next = NULL;
69087c478bd9Sstevel@tonic-gate }
69097c478bd9Sstevel@tonic-gate 
69107c478bd9Sstevel@tonic-gate 
69117c478bd9Sstevel@tonic-gate /*
69127c478bd9Sstevel@tonic-gate  * ohci_start_timer:
69137c478bd9Sstevel@tonic-gate  *
69147c478bd9Sstevel@tonic-gate  * Start the ohci timer
69157c478bd9Sstevel@tonic-gate  */
69167c478bd9Sstevel@tonic-gate static void
69177c478bd9Sstevel@tonic-gate ohci_start_timer(ohci_state_t	*ohcip)
69187c478bd9Sstevel@tonic-gate {
69197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS,  ohcip->ohci_log_hdl,
69207c478bd9Sstevel@tonic-gate 	    "ohci_start_timer: ohcip = 0x%p", ohcip);
69217c478bd9Sstevel@tonic-gate 
69227c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
69237c478bd9Sstevel@tonic-gate 
69247c478bd9Sstevel@tonic-gate 	/*
69257c478bd9Sstevel@tonic-gate 	 * Start the global timer only if currently timer is not
69267c478bd9Sstevel@tonic-gate 	 * running and if there are any transfers on the timeout
69277c478bd9Sstevel@tonic-gate 	 * list. This timer will be per USB Host Controller.
69287c478bd9Sstevel@tonic-gate 	 */
69297c478bd9Sstevel@tonic-gate 	if ((!ohcip->ohci_timer_id) && (ohcip->ohci_timeout_list)) {
69307c478bd9Sstevel@tonic-gate 		ohcip->ohci_timer_id = timeout(ohci_xfer_timeout_handler,
69317c478bd9Sstevel@tonic-gate 		    (void *)ohcip, drv_usectohz(1000000));
69327c478bd9Sstevel@tonic-gate 	}
69337c478bd9Sstevel@tonic-gate }
69347c478bd9Sstevel@tonic-gate 
69357c478bd9Sstevel@tonic-gate 
69367c478bd9Sstevel@tonic-gate /*
69377c478bd9Sstevel@tonic-gate  * ohci_deallocate_tw_resources:
69387c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
69397c478bd9Sstevel@tonic-gate  *
69407c478bd9Sstevel@tonic-gate  * Deallocate of a Transaction Wrapper (TW) and this involves the freeing of
69417c478bd9Sstevel@tonic-gate  * of DMA resources.
69427c478bd9Sstevel@tonic-gate  */
69437c478bd9Sstevel@tonic-gate void
69447c478bd9Sstevel@tonic-gate ohci_deallocate_tw_resources(
69457c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
69467c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
69477c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
69487c478bd9Sstevel@tonic-gate {
69497c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*prev, *next;
69507c478bd9Sstevel@tonic-gate 
69517c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
69527c478bd9Sstevel@tonic-gate 	    "ohci_deallocate_tw_resources: tw = 0x%p", tw);
69537c478bd9Sstevel@tonic-gate 
69547c478bd9Sstevel@tonic-gate 	/*
69557c478bd9Sstevel@tonic-gate 	 * If the transfer wrapper has no Host Controller (HC)
69567c478bd9Sstevel@tonic-gate 	 * Transfer Descriptors (TD) associated with it,  then
69577c478bd9Sstevel@tonic-gate 	 * remove the transfer wrapper.
69587c478bd9Sstevel@tonic-gate 	 */
69597c478bd9Sstevel@tonic-gate 	if (tw->tw_hctd_head) {
69607c478bd9Sstevel@tonic-gate 		ASSERT(tw->tw_hctd_tail != NULL);
69617c478bd9Sstevel@tonic-gate 
69627c478bd9Sstevel@tonic-gate 		return;
69637c478bd9Sstevel@tonic-gate 	}
69647c478bd9Sstevel@tonic-gate 
69657c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_hctd_tail == NULL);
69667c478bd9Sstevel@tonic-gate 
69677c478bd9Sstevel@tonic-gate 	/* Make sure we return all the unused td's to the pool as well */
69687c478bd9Sstevel@tonic-gate 	ohci_free_tw_tds_resources(ohcip, tw);
69697c478bd9Sstevel@tonic-gate 
69707c478bd9Sstevel@tonic-gate 	/*
69717c478bd9Sstevel@tonic-gate 	 * If pp->pp_tw_head and pp->pp_tw_tail are pointing to
69727c478bd9Sstevel@tonic-gate 	 * given TW then set the head and  tail  equal to NULL.
69737c478bd9Sstevel@tonic-gate 	 * Otherwise search for this TW in the linked TW's list
69747c478bd9Sstevel@tonic-gate 	 * and then remove this TW from the list.
69757c478bd9Sstevel@tonic-gate 	 */
69767c478bd9Sstevel@tonic-gate 	if (pp->pp_tw_head == tw) {
69777c478bd9Sstevel@tonic-gate 		if (pp->pp_tw_tail == tw) {
69787c478bd9Sstevel@tonic-gate 			pp->pp_tw_head = NULL;
69797c478bd9Sstevel@tonic-gate 			pp->pp_tw_tail = NULL;
69807c478bd9Sstevel@tonic-gate 		} else {
69817c478bd9Sstevel@tonic-gate 			pp->pp_tw_head = tw->tw_next;
69827c478bd9Sstevel@tonic-gate 		}
69837c478bd9Sstevel@tonic-gate 	} else {
69847c478bd9Sstevel@tonic-gate 		prev = pp->pp_tw_head;
69857c478bd9Sstevel@tonic-gate 		next = prev->tw_next;
69867c478bd9Sstevel@tonic-gate 
69877c478bd9Sstevel@tonic-gate 		while (next && (next != tw)) {
69887c478bd9Sstevel@tonic-gate 			prev = next;
69897c478bd9Sstevel@tonic-gate 			next = next->tw_next;
69907c478bd9Sstevel@tonic-gate 		}
69917c478bd9Sstevel@tonic-gate 
69927c478bd9Sstevel@tonic-gate 		if (next == tw) {
69937c478bd9Sstevel@tonic-gate 			prev->tw_next = next->tw_next;
69947c478bd9Sstevel@tonic-gate 
69957c478bd9Sstevel@tonic-gate 			if (pp->pp_tw_tail == tw) {
69967c478bd9Sstevel@tonic-gate 				pp->pp_tw_tail = prev;
69977c478bd9Sstevel@tonic-gate 			}
69987c478bd9Sstevel@tonic-gate 		}
69997c478bd9Sstevel@tonic-gate 	}
70007c478bd9Sstevel@tonic-gate 
70017c478bd9Sstevel@tonic-gate 	ohci_free_tw(ohcip, tw);
70027c478bd9Sstevel@tonic-gate }
70037c478bd9Sstevel@tonic-gate 
70047c478bd9Sstevel@tonic-gate 
70057c478bd9Sstevel@tonic-gate /*
70067c478bd9Sstevel@tonic-gate  * ohci_free_dma_resources:
70077c478bd9Sstevel@tonic-gate  *
70087c478bd9Sstevel@tonic-gate  * Free dma resources of a Transfer Wrapper (TW) and also free the TW.
70097c478bd9Sstevel@tonic-gate  */
70107c478bd9Sstevel@tonic-gate static void
70117c478bd9Sstevel@tonic-gate ohci_free_dma_resources(
70127c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
70137c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
70147c478bd9Sstevel@tonic-gate {
70157c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
70167c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*head_tw = pp->pp_tw_head;
70177c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*next_tw, *tw;
70187c478bd9Sstevel@tonic-gate 
70197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
70207c478bd9Sstevel@tonic-gate 	    "ohci_free_dma_resources: ph = 0x%p", (void *)ph);
70217c478bd9Sstevel@tonic-gate 
70227c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
70237c478bd9Sstevel@tonic-gate 
70247c478bd9Sstevel@tonic-gate 	/* Process the Transfer Wrappers */
70257c478bd9Sstevel@tonic-gate 	next_tw = head_tw;
70267c478bd9Sstevel@tonic-gate 	while (next_tw) {
70277c478bd9Sstevel@tonic-gate 		tw = next_tw;
70287c478bd9Sstevel@tonic-gate 		next_tw = tw->tw_next;
70297c478bd9Sstevel@tonic-gate 
70307c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
70317c478bd9Sstevel@tonic-gate 		    "ohci_free_dma_resources: Free TW = 0x%p", (void *)tw);
70327c478bd9Sstevel@tonic-gate 
70337c478bd9Sstevel@tonic-gate 		ohci_free_tw(ohcip, tw);
70347c478bd9Sstevel@tonic-gate 	}
70357c478bd9Sstevel@tonic-gate 
70367c478bd9Sstevel@tonic-gate 	/* Adjust the head and tail pointers */
70377c478bd9Sstevel@tonic-gate 	pp->pp_tw_head = NULL;
70387c478bd9Sstevel@tonic-gate 	pp->pp_tw_tail = NULL;
70397c478bd9Sstevel@tonic-gate }
70407c478bd9Sstevel@tonic-gate 
70417c478bd9Sstevel@tonic-gate 
70427c478bd9Sstevel@tonic-gate /*
70437c478bd9Sstevel@tonic-gate  * ohci_free_tw:
70447c478bd9Sstevel@tonic-gate  *
70457c478bd9Sstevel@tonic-gate  * Free the Transfer Wrapper (TW).
70467c478bd9Sstevel@tonic-gate  */
70477c478bd9Sstevel@tonic-gate static void
70487c478bd9Sstevel@tonic-gate ohci_free_tw(
70497c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
70507c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
70517c478bd9Sstevel@tonic-gate {
70527c478bd9Sstevel@tonic-gate 	int			rval;
70537c478bd9Sstevel@tonic-gate 
70547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
70557c478bd9Sstevel@tonic-gate 	    "ohci_free_tw: tw = 0x%p", tw);
70567c478bd9Sstevel@tonic-gate 
70577c478bd9Sstevel@tonic-gate 	ASSERT(tw != NULL);
70587c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_id != NULL);
70597c478bd9Sstevel@tonic-gate 
70607c478bd9Sstevel@tonic-gate 	/* Free 32bit ID */
70617c478bd9Sstevel@tonic-gate 	OHCI_FREE_ID((uint32_t)tw->tw_id);
70627c478bd9Sstevel@tonic-gate 
70637c478bd9Sstevel@tonic-gate 	rval = ddi_dma_unbind_handle(tw->tw_dmahandle);
70647c478bd9Sstevel@tonic-gate 	ASSERT(rval == DDI_SUCCESS);
70657c478bd9Sstevel@tonic-gate 
70667c478bd9Sstevel@tonic-gate 	ddi_dma_mem_free(&tw->tw_accesshandle);
70677c478bd9Sstevel@tonic-gate 	ddi_dma_free_handle(&tw->tw_dmahandle);
70687c478bd9Sstevel@tonic-gate 
70697c478bd9Sstevel@tonic-gate 	/* Free transfer wrapper */
70707c478bd9Sstevel@tonic-gate 	kmem_free(tw, sizeof (ohci_trans_wrapper_t));
70717c478bd9Sstevel@tonic-gate }
70727c478bd9Sstevel@tonic-gate 
70737c478bd9Sstevel@tonic-gate 
70747c478bd9Sstevel@tonic-gate /*
70757c478bd9Sstevel@tonic-gate  * Interrupt Handling functions
70767c478bd9Sstevel@tonic-gate  */
70777c478bd9Sstevel@tonic-gate 
70787c478bd9Sstevel@tonic-gate /*
70797c478bd9Sstevel@tonic-gate  * ohci_intr:
70807c478bd9Sstevel@tonic-gate  *
70817c478bd9Sstevel@tonic-gate  * OpenHCI (OHCI) interrupt handling routine.
70827c478bd9Sstevel@tonic-gate  */
70837c478bd9Sstevel@tonic-gate static uint_t
7084*9c75c6bfSgovinda ohci_intr(caddr_t arg1, caddr_t arg2)
70857c478bd9Sstevel@tonic-gate {
7086*9c75c6bfSgovinda 	ohci_state_t		*ohcip = (ohci_state_t *)arg1;
70877c478bd9Sstevel@tonic-gate 	uint_t			intr;
70887c478bd9Sstevel@tonic-gate 	ohci_td_t		*done_head = NULL;
70897c478bd9Sstevel@tonic-gate 	ohci_save_intr_sts_t	*ohci_intr_sts = &ohcip->ohci_save_intr_sts;
70907c478bd9Sstevel@tonic-gate 
70917c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
7092*9c75c6bfSgovinda 	    "ohci_intr: Interrupt occurred, arg1 0x%p arg2 0x%p", arg1, arg2);
70937c478bd9Sstevel@tonic-gate 
70947c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
70957c478bd9Sstevel@tonic-gate 
70967c478bd9Sstevel@tonic-gate 	/*
70977c478bd9Sstevel@tonic-gate 	 * Suppose if we switched to the polled mode from the normal
70987c478bd9Sstevel@tonic-gate 	 * mode when interrupt handler is executing then we  need to
70997c478bd9Sstevel@tonic-gate 	 * save the interrupt status information in the  polled mode
71007c478bd9Sstevel@tonic-gate 	 * to  avoid race conditions. The following flag will be set
71017c478bd9Sstevel@tonic-gate 	 * and reset on entering & exiting of ohci interrupt handler
71027c478bd9Sstevel@tonic-gate 	 * respectively.  This flag will be used in the  polled mode
71037c478bd9Sstevel@tonic-gate 	 * to check whether the interrupt handler was running when we
71047c478bd9Sstevel@tonic-gate 	 * switched to the polled mode from the normal mode.
71057c478bd9Sstevel@tonic-gate 	 */
71067c478bd9Sstevel@tonic-gate 	ohci_intr_sts->ohci_intr_flag = OHCI_INTR_HANDLING;
71077c478bd9Sstevel@tonic-gate 
71087c478bd9Sstevel@tonic-gate 	/* Temporarily turn off interrupts */
71097c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_disable, HCR_INTR_MIE);
71107c478bd9Sstevel@tonic-gate 
71117c478bd9Sstevel@tonic-gate 	/*
71127c478bd9Sstevel@tonic-gate 	 * Handle any missed ohci interrupt especially WriteDoneHead
71137c478bd9Sstevel@tonic-gate 	 * and SOF interrupts because of previous polled mode switch.
71147c478bd9Sstevel@tonic-gate 	 */
71157c478bd9Sstevel@tonic-gate 	ohci_handle_missed_intr(ohcip);
71167c478bd9Sstevel@tonic-gate 
71177c478bd9Sstevel@tonic-gate 	/*
71187c478bd9Sstevel@tonic-gate 	 * Now process the actual ohci interrupt events  that caused
71197c478bd9Sstevel@tonic-gate 	 * invocation of this ohci interrupt handler.
71207c478bd9Sstevel@tonic-gate 	 */
71217c478bd9Sstevel@tonic-gate 
71227c478bd9Sstevel@tonic-gate 	/*
71237c478bd9Sstevel@tonic-gate 	 * Updating the WriteDoneHead interrupt:
71247c478bd9Sstevel@tonic-gate 	 *
71257c478bd9Sstevel@tonic-gate 	 * (a) Host Controller
71267c478bd9Sstevel@tonic-gate 	 *
71277c478bd9Sstevel@tonic-gate 	 *	- First Host controller (HC) checks  whether WDH bit
71287c478bd9Sstevel@tonic-gate 	 *	  in the interrupt status register is cleared.
71297c478bd9Sstevel@tonic-gate 	 *
71307c478bd9Sstevel@tonic-gate 	 *	- If WDH bit is cleared then HC writes new done head
71317c478bd9Sstevel@tonic-gate 	 *	  list information into the HCCA done head field.
71327c478bd9Sstevel@tonic-gate 	 *
71337c478bd9Sstevel@tonic-gate 	 *	- Set WDH bit in the interrupt status register.
71347c478bd9Sstevel@tonic-gate 	 *
71357c478bd9Sstevel@tonic-gate 	 * (b) Host Controller Driver (HCD)
71367c478bd9Sstevel@tonic-gate 	 *
71377c478bd9Sstevel@tonic-gate 	 *	- First read the interrupt status register. The HCCA
71387c478bd9Sstevel@tonic-gate 	 *	  done head and WDH bit may be set or may not be set
71397c478bd9Sstevel@tonic-gate 	 *	  while reading the interrupt status register.
71407c478bd9Sstevel@tonic-gate 	 *
71417c478bd9Sstevel@tonic-gate 	 *	- Read the  HCCA done head list. By this time may be
71427c478bd9Sstevel@tonic-gate 	 *	  HC has updated HCCA done head and  WDH bit in ohci
71437c478bd9Sstevel@tonic-gate 	 *	  interrupt status register.
71447c478bd9Sstevel@tonic-gate 	 *
71457c478bd9Sstevel@tonic-gate 	 *	- If done head is non-null and if WDH bit is not set
71467c478bd9Sstevel@tonic-gate 	 *	  then Host Controller has updated HCCA  done head &
71477c478bd9Sstevel@tonic-gate 	 *	  WDH bit in the interrupt stats register in between
71487c478bd9Sstevel@tonic-gate 	 *	  reading the interrupt status register & HCCA	done
71497c478bd9Sstevel@tonic-gate 	 *	  head. In that case, definitely WDH bit will be set
71507c478bd9Sstevel@tonic-gate 	 *	  in the interrupt status register & driver can take
71517c478bd9Sstevel@tonic-gate 	 *	  it for granted.
71527c478bd9Sstevel@tonic-gate 	 *
71537c478bd9Sstevel@tonic-gate 	 * Now read the Interrupt Status & Interrupt enable register
71547c478bd9Sstevel@tonic-gate 	 * to determine the exact interrupt events.
71557c478bd9Sstevel@tonic-gate 	 */
71567c478bd9Sstevel@tonic-gate 	intr = ohci_intr_sts->ohci_curr_intr_sts =
71577c478bd9Sstevel@tonic-gate 	    (Get_OpReg(hcr_intr_status) & Get_OpReg(hcr_intr_enable));
71587c478bd9Sstevel@tonic-gate 
71597c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_hccap) {
71607c478bd9Sstevel@tonic-gate 		/* Sync HCCA area */
71617c478bd9Sstevel@tonic-gate 		Sync_HCCA(ohcip);
71627c478bd9Sstevel@tonic-gate 
71637c478bd9Sstevel@tonic-gate 		/* Read and Save the HCCA DoneHead value */
71647c478bd9Sstevel@tonic-gate 		done_head = ohci_intr_sts->ohci_curr_done_lst =
71657c478bd9Sstevel@tonic-gate 		    (ohci_td_t *)(uintptr_t)
71667c478bd9Sstevel@tonic-gate 		    (Get_HCCA(ohcip->ohci_hccap->HccaDoneHead) &
71677c478bd9Sstevel@tonic-gate 		    HCCA_DONE_HEAD_MASK);
71687c478bd9Sstevel@tonic-gate 
71697c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
71707c478bd9Sstevel@tonic-gate 		    "ohci_intr: Done head! 0x%p", (void *)done_head);
71717c478bd9Sstevel@tonic-gate 	}
71727c478bd9Sstevel@tonic-gate 
71737c478bd9Sstevel@tonic-gate 	/* Update kstat values */
71747c478bd9Sstevel@tonic-gate 	ohci_do_intrs_stats(ohcip, intr);
71757c478bd9Sstevel@tonic-gate 
71767c478bd9Sstevel@tonic-gate 	/*
71777c478bd9Sstevel@tonic-gate 	 * Look at the HccaDoneHead & if it is non-zero, then a done
71787c478bd9Sstevel@tonic-gate 	 * list update interrupt is indicated.
71797c478bd9Sstevel@tonic-gate 	 */
71807c478bd9Sstevel@tonic-gate 	if (done_head) {
71817c478bd9Sstevel@tonic-gate 
71827c478bd9Sstevel@tonic-gate 		/*
71837c478bd9Sstevel@tonic-gate 		 * Check for the  WriteDoneHead interrupt bit in the
71847c478bd9Sstevel@tonic-gate 		 * interrupt condition and set the WriteDoneHead bit
71857c478bd9Sstevel@tonic-gate 		 * in the interrupt events if it is not set.
71867c478bd9Sstevel@tonic-gate 		 */
71877c478bd9Sstevel@tonic-gate 		if (!(intr & HCR_INTR_WDH)) {
71887c478bd9Sstevel@tonic-gate 			intr |= HCR_INTR_WDH;
71897c478bd9Sstevel@tonic-gate 		}
71907c478bd9Sstevel@tonic-gate 	}
71917c478bd9Sstevel@tonic-gate 
71927c478bd9Sstevel@tonic-gate 	/*
71937c478bd9Sstevel@tonic-gate 	 * We could have gotten a spurious interrupts. If so, do not
71947c478bd9Sstevel@tonic-gate 	 * claim it.  This is quite  possible on some  architectures
71957c478bd9Sstevel@tonic-gate 	 * where more than one PCI slots share the IRQs.  If so, the
71967c478bd9Sstevel@tonic-gate 	 * associated driver's interrupt routine may get called even
71977c478bd9Sstevel@tonic-gate 	 * if the interrupt is not meant for them.
71987c478bd9Sstevel@tonic-gate 	 *
71997c478bd9Sstevel@tonic-gate 	 * By unclaiming the interrupt, the other driver gets chance
72007c478bd9Sstevel@tonic-gate 	 * to service its interrupt.
72017c478bd9Sstevel@tonic-gate 	 */
72027c478bd9Sstevel@tonic-gate 	if (!intr) {
72037c478bd9Sstevel@tonic-gate 
72047c478bd9Sstevel@tonic-gate 		/* Reset the interrupt handler flag */
72057c478bd9Sstevel@tonic-gate 		ohci_intr_sts->ohci_intr_flag &= ~OHCI_INTR_HANDLING;
72067c478bd9Sstevel@tonic-gate 
72077c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_intr_enable, HCR_INTR_MIE);
72087c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
72097c478bd9Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
72107c478bd9Sstevel@tonic-gate 	}
72117c478bd9Sstevel@tonic-gate 
72127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
72137c478bd9Sstevel@tonic-gate 	    "Interrupt status 0x%x", intr);
72147c478bd9Sstevel@tonic-gate 
72157c478bd9Sstevel@tonic-gate 	/*
72167c478bd9Sstevel@tonic-gate 	 * Check for Frame Number Overflow.
72177c478bd9Sstevel@tonic-gate 	 */
72187c478bd9Sstevel@tonic-gate 	if (intr & HCR_INTR_FNO) {
72197c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
72207c478bd9Sstevel@tonic-gate 		    "ohci_intr: Frame Number Overflow");
72217c478bd9Sstevel@tonic-gate 
72227c478bd9Sstevel@tonic-gate 		ohci_handle_frame_number_overflow(ohcip);
72237c478bd9Sstevel@tonic-gate 	}
72247c478bd9Sstevel@tonic-gate 
72257c478bd9Sstevel@tonic-gate 	if (intr & HCR_INTR_SOF) {
72267c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
72277c478bd9Sstevel@tonic-gate 		    "ohci_intr: Start of Frame");
72287c478bd9Sstevel@tonic-gate 
72297c478bd9Sstevel@tonic-gate 		/* Set ohci_sof_flag indicating SOF interrupt occurred */
72307c478bd9Sstevel@tonic-gate 		ohcip->ohci_sof_flag = B_TRUE;
72317c478bd9Sstevel@tonic-gate 
72327c478bd9Sstevel@tonic-gate 		/* Disabel SOF interrupt */
72337c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_intr_disable, HCR_INTR_SOF);
72347c478bd9Sstevel@tonic-gate 
72357c478bd9Sstevel@tonic-gate 		/*
72367c478bd9Sstevel@tonic-gate 		 * Call cv_broadcast on every SOF interrupt to wakeup
72377c478bd9Sstevel@tonic-gate 		 * all the threads that are waiting the SOF.  Calling
72387c478bd9Sstevel@tonic-gate 		 * cv_broadcast on every SOF has no effect even if no
72397c478bd9Sstevel@tonic-gate 		 * threads are waiting for the SOF.
72407c478bd9Sstevel@tonic-gate 		 */
72417c478bd9Sstevel@tonic-gate 		cv_broadcast(&ohcip->ohci_SOF_cv);
72427c478bd9Sstevel@tonic-gate 	}
72437c478bd9Sstevel@tonic-gate 
72447c478bd9Sstevel@tonic-gate 	if (intr & HCR_INTR_SO) {
72457c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
72467c478bd9Sstevel@tonic-gate 		    "ohci_intr: Schedule overrun");
72477c478bd9Sstevel@tonic-gate 
72487c478bd9Sstevel@tonic-gate 		ohcip->ohci_so_error++;
72497c478bd9Sstevel@tonic-gate 	}
72507c478bd9Sstevel@tonic-gate 
72517c478bd9Sstevel@tonic-gate 	if ((intr & HCR_INTR_WDH) && (done_head)) {
72527c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
72537c478bd9Sstevel@tonic-gate 		    "ohci_intr: Done Head");
72547c478bd9Sstevel@tonic-gate 
72557c478bd9Sstevel@tonic-gate 		/*
72567c478bd9Sstevel@tonic-gate 		 * Currently if we are processing one  WriteDoneHead
72577c478bd9Sstevel@tonic-gate 		 * interrupt  and also if we  switched to the polled
72587c478bd9Sstevel@tonic-gate 		 * mode at least once  during this time,  then there
72597c478bd9Sstevel@tonic-gate 		 * may be chance that  Host Controller generates one
72607c478bd9Sstevel@tonic-gate 		 * more Write DoneHead or Start of Frame  interrupts
72617c478bd9Sstevel@tonic-gate 		 * for the normal since the polled code clears WDH &
72627c478bd9Sstevel@tonic-gate 		 * SOF interrupt bits before returning to the normal
72637c478bd9Sstevel@tonic-gate 		 * mode. Under this condition, we must not clear the
72647c478bd9Sstevel@tonic-gate 		 * HCCA done head field & also we must not clear WDH
72657c478bd9Sstevel@tonic-gate 		 * interrupt bit in the interrupt  status register.
72667c478bd9Sstevel@tonic-gate 		 */
72677c478bd9Sstevel@tonic-gate 		if (done_head == (ohci_td_t *)(uintptr_t)
72687c478bd9Sstevel@tonic-gate 		    (Get_HCCA(ohcip->ohci_hccap->HccaDoneHead) &
72697c478bd9Sstevel@tonic-gate 		    HCCA_DONE_HEAD_MASK)) {
72707c478bd9Sstevel@tonic-gate 
72717c478bd9Sstevel@tonic-gate 			/* Reset the done head to NULL */
72727c478bd9Sstevel@tonic-gate 			Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, NULL);
72737c478bd9Sstevel@tonic-gate 		} else {
72747c478bd9Sstevel@tonic-gate 			intr &= ~HCR_INTR_WDH;
72757c478bd9Sstevel@tonic-gate 		}
72767c478bd9Sstevel@tonic-gate 
72777c478bd9Sstevel@tonic-gate 		/* Clear the current done head field */
72787c478bd9Sstevel@tonic-gate 		ohci_intr_sts->ohci_curr_done_lst = NULL;
72797c478bd9Sstevel@tonic-gate 
72807c478bd9Sstevel@tonic-gate 		ohci_traverse_done_list(ohcip, done_head);
72817c478bd9Sstevel@tonic-gate 	}
72827c478bd9Sstevel@tonic-gate 
72837c478bd9Sstevel@tonic-gate 	/* Process endpoint reclaimation list */
72847c478bd9Sstevel@tonic-gate 	if (ohcip->ohci_reclaim_list) {
72857c478bd9Sstevel@tonic-gate 		ohci_handle_endpoint_reclaimation(ohcip);
72867c478bd9Sstevel@tonic-gate 	}
72877c478bd9Sstevel@tonic-gate 
72887c478bd9Sstevel@tonic-gate 	if (intr & HCR_INTR_RD) {
72897c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
72907c478bd9Sstevel@tonic-gate 		    "ohci_intr: Resume Detected");
72917c478bd9Sstevel@tonic-gate 	}
72927c478bd9Sstevel@tonic-gate 
72937c478bd9Sstevel@tonic-gate 	if (intr & HCR_INTR_RHSC) {
72947c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
72957c478bd9Sstevel@tonic-gate 		    "ohci_intr: Root hub status change");
72967c478bd9Sstevel@tonic-gate 	}
72977c478bd9Sstevel@tonic-gate 
72987c478bd9Sstevel@tonic-gate 	if (intr & HCR_INTR_OC) {
72997c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
73007c478bd9Sstevel@tonic-gate 		    "ohci_intr: Change ownership");
73017c478bd9Sstevel@tonic-gate 
73027c478bd9Sstevel@tonic-gate 	}
73037c478bd9Sstevel@tonic-gate 
73047c478bd9Sstevel@tonic-gate 	if (intr & HCR_INTR_UE) {
73057c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
73067c478bd9Sstevel@tonic-gate 		    "ohci_intr: Unrecoverable error");
73077c478bd9Sstevel@tonic-gate 
73087c478bd9Sstevel@tonic-gate 		ohci_handle_ue(ohcip);
73097c478bd9Sstevel@tonic-gate 	}
73107c478bd9Sstevel@tonic-gate 
73117c478bd9Sstevel@tonic-gate 	/* Acknowledge the interrupt */
73127c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_status, intr);
73137c478bd9Sstevel@tonic-gate 
73147c478bd9Sstevel@tonic-gate 	/* Clear the current interrupt event field */
73157c478bd9Sstevel@tonic-gate 	ohci_intr_sts->ohci_curr_intr_sts = 0;
73167c478bd9Sstevel@tonic-gate 
73177c478bd9Sstevel@tonic-gate 	/*
73187c478bd9Sstevel@tonic-gate 	 * Reset the following flag indicating exiting the interrupt
73197c478bd9Sstevel@tonic-gate 	 * handler and this flag will be used in the polled  mode to
73207c478bd9Sstevel@tonic-gate 	 * do some extra processing.
73217c478bd9Sstevel@tonic-gate 	 */
73227c478bd9Sstevel@tonic-gate 	ohci_intr_sts->ohci_intr_flag &= ~OHCI_INTR_HANDLING;
73237c478bd9Sstevel@tonic-gate 
73247c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_enable, HCR_INTR_MIE);
73257c478bd9Sstevel@tonic-gate 
73267c478bd9Sstevel@tonic-gate 	/*
73277c478bd9Sstevel@tonic-gate 	 * Read interrupt status register to make sure that any PIO
73287c478bd9Sstevel@tonic-gate 	 * store to clear the ISR has made it on the PCI bus before
73297c478bd9Sstevel@tonic-gate 	 * returning from its interrupt handler.
73307c478bd9Sstevel@tonic-gate 	 */
73317c478bd9Sstevel@tonic-gate 	(void) Get_OpReg(hcr_intr_status);
73327c478bd9Sstevel@tonic-gate 
73337c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
73347c478bd9Sstevel@tonic-gate 
73357c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_INTR,  ohcip->ohci_log_hdl,
73367c478bd9Sstevel@tonic-gate 	    "Interrupt handling completed");
73377c478bd9Sstevel@tonic-gate 
73387c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
73397c478bd9Sstevel@tonic-gate }
73407c478bd9Sstevel@tonic-gate 
73417c478bd9Sstevel@tonic-gate 
73427c478bd9Sstevel@tonic-gate /*
73437c478bd9Sstevel@tonic-gate  * ohci_handle_missed_intr:
73447c478bd9Sstevel@tonic-gate  *
73457c478bd9Sstevel@tonic-gate  * Handle any ohci missed interrupts because of polled mode switch.
73467c478bd9Sstevel@tonic-gate  */
73477c478bd9Sstevel@tonic-gate static void
73487c478bd9Sstevel@tonic-gate ohci_handle_missed_intr(ohci_state_t	*ohcip)
73497c478bd9Sstevel@tonic-gate {
73507c478bd9Sstevel@tonic-gate 	ohci_save_intr_sts_t		*ohci_intr_sts =
73517c478bd9Sstevel@tonic-gate 					    &ohcip->ohci_save_intr_sts;
73527c478bd9Sstevel@tonic-gate 	ohci_td_t			*done_head;
73537c478bd9Sstevel@tonic-gate 	uint_t				intr;
73547c478bd9Sstevel@tonic-gate 
73557c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
73567c478bd9Sstevel@tonic-gate 
73577c478bd9Sstevel@tonic-gate 	/*
73587c478bd9Sstevel@tonic-gate 	 * Check whether we have  missed any ohci interrupts because
73597c478bd9Sstevel@tonic-gate 	 * of the polled mode switch during  previous ohci interrupt
73607c478bd9Sstevel@tonic-gate 	 * handler execution. Only  Write Done Head & SOF interrupts
73617c478bd9Sstevel@tonic-gate 	 * saved in the polled mode. First process  these interrupts
73627c478bd9Sstevel@tonic-gate 	 * before processing actual interrupts that caused invocation
73637c478bd9Sstevel@tonic-gate 	 * of ohci interrupt handler.
73647c478bd9Sstevel@tonic-gate 	 */
73657c478bd9Sstevel@tonic-gate 	if (!ohci_intr_sts->ohci_missed_intr_sts) {
73667c478bd9Sstevel@tonic-gate 		/* No interrupts are missed, simply return */
73677c478bd9Sstevel@tonic-gate 
73687c478bd9Sstevel@tonic-gate 		return;
73697c478bd9Sstevel@tonic-gate 	}
73707c478bd9Sstevel@tonic-gate 
73717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
73727c478bd9Sstevel@tonic-gate 	    "ohci_handle_missed_intr: Handle ohci missed interrupts");
73737c478bd9Sstevel@tonic-gate 
73747c478bd9Sstevel@tonic-gate 	/*
73757c478bd9Sstevel@tonic-gate 	 * The functionality and importance of critical code section
73767c478bd9Sstevel@tonic-gate 	 * in the normal mode ohci  interrupt handler & its usage in
73777c478bd9Sstevel@tonic-gate 	 * the polled mode is explained below.
73787c478bd9Sstevel@tonic-gate 	 *
73797c478bd9Sstevel@tonic-gate 	 * (a) Normal mode:
73807c478bd9Sstevel@tonic-gate 	 *
73817c478bd9Sstevel@tonic-gate 	 *	- Set the flag	indicating that  processing critical
73827c478bd9Sstevel@tonic-gate 	 *	  code in ohci interrupt handler.
73837c478bd9Sstevel@tonic-gate 	 *
73847c478bd9Sstevel@tonic-gate 	 *	- Process the missed ohci interrupts by  copying the
73857c478bd9Sstevel@tonic-gate 	 *	  miised interrupt events and done  head list fields
73867c478bd9Sstevel@tonic-gate 	 *	  information to the critical interrupt event & done
73877c478bd9Sstevel@tonic-gate 	 *	  list fields.
73887c478bd9Sstevel@tonic-gate 	 *
73897c478bd9Sstevel@tonic-gate 	 *	- Reset the missed ohci interrupt events & done head
73907c478bd9Sstevel@tonic-gate 	 *	  list fields so that the new missed interrupt event
73917c478bd9Sstevel@tonic-gate 	 *	  and done head list information can be saved.
73927c478bd9Sstevel@tonic-gate 	 *
73937c478bd9Sstevel@tonic-gate 	 *	- All above steps will be executed  with in critical
73947c478bd9Sstevel@tonic-gate 	 *	  section of the  interrupt handler.Then ohci missed
73957c478bd9Sstevel@tonic-gate 	 *	  interrupt handler will be called to service missed
73967c478bd9Sstevel@tonic-gate 	 *	  ohci interrupts.
73977c478bd9Sstevel@tonic-gate 	 *
73987c478bd9Sstevel@tonic-gate 	 * (b) Polled mode:
73997c478bd9Sstevel@tonic-gate 	 *
74007c478bd9Sstevel@tonic-gate 	 *	- On entering the polled code,it checks for critical
74017c478bd9Sstevel@tonic-gate 	 *	  section code execution within the normal mode ohci
74027c478bd9Sstevel@tonic-gate 	 *	  interrupt handler.
74037c478bd9Sstevel@tonic-gate 	 *
74047c478bd9Sstevel@tonic-gate 	 *	- If the critical section code is executing in normal
74057c478bd9Sstevel@tonic-gate 	 *	  mode ohci interrupt handler and if copying of ohci
74067c478bd9Sstevel@tonic-gate 	 *	  missed interrupt events & done head list fields to
74077c478bd9Sstevel@tonic-gate 	 *	  the critical fields is finished then save the "any
74087c478bd9Sstevel@tonic-gate 	 *	  missed interrupt events & done head list"  because
74097c478bd9Sstevel@tonic-gate 	 *	  of current polled mode switch into "critical missed
74107c478bd9Sstevel@tonic-gate 	 *	  interrupt events & done list fields" instead actual
74117c478bd9Sstevel@tonic-gate 	 *	  missed events and done list fields.
74127c478bd9Sstevel@tonic-gate 	 *
74137c478bd9Sstevel@tonic-gate 	 *	- Otherwise save "any missed interrupt events & done
74147c478bd9Sstevel@tonic-gate 	 *	  list" because of this  current polled  mode switch
74157c478bd9Sstevel@tonic-gate 	 *	  in the actual missed	interrupt events & done head
74167c478bd9Sstevel@tonic-gate 	 *	  list fields.
74177c478bd9Sstevel@tonic-gate 	 */
74187c478bd9Sstevel@tonic-gate 
74197c478bd9Sstevel@tonic-gate 	/*
74207c478bd9Sstevel@tonic-gate 	 * Set flag indicating that  interrupt handler is processing
74217c478bd9Sstevel@tonic-gate 	 * critical interrupt code,  so that polled mode code checks
74227c478bd9Sstevel@tonic-gate 	 * for this condition & will do extra processing as explained
74237c478bd9Sstevel@tonic-gate 	 * above in order to aviod the race conditions.
74247c478bd9Sstevel@tonic-gate 	 */
74257c478bd9Sstevel@tonic-gate 	ohci_intr_sts->ohci_intr_flag |= OHCI_INTR_CRITICAL;
74267c478bd9Sstevel@tonic-gate 	ohci_intr_sts->ohci_critical_intr_sts |=
74277c478bd9Sstevel@tonic-gate 	    ohci_intr_sts->ohci_missed_intr_sts;
74287c478bd9Sstevel@tonic-gate 
74297c478bd9Sstevel@tonic-gate 	if (ohci_intr_sts->ohci_missed_done_lst) {
74307c478bd9Sstevel@tonic-gate 
74317c478bd9Sstevel@tonic-gate 		ohci_intr_sts->ohci_critical_done_lst =
74327c478bd9Sstevel@tonic-gate 		    ohci_intr_sts->ohci_missed_done_lst;
74337c478bd9Sstevel@tonic-gate 	}
74347c478bd9Sstevel@tonic-gate 
74357c478bd9Sstevel@tonic-gate 	ohci_intr_sts->ohci_missed_intr_sts = 0;
74367c478bd9Sstevel@tonic-gate 	ohci_intr_sts->ohci_missed_done_lst = NULL;
74377c478bd9Sstevel@tonic-gate 	ohci_intr_sts->ohci_intr_flag &= ~OHCI_INTR_CRITICAL;
74387c478bd9Sstevel@tonic-gate 
74397c478bd9Sstevel@tonic-gate 	intr = ohci_intr_sts->ohci_critical_intr_sts;
74407c478bd9Sstevel@tonic-gate 	done_head = ohci_intr_sts->ohci_critical_done_lst;
74417c478bd9Sstevel@tonic-gate 
74427c478bd9Sstevel@tonic-gate 	if (intr & HCR_INTR_SOF) {
74437c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
74447c478bd9Sstevel@tonic-gate 		    "ohci_handle_missed_intr: Start of Frame");
74457c478bd9Sstevel@tonic-gate 
74467c478bd9Sstevel@tonic-gate 		/*
74477c478bd9Sstevel@tonic-gate 		 * Call cv_broadcast on every SOF interrupt to wakeup
74487c478bd9Sstevel@tonic-gate 		 * all the threads that are waiting the SOF.  Calling
74497c478bd9Sstevel@tonic-gate 		 * cv_broadcast on every SOF has no effect even if no
74507c478bd9Sstevel@tonic-gate 		 * threads are waiting for the SOF.
74517c478bd9Sstevel@tonic-gate 		 */
74527c478bd9Sstevel@tonic-gate 		cv_broadcast(&ohcip->ohci_SOF_cv);
74537c478bd9Sstevel@tonic-gate 	}
74547c478bd9Sstevel@tonic-gate 
74557c478bd9Sstevel@tonic-gate 	if ((intr & HCR_INTR_WDH) && (done_head)) {
74567c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
74577c478bd9Sstevel@tonic-gate 		    "ohci_handle_missed_intr: Done Head");
74587c478bd9Sstevel@tonic-gate 
74597c478bd9Sstevel@tonic-gate 		/* Clear the critical done head field */
74607c478bd9Sstevel@tonic-gate 		ohci_intr_sts->ohci_critical_done_lst = NULL;
74617c478bd9Sstevel@tonic-gate 
74627c478bd9Sstevel@tonic-gate 		ohci_traverse_done_list(ohcip, done_head);
74637c478bd9Sstevel@tonic-gate 	}
74647c478bd9Sstevel@tonic-gate 
74657c478bd9Sstevel@tonic-gate 	/* Clear the critical interrupt event field */
74667c478bd9Sstevel@tonic-gate 	ohci_intr_sts->ohci_critical_intr_sts = 0;
74677c478bd9Sstevel@tonic-gate }
74687c478bd9Sstevel@tonic-gate 
74697c478bd9Sstevel@tonic-gate 
74707c478bd9Sstevel@tonic-gate /*
74717c478bd9Sstevel@tonic-gate  * ohci_handle_ue:
74727c478bd9Sstevel@tonic-gate  *
74737c478bd9Sstevel@tonic-gate  * Handling of Unrecoverable Error interrupt (UE).
74747c478bd9Sstevel@tonic-gate  */
74757c478bd9Sstevel@tonic-gate static void
74767c478bd9Sstevel@tonic-gate ohci_handle_ue(ohci_state_t	*ohcip)
74777c478bd9Sstevel@tonic-gate {
74787c478bd9Sstevel@tonic-gate 	usb_frame_number_t	before_frame_number, after_frame_number;
74797c478bd9Sstevel@tonic-gate 
74807c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
74817c478bd9Sstevel@tonic-gate 
74827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
74837c478bd9Sstevel@tonic-gate 	    "ohci_handle_ue: Handling of UE interrupt");
74847c478bd9Sstevel@tonic-gate 
74857c478bd9Sstevel@tonic-gate 	/*
74867c478bd9Sstevel@tonic-gate 	 * First check whether current UE error occured due to USB or
74877c478bd9Sstevel@tonic-gate 	 * due to some other subsystem. This can be verified by reading
74887c478bd9Sstevel@tonic-gate 	 * usb frame numbers before & after a delay of few milliseconds.
74897c478bd9Sstevel@tonic-gate 	 * If usb frame number read after delay is greater than the one
74907c478bd9Sstevel@tonic-gate 	 * read before delay, then, USB subsystem is fine. In this case,
74917c478bd9Sstevel@tonic-gate 	 * disable UE error interrupt and return without shutdowning the
74927c478bd9Sstevel@tonic-gate 	 * USB subsystem.
74937c478bd9Sstevel@tonic-gate 	 *
74947c478bd9Sstevel@tonic-gate 	 * Otherwise, if usb frame number read after delay is less than
74957c478bd9Sstevel@tonic-gate 	 * or equal to one read before the delay, then, current UE error
74967c478bd9Sstevel@tonic-gate 	 * occured from USB susbsystem. In this case,go ahead with actual
74977c478bd9Sstevel@tonic-gate 	 * UE error recovery procedure.
74987c478bd9Sstevel@tonic-gate 	 *
74997c478bd9Sstevel@tonic-gate 	 * Get the current usb frame number before waiting for few
75007c478bd9Sstevel@tonic-gate 	 * milliseconds.
75017c478bd9Sstevel@tonic-gate 	 */
75027c478bd9Sstevel@tonic-gate 	before_frame_number = ohci_get_current_frame_number(ohcip);
75037c478bd9Sstevel@tonic-gate 
75047c478bd9Sstevel@tonic-gate 	/* Wait for few milliseconds */
75057c478bd9Sstevel@tonic-gate 	drv_usecwait(OHCI_TIMEWAIT);
75067c478bd9Sstevel@tonic-gate 
75077c478bd9Sstevel@tonic-gate 	/*
75087c478bd9Sstevel@tonic-gate 	 * Get the current usb frame number after waiting for
75097c478bd9Sstevel@tonic-gate 	 * milliseconds.
75107c478bd9Sstevel@tonic-gate 	 */
75117c478bd9Sstevel@tonic-gate 	after_frame_number = ohci_get_current_frame_number(ohcip);
75127c478bd9Sstevel@tonic-gate 
75137c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
75147c478bd9Sstevel@tonic-gate 	    "ohci_handle_ue: Before Frm No 0x%llx After Frm No 0x%llx",
75157c478bd9Sstevel@tonic-gate 	    before_frame_number, after_frame_number);
75167c478bd9Sstevel@tonic-gate 
75177c478bd9Sstevel@tonic-gate 	if (after_frame_number > before_frame_number) {
75187c478bd9Sstevel@tonic-gate 
75197c478bd9Sstevel@tonic-gate 		/* Disable UE interrupt */
75207c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_intr_disable, HCR_INTR_UE);
75217c478bd9Sstevel@tonic-gate 
75227c478bd9Sstevel@tonic-gate 		return;
75237c478bd9Sstevel@tonic-gate 	}
75247c478bd9Sstevel@tonic-gate 
75257c478bd9Sstevel@tonic-gate 	/*
75267c478bd9Sstevel@tonic-gate 	 * This UE is due to USB hardware error. Reset ohci controller
75277c478bd9Sstevel@tonic-gate 	 * and reprogram to bring it back to functional state.
75287c478bd9Sstevel@tonic-gate 	 */
75297c478bd9Sstevel@tonic-gate 	if ((ohci_do_soft_reset(ohcip)) != USB_SUCCESS) {
75307c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
75317c478bd9Sstevel@tonic-gate 		    "Unrecoverable USB Hardware Error");
75327c478bd9Sstevel@tonic-gate 
75337c478bd9Sstevel@tonic-gate 		/* Disable UE interrupt */
75347c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_intr_disable, HCR_INTR_UE);
75357c478bd9Sstevel@tonic-gate 
75367c478bd9Sstevel@tonic-gate 		/* Set host controller soft state to error */
75377c478bd9Sstevel@tonic-gate 		ohcip->ohci_hc_soft_state = OHCI_CTLR_ERROR_STATE;
75387c478bd9Sstevel@tonic-gate 	}
75397c478bd9Sstevel@tonic-gate }
75407c478bd9Sstevel@tonic-gate 
75417c478bd9Sstevel@tonic-gate 
75427c478bd9Sstevel@tonic-gate /*
75437c478bd9Sstevel@tonic-gate  * ohci_handle_frame_number_overflow:
75447c478bd9Sstevel@tonic-gate  *
75457c478bd9Sstevel@tonic-gate  * Update software based usb frame number part on every frame number
75467c478bd9Sstevel@tonic-gate  * overflow interrupt.
75477c478bd9Sstevel@tonic-gate  *
75487c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
75497c478bd9Sstevel@tonic-gate  *
75507c478bd9Sstevel@tonic-gate  * Refer ohci spec 1.0a, section 5.3, page 81 for more details.
75517c478bd9Sstevel@tonic-gate  */
75527c478bd9Sstevel@tonic-gate void
75537c478bd9Sstevel@tonic-gate ohci_handle_frame_number_overflow(ohci_state_t *ohcip)
75547c478bd9Sstevel@tonic-gate {
75557c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
75567c478bd9Sstevel@tonic-gate 	    "ohci_handle_frame_number_overflow:");
75577c478bd9Sstevel@tonic-gate 
75587c478bd9Sstevel@tonic-gate 	ohcip->ohci_fno += (0x10000 -
75597c478bd9Sstevel@tonic-gate 	    (((Get_HCCA(ohcip->ohci_hccap->HccaFrameNo) &
75607c478bd9Sstevel@tonic-gate 	    0xFFFF) ^ ohcip->ohci_fno) & 0x8000));
75617c478bd9Sstevel@tonic-gate 
75627c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
75637c478bd9Sstevel@tonic-gate 	    "ohci_handle_frame_number_overflow:"
75647c478bd9Sstevel@tonic-gate 	    "Frame Number Higher Part 0x%llx\n", ohcip->ohci_fno);
75657c478bd9Sstevel@tonic-gate }
75667c478bd9Sstevel@tonic-gate 
75677c478bd9Sstevel@tonic-gate 
75687c478bd9Sstevel@tonic-gate /*
75697c478bd9Sstevel@tonic-gate  * ohci_handle_endpoint_reclaimation:
75707c478bd9Sstevel@tonic-gate  *
75717c478bd9Sstevel@tonic-gate  * Reclamation of Host Controller (HC) Endpoint Descriptors (ED).
75727c478bd9Sstevel@tonic-gate  */
75737c478bd9Sstevel@tonic-gate static void
75747c478bd9Sstevel@tonic-gate ohci_handle_endpoint_reclaimation(ohci_state_t	*ohcip)
75757c478bd9Sstevel@tonic-gate {
75767c478bd9Sstevel@tonic-gate 	usb_frame_number_t	current_frame_number;
75777c478bd9Sstevel@tonic-gate 	usb_frame_number_t	endpoint_frame_number;
75787c478bd9Sstevel@tonic-gate 	ohci_ed_t		*reclaim_ed;
75797c478bd9Sstevel@tonic-gate 
75807c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
75817c478bd9Sstevel@tonic-gate 	    "ohci_handle_endpoint_reclaimation:");
75827c478bd9Sstevel@tonic-gate 
75837c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
75847c478bd9Sstevel@tonic-gate 
75857c478bd9Sstevel@tonic-gate 	current_frame_number = ohci_get_current_frame_number(ohcip);
75867c478bd9Sstevel@tonic-gate 
75877c478bd9Sstevel@tonic-gate 	/*
75887c478bd9Sstevel@tonic-gate 	 * Deallocate all Endpoint Descriptors (ED) which are on the
75897c478bd9Sstevel@tonic-gate 	 * reclaimation list. These ED's are already removed from the
75907c478bd9Sstevel@tonic-gate 	 * interrupt lattice tree.
75917c478bd9Sstevel@tonic-gate 	 */
75927c478bd9Sstevel@tonic-gate 	while (ohcip->ohci_reclaim_list) {
75937c478bd9Sstevel@tonic-gate 		reclaim_ed = ohcip->ohci_reclaim_list;
75947c478bd9Sstevel@tonic-gate 
75957c478bd9Sstevel@tonic-gate 		endpoint_frame_number = (usb_frame_number_t)(uintptr_t)
75967c478bd9Sstevel@tonic-gate 		    (OHCI_LOOKUP_ID(Get_ED(reclaim_ed->hced_reclaim_frame)));
75977c478bd9Sstevel@tonic-gate 
75987c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
75997c478bd9Sstevel@tonic-gate 		    "ohci_handle_endpoint_reclaimation:"
76007c478bd9Sstevel@tonic-gate 		    "current frame number 0x%llx endpoint frame number 0x%llx",
76017c478bd9Sstevel@tonic-gate 		    current_frame_number, endpoint_frame_number);
76027c478bd9Sstevel@tonic-gate 
76037c478bd9Sstevel@tonic-gate 		/*
76047c478bd9Sstevel@tonic-gate 		 * Deallocate current endpoint only if endpoint's usb frame
76057c478bd9Sstevel@tonic-gate 		 * number is less than or equal to current usb frame number.
76067c478bd9Sstevel@tonic-gate 		 *
76077c478bd9Sstevel@tonic-gate 		 * If endpoint's usb frame number is greater than the current
76087c478bd9Sstevel@tonic-gate 		 * usb frame number, ignore rest of the endpoints in the list
76097c478bd9Sstevel@tonic-gate 		 * since rest of the endpoints are inserted into the reclaim
76107c478bd9Sstevel@tonic-gate 		 * list later than the current reclaim endpoint.
76117c478bd9Sstevel@tonic-gate 		 */
76127c478bd9Sstevel@tonic-gate 		if (endpoint_frame_number > current_frame_number) {
76137c478bd9Sstevel@tonic-gate 			break;
76147c478bd9Sstevel@tonic-gate 		}
76157c478bd9Sstevel@tonic-gate 
76167c478bd9Sstevel@tonic-gate 		/* Get the next endpoint from the rec. list */
76177c478bd9Sstevel@tonic-gate 		ohcip->ohci_reclaim_list = ohci_ed_iommu_to_cpu(ohcip,
76187c478bd9Sstevel@tonic-gate 		    Get_ED(reclaim_ed->hced_reclaim_next));
76197c478bd9Sstevel@tonic-gate 
76207c478bd9Sstevel@tonic-gate 		/* Free 32bit ID */
76217c478bd9Sstevel@tonic-gate 		OHCI_FREE_ID((uint32_t)Get_ED(reclaim_ed->hced_reclaim_frame));
76227c478bd9Sstevel@tonic-gate 
76237c478bd9Sstevel@tonic-gate 		/* Deallocate the endpoint */
76247c478bd9Sstevel@tonic-gate 		ohci_deallocate_ed(ohcip, reclaim_ed);
76257c478bd9Sstevel@tonic-gate 	}
76267c478bd9Sstevel@tonic-gate }
76277c478bd9Sstevel@tonic-gate 
76287c478bd9Sstevel@tonic-gate 
76297c478bd9Sstevel@tonic-gate /*
76307c478bd9Sstevel@tonic-gate  * ohci_traverse_done_list:
76317c478bd9Sstevel@tonic-gate  */
76327c478bd9Sstevel@tonic-gate static void
76337c478bd9Sstevel@tonic-gate ohci_traverse_done_list(
76347c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
76357c478bd9Sstevel@tonic-gate 	ohci_td_t		*head_done_list)
76367c478bd9Sstevel@tonic-gate {
76377c478bd9Sstevel@tonic-gate 	uint_t			state;		/* TD state */
76387c478bd9Sstevel@tonic-gate 	ohci_td_t		*td, *old_td;	/* TD pointers */
76397c478bd9Sstevel@tonic-gate 	usb_cr_t		error;		/* Error from TD */
76407c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw = NULL;	/* Transfer wrapper */
76417c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = NULL;	/* Pipe private field */
76427c478bd9Sstevel@tonic-gate 
76437c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
76447c478bd9Sstevel@tonic-gate 	    "ohci_traverse_done_list:");
76457c478bd9Sstevel@tonic-gate 
76467c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
76477c478bd9Sstevel@tonic-gate 
76487c478bd9Sstevel@tonic-gate 	/* Sync ED and TD pool */
76497c478bd9Sstevel@tonic-gate 	Sync_ED_TD_Pool(ohcip);
76507c478bd9Sstevel@tonic-gate 
76517c478bd9Sstevel@tonic-gate 	/* Reverse the done list */
76527c478bd9Sstevel@tonic-gate 	td = ohci_reverse_done_list(ohcip, head_done_list);
76537c478bd9Sstevel@tonic-gate 
76547c478bd9Sstevel@tonic-gate 	/* Traverse the list of transfer descriptors */
76557c478bd9Sstevel@tonic-gate 	while (td) {
76567c478bd9Sstevel@tonic-gate 		/* Check for TD state */
76577c478bd9Sstevel@tonic-gate 		state = Get_TD(td->hctd_state);
76587c478bd9Sstevel@tonic-gate 
76597c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
76607c478bd9Sstevel@tonic-gate 		    "ohci_traverse_done_list:\n\t"
76617c478bd9Sstevel@tonic-gate 		    "td = 0x%p	state = 0x%x", (void *)td, state);
76627c478bd9Sstevel@tonic-gate 
76637c478bd9Sstevel@tonic-gate 		/*
76647c478bd9Sstevel@tonic-gate 		 * Obtain the  transfer wrapper only  if the TD is
76657c478bd9Sstevel@tonic-gate 		 * not marked as RECLAIM.
76667c478bd9Sstevel@tonic-gate 		 *
76677c478bd9Sstevel@tonic-gate 		 * A TD that is marked as  RECLAIM has had its DMA
76687c478bd9Sstevel@tonic-gate 		 * mappings, ED, TD and pipe private structure are
76697c478bd9Sstevel@tonic-gate 		 * ripped down. Just deallocate this TD.
76707c478bd9Sstevel@tonic-gate 		 */
76717c478bd9Sstevel@tonic-gate 		if (state != HC_TD_RECLAIM) {
76727c478bd9Sstevel@tonic-gate 
76737c478bd9Sstevel@tonic-gate 			tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
76747c478bd9Sstevel@tonic-gate 			    (uint32_t)Get_TD(td->hctd_trans_wrapper));
76757c478bd9Sstevel@tonic-gate 
76767c478bd9Sstevel@tonic-gate 			ASSERT(tw != NULL);
76777c478bd9Sstevel@tonic-gate 
76787c478bd9Sstevel@tonic-gate 			pp = tw->tw_pipe_private;
76797c478bd9Sstevel@tonic-gate 
76807c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
76817c478bd9Sstevel@tonic-gate 			    "ohci_traverse_done_list: PP = 0x%p TW = 0x%p",
76827c478bd9Sstevel@tonic-gate 			    pp, tw);
76837c478bd9Sstevel@tonic-gate 		}
76847c478bd9Sstevel@tonic-gate 
76857c478bd9Sstevel@tonic-gate 		/*
76867c478bd9Sstevel@tonic-gate 		 * Don't process the TD if its	state is marked as
76877c478bd9Sstevel@tonic-gate 		 * either RECLAIM or TIMEOUT.
76887c478bd9Sstevel@tonic-gate 		 *
76897c478bd9Sstevel@tonic-gate 		 * A TD that is marked as TIMEOUT has already been
76907c478bd9Sstevel@tonic-gate 		 * processed by TD timeout handler & client driver
76917c478bd9Sstevel@tonic-gate 		 * has been informed through exception callback.
76927c478bd9Sstevel@tonic-gate 		 */
76937c478bd9Sstevel@tonic-gate 		if ((state != HC_TD_RECLAIM) && (state != HC_TD_TIMEOUT)) {
76947c478bd9Sstevel@tonic-gate 
76957c478bd9Sstevel@tonic-gate 			/* Look at the error status */
76967c478bd9Sstevel@tonic-gate 			error = ohci_parse_error(ohcip, td);
76977c478bd9Sstevel@tonic-gate 
76987c478bd9Sstevel@tonic-gate 			if (error == USB_CR_OK) {
76997c478bd9Sstevel@tonic-gate 				ohci_handle_normal_td(ohcip, td, tw);
77007c478bd9Sstevel@tonic-gate 			} else {
77017c478bd9Sstevel@tonic-gate 				/* handle the error condition */
77027c478bd9Sstevel@tonic-gate 				ohci_handle_error(ohcip, td, error);
77037c478bd9Sstevel@tonic-gate 			}
77047c478bd9Sstevel@tonic-gate 		} else {
77057c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
77067c478bd9Sstevel@tonic-gate 			    "ohci_traverse_done_list: TD State = %d", state);
77077c478bd9Sstevel@tonic-gate 		}
77087c478bd9Sstevel@tonic-gate 
77097c478bd9Sstevel@tonic-gate 		/*
77107c478bd9Sstevel@tonic-gate 		 * Save a pointer to the current transfer descriptor
77117c478bd9Sstevel@tonic-gate 		 */
77127c478bd9Sstevel@tonic-gate 		old_td = td;
77137c478bd9Sstevel@tonic-gate 
77147c478bd9Sstevel@tonic-gate 		td = ohci_td_iommu_to_cpu(ohcip, Get_TD(td->hctd_next_td));
77157c478bd9Sstevel@tonic-gate 
77167c478bd9Sstevel@tonic-gate 		/* Deallocate this transfer descriptor */
77177c478bd9Sstevel@tonic-gate 		ohci_deallocate_td(ohcip, old_td);
77187c478bd9Sstevel@tonic-gate 
77197c478bd9Sstevel@tonic-gate 		/*
77207c478bd9Sstevel@tonic-gate 		 * Deallocate the transfer wrapper if there are no more
77217c478bd9Sstevel@tonic-gate 		 * TD's for the transfer wrapper. ohci_deallocate_tw_resources()
77227c478bd9Sstevel@tonic-gate 		 * will  not deallocate the tw for a periodic  endpoint
77237c478bd9Sstevel@tonic-gate 		 * since it will always have a TD attached to it.
77247c478bd9Sstevel@tonic-gate 		 *
77257c478bd9Sstevel@tonic-gate 		 * Do not deallocate the TW if it is a isoc or intr pipe in.
77267c478bd9Sstevel@tonic-gate 		 * The tw's are reused.
77277c478bd9Sstevel@tonic-gate 		 *
77287c478bd9Sstevel@tonic-gate 		 * An TD that is marked as reclaim doesn't have a  pipe
77297c478bd9Sstevel@tonic-gate 		 * or a TW associated with it anymore so don't call this
77307c478bd9Sstevel@tonic-gate 		 * function.
77317c478bd9Sstevel@tonic-gate 		 */
77327c478bd9Sstevel@tonic-gate 		if (state != HC_TD_RECLAIM) {
77337c478bd9Sstevel@tonic-gate 			ASSERT(tw != NULL);
77347c478bd9Sstevel@tonic-gate 			ohci_deallocate_tw_resources(ohcip, pp, tw);
77357c478bd9Sstevel@tonic-gate 		}
77367c478bd9Sstevel@tonic-gate 	}
77377c478bd9Sstevel@tonic-gate }
77387c478bd9Sstevel@tonic-gate 
77397c478bd9Sstevel@tonic-gate 
77407c478bd9Sstevel@tonic-gate /*
77417c478bd9Sstevel@tonic-gate  * ohci_reverse_done_list:
77427c478bd9Sstevel@tonic-gate  *
77437c478bd9Sstevel@tonic-gate  * Reverse the order of the Transfer Descriptor (TD) Done List.
77447c478bd9Sstevel@tonic-gate  */
77457c478bd9Sstevel@tonic-gate static ohci_td_t *
77467c478bd9Sstevel@tonic-gate ohci_reverse_done_list(
77477c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
77487c478bd9Sstevel@tonic-gate 	ohci_td_t	*head_done_list)
77497c478bd9Sstevel@tonic-gate {
77507c478bd9Sstevel@tonic-gate 	ohci_td_t	*cpu_new_tail, *cpu_new_head, *cpu_save;
77517c478bd9Sstevel@tonic-gate 
77527c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
77537c478bd9Sstevel@tonic-gate 	    "ohci_reverse_done_list:");
77547c478bd9Sstevel@tonic-gate 
77557c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
77567c478bd9Sstevel@tonic-gate 	ASSERT(head_done_list != NULL);
77577c478bd9Sstevel@tonic-gate 
77587c478bd9Sstevel@tonic-gate 	/* At first, both the tail and head pointers point to the same elem */
77597c478bd9Sstevel@tonic-gate 	cpu_new_tail = cpu_new_head =
77607c478bd9Sstevel@tonic-gate 	    ohci_td_iommu_to_cpu(ohcip, (uintptr_t)head_done_list);
77617c478bd9Sstevel@tonic-gate 
77627c478bd9Sstevel@tonic-gate 	/* See if the list has only one element */
77637c478bd9Sstevel@tonic-gate 	if (Get_TD(cpu_new_head->hctd_next_td) == NULL) {
77647c478bd9Sstevel@tonic-gate 
77657c478bd9Sstevel@tonic-gate 		return (cpu_new_head);
77667c478bd9Sstevel@tonic-gate 	}
77677c478bd9Sstevel@tonic-gate 
77687c478bd9Sstevel@tonic-gate 	/* Advance the head pointer */
77697c478bd9Sstevel@tonic-gate 	cpu_new_head = (ohci_td_t *)
77707c478bd9Sstevel@tonic-gate 	    ohci_td_iommu_to_cpu(ohcip, Get_TD(cpu_new_head->hctd_next_td));
77717c478bd9Sstevel@tonic-gate 
77727c478bd9Sstevel@tonic-gate 	/* The new tail now points to nothing */
77737c478bd9Sstevel@tonic-gate 	Set_TD(cpu_new_tail->hctd_next_td, NULL);
77747c478bd9Sstevel@tonic-gate 
77757c478bd9Sstevel@tonic-gate 	cpu_save = (ohci_td_t *)
77767c478bd9Sstevel@tonic-gate 	    ohci_td_iommu_to_cpu(ohcip, Get_TD(cpu_new_head->hctd_next_td));
77777c478bd9Sstevel@tonic-gate 
77787c478bd9Sstevel@tonic-gate 	/* Reverse the list and store the pointers as CPU addresses */
77797c478bd9Sstevel@tonic-gate 	while (cpu_save) {
77807c478bd9Sstevel@tonic-gate 		Set_TD(cpu_new_head->hctd_next_td,
77817c478bd9Sstevel@tonic-gate 		    ohci_td_cpu_to_iommu(ohcip, cpu_new_tail));
77827c478bd9Sstevel@tonic-gate 
77837c478bd9Sstevel@tonic-gate 		cpu_new_tail = cpu_new_head;
77847c478bd9Sstevel@tonic-gate 		cpu_new_head = cpu_save;
77857c478bd9Sstevel@tonic-gate 
77867c478bd9Sstevel@tonic-gate 		cpu_save = (ohci_td_t *)
77877c478bd9Sstevel@tonic-gate 		    ohci_td_iommu_to_cpu(ohcip,
77887c478bd9Sstevel@tonic-gate 		    Get_TD(cpu_new_head->hctd_next_td));
77897c478bd9Sstevel@tonic-gate 	}
77907c478bd9Sstevel@tonic-gate 
77917c478bd9Sstevel@tonic-gate 	Set_TD(cpu_new_head->hctd_next_td,
77927c478bd9Sstevel@tonic-gate 	    ohci_td_cpu_to_iommu(ohcip, cpu_new_tail));
77937c478bd9Sstevel@tonic-gate 
77947c478bd9Sstevel@tonic-gate 	return (cpu_new_head);
77957c478bd9Sstevel@tonic-gate }
77967c478bd9Sstevel@tonic-gate 
77977c478bd9Sstevel@tonic-gate 
77987c478bd9Sstevel@tonic-gate /*
77997c478bd9Sstevel@tonic-gate  * ohci_parse_error:
78007c478bd9Sstevel@tonic-gate  *
78017c478bd9Sstevel@tonic-gate  * Parse the result for any errors.
78027c478bd9Sstevel@tonic-gate  */
78037c478bd9Sstevel@tonic-gate static usb_cr_t
78047c478bd9Sstevel@tonic-gate ohci_parse_error(
78057c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
78067c478bd9Sstevel@tonic-gate 	ohci_td_t		*td)
78077c478bd9Sstevel@tonic-gate {
78087c478bd9Sstevel@tonic-gate 	uint_t			ctrl;
78097c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd;
78107c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
78117c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;
78127c478bd9Sstevel@tonic-gate 	uint_t			flag;
78137c478bd9Sstevel@tonic-gate 	usb_cr_t		error;
78147c478bd9Sstevel@tonic-gate 
78157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
78167c478bd9Sstevel@tonic-gate 	    "ohci_parse_error:");
78177c478bd9Sstevel@tonic-gate 
78187c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
78197c478bd9Sstevel@tonic-gate 
78207c478bd9Sstevel@tonic-gate 	ASSERT(td != NULL);
78217c478bd9Sstevel@tonic-gate 
78227c478bd9Sstevel@tonic-gate 	/* Obtain the transfer wrapper from the TD */
78237c478bd9Sstevel@tonic-gate 	tw = (ohci_trans_wrapper_t *)
78247c478bd9Sstevel@tonic-gate 	    OHCI_LOOKUP_ID((uint32_t)Get_TD(td->hctd_trans_wrapper));
78257c478bd9Sstevel@tonic-gate 
78267c478bd9Sstevel@tonic-gate 	ASSERT(tw != NULL);
78277c478bd9Sstevel@tonic-gate 
78287c478bd9Sstevel@tonic-gate 	/* Obtain the pipe private structure */
78297c478bd9Sstevel@tonic-gate 	pp = tw->tw_pipe_private;
78307c478bd9Sstevel@tonic-gate 
78317c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
78327c478bd9Sstevel@tonic-gate 	    "ohci_parse_error: PP 0x%p TW 0x%p", pp, tw);
78337c478bd9Sstevel@tonic-gate 
78347c478bd9Sstevel@tonic-gate 	eptd = &pp->pp_pipe_handle->p_ep;
78357c478bd9Sstevel@tonic-gate 
78367c478bd9Sstevel@tonic-gate 	ctrl = (uint_t)Get_TD(td->hctd_ctrl) & (uint32_t)HC_TD_CC;
78377c478bd9Sstevel@tonic-gate 
78387c478bd9Sstevel@tonic-gate 	/*
78397c478bd9Sstevel@tonic-gate 	 * Check the condition code of completed TD and report errors
78407c478bd9Sstevel@tonic-gate 	 * if any. This checking will be done both for the general and
78417c478bd9Sstevel@tonic-gate 	 * the isochronous TDs.
78427c478bd9Sstevel@tonic-gate 	 */
78437c478bd9Sstevel@tonic-gate 	if ((error = ohci_check_for_error(ohcip, pp, tw, td, ctrl)) !=
78447c478bd9Sstevel@tonic-gate 	    USB_CR_OK) {
78457c478bd9Sstevel@tonic-gate 		flag = OHCI_REMOVE_XFER_ALWAYS;
78467c478bd9Sstevel@tonic-gate 	} else {
78477c478bd9Sstevel@tonic-gate 		flag  = OHCI_REMOVE_XFER_IFLAST;
78487c478bd9Sstevel@tonic-gate 	}
78497c478bd9Sstevel@tonic-gate 
78507c478bd9Sstevel@tonic-gate 	/* Stop the the transfer timer */
78517c478bd9Sstevel@tonic-gate 	ohci_stop_xfer_timer(ohcip, tw, flag);
78527c478bd9Sstevel@tonic-gate 
78537c478bd9Sstevel@tonic-gate 	/*
78547c478bd9Sstevel@tonic-gate 	 * The isochronous endpoint needs additional error checking
78557c478bd9Sstevel@tonic-gate 	 * and special processing.
78567c478bd9Sstevel@tonic-gate 	 */
78577c478bd9Sstevel@tonic-gate 	if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
78587c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_ISOCH) {
78597c478bd9Sstevel@tonic-gate 
78607c478bd9Sstevel@tonic-gate 		ohci_parse_isoc_error(ohcip, pp, tw, td);
78617c478bd9Sstevel@tonic-gate 
78627c478bd9Sstevel@tonic-gate 		/* always reset error */
78637c478bd9Sstevel@tonic-gate 		error = USB_CR_OK;
78647c478bd9Sstevel@tonic-gate 	}
78657c478bd9Sstevel@tonic-gate 
78667c478bd9Sstevel@tonic-gate 	return (error);
78677c478bd9Sstevel@tonic-gate }
78687c478bd9Sstevel@tonic-gate 
78697c478bd9Sstevel@tonic-gate 
78707c478bd9Sstevel@tonic-gate /*
78717c478bd9Sstevel@tonic-gate  * ohci_parse_isoc_error:
78727c478bd9Sstevel@tonic-gate  *
78737c478bd9Sstevel@tonic-gate  * Check for any errors in the isochronous data packets. Also fillup
78747c478bd9Sstevel@tonic-gate  * the status for each of the isochrnous data packets.
78757c478bd9Sstevel@tonic-gate  */
78767c478bd9Sstevel@tonic-gate void
78777c478bd9Sstevel@tonic-gate ohci_parse_isoc_error(
78787c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
78797c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
78807c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
78817c478bd9Sstevel@tonic-gate 	ohci_td_t		*td)
78827c478bd9Sstevel@tonic-gate {
78837c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_reqp;
78847c478bd9Sstevel@tonic-gate 	usb_isoc_pkt_descr_t	*isoc_pkt_descr;
78857c478bd9Sstevel@tonic-gate 	uint_t			toggle = 0, fc, ctrl, psw;
78867c478bd9Sstevel@tonic-gate 	int			i;
78877c478bd9Sstevel@tonic-gate 
78887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
78897c478bd9Sstevel@tonic-gate 	    "ohci_parse_isoc_error: td 0x%p", td);
78907c478bd9Sstevel@tonic-gate 
78917c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
78927c478bd9Sstevel@tonic-gate 
78937c478bd9Sstevel@tonic-gate 	fc = ((uint_t)Get_TD(td->hctd_ctrl) &
78947c478bd9Sstevel@tonic-gate 	    HC_ITD_FC) >> HC_ITD_FC_SHIFT;
78957c478bd9Sstevel@tonic-gate 
78967c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
78977c478bd9Sstevel@tonic-gate 	    "ohci_parse_isoc_error: frame count %d", fc);
78987c478bd9Sstevel@tonic-gate 
78997c478bd9Sstevel@tonic-gate 	/*
79007c478bd9Sstevel@tonic-gate 	 * Get the address of current usb isochronous request
79017c478bd9Sstevel@tonic-gate 	 * and array of packet descriptors.
79027c478bd9Sstevel@tonic-gate 	 */
79037c478bd9Sstevel@tonic-gate 	isoc_reqp = (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
79047c478bd9Sstevel@tonic-gate 	isoc_pkt_descr = isoc_reqp->isoc_pkt_descr;
79057c478bd9Sstevel@tonic-gate 
79067c478bd9Sstevel@tonic-gate 	for (i = 0; i <= fc; i++) {
79077c478bd9Sstevel@tonic-gate 
79087c478bd9Sstevel@tonic-gate 		psw = Get_TD(td->hctd_offsets[i / 2]);
79097c478bd9Sstevel@tonic-gate 
79107c478bd9Sstevel@tonic-gate 		if (toggle) {
79117c478bd9Sstevel@tonic-gate 			ctrl = psw & HC_ITD_ODD_OFFSET;
79127c478bd9Sstevel@tonic-gate 			toggle = 0;
79137c478bd9Sstevel@tonic-gate 		} else {
79147c478bd9Sstevel@tonic-gate 			ctrl =	(psw & HC_ITD_EVEN_OFFSET) <<
79157c478bd9Sstevel@tonic-gate 					HC_ITD_OFFSET_SHIFT;
79167c478bd9Sstevel@tonic-gate 			toggle = 1;
79177c478bd9Sstevel@tonic-gate 		}
79187c478bd9Sstevel@tonic-gate 
79197c478bd9Sstevel@tonic-gate 		isoc_pkt_descr->isoc_pkt_actual_length =
79207c478bd9Sstevel@tonic-gate 		    (ctrl >> HC_ITD_OFFSET_SHIFT) & HC_ITD_OFFSET_ADDR;
79217c478bd9Sstevel@tonic-gate 
79227c478bd9Sstevel@tonic-gate 		ctrl = (uint_t)(ctrl & (uint32_t)HC_TD_CC);
79237c478bd9Sstevel@tonic-gate 
79247c478bd9Sstevel@tonic-gate 		/* Write the status of isoc data packet */
79257c478bd9Sstevel@tonic-gate 		isoc_pkt_descr->isoc_pkt_status =
79267c478bd9Sstevel@tonic-gate 		    ohci_check_for_error(ohcip, pp, tw, td, ctrl);
79277c478bd9Sstevel@tonic-gate 
79287c478bd9Sstevel@tonic-gate 		if (isoc_pkt_descr->isoc_pkt_status) {
79297c478bd9Sstevel@tonic-gate 			/* Increment isoc data packet error count */
79307c478bd9Sstevel@tonic-gate 			isoc_reqp->isoc_error_count++;
79317c478bd9Sstevel@tonic-gate 		}
79327c478bd9Sstevel@tonic-gate 
79337c478bd9Sstevel@tonic-gate 		/*
79347c478bd9Sstevel@tonic-gate 		 * Get the address of next isoc data packet descriptor.
79357c478bd9Sstevel@tonic-gate 		 */
79367c478bd9Sstevel@tonic-gate 		isoc_pkt_descr++;
79377c478bd9Sstevel@tonic-gate 	}
79387c478bd9Sstevel@tonic-gate }
79397c478bd9Sstevel@tonic-gate 
79407c478bd9Sstevel@tonic-gate 
79417c478bd9Sstevel@tonic-gate /*
79427c478bd9Sstevel@tonic-gate  * ohci_check_for_error:
79437c478bd9Sstevel@tonic-gate  *
79447c478bd9Sstevel@tonic-gate  * Check for any errors.
79457c478bd9Sstevel@tonic-gate  */
79467c478bd9Sstevel@tonic-gate static usb_cr_t
79477c478bd9Sstevel@tonic-gate ohci_check_for_error(
79487c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
79497c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
79507c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
79517c478bd9Sstevel@tonic-gate 	ohci_td_t		*td,
79527c478bd9Sstevel@tonic-gate 	uint_t			ctrl)
79537c478bd9Sstevel@tonic-gate {
79547c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
79557c478bd9Sstevel@tonic-gate 	uchar_t			ep_attrs = ph->p_ep.bmAttributes;
79567c478bd9Sstevel@tonic-gate 	usb_cr_t		error = USB_CR_OK;
79577c478bd9Sstevel@tonic-gate 	usb_req_attrs_t		xfer_attrs;
79587c478bd9Sstevel@tonic-gate 
79597c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
79607c478bd9Sstevel@tonic-gate 	    "ohci_check_for_error: td = 0x%p ctrl = 0x%x",
79617c478bd9Sstevel@tonic-gate 	    td, ctrl);
79627c478bd9Sstevel@tonic-gate 
79637c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
79647c478bd9Sstevel@tonic-gate 
79657c478bd9Sstevel@tonic-gate 	switch (ctrl) {
79667c478bd9Sstevel@tonic-gate 	case HC_TD_CC_NO_E:
79677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
79687c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: No Error");
79697c478bd9Sstevel@tonic-gate 		error = USB_CR_OK;
79707c478bd9Sstevel@tonic-gate 		break;
79717c478bd9Sstevel@tonic-gate 	case HC_TD_CC_CRC:
79727c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
79737c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: CRC error");
79747c478bd9Sstevel@tonic-gate 		error = USB_CR_CRC;
79757c478bd9Sstevel@tonic-gate 		break;
79767c478bd9Sstevel@tonic-gate 	case HC_TD_CC_BS:
79777c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
79787c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: Bit stuffing");
79797c478bd9Sstevel@tonic-gate 		error = USB_CR_BITSTUFFING;
79807c478bd9Sstevel@tonic-gate 		break;
79817c478bd9Sstevel@tonic-gate 	case HC_TD_CC_DTM:
79827c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
79837c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: Data Toggle Mismatch");
79847c478bd9Sstevel@tonic-gate 		error = USB_CR_DATA_TOGGLE_MM;
79857c478bd9Sstevel@tonic-gate 		break;
79867c478bd9Sstevel@tonic-gate 	case HC_TD_CC_STALL:
79877c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
79887c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: Stall");
79897c478bd9Sstevel@tonic-gate 		error = USB_CR_STALL;
79907c478bd9Sstevel@tonic-gate 		break;
79917c478bd9Sstevel@tonic-gate 	case HC_TD_CC_DNR:
79927c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
79937c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: Device not responding");
79947c478bd9Sstevel@tonic-gate 		error = USB_CR_DEV_NOT_RESP;
79957c478bd9Sstevel@tonic-gate 		break;
79967c478bd9Sstevel@tonic-gate 	case HC_TD_CC_PCF:
79977c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
79987c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: PID check failure");
79997c478bd9Sstevel@tonic-gate 		error = USB_CR_PID_CHECKFAILURE;
80007c478bd9Sstevel@tonic-gate 		break;
80017c478bd9Sstevel@tonic-gate 	case HC_TD_CC_UPID:
80027c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
80037c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: Unexpected PID");
80047c478bd9Sstevel@tonic-gate 		error = USB_CR_UNEXP_PID;
80057c478bd9Sstevel@tonic-gate 		break;
80067c478bd9Sstevel@tonic-gate 	case HC_TD_CC_DO:
80077c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
80087c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: Data overrrun");
80097c478bd9Sstevel@tonic-gate 		error = USB_CR_DATA_OVERRUN;
80107c478bd9Sstevel@tonic-gate 		break;
80117c478bd9Sstevel@tonic-gate 	case HC_TD_CC_DU:
80127c478bd9Sstevel@tonic-gate 		/*
80137c478bd9Sstevel@tonic-gate 		 * Check whether short packets are acceptable.
80147c478bd9Sstevel@tonic-gate 		 * If so don't report error to client drivers
80157c478bd9Sstevel@tonic-gate 		 * and restart the endpoint. Otherwise report
80167c478bd9Sstevel@tonic-gate 		 * data underrun error to client driver.
80177c478bd9Sstevel@tonic-gate 		 */
80187c478bd9Sstevel@tonic-gate 		xfer_attrs = ohci_get_xfer_attrs(ohcip, pp, tw);
80197c478bd9Sstevel@tonic-gate 
80207c478bd9Sstevel@tonic-gate 		if (xfer_attrs & USB_ATTRS_SHORT_XFER_OK) {
80217c478bd9Sstevel@tonic-gate 			error = USB_CR_OK;
80227c478bd9Sstevel@tonic-gate 			if ((ep_attrs & USB_EP_ATTR_MASK) !=
80237c478bd9Sstevel@tonic-gate 			    USB_EP_ATTR_ISOCH) {
80247c478bd9Sstevel@tonic-gate 				/*
80257c478bd9Sstevel@tonic-gate 				 * Cleanup the remaining resources that may have
80267c478bd9Sstevel@tonic-gate 				 * been allocated for this transfer.
80277c478bd9Sstevel@tonic-gate 				 */
80287c478bd9Sstevel@tonic-gate 				if (ohci_cleanup_data_underrun(ohcip, pp, tw,
80297c478bd9Sstevel@tonic-gate 				    td) == USB_SUCCESS) {
80307c478bd9Sstevel@tonic-gate 					/* Clear the halt bit */
80317c478bd9Sstevel@tonic-gate 					Set_ED(pp->pp_ept->hced_headp,
80327c478bd9Sstevel@tonic-gate 					    (Get_ED(pp->pp_ept->hced_headp) &
80337c478bd9Sstevel@tonic-gate 						~HC_EPT_Halt));
80347c478bd9Sstevel@tonic-gate 				} else {
80357c478bd9Sstevel@tonic-gate 					error = USB_CR_UNSPECIFIED_ERR;
80367c478bd9Sstevel@tonic-gate 				}
80377c478bd9Sstevel@tonic-gate 			}
80387c478bd9Sstevel@tonic-gate 		} else {
80397c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
80407c478bd9Sstevel@tonic-gate 			    "ohci_check_for_error: Data underrun");
80417c478bd9Sstevel@tonic-gate 
80427c478bd9Sstevel@tonic-gate 			error = USB_CR_DATA_UNDERRUN;
80437c478bd9Sstevel@tonic-gate 		}
80447c478bd9Sstevel@tonic-gate 
80457c478bd9Sstevel@tonic-gate 		break;
80467c478bd9Sstevel@tonic-gate 	case HC_TD_CC_BO:
80477c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
80487c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: Buffer overrun");
80497c478bd9Sstevel@tonic-gate 		error = USB_CR_BUFFER_OVERRUN;
80507c478bd9Sstevel@tonic-gate 		break;
80517c478bd9Sstevel@tonic-gate 	case HC_TD_CC_BU:
80527c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
80537c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: Buffer underrun");
80547c478bd9Sstevel@tonic-gate 		error = USB_CR_BUFFER_UNDERRUN;
80557c478bd9Sstevel@tonic-gate 		break;
80567c478bd9Sstevel@tonic-gate 	case HC_TD_CC_NA:
80577c478bd9Sstevel@tonic-gate 	default:
80587c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
80597c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: Not accessed");
80607c478bd9Sstevel@tonic-gate 		error = USB_CR_NOT_ACCESSED;
80617c478bd9Sstevel@tonic-gate 		break;
80627c478bd9Sstevel@tonic-gate 	}
80637c478bd9Sstevel@tonic-gate 
80647c478bd9Sstevel@tonic-gate 	if (error) {
80657c478bd9Sstevel@tonic-gate 		uint_t hced_ctrl =  Get_ED(pp->pp_ept->hced_ctrl);
80667c478bd9Sstevel@tonic-gate 
80677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
80687c478bd9Sstevel@tonic-gate 		    "ohci_check_for_error: Error %d Device address %d "
80697c478bd9Sstevel@tonic-gate 		    "Endpoint number %d", error, (hced_ctrl & HC_EPT_FUNC),
80707c478bd9Sstevel@tonic-gate 		    ((hced_ctrl & HC_EPT_EP) >> HC_EPT_EP_SHFT));
80717c478bd9Sstevel@tonic-gate 	}
80727c478bd9Sstevel@tonic-gate 
80737c478bd9Sstevel@tonic-gate 	return (error);
80747c478bd9Sstevel@tonic-gate }
80757c478bd9Sstevel@tonic-gate 
80767c478bd9Sstevel@tonic-gate 
80777c478bd9Sstevel@tonic-gate /*
80787c478bd9Sstevel@tonic-gate  * ohci_handle_error:
80797c478bd9Sstevel@tonic-gate  *
80807c478bd9Sstevel@tonic-gate  * Inform USBA about occured transaction errors by calling the USBA callback
80817c478bd9Sstevel@tonic-gate  * routine.
80827c478bd9Sstevel@tonic-gate  */
80837c478bd9Sstevel@tonic-gate static void
80847c478bd9Sstevel@tonic-gate ohci_handle_error(
80857c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
80867c478bd9Sstevel@tonic-gate 	ohci_td_t		*td,
80877c478bd9Sstevel@tonic-gate 	usb_cr_t		error)
80887c478bd9Sstevel@tonic-gate {
80897c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
80907c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
80917c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;
80927c478bd9Sstevel@tonic-gate 	mblk_t			*mp = NULL;
80937c478bd9Sstevel@tonic-gate 	size_t			length = 0;
80947c478bd9Sstevel@tonic-gate 	uchar_t			attributes;
80957c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
80967c478bd9Sstevel@tonic-gate 
80977c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
80987c478bd9Sstevel@tonic-gate 	    "ohci_handle_error: error = 0x%x", error);
80997c478bd9Sstevel@tonic-gate 
81007c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
81017c478bd9Sstevel@tonic-gate 
81027c478bd9Sstevel@tonic-gate 	ASSERT(td != NULL);
81037c478bd9Sstevel@tonic-gate 
81047c478bd9Sstevel@tonic-gate 	/* Print the values in the td */
81057c478bd9Sstevel@tonic-gate 	ohci_print_td(ohcip, td);
81067c478bd9Sstevel@tonic-gate 
81077c478bd9Sstevel@tonic-gate 	/* Obtain the transfer wrapper from the TD */
81087c478bd9Sstevel@tonic-gate 	tw = (ohci_trans_wrapper_t *)
81097c478bd9Sstevel@tonic-gate 	    OHCI_LOOKUP_ID((uint32_t)Get_TD(td->hctd_trans_wrapper));
81107c478bd9Sstevel@tonic-gate 
81117c478bd9Sstevel@tonic-gate 	ASSERT(tw != NULL);
81127c478bd9Sstevel@tonic-gate 
81137c478bd9Sstevel@tonic-gate 	/* Obtain the pipe private structure */
81147c478bd9Sstevel@tonic-gate 	pp = tw->tw_pipe_private;
81157c478bd9Sstevel@tonic-gate 
81167c478bd9Sstevel@tonic-gate 	ph = tw->tw_pipe_private->pp_pipe_handle;
81177c478bd9Sstevel@tonic-gate 	attributes = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
81187c478bd9Sstevel@tonic-gate 
81197c478bd9Sstevel@tonic-gate 	/*
81207c478bd9Sstevel@tonic-gate 	 * Special error handling
81217c478bd9Sstevel@tonic-gate 	 */
81227c478bd9Sstevel@tonic-gate 	if (tw->tw_direction == HC_TD_IN) {
81237c478bd9Sstevel@tonic-gate 
81247c478bd9Sstevel@tonic-gate 		switch (attributes) {
81257c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
81267c478bd9Sstevel@tonic-gate 			if (((ph->p_ep.bmAttributes &
81277c478bd9Sstevel@tonic-gate 			    USB_EP_ATTR_MASK) ==
81287c478bd9Sstevel@tonic-gate 			    USB_EP_ATTR_CONTROL) &&
81297c478bd9Sstevel@tonic-gate 			    (Get_TD(td->hctd_ctrl_phase) ==
81307c478bd9Sstevel@tonic-gate 			    OHCI_CTRL_SETUP_PHASE)) {
81317c478bd9Sstevel@tonic-gate 
81327c478bd9Sstevel@tonic-gate 				break;
81337c478bd9Sstevel@tonic-gate 			}
81347c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
81357c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
81367c478bd9Sstevel@tonic-gate 			/*
81377c478bd9Sstevel@tonic-gate 			 * Call ohci_sendup_td_message
81387c478bd9Sstevel@tonic-gate 			 * to send message to upstream.
81397c478bd9Sstevel@tonic-gate 			 */
81407c478bd9Sstevel@tonic-gate 			ohci_sendup_td_message(ohcip, pp, tw, td, error);
81417c478bd9Sstevel@tonic-gate 
81427c478bd9Sstevel@tonic-gate 			return;
81437c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
81447c478bd9Sstevel@tonic-gate 			curr_intr_reqp =
81457c478bd9Sstevel@tonic-gate 			    (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
81467c478bd9Sstevel@tonic-gate 
81477c478bd9Sstevel@tonic-gate 			if (curr_intr_reqp->intr_attributes &
81487c478bd9Sstevel@tonic-gate 			    USB_ATTRS_ONE_XFER) {
81497c478bd9Sstevel@tonic-gate 
81507c478bd9Sstevel@tonic-gate 				ohci_handle_one_xfer_completion(ohcip, tw);
81517c478bd9Sstevel@tonic-gate 			}
81527c478bd9Sstevel@tonic-gate 
81537c478bd9Sstevel@tonic-gate 			/* Decrement periodic in request count */
81547c478bd9Sstevel@tonic-gate 			pp->pp_cur_periodic_req_cnt--;
81557c478bd9Sstevel@tonic-gate 			break;
81567c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
81577c478bd9Sstevel@tonic-gate 		default:
81587c478bd9Sstevel@tonic-gate 			break;
81597c478bd9Sstevel@tonic-gate 		}
81607c478bd9Sstevel@tonic-gate 	} else {
81617c478bd9Sstevel@tonic-gate 		switch (attributes) {
81627c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
81637c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
81647c478bd9Sstevel@tonic-gate 			/*
81657c478bd9Sstevel@tonic-gate 			 * If "CurrentBufferPointer" of Transfer
81667c478bd9Sstevel@tonic-gate 			 * Descriptor (TD) is not equal to zero,
81677c478bd9Sstevel@tonic-gate 			 * then we sent less data  to the device
81687c478bd9Sstevel@tonic-gate 			 * than requested by client. In that case,
81697c478bd9Sstevel@tonic-gate 			 * return the mblk after updating the
81707c478bd9Sstevel@tonic-gate 			 * data->r_ptr.
81717c478bd9Sstevel@tonic-gate 			 */
81727c478bd9Sstevel@tonic-gate 			if (Get_TD(td->hctd_cbp)) {
81737c478bd9Sstevel@tonic-gate 				usb_opaque_t xfer_reqp = tw->tw_curr_xfer_reqp;
81747c478bd9Sstevel@tonic-gate 
81757c478bd9Sstevel@tonic-gate 				length = Get_TD(td->hctd_cbp) -
81767c478bd9Sstevel@tonic-gate 				    tw->tw_cookie.dmac_address;
81777c478bd9Sstevel@tonic-gate 
81787c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_INTR,
81797c478bd9Sstevel@tonic-gate 				    ohcip->ohci_log_hdl,
81807c478bd9Sstevel@tonic-gate 				    "ohci_handle_error: requested data %lu "
81817c478bd9Sstevel@tonic-gate 				    "sent data %lu", tw->tw_length, length);
81827c478bd9Sstevel@tonic-gate 
81837c478bd9Sstevel@tonic-gate 				if (attributes == USB_EP_ATTR_BULK) {
81847c478bd9Sstevel@tonic-gate 					mp = (mblk_t *)((usb_bulk_req_t *)
81857c478bd9Sstevel@tonic-gate 					    (xfer_reqp))->bulk_data;
81867c478bd9Sstevel@tonic-gate 				} else {
81877c478bd9Sstevel@tonic-gate 					mp = (mblk_t *)((usb_intr_req_t *)
81887c478bd9Sstevel@tonic-gate 					    (xfer_reqp))->intr_data;
81897c478bd9Sstevel@tonic-gate 				}
81907c478bd9Sstevel@tonic-gate 
81917c478bd9Sstevel@tonic-gate 				/* Increment the read pointer */
81927c478bd9Sstevel@tonic-gate 				mp->b_rptr = mp->b_rptr + length;
81937c478bd9Sstevel@tonic-gate 			}
81947c478bd9Sstevel@tonic-gate 			break;
81957c478bd9Sstevel@tonic-gate 		default:
81967c478bd9Sstevel@tonic-gate 			break;
81977c478bd9Sstevel@tonic-gate 		}
81987c478bd9Sstevel@tonic-gate 	}
81997c478bd9Sstevel@tonic-gate 
82007c478bd9Sstevel@tonic-gate 	/*
82017c478bd9Sstevel@tonic-gate 	 * Callback the client with the
82027c478bd9Sstevel@tonic-gate 	 * failure reason.
82037c478bd9Sstevel@tonic-gate 	 */
82047c478bd9Sstevel@tonic-gate 	ohci_hcdi_callback(ph, tw, error);
82057c478bd9Sstevel@tonic-gate 
82067c478bd9Sstevel@tonic-gate 	/* Check anybody is waiting for transfers completion event */
82077c478bd9Sstevel@tonic-gate 	ohci_check_for_transfers_completion(ohcip, pp);
82087c478bd9Sstevel@tonic-gate }
82097c478bd9Sstevel@tonic-gate 
82107c478bd9Sstevel@tonic-gate /*
82117c478bd9Sstevel@tonic-gate  * ohci_cleanup_data_underrun:
82127c478bd9Sstevel@tonic-gate  *
82137c478bd9Sstevel@tonic-gate  * Cleans up resources when a short xfer occurs
82147c478bd9Sstevel@tonic-gate  */
82157c478bd9Sstevel@tonic-gate static int
82167c478bd9Sstevel@tonic-gate ohci_cleanup_data_underrun(
82177c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
82187c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
82197c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
82207c478bd9Sstevel@tonic-gate 	ohci_td_t		*td)
82217c478bd9Sstevel@tonic-gate {
82227c478bd9Sstevel@tonic-gate 	ohci_td_t		*next_td;
82237c478bd9Sstevel@tonic-gate 	ohci_td_t		*last_td;
82247c478bd9Sstevel@tonic-gate 	ohci_td_t		*temp_td;
82257c478bd9Sstevel@tonic-gate 	uint32_t		last_td_addr;
82267c478bd9Sstevel@tonic-gate 	uint_t			hced_head;
82277c478bd9Sstevel@tonic-gate 
82287c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
82297c478bd9Sstevel@tonic-gate 	    "ohci_cleanup_data_underrun: td 0x%p, tw 0x%p", td, tw);
82307c478bd9Sstevel@tonic-gate 
82317c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
82327c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_hctd_head == td);
82337c478bd9Sstevel@tonic-gate 
82347c478bd9Sstevel@tonic-gate 	/* Check if this TD is the last td in the tw */
82357c478bd9Sstevel@tonic-gate 	last_td = tw->tw_hctd_tail;
82367c478bd9Sstevel@tonic-gate 	if (td == last_td) {
82377c478bd9Sstevel@tonic-gate 		/* There is no need for cleanup */
82387c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
82397c478bd9Sstevel@tonic-gate 	}
82407c478bd9Sstevel@tonic-gate 
82417c478bd9Sstevel@tonic-gate 	/*
82427c478bd9Sstevel@tonic-gate 	 * Make sure the ED is halted before we change any td's.
82437c478bd9Sstevel@tonic-gate 	 * If for some reason it is not halted, return error to client
82447c478bd9Sstevel@tonic-gate 	 * driver so they can reset the port.
82457c478bd9Sstevel@tonic-gate 	 */
82467c478bd9Sstevel@tonic-gate 	hced_head = Get_ED(pp->pp_ept->hced_headp);
82477c478bd9Sstevel@tonic-gate 	if (!(hced_head & HC_EPT_Halt)) {
82487c478bd9Sstevel@tonic-gate 		uint_t hced_ctrl = Get_ED(pp->pp_ept->hced_ctrl);
82497c478bd9Sstevel@tonic-gate 
82507c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
82517c478bd9Sstevel@tonic-gate 		    "ohci_cleanup_data_underrun: Unable to clean up a short "
82527c478bd9Sstevel@tonic-gate 		    "xfer error.  Client might send/receive irrelevant data."
82537c478bd9Sstevel@tonic-gate 		    " Device address %d Endpoint number %d",
82547c478bd9Sstevel@tonic-gate 		    (hced_ctrl & HC_EPT_FUNC),
82557c478bd9Sstevel@tonic-gate 		    ((hced_ctrl & HC_EPT_EP) >> HC_EPT_EP_SHFT));
82567c478bd9Sstevel@tonic-gate 
82577c478bd9Sstevel@tonic-gate 		Set_ED(pp->pp_ept->hced_headp, hced_head | HC_EPT_Halt);
82587c478bd9Sstevel@tonic-gate 
82597c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
82607c478bd9Sstevel@tonic-gate 	}
82617c478bd9Sstevel@tonic-gate 
82627c478bd9Sstevel@tonic-gate 	/*
82637c478bd9Sstevel@tonic-gate 	 * Get the address of the first td of the next transfer (tw).
82647c478bd9Sstevel@tonic-gate 	 * This td, may currently be a dummy td, but when a new request
82657c478bd9Sstevel@tonic-gate 	 * arrives, it will be transformed into a regular td.
82667c478bd9Sstevel@tonic-gate 	 */
82677c478bd9Sstevel@tonic-gate 	last_td_addr = Get_TD(last_td->hctd_next_td);
82687c478bd9Sstevel@tonic-gate 	/* Set ED head to this last td */
82697c478bd9Sstevel@tonic-gate 	Set_ED(pp->pp_ept->hced_headp,
82707c478bd9Sstevel@tonic-gate 	    (last_td_addr & HC_EPT_TD_HEAD) |
82717c478bd9Sstevel@tonic-gate 	    (hced_head & ~HC_EPT_TD_HEAD));
82727c478bd9Sstevel@tonic-gate 
82737c478bd9Sstevel@tonic-gate 	/*
82747c478bd9Sstevel@tonic-gate 	 * Start removing all the unused TD's from the TW,
82757c478bd9Sstevel@tonic-gate 	 * but keep the first one.
82767c478bd9Sstevel@tonic-gate 	 */
82777c478bd9Sstevel@tonic-gate 	tw->tw_hctd_tail = td;
82787c478bd9Sstevel@tonic-gate 
82797c478bd9Sstevel@tonic-gate 	/*
82807c478bd9Sstevel@tonic-gate 	 * Get the last_td, the next td in the tw list.
82817c478bd9Sstevel@tonic-gate 	 * Afterwards completely disassociate the current td from other tds
82827c478bd9Sstevel@tonic-gate 	 */
82837c478bd9Sstevel@tonic-gate 	next_td = (ohci_td_t *)ohci_td_iommu_to_cpu(ohcip,
82847c478bd9Sstevel@tonic-gate 	    Get_TD(td->hctd_tw_next_td));
82857c478bd9Sstevel@tonic-gate 	Set_TD(td->hctd_tw_next_td, NULL);
82867c478bd9Sstevel@tonic-gate 
82877c478bd9Sstevel@tonic-gate 	/*
82887c478bd9Sstevel@tonic-gate 	 * Iterate down the tw list and deallocate them
82897c478bd9Sstevel@tonic-gate 	 */
82907c478bd9Sstevel@tonic-gate 	while (next_td != NULL) {
82917c478bd9Sstevel@tonic-gate 		tw->tw_num_tds--;
82927c478bd9Sstevel@tonic-gate 		/* Disassociate this td from it's TW and set to RECLAIM */
82937c478bd9Sstevel@tonic-gate 		Set_TD(next_td->hctd_trans_wrapper, NULL);
82947c478bd9Sstevel@tonic-gate 		Set_TD(next_td->hctd_state, HC_TD_RECLAIM);
82957c478bd9Sstevel@tonic-gate 
82967c478bd9Sstevel@tonic-gate 		temp_td = next_td;
82977c478bd9Sstevel@tonic-gate 
82987c478bd9Sstevel@tonic-gate 		next_td = (ohci_td_t *)ohci_td_iommu_to_cpu(ohcip,
82997c478bd9Sstevel@tonic-gate 		    Get_TD(next_td->hctd_tw_next_td));
83007c478bd9Sstevel@tonic-gate 
83017c478bd9Sstevel@tonic-gate 		ohci_deallocate_td(ohcip, temp_td);
83027c478bd9Sstevel@tonic-gate 	}
83037c478bd9Sstevel@tonic-gate 
83047c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_num_tds == 1);
83057c478bd9Sstevel@tonic-gate 
83067c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
83077c478bd9Sstevel@tonic-gate }
83087c478bd9Sstevel@tonic-gate 
83097c478bd9Sstevel@tonic-gate /*
83107c478bd9Sstevel@tonic-gate  * ohci_handle_normal_td:
83117c478bd9Sstevel@tonic-gate  */
83127c478bd9Sstevel@tonic-gate static void
83137c478bd9Sstevel@tonic-gate ohci_handle_normal_td(
83147c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
83157c478bd9Sstevel@tonic-gate 	ohci_td_t		*td,
83167c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
83177c478bd9Sstevel@tonic-gate {
83187c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;	/* Pipe private field */
83197c478bd9Sstevel@tonic-gate 
83207c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
83217c478bd9Sstevel@tonic-gate 	    "ohci_handle_normal_td:");
83227c478bd9Sstevel@tonic-gate 
83237c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
83247c478bd9Sstevel@tonic-gate 	ASSERT(tw != NULL);
83257c478bd9Sstevel@tonic-gate 
83267c478bd9Sstevel@tonic-gate 	/* Obtain the pipe private structure */
83277c478bd9Sstevel@tonic-gate 	pp = tw->tw_pipe_private;
83287c478bd9Sstevel@tonic-gate 
83297c478bd9Sstevel@tonic-gate 	(*tw->tw_handle_td)(ohcip, pp, tw,
83307c478bd9Sstevel@tonic-gate 	    td, tw->tw_handle_callback_value);
83317c478bd9Sstevel@tonic-gate 
83327c478bd9Sstevel@tonic-gate 	/* Check anybody is waiting for transfers completion event */
83337c478bd9Sstevel@tonic-gate 	ohci_check_for_transfers_completion(ohcip, pp);
83347c478bd9Sstevel@tonic-gate }
83357c478bd9Sstevel@tonic-gate 
83367c478bd9Sstevel@tonic-gate 
83377c478bd9Sstevel@tonic-gate /*
83387c478bd9Sstevel@tonic-gate  * ohci_handle_ctrl_td:
83397c478bd9Sstevel@tonic-gate  *
83407c478bd9Sstevel@tonic-gate  * Handle a control Transfer Descriptor (TD).
83417c478bd9Sstevel@tonic-gate  */
83427c478bd9Sstevel@tonic-gate /* ARGSUSED */
83437c478bd9Sstevel@tonic-gate static void
83447c478bd9Sstevel@tonic-gate ohci_handle_ctrl_td(
83457c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
83467c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
83477c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
83487c478bd9Sstevel@tonic-gate 	ohci_td_t		*td,
83497c478bd9Sstevel@tonic-gate 	void			*tw_handle_callback_value)
83507c478bd9Sstevel@tonic-gate {
83517c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
83527c478bd9Sstevel@tonic-gate 
83537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
83547c478bd9Sstevel@tonic-gate 	    "ohci_handle_ctrl_td: pp = 0x%p tw = 0x%p td = 0x%p state = 0x%x",
83557c478bd9Sstevel@tonic-gate 	    (void *)pp, (void *)tw, (void *)td, Get_TD(td->hctd_ctrl_phase));
83567c478bd9Sstevel@tonic-gate 
83577c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
83587c478bd9Sstevel@tonic-gate 
83597c478bd9Sstevel@tonic-gate 	/*
83607c478bd9Sstevel@tonic-gate 	 * Check which control transfer phase got completed.
83617c478bd9Sstevel@tonic-gate 	 */
83627c478bd9Sstevel@tonic-gate 	tw->tw_num_tds--;
83637c478bd9Sstevel@tonic-gate 	switch (Get_TD(td->hctd_ctrl_phase)) {
83647c478bd9Sstevel@tonic-gate 	case OHCI_CTRL_SETUP_PHASE:
83657c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
83667c478bd9Sstevel@tonic-gate 		    "Setup complete: pp 0x%p td 0x%p", (void *)pp, (void *)td);
83677c478bd9Sstevel@tonic-gate 
83687c478bd9Sstevel@tonic-gate 		break;
83697c478bd9Sstevel@tonic-gate 	case OHCI_CTRL_DATA_PHASE:
83707c478bd9Sstevel@tonic-gate 		/*
83717c478bd9Sstevel@tonic-gate 		 * If "CurrentBufferPointer" of Transfer Descriptor (TD)
83727c478bd9Sstevel@tonic-gate 		 * is not equal to zero, then we received less data from
83737c478bd9Sstevel@tonic-gate 		 * the device than requested by us. In that case, get the
83747c478bd9Sstevel@tonic-gate 		 * actual received data size.
83757c478bd9Sstevel@tonic-gate 		 */
83767c478bd9Sstevel@tonic-gate 		if (Get_TD(td->hctd_cbp)) {
83777c478bd9Sstevel@tonic-gate 			size_t			length;
83787c478bd9Sstevel@tonic-gate 
83797c478bd9Sstevel@tonic-gate 			length = Get_TD(td->hctd_cbp) -
83807c478bd9Sstevel@tonic-gate 			    tw->tw_cookie.dmac_address;
83817c478bd9Sstevel@tonic-gate 
83827c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
83837c478bd9Sstevel@tonic-gate 			    "ohci_handle_ctrl_qtd: requested data %lu "
83847c478bd9Sstevel@tonic-gate 			    "received data %lu", tw->tw_length, length);
83857c478bd9Sstevel@tonic-gate 
83867c478bd9Sstevel@tonic-gate 			/* Save actual received data length */
83877c478bd9Sstevel@tonic-gate 			tw->tw_length = length;
83887c478bd9Sstevel@tonic-gate 		}
83897c478bd9Sstevel@tonic-gate 
83907c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
83917c478bd9Sstevel@tonic-gate 		    "Data complete: pp 0x%p td 0x%p",
83927c478bd9Sstevel@tonic-gate 		    (void *)pp, (void *)td);
83937c478bd9Sstevel@tonic-gate 
83947c478bd9Sstevel@tonic-gate 		break;
83957c478bd9Sstevel@tonic-gate 	case OHCI_CTRL_STATUS_PHASE:
83967c478bd9Sstevel@tonic-gate 		if ((tw->tw_length != 0) &&
83977c478bd9Sstevel@tonic-gate 		    (tw->tw_direction == HC_TD_IN)) {
83987c478bd9Sstevel@tonic-gate 
83997c478bd9Sstevel@tonic-gate 			/*
84007c478bd9Sstevel@tonic-gate 			 * Call ohci_sendup_td_message
84017c478bd9Sstevel@tonic-gate 			 * to send message to upstream.
84027c478bd9Sstevel@tonic-gate 			 */
84037c478bd9Sstevel@tonic-gate 			ohci_sendup_td_message(ohcip,
84047c478bd9Sstevel@tonic-gate 			    pp, tw, td, USB_CR_OK);
84057c478bd9Sstevel@tonic-gate 		} else {
84067c478bd9Sstevel@tonic-gate 			ohci_do_byte_stats(ohcip,
84077c478bd9Sstevel@tonic-gate 			    tw->tw_length - SETUP_SIZE,
84087c478bd9Sstevel@tonic-gate 			    ph->p_ep.bmAttributes,
84097c478bd9Sstevel@tonic-gate 			    ph->p_ep.bEndpointAddress);
84107c478bd9Sstevel@tonic-gate 
84117c478bd9Sstevel@tonic-gate 			ohci_hcdi_callback(ph, tw, USB_CR_OK);
84127c478bd9Sstevel@tonic-gate 		}
84137c478bd9Sstevel@tonic-gate 
84147c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
84157c478bd9Sstevel@tonic-gate 		    "Status complete: pp 0x%p td 0x%p", (void *)pp, (void *)td);
84167c478bd9Sstevel@tonic-gate 
84177c478bd9Sstevel@tonic-gate 		break;
84187c478bd9Sstevel@tonic-gate 	}
84197c478bd9Sstevel@tonic-gate }
84207c478bd9Sstevel@tonic-gate 
84217c478bd9Sstevel@tonic-gate 
84227c478bd9Sstevel@tonic-gate /*
84237c478bd9Sstevel@tonic-gate  * ohci_handle_bulk_td:
84247c478bd9Sstevel@tonic-gate  *
84257c478bd9Sstevel@tonic-gate  * Handle a bulk Transfer Descriptor (TD).
84267c478bd9Sstevel@tonic-gate  */
84277c478bd9Sstevel@tonic-gate /* ARGSUSED */
84287c478bd9Sstevel@tonic-gate static void
84297c478bd9Sstevel@tonic-gate ohci_handle_bulk_td(
84307c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
84317c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
84327c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
84337c478bd9Sstevel@tonic-gate 	ohci_td_t		*td,
84347c478bd9Sstevel@tonic-gate 	void			*tw_handle_callback_value)
84357c478bd9Sstevel@tonic-gate {
84367c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
84377c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
84387c478bd9Sstevel@tonic-gate 
84397c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
84407c478bd9Sstevel@tonic-gate 	    "ohci_handle_bulk_td:");
84417c478bd9Sstevel@tonic-gate 
84427c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
84437c478bd9Sstevel@tonic-gate 
84447c478bd9Sstevel@tonic-gate 	/*
84457c478bd9Sstevel@tonic-gate 	 * Decrement the TDs counter and check whether all the bulk
84467c478bd9Sstevel@tonic-gate 	 * data has been send or received. If TDs counter reaches
84477c478bd9Sstevel@tonic-gate 	 * zero then inform client driver about completion current
84487c478bd9Sstevel@tonic-gate 	 * bulk request. Other wise wait for completion of other bulk
84497c478bd9Sstevel@tonic-gate 	 * TDs or transactions on this pipe.
84507c478bd9Sstevel@tonic-gate 	 */
84517c478bd9Sstevel@tonic-gate 	if (--tw->tw_num_tds != 0) {
84527c478bd9Sstevel@tonic-gate 
84537c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
84547c478bd9Sstevel@tonic-gate 		    "ohci_handle_bulk_td: Number of TDs %d", tw->tw_num_tds);
84557c478bd9Sstevel@tonic-gate 
84567c478bd9Sstevel@tonic-gate 		return;
84577c478bd9Sstevel@tonic-gate 	}
84587c478bd9Sstevel@tonic-gate 
84597c478bd9Sstevel@tonic-gate 	/*
84607c478bd9Sstevel@tonic-gate 	 * If this is a bulk in pipe, return the data to the client.
84617c478bd9Sstevel@tonic-gate 	 * For a bulk out pipe, there is no need to do anything.
84627c478bd9Sstevel@tonic-gate 	 */
84637c478bd9Sstevel@tonic-gate 	if ((eptd->bEndpointAddress &
84647c478bd9Sstevel@tonic-gate 	    USB_EP_DIR_MASK) == USB_EP_DIR_OUT) {
84657c478bd9Sstevel@tonic-gate 
84667c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
84677c478bd9Sstevel@tonic-gate 		    "ohci_handle_bulk_td: Bulk out pipe");
84687c478bd9Sstevel@tonic-gate 
84697c478bd9Sstevel@tonic-gate 		ohci_do_byte_stats(ohcip, tw->tw_length,
84707c478bd9Sstevel@tonic-gate 		    eptd->bmAttributes, eptd->bEndpointAddress);
84717c478bd9Sstevel@tonic-gate 
84727c478bd9Sstevel@tonic-gate 		/* Do the callback */
84737c478bd9Sstevel@tonic-gate 		ohci_hcdi_callback(ph, tw, USB_CR_OK);
84747c478bd9Sstevel@tonic-gate 
84757c478bd9Sstevel@tonic-gate 		return;
84767c478bd9Sstevel@tonic-gate 	}
84777c478bd9Sstevel@tonic-gate 
84787c478bd9Sstevel@tonic-gate 	/* Call ohci_sendup_td_message to send message to upstream */
84797c478bd9Sstevel@tonic-gate 	ohci_sendup_td_message(ohcip, pp, tw, td, USB_CR_OK);
84807c478bd9Sstevel@tonic-gate }
84817c478bd9Sstevel@tonic-gate 
84827c478bd9Sstevel@tonic-gate 
84837c478bd9Sstevel@tonic-gate /*
84847c478bd9Sstevel@tonic-gate  * ohci_handle_intr_td:
84857c478bd9Sstevel@tonic-gate  *
84867c478bd9Sstevel@tonic-gate  * Handle a interrupt Transfer Descriptor (TD).
84877c478bd9Sstevel@tonic-gate  */
84887c478bd9Sstevel@tonic-gate /* ARGSUSED */
84897c478bd9Sstevel@tonic-gate static void
84907c478bd9Sstevel@tonic-gate ohci_handle_intr_td(
84917c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
84927c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
84937c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
84947c478bd9Sstevel@tonic-gate 	ohci_td_t		*td,
84957c478bd9Sstevel@tonic-gate 	void			*tw_handle_callback_value)
84967c478bd9Sstevel@tonic-gate {
84977c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp =
84987c478bd9Sstevel@tonic-gate 				    (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
84997c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
85007c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
85017c478bd9Sstevel@tonic-gate 	usb_req_attrs_t		attrs;
85027c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
85037c478bd9Sstevel@tonic-gate 
85047c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
85057c478bd9Sstevel@tonic-gate 	    "ohci_handle_intr_td: pp=0x%p tw=0x%p td=0x%p"
85067c478bd9Sstevel@tonic-gate 	    "intr_reqp=0%p data=0x%p", pp, tw, td, curr_intr_reqp,
85077c478bd9Sstevel@tonic-gate 	    curr_intr_reqp->intr_data);
85087c478bd9Sstevel@tonic-gate 
85097c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
85107c478bd9Sstevel@tonic-gate 
85117c478bd9Sstevel@tonic-gate 	/* Get the interrupt xfer attributes */
85127c478bd9Sstevel@tonic-gate 	attrs = curr_intr_reqp->intr_attributes;
85137c478bd9Sstevel@tonic-gate 
85147c478bd9Sstevel@tonic-gate 	/*
85157c478bd9Sstevel@tonic-gate 	 * For a Interrupt OUT pipe, we just callback and we are done
85167c478bd9Sstevel@tonic-gate 	 */
85177c478bd9Sstevel@tonic-gate 	if ((eptd->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_OUT) {
85187c478bd9Sstevel@tonic-gate 
85197c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
85207c478bd9Sstevel@tonic-gate 		    "ohci_handle_intr_td: Intr out pipe, intr_reqp=0x%p,"
85217c478bd9Sstevel@tonic-gate 		    "data=0x%p", curr_intr_reqp, curr_intr_reqp->intr_data);
85227c478bd9Sstevel@tonic-gate 
85237c478bd9Sstevel@tonic-gate 		ohci_do_byte_stats(ohcip, tw->tw_length,
85247c478bd9Sstevel@tonic-gate 		    eptd->bmAttributes, eptd->bEndpointAddress);
85257c478bd9Sstevel@tonic-gate 
85267c478bd9Sstevel@tonic-gate 		/* Do the callback */
85277c478bd9Sstevel@tonic-gate 		ohci_hcdi_callback(ph, tw, USB_CR_OK);
85287c478bd9Sstevel@tonic-gate 
85297c478bd9Sstevel@tonic-gate 		return;
85307c478bd9Sstevel@tonic-gate 	}
85317c478bd9Sstevel@tonic-gate 
85327c478bd9Sstevel@tonic-gate 	/* Decrement number of interrupt request count */
85337c478bd9Sstevel@tonic-gate 	pp->pp_cur_periodic_req_cnt--;
85347c478bd9Sstevel@tonic-gate 
85357c478bd9Sstevel@tonic-gate 	/*
85367c478bd9Sstevel@tonic-gate 	 * Check usb flag whether USB_FLAGS_ONE_XFER flag is set
85377c478bd9Sstevel@tonic-gate 	 * and if so, free duplicate request.
85387c478bd9Sstevel@tonic-gate 	 */
85397c478bd9Sstevel@tonic-gate 	if (attrs & USB_ATTRS_ONE_XFER) {
85407c478bd9Sstevel@tonic-gate 		ohci_handle_one_xfer_completion(ohcip, tw);
85417c478bd9Sstevel@tonic-gate 	}
85427c478bd9Sstevel@tonic-gate 
85437c478bd9Sstevel@tonic-gate 	/* Call ohci_sendup_td_message to callback into client */
85447c478bd9Sstevel@tonic-gate 	ohci_sendup_td_message(ohcip, pp, tw, td, USB_CR_OK);
85457c478bd9Sstevel@tonic-gate 
85467c478bd9Sstevel@tonic-gate 	/*
85477c478bd9Sstevel@tonic-gate 	 * If interrupt pipe state is still active, insert next Interrupt
85487c478bd9Sstevel@tonic-gate 	 * request into the Host Controller's Interrupt list.  Otherwise
85497c478bd9Sstevel@tonic-gate 	 * you are done.
85507c478bd9Sstevel@tonic-gate 	 */
85517c478bd9Sstevel@tonic-gate 	if (pp->pp_state != OHCI_PIPE_STATE_ACTIVE) {
85527c478bd9Sstevel@tonic-gate 		return;
85537c478bd9Sstevel@tonic-gate 	}
85547c478bd9Sstevel@tonic-gate 
85557c478bd9Sstevel@tonic-gate 	if ((error = ohci_allocate_periodic_in_resource(ohcip, pp, tw, 0)) ==
85567c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
85577c478bd9Sstevel@tonic-gate 		curr_intr_reqp = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
85587c478bd9Sstevel@tonic-gate 
85597c478bd9Sstevel@tonic-gate 		ASSERT(curr_intr_reqp != NULL);
85607c478bd9Sstevel@tonic-gate 
85617c478bd9Sstevel@tonic-gate 		tw->tw_num_tds = 1;
85627c478bd9Sstevel@tonic-gate 
85637c478bd9Sstevel@tonic-gate 		if (ohci_allocate_tds_for_tw(ohcip, tw, tw->tw_num_tds) !=
85647c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
85657c478bd9Sstevel@tonic-gate 			ohci_deallocate_periodic_in_resource(ohcip, pp, tw);
85667c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
85677c478bd9Sstevel@tonic-gate 		}
85687c478bd9Sstevel@tonic-gate 	}
85697c478bd9Sstevel@tonic-gate 
85707c478bd9Sstevel@tonic-gate 	if (error != USB_SUCCESS) {
85717c478bd9Sstevel@tonic-gate 		/*
85727c478bd9Sstevel@tonic-gate 		 * Set pipe state to stop polling and error to no
85737c478bd9Sstevel@tonic-gate 		 * resource. Don't insert any more interrupt polling
85747c478bd9Sstevel@tonic-gate 		 * requests.
85757c478bd9Sstevel@tonic-gate 		 */
85767c478bd9Sstevel@tonic-gate 		pp->pp_state = OHCI_PIPE_STATE_STOP_POLLING;
85777c478bd9Sstevel@tonic-gate 		pp->pp_error = USB_CR_NO_RESOURCES;
85787c478bd9Sstevel@tonic-gate 	} else {
85797c478bd9Sstevel@tonic-gate 		ohci_insert_intr_req(ohcip, pp, tw, 0);
85807c478bd9Sstevel@tonic-gate 
85817c478bd9Sstevel@tonic-gate 		/* Increment number of interrupt request count */
85827c478bd9Sstevel@tonic-gate 		pp->pp_cur_periodic_req_cnt++;
85837c478bd9Sstevel@tonic-gate 
85847c478bd9Sstevel@tonic-gate 		ASSERT(pp->pp_cur_periodic_req_cnt ==
85857c478bd9Sstevel@tonic-gate 		    pp->pp_max_periodic_req_cnt);
85867c478bd9Sstevel@tonic-gate 	}
85877c478bd9Sstevel@tonic-gate }
85887c478bd9Sstevel@tonic-gate 
85897c478bd9Sstevel@tonic-gate 
85907c478bd9Sstevel@tonic-gate /*
85917c478bd9Sstevel@tonic-gate  * ohci_handle_one_xfer_completion:
85927c478bd9Sstevel@tonic-gate  */
85937c478bd9Sstevel@tonic-gate static void
85947c478bd9Sstevel@tonic-gate ohci_handle_one_xfer_completion(
85957c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
85967c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
85977c478bd9Sstevel@tonic-gate {
85987c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = tw->tw_pipe_private->pp_pipe_handle;
85997c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = tw->tw_pipe_private;
86007c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp =
86017c478bd9Sstevel@tonic-gate 				    (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
86027c478bd9Sstevel@tonic-gate 
86037c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
86047c478bd9Sstevel@tonic-gate 	    "ohci_handle_one_xfer_completion: tw = 0x%p", tw);
86057c478bd9Sstevel@tonic-gate 
86067c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
86077c478bd9Sstevel@tonic-gate 	ASSERT(curr_intr_reqp->intr_attributes & USB_ATTRS_ONE_XFER);
86087c478bd9Sstevel@tonic-gate 
86097c478bd9Sstevel@tonic-gate 	pp->pp_state = OHCI_PIPE_STATE_IDLE;
86107c478bd9Sstevel@tonic-gate 
86117c478bd9Sstevel@tonic-gate 	/*
86127c478bd9Sstevel@tonic-gate 	 * For one xfer, we need to copy back data ptr
86137c478bd9Sstevel@tonic-gate 	 * and free current request
86147c478bd9Sstevel@tonic-gate 	 */
86157c478bd9Sstevel@tonic-gate 	((usb_intr_req_t *)(pp->pp_client_periodic_in_reqp))->
86167c478bd9Sstevel@tonic-gate 	    intr_data = ((usb_intr_req_t *)
86177c478bd9Sstevel@tonic-gate 	    (tw->tw_curr_xfer_reqp))->intr_data;
86187c478bd9Sstevel@tonic-gate 
86197c478bd9Sstevel@tonic-gate 	((usb_intr_req_t *)tw->tw_curr_xfer_reqp)->intr_data = NULL;
86207c478bd9Sstevel@tonic-gate 
86217c478bd9Sstevel@tonic-gate 	/* Now free duplicate current request */
86227c478bd9Sstevel@tonic-gate 	usb_free_intr_req((usb_intr_req_t *)tw-> tw_curr_xfer_reqp);
86237c478bd9Sstevel@tonic-gate 
86247c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
86257c478bd9Sstevel@tonic-gate 	ph->p_req_count--;
86267c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
86277c478bd9Sstevel@tonic-gate 
86287c478bd9Sstevel@tonic-gate 	/* Make client's request the current request */
86297c478bd9Sstevel@tonic-gate 	tw->tw_curr_xfer_reqp = pp->pp_client_periodic_in_reqp;
86307c478bd9Sstevel@tonic-gate 	pp->pp_client_periodic_in_reqp = NULL;
86317c478bd9Sstevel@tonic-gate }
86327c478bd9Sstevel@tonic-gate 
86337c478bd9Sstevel@tonic-gate 
86347c478bd9Sstevel@tonic-gate /*
86357c478bd9Sstevel@tonic-gate  * ohci_handle_isoc_td:
86367c478bd9Sstevel@tonic-gate  *
86377c478bd9Sstevel@tonic-gate  * Handle an isochronous Transfer Descriptor (TD).
86387c478bd9Sstevel@tonic-gate  */
86397c478bd9Sstevel@tonic-gate /* ARGSUSED */
86407c478bd9Sstevel@tonic-gate static void
86417c478bd9Sstevel@tonic-gate ohci_handle_isoc_td(
86427c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
86437c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
86447c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
86457c478bd9Sstevel@tonic-gate 	ohci_td_t		*td,
86467c478bd9Sstevel@tonic-gate 	void			*tw_handle_callback_value)
86477c478bd9Sstevel@tonic-gate {
86487c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
86497c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
86507c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*curr_isoc_reqp =
86517c478bd9Sstevel@tonic-gate 				    (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
86527c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
86537c478bd9Sstevel@tonic-gate 
86547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
86557c478bd9Sstevel@tonic-gate 	    "ohci_handle_isoc_td: pp=0x%p tw=0x%p td=0x%p"
86567c478bd9Sstevel@tonic-gate 	    "isoc_reqp=0%p data=0x%p", pp, tw, td, curr_isoc_reqp,
86577c478bd9Sstevel@tonic-gate 	    curr_isoc_reqp->isoc_data);
86587c478bd9Sstevel@tonic-gate 
86597c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
86607c478bd9Sstevel@tonic-gate 
86617c478bd9Sstevel@tonic-gate 	/*
86627c478bd9Sstevel@tonic-gate 	 * Decrement the TDs counter and check whether all the isoc
86637c478bd9Sstevel@tonic-gate 	 * data has been send or received. If TDs counter reaches
86647c478bd9Sstevel@tonic-gate 	 * zero then inform client driver about completion current
86657c478bd9Sstevel@tonic-gate 	 * isoc request. Otherwise wait for completion of other isoc
86667c478bd9Sstevel@tonic-gate 	 * TDs or transactions on this pipe.
86677c478bd9Sstevel@tonic-gate 	 */
86687c478bd9Sstevel@tonic-gate 	if (--tw->tw_num_tds != 0) {
86697c478bd9Sstevel@tonic-gate 
86707c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
86717c478bd9Sstevel@tonic-gate 		    "ohci_handle_isoc_td: Number of TDs %d", tw->tw_num_tds);
86727c478bd9Sstevel@tonic-gate 
86737c478bd9Sstevel@tonic-gate 		return;
86747c478bd9Sstevel@tonic-gate 	}
86757c478bd9Sstevel@tonic-gate 
86767c478bd9Sstevel@tonic-gate 	/*
86777c478bd9Sstevel@tonic-gate 	 * If this is a isoc in pipe, return the data to the client.
86787c478bd9Sstevel@tonic-gate 	 * For a isoc out pipe, there is no need to do anything.
86797c478bd9Sstevel@tonic-gate 	 */
86807c478bd9Sstevel@tonic-gate 	if ((eptd->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_OUT) {
86817c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
86827c478bd9Sstevel@tonic-gate 		    "ohci_handle_isoc_td: Isoc out pipe, isoc_reqp=0x%p,"
86837c478bd9Sstevel@tonic-gate 		    "data=0x%p", curr_isoc_reqp, curr_isoc_reqp->isoc_data);
86847c478bd9Sstevel@tonic-gate 
86857c478bd9Sstevel@tonic-gate 		ohci_do_byte_stats(ohcip, tw->tw_length,
86867c478bd9Sstevel@tonic-gate 		    eptd->bmAttributes, eptd->bEndpointAddress);
86877c478bd9Sstevel@tonic-gate 
86887c478bd9Sstevel@tonic-gate 		/* Do the callback */
86897c478bd9Sstevel@tonic-gate 		ohci_hcdi_callback(ph, tw, USB_CR_OK);
86907c478bd9Sstevel@tonic-gate 
86917c478bd9Sstevel@tonic-gate 		return;
86927c478bd9Sstevel@tonic-gate 	}
86937c478bd9Sstevel@tonic-gate 
86947c478bd9Sstevel@tonic-gate 	/* Decrement number of IN isochronous request count */
86957c478bd9Sstevel@tonic-gate 	pp->pp_cur_periodic_req_cnt--;
86967c478bd9Sstevel@tonic-gate 
86977c478bd9Sstevel@tonic-gate 	/* Call ohci_sendup_td_message to send message to upstream */
86987c478bd9Sstevel@tonic-gate 	ohci_sendup_td_message(ohcip, pp, tw, td, USB_CR_OK);
86997c478bd9Sstevel@tonic-gate 
87007c478bd9Sstevel@tonic-gate 	/*
87017c478bd9Sstevel@tonic-gate 	 * If isochronous pipe state is still active, insert next isochronous
87027c478bd9Sstevel@tonic-gate 	 * request into the Host Controller's isochronous list.
87037c478bd9Sstevel@tonic-gate 	 */
87047c478bd9Sstevel@tonic-gate 	if (pp->pp_state != OHCI_PIPE_STATE_ACTIVE) {
87057c478bd9Sstevel@tonic-gate 		return;
87067c478bd9Sstevel@tonic-gate 	}
87077c478bd9Sstevel@tonic-gate 
87087c478bd9Sstevel@tonic-gate 	if ((error = ohci_allocate_periodic_in_resource(ohcip, pp, tw, 0)) ==
87097c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
87107c478bd9Sstevel@tonic-gate 		curr_isoc_reqp = (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
87117c478bd9Sstevel@tonic-gate 
87127c478bd9Sstevel@tonic-gate 		ASSERT(curr_isoc_reqp != NULL);
87137c478bd9Sstevel@tonic-gate 
87147c478bd9Sstevel@tonic-gate 		tw->tw_num_tds =
87157c478bd9Sstevel@tonic-gate 		    curr_isoc_reqp->isoc_pkts_count / OHCI_ISOC_PKTS_PER_TD;
87167c478bd9Sstevel@tonic-gate 		if (curr_isoc_reqp->isoc_pkts_count % OHCI_ISOC_PKTS_PER_TD) {
87177c478bd9Sstevel@tonic-gate 			tw->tw_num_tds++;
87187c478bd9Sstevel@tonic-gate 		}
87197c478bd9Sstevel@tonic-gate 
87207c478bd9Sstevel@tonic-gate 		if (ohci_allocate_tds_for_tw(ohcip, tw, tw->tw_num_tds) !=
87217c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
87227c478bd9Sstevel@tonic-gate 			ohci_deallocate_periodic_in_resource(ohcip, pp, tw);
87237c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
87247c478bd9Sstevel@tonic-gate 		}
87257c478bd9Sstevel@tonic-gate 	}
87267c478bd9Sstevel@tonic-gate 
87277c478bd9Sstevel@tonic-gate 	if (error != USB_SUCCESS ||
87287c478bd9Sstevel@tonic-gate 	    ohci_insert_isoc_req(ohcip, pp, tw, 0) != USB_SUCCESS) {
87297c478bd9Sstevel@tonic-gate 		/*
87307c478bd9Sstevel@tonic-gate 		 * Set pipe state to stop polling and error to no
87317c478bd9Sstevel@tonic-gate 		 * resource. Don't insert any more isoch polling
87327c478bd9Sstevel@tonic-gate 		 * requests.
87337c478bd9Sstevel@tonic-gate 		 */
87347c478bd9Sstevel@tonic-gate 		pp->pp_state = OHCI_PIPE_STATE_STOP_POLLING;
87357c478bd9Sstevel@tonic-gate 		pp->pp_error = USB_CR_NO_RESOURCES;
87367c478bd9Sstevel@tonic-gate 
87377c478bd9Sstevel@tonic-gate 	} else {
87387c478bd9Sstevel@tonic-gate 		/* Increment number of IN isochronous request count */
87397c478bd9Sstevel@tonic-gate 		pp->pp_cur_periodic_req_cnt++;
87407c478bd9Sstevel@tonic-gate 
87417c478bd9Sstevel@tonic-gate 		ASSERT(pp->pp_cur_periodic_req_cnt ==
87427c478bd9Sstevel@tonic-gate 		    pp->pp_max_periodic_req_cnt);
87437c478bd9Sstevel@tonic-gate 	}
87447c478bd9Sstevel@tonic-gate }
87457c478bd9Sstevel@tonic-gate 
87467c478bd9Sstevel@tonic-gate 
87477c478bd9Sstevel@tonic-gate /*
87487c478bd9Sstevel@tonic-gate  * ohci_sendup_td_message:
87497c478bd9Sstevel@tonic-gate  *	copy data, if necessary and do callback
87507c478bd9Sstevel@tonic-gate  */
87517c478bd9Sstevel@tonic-gate static void
87527c478bd9Sstevel@tonic-gate ohci_sendup_td_message(
87537c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
87547c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
87557c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
87567c478bd9Sstevel@tonic-gate 	ohci_td_t		*td,
87577c478bd9Sstevel@tonic-gate 	usb_cr_t		error)
87587c478bd9Sstevel@tonic-gate {
87597c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &pp->pp_pipe_handle->p_ep;
87607c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
87617c478bd9Sstevel@tonic-gate 	size_t			length = 0, skip_len = 0;
87627c478bd9Sstevel@tonic-gate 	mblk_t			*mp;
87637c478bd9Sstevel@tonic-gate 	uchar_t			*buf;
87647c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp = tw->tw_curr_xfer_reqp;
87657c478bd9Sstevel@tonic-gate 
87667c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
87677c478bd9Sstevel@tonic-gate 
87687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
87697c478bd9Sstevel@tonic-gate 	    "ohci_sendup_td_message:");
87707c478bd9Sstevel@tonic-gate 
87717c478bd9Sstevel@tonic-gate 	ASSERT(tw != NULL);
87727c478bd9Sstevel@tonic-gate 
87737c478bd9Sstevel@tonic-gate 	length = tw->tw_length;
87747c478bd9Sstevel@tonic-gate 
87757c478bd9Sstevel@tonic-gate 	switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
87767c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
87777c478bd9Sstevel@tonic-gate 		/*
87787c478bd9Sstevel@tonic-gate 		 * Get the correct length, adjust it for the setup size
87797c478bd9Sstevel@tonic-gate 		 * which is not part of the data length in control end
87807c478bd9Sstevel@tonic-gate 		 * points.  Update tw->tw_length for future references.
87817c478bd9Sstevel@tonic-gate 		 */
87827c478bd9Sstevel@tonic-gate 		tw->tw_length = length = length - SETUP_SIZE;
87837c478bd9Sstevel@tonic-gate 
87847c478bd9Sstevel@tonic-gate 		/* Set the length of the buffer to skip */
87857c478bd9Sstevel@tonic-gate 		skip_len = SETUP_SIZE;
87867c478bd9Sstevel@tonic-gate 
87877c478bd9Sstevel@tonic-gate 		if (Get_TD(td->hctd_ctrl_phase) != OHCI_CTRL_DATA_PHASE) {
87887c478bd9Sstevel@tonic-gate 			break;
87897c478bd9Sstevel@tonic-gate 		}
87907c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
87917c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
87927c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
87937c478bd9Sstevel@tonic-gate 		/*
87947c478bd9Sstevel@tonic-gate 		 * If error is "data overrun", do not check for the
87957c478bd9Sstevel@tonic-gate 		 * "CurrentBufferPointer"  and return whatever data
87967c478bd9Sstevel@tonic-gate 		 * received to the client driver.
87977c478bd9Sstevel@tonic-gate 		 */
87987c478bd9Sstevel@tonic-gate 		if (error == USB_CR_DATA_OVERRUN) {
87997c478bd9Sstevel@tonic-gate 			break;
88007c478bd9Sstevel@tonic-gate 		}
88017c478bd9Sstevel@tonic-gate 
88027c478bd9Sstevel@tonic-gate 		/*
88037c478bd9Sstevel@tonic-gate 		 * If "CurrentBufferPointer" of Transfer Descriptor
88047c478bd9Sstevel@tonic-gate 		 * (TD) is not equal to zero, then we received less
88057c478bd9Sstevel@tonic-gate 		 * data from the device than requested by us. In that
88067c478bd9Sstevel@tonic-gate 		 * case, get the actual received data size.
88077c478bd9Sstevel@tonic-gate 		 */
88087c478bd9Sstevel@tonic-gate 		if (Get_TD(td->hctd_cbp)) {
88097c478bd9Sstevel@tonic-gate 
88107c478bd9Sstevel@tonic-gate 			length = Get_TD(td->hctd_cbp) -
88117c478bd9Sstevel@tonic-gate 			    tw->tw_cookie.dmac_address - skip_len;
88127c478bd9Sstevel@tonic-gate 
88137c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
88147c478bd9Sstevel@tonic-gate 			    "ohci_sendup_qtd_message: requested data %lu "
88157c478bd9Sstevel@tonic-gate 			    "received data %lu", tw->tw_length, length);
88167c478bd9Sstevel@tonic-gate 		}
88177c478bd9Sstevel@tonic-gate 
88187c478bd9Sstevel@tonic-gate 		break;
88197c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
88207c478bd9Sstevel@tonic-gate 	default:
88217c478bd9Sstevel@tonic-gate 		break;
88227c478bd9Sstevel@tonic-gate 	}
88237c478bd9Sstevel@tonic-gate 
88247c478bd9Sstevel@tonic-gate 	/* Copy the data into the mblk_t */
88257c478bd9Sstevel@tonic-gate 	buf = (uchar_t *)tw->tw_buf + skip_len;
88267c478bd9Sstevel@tonic-gate 
88277c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
88287c478bd9Sstevel@tonic-gate 	    "ohci_sendup_qtd_message: length %lu error %d", length, error);
88297c478bd9Sstevel@tonic-gate 
88307c478bd9Sstevel@tonic-gate 	/* Get the message block */
88317c478bd9Sstevel@tonic-gate 	switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
88327c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
88337c478bd9Sstevel@tonic-gate 		mp = ((usb_ctrl_req_t *)curr_xfer_reqp)->ctrl_data;
88347c478bd9Sstevel@tonic-gate 		break;
88357c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
88367c478bd9Sstevel@tonic-gate 		mp = ((usb_bulk_req_t *)curr_xfer_reqp)->bulk_data;
88377c478bd9Sstevel@tonic-gate 		break;
88387c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
88397c478bd9Sstevel@tonic-gate 		mp = ((usb_intr_req_t *)curr_xfer_reqp)->intr_data;
88407c478bd9Sstevel@tonic-gate 		break;
88417c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
88427c478bd9Sstevel@tonic-gate 		mp = ((usb_isoc_req_t *)curr_xfer_reqp)->isoc_data;
88437c478bd9Sstevel@tonic-gate 		break;
88447c478bd9Sstevel@tonic-gate 	}
88457c478bd9Sstevel@tonic-gate 
88467c478bd9Sstevel@tonic-gate 	ASSERT(mp != NULL);
88477c478bd9Sstevel@tonic-gate 
88487c478bd9Sstevel@tonic-gate 	if (length) {
88497c478bd9Sstevel@tonic-gate 		/*
88507c478bd9Sstevel@tonic-gate 		 * Update kstat byte counts
88517c478bd9Sstevel@tonic-gate 		 * The control endpoints don't have direction bits so in
88527c478bd9Sstevel@tonic-gate 		 * order for control stats to be counted correctly an in
88537c478bd9Sstevel@tonic-gate 		 * bit must be faked on a control read.
88547c478bd9Sstevel@tonic-gate 		 */
88557c478bd9Sstevel@tonic-gate 		if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
88567c478bd9Sstevel@tonic-gate 		    USB_EP_ATTR_CONTROL) {
88577c478bd9Sstevel@tonic-gate 			ohci_do_byte_stats(ohcip, length,
88587c478bd9Sstevel@tonic-gate 			    eptd->bmAttributes, USB_EP_DIR_IN);
88597c478bd9Sstevel@tonic-gate 		} else {
88607c478bd9Sstevel@tonic-gate 			ohci_do_byte_stats(ohcip, length,
88617c478bd9Sstevel@tonic-gate 			    eptd->bmAttributes, eptd->bEndpointAddress);
88627c478bd9Sstevel@tonic-gate 		}
88637c478bd9Sstevel@tonic-gate 
88647c478bd9Sstevel@tonic-gate 		/* Sync IO buffer */
88657c478bd9Sstevel@tonic-gate 		Sync_IO_Buffer(tw->tw_dmahandle, length);
88667c478bd9Sstevel@tonic-gate 
88677c478bd9Sstevel@tonic-gate 		/* Copy the data into the message */
88687c478bd9Sstevel@tonic-gate 		ddi_rep_get8(tw->tw_accesshandle,
88697c478bd9Sstevel@tonic-gate 		    mp->b_rptr, buf, length, DDI_DEV_AUTOINCR);
88707c478bd9Sstevel@tonic-gate 
88717c478bd9Sstevel@tonic-gate 		/* Increment the write pointer */
88727c478bd9Sstevel@tonic-gate 		mp->b_wptr = mp->b_wptr + length;
88737c478bd9Sstevel@tonic-gate 	} else {
88747c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
88757c478bd9Sstevel@tonic-gate 		    "ohci_sendup_td_message: Zero length packet");
88767c478bd9Sstevel@tonic-gate 	}
88777c478bd9Sstevel@tonic-gate 
88787c478bd9Sstevel@tonic-gate 	ohci_hcdi_callback(ph, tw, error);
88797c478bd9Sstevel@tonic-gate }
88807c478bd9Sstevel@tonic-gate 
88817c478bd9Sstevel@tonic-gate 
88827c478bd9Sstevel@tonic-gate /*
88837c478bd9Sstevel@tonic-gate  * Miscellaneous functions
88847c478bd9Sstevel@tonic-gate  */
88857c478bd9Sstevel@tonic-gate 
88867c478bd9Sstevel@tonic-gate /*
88877c478bd9Sstevel@tonic-gate  * ohci_obtain_state:
88887c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
88897c478bd9Sstevel@tonic-gate  */
88907c478bd9Sstevel@tonic-gate ohci_state_t *
88917c478bd9Sstevel@tonic-gate ohci_obtain_state(dev_info_t	*dip)
88927c478bd9Sstevel@tonic-gate {
88937c478bd9Sstevel@tonic-gate 	int			instance = ddi_get_instance(dip);
88947c478bd9Sstevel@tonic-gate 	ohci_state_t 		*state = ddi_get_soft_state(
88957c478bd9Sstevel@tonic-gate 				    ohci_statep, instance);
88967c478bd9Sstevel@tonic-gate 
88977c478bd9Sstevel@tonic-gate 	ASSERT(state != NULL);
88987c478bd9Sstevel@tonic-gate 
88997c478bd9Sstevel@tonic-gate 	return (state);
89007c478bd9Sstevel@tonic-gate }
89017c478bd9Sstevel@tonic-gate 
89027c478bd9Sstevel@tonic-gate 
89037c478bd9Sstevel@tonic-gate /*
89047c478bd9Sstevel@tonic-gate  * ohci_state_is_operational:
89057c478bd9Sstevel@tonic-gate  *
89067c478bd9Sstevel@tonic-gate  * Check the Host controller state and return proper values.
89077c478bd9Sstevel@tonic-gate  */
89087c478bd9Sstevel@tonic-gate int
89097c478bd9Sstevel@tonic-gate ohci_state_is_operational(ohci_state_t	*ohcip)
89107c478bd9Sstevel@tonic-gate {
89117c478bd9Sstevel@tonic-gate 	int				val;
89127c478bd9Sstevel@tonic-gate 
89137c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
89147c478bd9Sstevel@tonic-gate 
89157c478bd9Sstevel@tonic-gate 	switch (ohcip->ohci_hc_soft_state) {
89167c478bd9Sstevel@tonic-gate 	case OHCI_CTLR_INIT_STATE:
89177c478bd9Sstevel@tonic-gate 	case OHCI_CTLR_SUSPEND_STATE:
89187c478bd9Sstevel@tonic-gate 		val = USB_FAILURE;
89197c478bd9Sstevel@tonic-gate 		break;
89207c478bd9Sstevel@tonic-gate 	case OHCI_CTLR_OPERATIONAL_STATE:
89217c478bd9Sstevel@tonic-gate 		val = USB_SUCCESS;
89227c478bd9Sstevel@tonic-gate 		break;
89237c478bd9Sstevel@tonic-gate 	case OHCI_CTLR_ERROR_STATE:
89247c478bd9Sstevel@tonic-gate 		val = USB_HC_HARDWARE_ERROR;
89257c478bd9Sstevel@tonic-gate 		break;
89267c478bd9Sstevel@tonic-gate 	default:
89277c478bd9Sstevel@tonic-gate 		val = USB_FAILURE;
89287c478bd9Sstevel@tonic-gate 		break;
89297c478bd9Sstevel@tonic-gate 	}
89307c478bd9Sstevel@tonic-gate 
89317c478bd9Sstevel@tonic-gate 	return (val);
89327c478bd9Sstevel@tonic-gate }
89337c478bd9Sstevel@tonic-gate 
89347c478bd9Sstevel@tonic-gate 
89357c478bd9Sstevel@tonic-gate /*
89367c478bd9Sstevel@tonic-gate  * ohci_do_soft_reset
89377c478bd9Sstevel@tonic-gate  *
89387c478bd9Sstevel@tonic-gate  * Do soft reset of ohci host controller.
89397c478bd9Sstevel@tonic-gate  */
89407c478bd9Sstevel@tonic-gate int
89417c478bd9Sstevel@tonic-gate ohci_do_soft_reset(ohci_state_t	*ohcip)
89427c478bd9Sstevel@tonic-gate {
89437c478bd9Sstevel@tonic-gate 	usb_frame_number_t	before_frame_number, after_frame_number;
89447c478bd9Sstevel@tonic-gate 	timeout_id_t		xfer_timer_id, rh_timer_id;
89457c478bd9Sstevel@tonic-gate 	ohci_regs_t		*ohci_save_regs;
89467c478bd9Sstevel@tonic-gate 	ohci_td_t		*done_head;
89477c478bd9Sstevel@tonic-gate 
89487c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
89497c478bd9Sstevel@tonic-gate 
89507c478bd9Sstevel@tonic-gate 	/* Increment host controller error count */
89517c478bd9Sstevel@tonic-gate 	ohcip->ohci_hc_error++;
89527c478bd9Sstevel@tonic-gate 
89537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
89547c478bd9Sstevel@tonic-gate 	    "ohci_do_soft_reset:"
89557c478bd9Sstevel@tonic-gate 	    "Reset ohci host controller 0x%x", ohcip->ohci_hc_error);
89567c478bd9Sstevel@tonic-gate 
89577c478bd9Sstevel@tonic-gate 	/*
89587c478bd9Sstevel@tonic-gate 	 * Allocate space for saving current Host Controller
89597c478bd9Sstevel@tonic-gate 	 * registers. Don't do any recovery if allocation
89607c478bd9Sstevel@tonic-gate 	 * fails.
89617c478bd9Sstevel@tonic-gate 	 */
89627c478bd9Sstevel@tonic-gate 	ohci_save_regs = (ohci_regs_t *)
89637c478bd9Sstevel@tonic-gate 	    kmem_zalloc(sizeof (ohci_regs_t), KM_NOSLEEP);
89647c478bd9Sstevel@tonic-gate 
89657c478bd9Sstevel@tonic-gate 	if (ohci_save_regs == NULL) {
89667c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR,  ohcip->ohci_log_hdl,
89677c478bd9Sstevel@tonic-gate 		    "ohci_do_soft_reset: kmem_zalloc failed");
89687c478bd9Sstevel@tonic-gate 
89697c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
89707c478bd9Sstevel@tonic-gate 	}
89717c478bd9Sstevel@tonic-gate 
89727c478bd9Sstevel@tonic-gate 	/* Save current ohci registers */
89737c478bd9Sstevel@tonic-gate 	ohci_save_regs->hcr_control = Get_OpReg(hcr_control);
89747c478bd9Sstevel@tonic-gate 	ohci_save_regs->hcr_cmd_status = Get_OpReg(hcr_cmd_status);
89757c478bd9Sstevel@tonic-gate 	ohci_save_regs->hcr_intr_enable = Get_OpReg(hcr_intr_enable);
89767c478bd9Sstevel@tonic-gate 	ohci_save_regs->hcr_periodic_strt = Get_OpReg(hcr_periodic_strt);
89777c478bd9Sstevel@tonic-gate 	ohci_save_regs->hcr_frame_interval = Get_OpReg(hcr_frame_interval);
89787c478bd9Sstevel@tonic-gate 	ohci_save_regs->hcr_HCCA = Get_OpReg(hcr_HCCA);
89797c478bd9Sstevel@tonic-gate 	ohci_save_regs->hcr_bulk_head = Get_OpReg(hcr_bulk_head);
89807c478bd9Sstevel@tonic-gate 	ohci_save_regs->hcr_ctrl_head = Get_OpReg(hcr_ctrl_head);
89817c478bd9Sstevel@tonic-gate 
89827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
89837c478bd9Sstevel@tonic-gate 	    "ohci_do_soft_reset: Save reg = 0x%p", ohci_save_regs);
89847c478bd9Sstevel@tonic-gate 
89857c478bd9Sstevel@tonic-gate 	/* Disable all list processing and interrupts */
89867c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_control, (Get_OpReg(hcr_control) & ~(HCR_CONTROL_CLE |
89877c478bd9Sstevel@tonic-gate 	    HCR_CONTROL_BLE | HCR_CONTROL_PLE | HCR_CONTROL_IE)));
89887c478bd9Sstevel@tonic-gate 
89897c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_disable, HCR_INTR_SO |
89907c478bd9Sstevel@tonic-gate 	    HCR_INTR_WDH | HCR_INTR_RD | HCR_INTR_UE |
89917c478bd9Sstevel@tonic-gate 	    HCR_INTR_FNO | HCR_INTR_SOF | HCR_INTR_MIE);
89927c478bd9Sstevel@tonic-gate 
89937c478bd9Sstevel@tonic-gate 	/* Wait for few milliseconds */
89947c478bd9Sstevel@tonic-gate 	drv_usecwait(OHCI_TIMEWAIT);
89957c478bd9Sstevel@tonic-gate 
89967c478bd9Sstevel@tonic-gate 	/* Root hub interrupt pipe timeout id */
89977c478bd9Sstevel@tonic-gate 	rh_timer_id = ohcip->ohci_root_hub.rh_intr_pipe_timer_id;
89987c478bd9Sstevel@tonic-gate 
89997c478bd9Sstevel@tonic-gate 	/* Stop the root hub interrupt timer */
90007c478bd9Sstevel@tonic-gate 	if (rh_timer_id) {
90017c478bd9Sstevel@tonic-gate 		ohcip->ohci_root_hub.rh_intr_pipe_timer_id = 0;
90027c478bd9Sstevel@tonic-gate 		ohcip->ohci_root_hub.rh_intr_pipe_state =
90037c478bd9Sstevel@tonic-gate 		    OHCI_PIPE_STATE_IDLE;
90047c478bd9Sstevel@tonic-gate 
90057c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
90067c478bd9Sstevel@tonic-gate 		(void) untimeout(rh_timer_id);
90077c478bd9Sstevel@tonic-gate 		mutex_enter(&ohcip->ohci_int_mutex);
90087c478bd9Sstevel@tonic-gate 	}
90097c478bd9Sstevel@tonic-gate 
90107c478bd9Sstevel@tonic-gate 	/* Transfer timeout id */
90117c478bd9Sstevel@tonic-gate 	xfer_timer_id = ohcip->ohci_timer_id;
90127c478bd9Sstevel@tonic-gate 
90137c478bd9Sstevel@tonic-gate 	/* Stop the global transfer timer */
90147c478bd9Sstevel@tonic-gate 	if (xfer_timer_id) {
90157c478bd9Sstevel@tonic-gate 		ohcip->ohci_timer_id = 0;
90167c478bd9Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
90177c478bd9Sstevel@tonic-gate 		(void) untimeout(xfer_timer_id);
90187c478bd9Sstevel@tonic-gate 		mutex_enter(&ohcip->ohci_int_mutex);
90197c478bd9Sstevel@tonic-gate 	}
90207c478bd9Sstevel@tonic-gate 
90217c478bd9Sstevel@tonic-gate 	/* Process any pending HCCA DoneHead */
90227c478bd9Sstevel@tonic-gate 	done_head = (ohci_td_t *)(uintptr_t)
90237c478bd9Sstevel@tonic-gate 	    (Get_HCCA(ohcip->ohci_hccap->HccaDoneHead) & HCCA_DONE_HEAD_MASK);
90247c478bd9Sstevel@tonic-gate 
90257c478bd9Sstevel@tonic-gate 	if (done_head) {
90267c478bd9Sstevel@tonic-gate 		/* Reset the done head to NULL */
90277c478bd9Sstevel@tonic-gate 		Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, NULL);
90287c478bd9Sstevel@tonic-gate 
90297c478bd9Sstevel@tonic-gate 		ohci_traverse_done_list(ohcip, done_head);
90307c478bd9Sstevel@tonic-gate 	}
90317c478bd9Sstevel@tonic-gate 
90327c478bd9Sstevel@tonic-gate 	/* Process any pending hcr_done_head value */
90337c478bd9Sstevel@tonic-gate 	if (Get_OpReg(hcr_done_head)) {
90347c478bd9Sstevel@tonic-gate 		ohci_traverse_done_list(ohcip,
90357c478bd9Sstevel@tonic-gate 		    (ohci_td_t *)(uintptr_t)Get_OpReg(hcr_done_head));
90367c478bd9Sstevel@tonic-gate 	}
90377c478bd9Sstevel@tonic-gate 
90387c478bd9Sstevel@tonic-gate 	/* Do soft reset of ohci host controller */
90397c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_cmd_status, HCR_STATUS_RESET);
90407c478bd9Sstevel@tonic-gate 
90417c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
90427c478bd9Sstevel@tonic-gate 	    "ohci_do_soft_reset: Reset in progress");
90437c478bd9Sstevel@tonic-gate 
90447c478bd9Sstevel@tonic-gate 	/* Wait for reset to complete */
90457c478bd9Sstevel@tonic-gate 	drv_usecwait(OHCI_RESET_TIMEWAIT);
90467c478bd9Sstevel@tonic-gate 
90477c478bd9Sstevel@tonic-gate 	/* Reset HCCA HcFrameNumber */
90487c478bd9Sstevel@tonic-gate 	Set_HCCA(ohcip->ohci_hccap->HccaFrameNo, 0x00000000);
90497c478bd9Sstevel@tonic-gate 
90507c478bd9Sstevel@tonic-gate 	/*
90517c478bd9Sstevel@tonic-gate 	 * Restore previous saved HC register value
90527c478bd9Sstevel@tonic-gate 	 * into the current HC registers.
90537c478bd9Sstevel@tonic-gate 	 */
90547c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_periodic_strt, (uint32_t)
90557c478bd9Sstevel@tonic-gate 	    ohci_save_regs->hcr_periodic_strt);
90567c478bd9Sstevel@tonic-gate 
90577c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_frame_interval, (uint32_t)
90587c478bd9Sstevel@tonic-gate 	    ohci_save_regs->hcr_frame_interval);
90597c478bd9Sstevel@tonic-gate 
90607c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_done_head, 0x0);
90617c478bd9Sstevel@tonic-gate 
90627c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_bulk_curr, 0x0);
90637c478bd9Sstevel@tonic-gate 
90647c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_bulk_head, (uint32_t)
90657c478bd9Sstevel@tonic-gate 	    ohci_save_regs->hcr_bulk_head);
90667c478bd9Sstevel@tonic-gate 
90677c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_ctrl_curr, 0x0);
90687c478bd9Sstevel@tonic-gate 
90697c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_ctrl_head, (uint32_t)
90707c478bd9Sstevel@tonic-gate 	    ohci_save_regs->hcr_ctrl_head);
90717c478bd9Sstevel@tonic-gate 
90727c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_periodic_curr, 0x0);
90737c478bd9Sstevel@tonic-gate 
90747c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_HCCA, (uint32_t)
90757c478bd9Sstevel@tonic-gate 	    ohci_save_regs->hcr_HCCA);
90767c478bd9Sstevel@tonic-gate 
90777c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_status, 0x0);
90787c478bd9Sstevel@tonic-gate 
90797c478bd9Sstevel@tonic-gate 	/*
90807c478bd9Sstevel@tonic-gate 	 * Set HcInterruptEnable to enable all interrupts except
90817c478bd9Sstevel@tonic-gate 	 * Root Hub Status change interrupt.
90827c478bd9Sstevel@tonic-gate 	 */
90837c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_intr_enable,
90847c478bd9Sstevel@tonic-gate 	    HCR_INTR_SO | HCR_INTR_WDH | HCR_INTR_RD | HCR_INTR_UE |
90857c478bd9Sstevel@tonic-gate 	    HCR_INTR_FNO | HCR_INTR_SOF | HCR_INTR_MIE);
90867c478bd9Sstevel@tonic-gate 
90877c478bd9Sstevel@tonic-gate 	/* Start Control and Bulk list processing */
90887c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_cmd_status, (HCR_STATUS_CLF | HCR_STATUS_BLF));
90897c478bd9Sstevel@tonic-gate 
90907c478bd9Sstevel@tonic-gate 	/*
90917c478bd9Sstevel@tonic-gate 	 * Start up Control, Bulk, Periodic and Isochronous lists
90927c478bd9Sstevel@tonic-gate 	 * processing.
90937c478bd9Sstevel@tonic-gate 	 */
90947c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_control, (uint32_t)
90957c478bd9Sstevel@tonic-gate 	    (ohci_save_regs->hcr_control & (~HCR_CONTROL_HCFS)));
90967c478bd9Sstevel@tonic-gate 
90977c478bd9Sstevel@tonic-gate 	/*
90987c478bd9Sstevel@tonic-gate 	 * Deallocate the space that allocated for saving
90997c478bd9Sstevel@tonic-gate 	 * HC registers.
91007c478bd9Sstevel@tonic-gate 	 */
91017c478bd9Sstevel@tonic-gate 	kmem_free((void *) ohci_save_regs, sizeof (ohci_regs_t));
91027c478bd9Sstevel@tonic-gate 
91037c478bd9Sstevel@tonic-gate 	/* Resume the host controller */
91047c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_control, ((Get_OpReg(hcr_control) &
91057c478bd9Sstevel@tonic-gate 	    (~HCR_CONTROL_HCFS)) | HCR_CONTROL_RESUME));
91067c478bd9Sstevel@tonic-gate 
91077c478bd9Sstevel@tonic-gate 	/* Wait for resume to complete */
91087c478bd9Sstevel@tonic-gate 	drv_usecwait(OHCI_RESUME_TIMEWAIT);
91097c478bd9Sstevel@tonic-gate 
91107c478bd9Sstevel@tonic-gate 	/* Set the Host Controller Functional State to Operational */
91117c478bd9Sstevel@tonic-gate 	Set_OpReg(hcr_control, ((Get_OpReg(hcr_control) &
91127c478bd9Sstevel@tonic-gate 	    (~HCR_CONTROL_HCFS)) | HCR_CONTROL_OPERAT));
91137c478bd9Sstevel@tonic-gate 
91147c478bd9Sstevel@tonic-gate 	/* Wait 10ms for HC to start sending SOF */
91157c478bd9Sstevel@tonic-gate 	drv_usecwait(OHCI_TIMEWAIT);
91167c478bd9Sstevel@tonic-gate 
91177c478bd9Sstevel@tonic-gate 	/*
91187c478bd9Sstevel@tonic-gate 	 * Get the current usb frame number before waiting for few
91197c478bd9Sstevel@tonic-gate 	 * milliseconds.
91207c478bd9Sstevel@tonic-gate 	 */
91217c478bd9Sstevel@tonic-gate 	before_frame_number = ohci_get_current_frame_number(ohcip);
91227c478bd9Sstevel@tonic-gate 
91237c478bd9Sstevel@tonic-gate 	/* Wait for few milliseconds */
91247c478bd9Sstevel@tonic-gate 	drv_usecwait(OHCI_TIMEWAIT);
91257c478bd9Sstevel@tonic-gate 
91267c478bd9Sstevel@tonic-gate 	/*
91277c478bd9Sstevel@tonic-gate 	 * Get the current usb frame number after waiting for few
91287c478bd9Sstevel@tonic-gate 	 * milliseconds.
91297c478bd9Sstevel@tonic-gate 	 */
91307c478bd9Sstevel@tonic-gate 	after_frame_number = ohci_get_current_frame_number(ohcip);
91317c478bd9Sstevel@tonic-gate 
91327c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
91337c478bd9Sstevel@tonic-gate 	    "ohci_do_soft_reset: Before Frm No 0x%llx After Frm No 0x%llx",
91347c478bd9Sstevel@tonic-gate 	    before_frame_number, after_frame_number);
91357c478bd9Sstevel@tonic-gate 
91367c478bd9Sstevel@tonic-gate 	if (after_frame_number <= before_frame_number) {
91377c478bd9Sstevel@tonic-gate 
91387c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, ohcip->ohci_log_hdl,
91397c478bd9Sstevel@tonic-gate 		    "ohci_do_soft_reset: Soft reset failed");
91407c478bd9Sstevel@tonic-gate 
91417c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
91427c478bd9Sstevel@tonic-gate 	}
91437c478bd9Sstevel@tonic-gate 
91447c478bd9Sstevel@tonic-gate 	/* Start the timer for the root hub interrupt pipe polling */
91457c478bd9Sstevel@tonic-gate 	if (rh_timer_id) {
91467c478bd9Sstevel@tonic-gate 		ohcip->ohci_root_hub.rh_intr_pipe_timer_id =
91477c478bd9Sstevel@tonic-gate 		    timeout(ohci_handle_root_hub_status_change,
91487c478bd9Sstevel@tonic-gate 		    (void *)ohcip, drv_usectohz(OHCI_RH_POLL_TIME));
91497c478bd9Sstevel@tonic-gate 
91507c478bd9Sstevel@tonic-gate 		ohcip->ohci_root_hub.
91517c478bd9Sstevel@tonic-gate 		    rh_intr_pipe_state = OHCI_PIPE_STATE_ACTIVE;
91527c478bd9Sstevel@tonic-gate 	}
91537c478bd9Sstevel@tonic-gate 
91547c478bd9Sstevel@tonic-gate 	/* Start the global timer */
91557c478bd9Sstevel@tonic-gate 	if (xfer_timer_id) {
91567c478bd9Sstevel@tonic-gate 		ohcip->ohci_timer_id = timeout(ohci_xfer_timeout_handler,
91577c478bd9Sstevel@tonic-gate 		    (void *)ohcip, drv_usectohz(1000000));
91587c478bd9Sstevel@tonic-gate 	}
91597c478bd9Sstevel@tonic-gate 
91607c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
91617c478bd9Sstevel@tonic-gate }
91627c478bd9Sstevel@tonic-gate 
91637c478bd9Sstevel@tonic-gate 
91647c478bd9Sstevel@tonic-gate /*
91657c478bd9Sstevel@tonic-gate  * ohci_get_current_frame_number:
91667c478bd9Sstevel@tonic-gate  *
91677c478bd9Sstevel@tonic-gate  * Get the current software based usb frame number.
91687c478bd9Sstevel@tonic-gate  */
91697c478bd9Sstevel@tonic-gate usb_frame_number_t
91707c478bd9Sstevel@tonic-gate ohci_get_current_frame_number(ohci_state_t *ohcip)
91717c478bd9Sstevel@tonic-gate {
91727c478bd9Sstevel@tonic-gate 	usb_frame_number_t	usb_frame_number;
91737c478bd9Sstevel@tonic-gate 	usb_frame_number_t	ohci_fno, frame_number;
91747c478bd9Sstevel@tonic-gate 	ohci_save_intr_sts_t	*ohci_intr_sts =
91757c478bd9Sstevel@tonic-gate 				    &ohcip->ohci_save_intr_sts;
91767c478bd9Sstevel@tonic-gate 
91777c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
91787c478bd9Sstevel@tonic-gate 
91797c478bd9Sstevel@tonic-gate 	/*
91807c478bd9Sstevel@tonic-gate 	 * Sync HCCA area only if this function
91817c478bd9Sstevel@tonic-gate 	 * is invoked in non interrupt context.
91827c478bd9Sstevel@tonic-gate 	 */
91837c478bd9Sstevel@tonic-gate 	if (!(ohci_intr_sts->ohci_intr_flag &
91847c478bd9Sstevel@tonic-gate 	    OHCI_INTR_HANDLING)) {
91857c478bd9Sstevel@tonic-gate 
91867c478bd9Sstevel@tonic-gate 		/* Sync HCCA area */
91877c478bd9Sstevel@tonic-gate 		Sync_HCCA(ohcip);
91887c478bd9Sstevel@tonic-gate 	}
91897c478bd9Sstevel@tonic-gate 
91907c478bd9Sstevel@tonic-gate 	ohci_fno = ohcip->ohci_fno;
91917c478bd9Sstevel@tonic-gate 	frame_number = Get_HCCA(ohcip->ohci_hccap->HccaFrameNo);
91927c478bd9Sstevel@tonic-gate 
91937c478bd9Sstevel@tonic-gate 	/*
91947c478bd9Sstevel@tonic-gate 	 * Calculate current software based usb frame number.
91957c478bd9Sstevel@tonic-gate 	 *
91967c478bd9Sstevel@tonic-gate 	 * This code accounts for the fact that frame number is
91977c478bd9Sstevel@tonic-gate 	 * updated by the Host Controller before the ohci driver
91987c478bd9Sstevel@tonic-gate 	 * gets an FrameNumberOverflow (FNO) interrupt that will
91997c478bd9Sstevel@tonic-gate 	 * adjust Frame higher part.
92007c478bd9Sstevel@tonic-gate 	 *
92017c478bd9Sstevel@tonic-gate 	 * Refer ohci specification 1.0a, section 5.4, page 86.
92027c478bd9Sstevel@tonic-gate 	 */
92037c478bd9Sstevel@tonic-gate 	usb_frame_number = ((frame_number & 0x7FFF) | ohci_fno) +
92047c478bd9Sstevel@tonic-gate 	    (((frame_number & 0xFFFF) ^ ohci_fno) & 0x8000);
92057c478bd9Sstevel@tonic-gate 
92067c478bd9Sstevel@tonic-gate 	return (usb_frame_number);
92077c478bd9Sstevel@tonic-gate }
92087c478bd9Sstevel@tonic-gate 
92097c478bd9Sstevel@tonic-gate 
92107c478bd9Sstevel@tonic-gate /*
92117c478bd9Sstevel@tonic-gate  * ohci_cpr_cleanup:
92127c478bd9Sstevel@tonic-gate  *
92137c478bd9Sstevel@tonic-gate  * Cleanup ohci state and other ohci specific informations across
92147c478bd9Sstevel@tonic-gate  * Check Point Resume (CPR).
92157c478bd9Sstevel@tonic-gate  */
92167c478bd9Sstevel@tonic-gate static	void
92177c478bd9Sstevel@tonic-gate ohci_cpr_cleanup(ohci_state_t *ohcip)
92187c478bd9Sstevel@tonic-gate {
92197c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
92207c478bd9Sstevel@tonic-gate 
92217c478bd9Sstevel@tonic-gate 	/* Reset software part of usb frame number */
92227c478bd9Sstevel@tonic-gate 	ohcip->ohci_fno = 0;
92237c478bd9Sstevel@tonic-gate 
92247c478bd9Sstevel@tonic-gate 	/* Reset Schedule Overrrun Error Counter */
92257c478bd9Sstevel@tonic-gate 	ohcip->ohci_so_error = 0;
92267c478bd9Sstevel@tonic-gate 
92277c478bd9Sstevel@tonic-gate 	/* Reset HCCA HcFrameNumber */
92287c478bd9Sstevel@tonic-gate 	Set_HCCA(ohcip->ohci_hccap->HccaFrameNo, 0x00000000);
92297c478bd9Sstevel@tonic-gate }
92307c478bd9Sstevel@tonic-gate 
92317c478bd9Sstevel@tonic-gate 
92327c478bd9Sstevel@tonic-gate /*
92337c478bd9Sstevel@tonic-gate  * ohci_get_xfer_attrs:
92347c478bd9Sstevel@tonic-gate  *
92357c478bd9Sstevel@tonic-gate  * Get the attributes of a particular xfer.
92367c478bd9Sstevel@tonic-gate  */
92377c478bd9Sstevel@tonic-gate static usb_req_attrs_t
92387c478bd9Sstevel@tonic-gate ohci_get_xfer_attrs(
92397c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
92407c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
92417c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
92427c478bd9Sstevel@tonic-gate {
92437c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &pp->pp_pipe_handle->p_ep;
92447c478bd9Sstevel@tonic-gate 	usb_req_attrs_t		attrs = 0;
92457c478bd9Sstevel@tonic-gate 
92467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
92477c478bd9Sstevel@tonic-gate 	    "ohci_get_xfer_attrs:");
92487c478bd9Sstevel@tonic-gate 
92497c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
92507c478bd9Sstevel@tonic-gate 
92517c478bd9Sstevel@tonic-gate 	switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
92527c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
92537c478bd9Sstevel@tonic-gate 		attrs = ((usb_ctrl_req_t *)
92547c478bd9Sstevel@tonic-gate 		    tw->tw_curr_xfer_reqp)->ctrl_attributes;
92557c478bd9Sstevel@tonic-gate 		break;
92567c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
92577c478bd9Sstevel@tonic-gate 		attrs = ((usb_bulk_req_t *)
92587c478bd9Sstevel@tonic-gate 		    tw->tw_curr_xfer_reqp)->bulk_attributes;
92597c478bd9Sstevel@tonic-gate 		break;
92607c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
92617c478bd9Sstevel@tonic-gate 		attrs = ((usb_intr_req_t *)
92627c478bd9Sstevel@tonic-gate 		    tw->tw_curr_xfer_reqp)->intr_attributes;
92637c478bd9Sstevel@tonic-gate 		break;
92647c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
92657c478bd9Sstevel@tonic-gate 		attrs = ((usb_isoc_req_t *)
92667c478bd9Sstevel@tonic-gate 		    tw->tw_curr_xfer_reqp)->isoc_attributes;
92677c478bd9Sstevel@tonic-gate 		break;
92687c478bd9Sstevel@tonic-gate 	}
92697c478bd9Sstevel@tonic-gate 
92707c478bd9Sstevel@tonic-gate 	return (attrs);
92717c478bd9Sstevel@tonic-gate }
92727c478bd9Sstevel@tonic-gate 
92737c478bd9Sstevel@tonic-gate 
92747c478bd9Sstevel@tonic-gate /*
92757c478bd9Sstevel@tonic-gate  * ohci_allocate_periodic_in_resource
92767c478bd9Sstevel@tonic-gate  *
92777c478bd9Sstevel@tonic-gate  * Allocate interrupt/isochronous request structure for the
92787c478bd9Sstevel@tonic-gate  * interrupt/isochronous IN transfer.
92797c478bd9Sstevel@tonic-gate  */
92807c478bd9Sstevel@tonic-gate static int
92817c478bd9Sstevel@tonic-gate ohci_allocate_periodic_in_resource(
92827c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
92837c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
92847c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
92857c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
92867c478bd9Sstevel@tonic-gate {
92877c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
92887c478bd9Sstevel@tonic-gate 	uchar_t			ep_attr = ph->p_ep.bmAttributes;
92897c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
92907c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*curr_isoc_reqp;
92917c478bd9Sstevel@tonic-gate 	usb_opaque_t		client_periodic_in_reqp;
92927c478bd9Sstevel@tonic-gate 	size_t			length = 0;
92937c478bd9Sstevel@tonic-gate 
92947c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
92957c478bd9Sstevel@tonic-gate 	    "ohci_allocate_periodic_in_resource:"
92967c478bd9Sstevel@tonic-gate 	    "pp = 0x%p tw = 0x%p flags = 0x%x", pp, tw, flags);
92977c478bd9Sstevel@tonic-gate 
92987c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
92997c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_curr_xfer_reqp == NULL);
93007c478bd9Sstevel@tonic-gate 
93017c478bd9Sstevel@tonic-gate 	/* Get the client periodic in request pointer */
93027c478bd9Sstevel@tonic-gate 	client_periodic_in_reqp = pp->pp_client_periodic_in_reqp;
93037c478bd9Sstevel@tonic-gate 
93047c478bd9Sstevel@tonic-gate 	/*
93057c478bd9Sstevel@tonic-gate 	 * If it a periodic IN request and periodic request is NULL,
93067c478bd9Sstevel@tonic-gate 	 * allocate corresponding usb periodic IN request for the
93077c478bd9Sstevel@tonic-gate 	 * current periodic polling request and copy the information
93087c478bd9Sstevel@tonic-gate 	 * from the saved periodic request structure.
93097c478bd9Sstevel@tonic-gate 	 */
93107c478bd9Sstevel@tonic-gate 	if ((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) {
93117c478bd9Sstevel@tonic-gate 
93127c478bd9Sstevel@tonic-gate 		if (client_periodic_in_reqp) {
93137c478bd9Sstevel@tonic-gate 
93147c478bd9Sstevel@tonic-gate 			/* Get the interrupt transfer length */
93157c478bd9Sstevel@tonic-gate 			length = ((usb_intr_req_t *)
93167c478bd9Sstevel@tonic-gate 			client_periodic_in_reqp)->intr_len;
93177c478bd9Sstevel@tonic-gate 
93187c478bd9Sstevel@tonic-gate 			curr_intr_reqp = usba_hcdi_dup_intr_req(
93197c478bd9Sstevel@tonic-gate 			    ph->p_dip, (usb_intr_req_t *)
93207c478bd9Sstevel@tonic-gate 			    client_periodic_in_reqp, length, flags);
93217c478bd9Sstevel@tonic-gate 		} else {
93227c478bd9Sstevel@tonic-gate 			curr_intr_reqp = usb_alloc_intr_req(
93237c478bd9Sstevel@tonic-gate 			    ph->p_dip, length, flags);
93247c478bd9Sstevel@tonic-gate 		}
93257c478bd9Sstevel@tonic-gate 
93267c478bd9Sstevel@tonic-gate 		if (curr_intr_reqp == NULL) {
93277c478bd9Sstevel@tonic-gate 
93287c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
93297c478bd9Sstevel@tonic-gate 			    "ohci_allocate_periodic_in_resource: Interrupt "
93307c478bd9Sstevel@tonic-gate 			    "request structure allocation failed");
93317c478bd9Sstevel@tonic-gate 
93327c478bd9Sstevel@tonic-gate 			return (USB_NO_RESOURCES);
93337c478bd9Sstevel@tonic-gate 		}
93347c478bd9Sstevel@tonic-gate 
93357c478bd9Sstevel@tonic-gate 		if (client_periodic_in_reqp == NULL) {
93367c478bd9Sstevel@tonic-gate 			/* For polled mode */
93377c478bd9Sstevel@tonic-gate 			curr_intr_reqp->
93387c478bd9Sstevel@tonic-gate 			    intr_attributes = USB_ATTRS_SHORT_XFER_OK;
93397c478bd9Sstevel@tonic-gate 			curr_intr_reqp->
93407c478bd9Sstevel@tonic-gate 			    intr_len = ph->p_ep.wMaxPacketSize;
93417c478bd9Sstevel@tonic-gate 		} else {
93427c478bd9Sstevel@tonic-gate 			/* Check and save the timeout value */
93437c478bd9Sstevel@tonic-gate 			tw->tw_timeout = (curr_intr_reqp->intr_attributes &
93447c478bd9Sstevel@tonic-gate 			    USB_ATTRS_ONE_XFER) ?
93457c478bd9Sstevel@tonic-gate 			    curr_intr_reqp->intr_timeout: 0;
93467c478bd9Sstevel@tonic-gate 		}
93477c478bd9Sstevel@tonic-gate 
93487c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = (usb_opaque_t)curr_intr_reqp;
93497c478bd9Sstevel@tonic-gate 		tw->tw_length = curr_intr_reqp->intr_len;
93507c478bd9Sstevel@tonic-gate 	} else {
93517c478bd9Sstevel@tonic-gate 		ASSERT(client_periodic_in_reqp != NULL);
93527c478bd9Sstevel@tonic-gate 
93537c478bd9Sstevel@tonic-gate 		curr_isoc_reqp = usba_hcdi_dup_isoc_req(ph->p_dip,
93547c478bd9Sstevel@tonic-gate 		    (usb_isoc_req_t *)client_periodic_in_reqp, flags);
93557c478bd9Sstevel@tonic-gate 
93567c478bd9Sstevel@tonic-gate 		if (curr_isoc_reqp == NULL) {
93577c478bd9Sstevel@tonic-gate 
93587c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
93597c478bd9Sstevel@tonic-gate 			    "ohci_allocate_periodic_in_resource: Isochronous"
93607c478bd9Sstevel@tonic-gate 			    "request structure allocation failed");
93617c478bd9Sstevel@tonic-gate 
93627c478bd9Sstevel@tonic-gate 			return (USB_NO_RESOURCES);
93637c478bd9Sstevel@tonic-gate 		}
93647c478bd9Sstevel@tonic-gate 
93657c478bd9Sstevel@tonic-gate 		/*
93667c478bd9Sstevel@tonic-gate 		 * Save the client's isochronous request pointer and
93677c478bd9Sstevel@tonic-gate 		 * length of isochronous transfer in transfer wrapper.
93687c478bd9Sstevel@tonic-gate 		 * The dup'ed request is saved in pp_client_periodic_in_reqp
93697c478bd9Sstevel@tonic-gate 		 */
93707c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp =
93717c478bd9Sstevel@tonic-gate 		    (usb_opaque_t)pp->pp_client_periodic_in_reqp;
93727c478bd9Sstevel@tonic-gate 		pp->pp_client_periodic_in_reqp = (usb_opaque_t)curr_isoc_reqp;
93737c478bd9Sstevel@tonic-gate 		tw->tw_length = curr_isoc_reqp->isoc_pkts_length;
93747c478bd9Sstevel@tonic-gate 	}
93757c478bd9Sstevel@tonic-gate 
93767c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
93777c478bd9Sstevel@tonic-gate 	ph->p_req_count++;
93787c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
93797c478bd9Sstevel@tonic-gate 
93807c478bd9Sstevel@tonic-gate 	pp->pp_state = OHCI_PIPE_STATE_ACTIVE;
93817c478bd9Sstevel@tonic-gate 
93827c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
93837c478bd9Sstevel@tonic-gate }
93847c478bd9Sstevel@tonic-gate 
93857c478bd9Sstevel@tonic-gate 
93867c478bd9Sstevel@tonic-gate /*
93877c478bd9Sstevel@tonic-gate  * ohci_wait_for_sof:
93887c478bd9Sstevel@tonic-gate  *
93897c478bd9Sstevel@tonic-gate  * Wait for couple of SOF interrupts
93907c478bd9Sstevel@tonic-gate  */
93917c478bd9Sstevel@tonic-gate static int
93927c478bd9Sstevel@tonic-gate ohci_wait_for_sof(ohci_state_t	*ohcip)
93937c478bd9Sstevel@tonic-gate {
93947c478bd9Sstevel@tonic-gate 	usb_frame_number_t	before_frame_number, after_frame_number;
93957c478bd9Sstevel@tonic-gate 	clock_t			sof_time_wait;
93967c478bd9Sstevel@tonic-gate 	int			rval, sof_wait_count;
93977c478bd9Sstevel@tonic-gate 
93987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
93997c478bd9Sstevel@tonic-gate 	    "ohci_wait_for_sof");
94007c478bd9Sstevel@tonic-gate 
94017c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
94027c478bd9Sstevel@tonic-gate 
94037c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
94047c478bd9Sstevel@tonic-gate 
94057c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
94067c478bd9Sstevel@tonic-gate 
94077c478bd9Sstevel@tonic-gate 		return (rval);
94087c478bd9Sstevel@tonic-gate 	}
94097c478bd9Sstevel@tonic-gate 
94107c478bd9Sstevel@tonic-gate 	/* Get the number of clock ticks to wait */
94117c478bd9Sstevel@tonic-gate 	sof_time_wait = drv_usectohz(OHCI_MAX_SOF_TIMEWAIT * 1000000);
94127c478bd9Sstevel@tonic-gate 
94137c478bd9Sstevel@tonic-gate 	sof_wait_count = 0;
94147c478bd9Sstevel@tonic-gate 
94157c478bd9Sstevel@tonic-gate 	/*
94167c478bd9Sstevel@tonic-gate 	 * Get the current usb frame number before waiting for the
94177c478bd9Sstevel@tonic-gate 	 * SOF interrupt event.
94187c478bd9Sstevel@tonic-gate 	 */
94197c478bd9Sstevel@tonic-gate 	before_frame_number = ohci_get_current_frame_number(ohcip);
94207c478bd9Sstevel@tonic-gate 
94217c478bd9Sstevel@tonic-gate 	while (sof_wait_count < MAX_SOF_WAIT_COUNT) {
94227c478bd9Sstevel@tonic-gate 		/* Enable the SOF interrupt */
94237c478bd9Sstevel@tonic-gate 		Set_OpReg(hcr_intr_enable, HCR_INTR_SOF);
94247c478bd9Sstevel@tonic-gate 
94257c478bd9Sstevel@tonic-gate 		ASSERT(Get_OpReg(hcr_intr_enable) & HCR_INTR_SOF);
94267c478bd9Sstevel@tonic-gate 
94277c478bd9Sstevel@tonic-gate 		/* Wait for the SOF or timeout event */
94287c478bd9Sstevel@tonic-gate 		rval = cv_timedwait(&ohcip->ohci_SOF_cv,
94297c478bd9Sstevel@tonic-gate 		    &ohcip->ohci_int_mutex, ddi_get_lbolt() + sof_time_wait);
94307c478bd9Sstevel@tonic-gate 
94317c478bd9Sstevel@tonic-gate 		/*
94327c478bd9Sstevel@tonic-gate 		 * Get the current usb frame number after woken up either
94337c478bd9Sstevel@tonic-gate 		 * from SOF interrupt or timer expired event.
94347c478bd9Sstevel@tonic-gate 		 */
94357c478bd9Sstevel@tonic-gate 		after_frame_number = ohci_get_current_frame_number(ohcip);
94367c478bd9Sstevel@tonic-gate 
94377c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
94387c478bd9Sstevel@tonic-gate 		    "ohci_wait_for_sof: before 0x%llx, after 0x%llx",
94397c478bd9Sstevel@tonic-gate 		    before_frame_number, after_frame_number);
94407c478bd9Sstevel@tonic-gate 
94417c478bd9Sstevel@tonic-gate 		/*
94427c478bd9Sstevel@tonic-gate 		 * Return failure, if we are woken up becuase of timer expired
94437c478bd9Sstevel@tonic-gate 		 * event and if usb frame number has not been changed.
94447c478bd9Sstevel@tonic-gate 		 */
94457c478bd9Sstevel@tonic-gate 		if ((rval == -1) &&
94467c478bd9Sstevel@tonic-gate 		    (after_frame_number <= before_frame_number)) {
94477c478bd9Sstevel@tonic-gate 
94487c478bd9Sstevel@tonic-gate 			if ((ohci_do_soft_reset(ohcip)) != USB_SUCCESS) {
94497c478bd9Sstevel@tonic-gate 
94507c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L0(PRINT_MASK_LISTS,
94517c478bd9Sstevel@tonic-gate 				    ohcip->ohci_log_hdl, "No SOF interrupts");
94527c478bd9Sstevel@tonic-gate 
94537c478bd9Sstevel@tonic-gate 				/* Set host controller soft state to error */
94547c478bd9Sstevel@tonic-gate 				ohcip->ohci_hc_soft_state =
94557c478bd9Sstevel@tonic-gate 				    OHCI_CTLR_ERROR_STATE;
94567c478bd9Sstevel@tonic-gate 
94577c478bd9Sstevel@tonic-gate 				return (USB_FAILURE);
94587c478bd9Sstevel@tonic-gate 			}
94597c478bd9Sstevel@tonic-gate 
94607c478bd9Sstevel@tonic-gate 			/* Get new usb frame number */
94617c478bd9Sstevel@tonic-gate 			after_frame_number = before_frame_number =
94627c478bd9Sstevel@tonic-gate 			    ohci_get_current_frame_number(ohcip);
94637c478bd9Sstevel@tonic-gate 		}
94647c478bd9Sstevel@tonic-gate 
94657c478bd9Sstevel@tonic-gate 		ASSERT(after_frame_number >= before_frame_number);
94667c478bd9Sstevel@tonic-gate 
94677c478bd9Sstevel@tonic-gate 		before_frame_number = after_frame_number;
94687c478bd9Sstevel@tonic-gate 		sof_wait_count++;
94697c478bd9Sstevel@tonic-gate 	}
94707c478bd9Sstevel@tonic-gate 
94717c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
94727c478bd9Sstevel@tonic-gate }
94737c478bd9Sstevel@tonic-gate 
94747c478bd9Sstevel@tonic-gate 
94757c478bd9Sstevel@tonic-gate /*
94767c478bd9Sstevel@tonic-gate  * ohci_pipe_cleanup
94777c478bd9Sstevel@tonic-gate  *
94787c478bd9Sstevel@tonic-gate  * Cleanup ohci pipe.
94797c478bd9Sstevel@tonic-gate  */
94807c478bd9Sstevel@tonic-gate static void
94817c478bd9Sstevel@tonic-gate ohci_pipe_cleanup(
94827c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
94837c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
94847c478bd9Sstevel@tonic-gate {
94857c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
94867c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
94877c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason;
94887c478bd9Sstevel@tonic-gate 	uint_t			pipe_state = pp->pp_state;
94897c478bd9Sstevel@tonic-gate 	uint_t			bit = 0;
94907c478bd9Sstevel@tonic-gate 
94917c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
94927c478bd9Sstevel@tonic-gate 	    "ohci_pipe_cleanup: ph = 0x%p", ph);
94937c478bd9Sstevel@tonic-gate 
94947c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
94957c478bd9Sstevel@tonic-gate 
94967c478bd9Sstevel@tonic-gate 	switch (pipe_state) {
94977c478bd9Sstevel@tonic-gate 	case OHCI_PIPE_STATE_CLOSE:
94987c478bd9Sstevel@tonic-gate 		if (OHCI_NON_PERIODIC_ENDPOINT(eptd)) {
94997c478bd9Sstevel@tonic-gate 
95007c478bd9Sstevel@tonic-gate 			bit = ((eptd->bmAttributes &
95017c478bd9Sstevel@tonic-gate 			    USB_EP_ATTR_MASK) == USB_EP_ATTR_CONTROL) ?
95027c478bd9Sstevel@tonic-gate 			    HCR_CONTROL_CLE: HCR_CONTROL_BLE;
95037c478bd9Sstevel@tonic-gate 
95047c478bd9Sstevel@tonic-gate 			Set_OpReg(hcr_control,
95057c478bd9Sstevel@tonic-gate 			    (Get_OpReg(hcr_control) & ~(bit)));
95067c478bd9Sstevel@tonic-gate 
95077c478bd9Sstevel@tonic-gate 			/* Wait for the next SOF */
95087c478bd9Sstevel@tonic-gate 			(void) ohci_wait_for_sof(ohcip);
95097c478bd9Sstevel@tonic-gate 
95107c478bd9Sstevel@tonic-gate 			break;
95117c478bd9Sstevel@tonic-gate 		}
95127c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
95137c478bd9Sstevel@tonic-gate 	case OHCI_PIPE_STATE_RESET:
95147c478bd9Sstevel@tonic-gate 	case OHCI_PIPE_STATE_STOP_POLLING:
95157c478bd9Sstevel@tonic-gate 		/*
95167c478bd9Sstevel@tonic-gate 		 * Set the sKip bit to stop all transactions on
95177c478bd9Sstevel@tonic-gate 		 * this pipe
95187c478bd9Sstevel@tonic-gate 		 */
95197c478bd9Sstevel@tonic-gate 		ohci_modify_sKip_bit(ohcip, pp, SET_sKip,
95207c478bd9Sstevel@tonic-gate 		    OHCI_FLAGS_SLEEP | OHCI_FLAGS_DMA_SYNC);
95217c478bd9Sstevel@tonic-gate 
95227c478bd9Sstevel@tonic-gate 		break;
95237c478bd9Sstevel@tonic-gate 	default:
95247c478bd9Sstevel@tonic-gate 		return;
95257c478bd9Sstevel@tonic-gate 	}
95267c478bd9Sstevel@tonic-gate 
95277c478bd9Sstevel@tonic-gate 	/*
95287c478bd9Sstevel@tonic-gate 	 * Wait for processing all completed transfers and
95297c478bd9Sstevel@tonic-gate 	 * to send results to upstream.
95307c478bd9Sstevel@tonic-gate 	 */
95317c478bd9Sstevel@tonic-gate 	ohci_wait_for_transfers_completion(ohcip, pp);
95327c478bd9Sstevel@tonic-gate 
95337c478bd9Sstevel@tonic-gate 	/* Save the data toggle information */
95347c478bd9Sstevel@tonic-gate 	ohci_save_data_toggle(ohcip, ph);
95357c478bd9Sstevel@tonic-gate 
95367c478bd9Sstevel@tonic-gate 	/*
95377c478bd9Sstevel@tonic-gate 	 * Traverse the list of TD's on this endpoint and
95387c478bd9Sstevel@tonic-gate 	 * these TD's have outstanding transfer requests.
95397c478bd9Sstevel@tonic-gate 	 * Since the list processing is stopped, these tds
95407c478bd9Sstevel@tonic-gate 	 * can be deallocated.
95417c478bd9Sstevel@tonic-gate 	 */
95427c478bd9Sstevel@tonic-gate 	ohci_traverse_tds(ohcip, ph);
95437c478bd9Sstevel@tonic-gate 
95447c478bd9Sstevel@tonic-gate 	/*
95457c478bd9Sstevel@tonic-gate 	 * If all of the endpoint's TD's have been deallocated,
95467c478bd9Sstevel@tonic-gate 	 * then the DMA mappings can be torn down. If not there
95477c478bd9Sstevel@tonic-gate 	 * are some TD's on the  done list that have not been
95487c478bd9Sstevel@tonic-gate 	 * processed. Tag these TD's  so that they are thrown
95497c478bd9Sstevel@tonic-gate 	 * away when the done list is processed.
95507c478bd9Sstevel@tonic-gate 	 */
95517c478bd9Sstevel@tonic-gate 	ohci_done_list_tds(ohcip, ph);
95527c478bd9Sstevel@tonic-gate 
95537c478bd9Sstevel@tonic-gate 	/* Do callbacks for all unfinished requests */
95547c478bd9Sstevel@tonic-gate 	ohci_handle_outstanding_requests(ohcip, pp);
95557c478bd9Sstevel@tonic-gate 
95567c478bd9Sstevel@tonic-gate 	/* Free DMA resources */
95577c478bd9Sstevel@tonic-gate 	ohci_free_dma_resources(ohcip, ph);
95587c478bd9Sstevel@tonic-gate 
95597c478bd9Sstevel@tonic-gate 	switch (pipe_state) {
95607c478bd9Sstevel@tonic-gate 	case OHCI_PIPE_STATE_CLOSE:
95617c478bd9Sstevel@tonic-gate 		completion_reason = USB_CR_PIPE_CLOSING;
95627c478bd9Sstevel@tonic-gate 		break;
95637c478bd9Sstevel@tonic-gate 	case OHCI_PIPE_STATE_RESET:
95647c478bd9Sstevel@tonic-gate 	case OHCI_PIPE_STATE_STOP_POLLING:
95657c478bd9Sstevel@tonic-gate 		/* Set completion reason */
95667c478bd9Sstevel@tonic-gate 		completion_reason = (pipe_state ==
95677c478bd9Sstevel@tonic-gate 		    OHCI_PIPE_STATE_RESET) ?
95687c478bd9Sstevel@tonic-gate 		    USB_CR_PIPE_RESET: USB_CR_STOPPED_POLLING;
95697c478bd9Sstevel@tonic-gate 
95707c478bd9Sstevel@tonic-gate 		/* Restore the data toggle information */
95717c478bd9Sstevel@tonic-gate 		ohci_restore_data_toggle(ohcip, ph);
95727c478bd9Sstevel@tonic-gate 
95737c478bd9Sstevel@tonic-gate 		/*
95747c478bd9Sstevel@tonic-gate 		 * Clear the sKip bit to restart all the
95757c478bd9Sstevel@tonic-gate 		 * transactions on this pipe.
95767c478bd9Sstevel@tonic-gate 		 */
95777c478bd9Sstevel@tonic-gate 		ohci_modify_sKip_bit(ohcip, pp,
95787c478bd9Sstevel@tonic-gate 		    CLEAR_sKip, OHCI_FLAGS_NOSLEEP);
95797c478bd9Sstevel@tonic-gate 
95807c478bd9Sstevel@tonic-gate 		/* Set pipe state to idle */
95817c478bd9Sstevel@tonic-gate 		pp->pp_state = OHCI_PIPE_STATE_IDLE;
95827c478bd9Sstevel@tonic-gate 
95837c478bd9Sstevel@tonic-gate 		break;
95847c478bd9Sstevel@tonic-gate 	}
95857c478bd9Sstevel@tonic-gate 
95867c478bd9Sstevel@tonic-gate 	ASSERT((Get_ED(pp->pp_ept->hced_tailp) & HC_EPT_TD_TAIL) ==
95877c478bd9Sstevel@tonic-gate 	    (Get_ED(pp->pp_ept->hced_headp) & HC_EPT_TD_HEAD));
95887c478bd9Sstevel@tonic-gate 
95897c478bd9Sstevel@tonic-gate 	ASSERT((pp->pp_tw_head == NULL) && (pp->pp_tw_tail == NULL));
95907c478bd9Sstevel@tonic-gate 
95917c478bd9Sstevel@tonic-gate 	/*
95927c478bd9Sstevel@tonic-gate 	 * Do the callback for the original client
95937c478bd9Sstevel@tonic-gate 	 * periodic IN request.
95947c478bd9Sstevel@tonic-gate 	 */
95957c478bd9Sstevel@tonic-gate 	if ((OHCI_PERIODIC_ENDPOINT(eptd)) &&
95967c478bd9Sstevel@tonic-gate 	    ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) ==
95977c478bd9Sstevel@tonic-gate 	    USB_EP_DIR_IN)) {
95987c478bd9Sstevel@tonic-gate 
95997c478bd9Sstevel@tonic-gate 		ohci_do_client_periodic_in_req_callback(
96007c478bd9Sstevel@tonic-gate 		    ohcip, pp, completion_reason);
96017c478bd9Sstevel@tonic-gate 	}
96027c478bd9Sstevel@tonic-gate }
96037c478bd9Sstevel@tonic-gate 
96047c478bd9Sstevel@tonic-gate 
96057c478bd9Sstevel@tonic-gate /*
96067c478bd9Sstevel@tonic-gate  * ohci_wait_for_transfers_completion:
96077c478bd9Sstevel@tonic-gate  *
96087c478bd9Sstevel@tonic-gate  * Wait for processing all completed transfers and to send results
96097c478bd9Sstevel@tonic-gate  * to upstream.
96107c478bd9Sstevel@tonic-gate  */
96117c478bd9Sstevel@tonic-gate static void
96127c478bd9Sstevel@tonic-gate ohci_wait_for_transfers_completion(
96137c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
96147c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
96157c478bd9Sstevel@tonic-gate {
96167c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*head_tw = pp->pp_tw_head;
96177c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*next_tw;
96187c478bd9Sstevel@tonic-gate 	clock_t			xfer_cmpl_time_wait;
96197c478bd9Sstevel@tonic-gate 	ohci_td_t		*tailp, *headp, *nextp;
96207c478bd9Sstevel@tonic-gate 	ohci_td_t		*head_td, *next_td;
96217c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ept = pp->pp_ept;
96227c478bd9Sstevel@tonic-gate 	int			rval;
96237c478bd9Sstevel@tonic-gate 
96247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
96257c478bd9Sstevel@tonic-gate 	    "ohci_wait_for_transfers_completion: pp = 0x%p", pp);
96267c478bd9Sstevel@tonic-gate 
96277c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
96287c478bd9Sstevel@tonic-gate 
96297c478bd9Sstevel@tonic-gate 	headp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip,
96307c478bd9Sstevel@tonic-gate 	    Get_ED(ept->hced_headp) & (uint32_t)HC_EPT_TD_HEAD));
96317c478bd9Sstevel@tonic-gate 
96327c478bd9Sstevel@tonic-gate 	tailp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip,
96337c478bd9Sstevel@tonic-gate 	    Get_ED(ept->hced_tailp) & (uint32_t)HC_EPT_TD_TAIL));
96347c478bd9Sstevel@tonic-gate 
96357c478bd9Sstevel@tonic-gate 	rval = ohci_state_is_operational(ohcip);
96367c478bd9Sstevel@tonic-gate 
96377c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
96387c478bd9Sstevel@tonic-gate 
96397c478bd9Sstevel@tonic-gate 		return;
96407c478bd9Sstevel@tonic-gate 	}
96417c478bd9Sstevel@tonic-gate 
96427c478bd9Sstevel@tonic-gate 	pp->pp_count_done_tds = 0;
96437c478bd9Sstevel@tonic-gate 
96447c478bd9Sstevel@tonic-gate 	/* Process the transfer wrappers for this pipe */
96457c478bd9Sstevel@tonic-gate 	next_tw = head_tw;
96467c478bd9Sstevel@tonic-gate 	while (next_tw) {
96477c478bd9Sstevel@tonic-gate 		head_td = (ohci_td_t *)next_tw->tw_hctd_head;
96487c478bd9Sstevel@tonic-gate 		next_td = head_td;
96497c478bd9Sstevel@tonic-gate 
96507c478bd9Sstevel@tonic-gate 		if (head_td) {
96517c478bd9Sstevel@tonic-gate 			/*
96527c478bd9Sstevel@tonic-gate 			 * Walk through each TD for this transfer
96537c478bd9Sstevel@tonic-gate 			 * wrapper. If a TD still exists, then it
96547c478bd9Sstevel@tonic-gate 			 * is currently on the done list.
96557c478bd9Sstevel@tonic-gate 			 */
96567c478bd9Sstevel@tonic-gate 			while (next_td) {
96577c478bd9Sstevel@tonic-gate 
96587c478bd9Sstevel@tonic-gate 				nextp = headp;
96597c478bd9Sstevel@tonic-gate 
96607c478bd9Sstevel@tonic-gate 				while (nextp != tailp) {
96617c478bd9Sstevel@tonic-gate 
96627c478bd9Sstevel@tonic-gate 					/* TD is on the ED */
96637c478bd9Sstevel@tonic-gate 					if (nextp == next_td) {
96647c478bd9Sstevel@tonic-gate 						break;
96657c478bd9Sstevel@tonic-gate 					}
96667c478bd9Sstevel@tonic-gate 
96677c478bd9Sstevel@tonic-gate 					nextp = (ohci_td_t *)
96687c478bd9Sstevel@tonic-gate 					    (ohci_td_iommu_to_cpu(ohcip,
96697c478bd9Sstevel@tonic-gate 					    (Get_TD(nextp->hctd_next_td) &
96707c478bd9Sstevel@tonic-gate 					    HC_EPT_TD_TAIL)));
96717c478bd9Sstevel@tonic-gate 				}
96727c478bd9Sstevel@tonic-gate 
96737c478bd9Sstevel@tonic-gate 				if (nextp == tailp) {
96747c478bd9Sstevel@tonic-gate 					pp->pp_count_done_tds++;
96757c478bd9Sstevel@tonic-gate 				}
96767c478bd9Sstevel@tonic-gate 
96777c478bd9Sstevel@tonic-gate 				next_td = ohci_td_iommu_to_cpu(ohcip,
96787c478bd9Sstevel@tonic-gate 				    Get_TD(next_td->hctd_tw_next_td));
96797c478bd9Sstevel@tonic-gate 			}
96807c478bd9Sstevel@tonic-gate 		}
96817c478bd9Sstevel@tonic-gate 
96827c478bd9Sstevel@tonic-gate 		next_tw = next_tw->tw_next;
96837c478bd9Sstevel@tonic-gate 	}
96847c478bd9Sstevel@tonic-gate 
96857c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
96867c478bd9Sstevel@tonic-gate 	    "ohci_wait_for_transfers_completion: count_done_tds = 0x%x",
96877c478bd9Sstevel@tonic-gate 	    pp->pp_count_done_tds);
96887c478bd9Sstevel@tonic-gate 
96897c478bd9Sstevel@tonic-gate 	if (!pp->pp_count_done_tds) {
96907c478bd9Sstevel@tonic-gate 
96917c478bd9Sstevel@tonic-gate 		return;
96927c478bd9Sstevel@tonic-gate 	}
96937c478bd9Sstevel@tonic-gate 
96947c478bd9Sstevel@tonic-gate 	/* Get the number of clock ticks to wait */
96957c478bd9Sstevel@tonic-gate 	xfer_cmpl_time_wait = drv_usectohz(OHCI_XFER_CMPL_TIMEWAIT * 1000000);
96967c478bd9Sstevel@tonic-gate 
96977c478bd9Sstevel@tonic-gate 	(void) cv_timedwait(&pp->pp_xfer_cmpl_cv,
96987c478bd9Sstevel@tonic-gate 	    &ohcip->ohci_int_mutex,
96997c478bd9Sstevel@tonic-gate 	    ddi_get_lbolt() + xfer_cmpl_time_wait);
97007c478bd9Sstevel@tonic-gate 
97017c478bd9Sstevel@tonic-gate 	if (pp->pp_count_done_tds) {
97027c478bd9Sstevel@tonic-gate 
97037c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
97047c478bd9Sstevel@tonic-gate 		    "ohci_wait_for_transfers_completion: No transfers "
97057c478bd9Sstevel@tonic-gate 		    "completion confirmation received for 0x%x requests",
97067c478bd9Sstevel@tonic-gate 		    pp->pp_count_done_tds);
97077c478bd9Sstevel@tonic-gate 	}
97087c478bd9Sstevel@tonic-gate }
97097c478bd9Sstevel@tonic-gate 
97107c478bd9Sstevel@tonic-gate 
97117c478bd9Sstevel@tonic-gate /*
97127c478bd9Sstevel@tonic-gate  * ohci_check_for_transfers_completion:
97137c478bd9Sstevel@tonic-gate  *
97147c478bd9Sstevel@tonic-gate  * Check whether anybody is waiting for transfers completion event. If so, send
97157c478bd9Sstevel@tonic-gate  * this event and also stop initiating any new transfers on this pipe.
97167c478bd9Sstevel@tonic-gate  */
97177c478bd9Sstevel@tonic-gate static void
97187c478bd9Sstevel@tonic-gate ohci_check_for_transfers_completion(
97197c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
97207c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
97217c478bd9Sstevel@tonic-gate {
97227c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
97237c478bd9Sstevel@tonic-gate 	    "ohci_check_for_transfers_completion: pp = 0x%p", pp);
97247c478bd9Sstevel@tonic-gate 
97257c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
97267c478bd9Sstevel@tonic-gate 
97277c478bd9Sstevel@tonic-gate 	if ((pp->pp_state == OHCI_PIPE_STATE_STOP_POLLING) &&
97287c478bd9Sstevel@tonic-gate 	    (pp->pp_error == USB_CR_NO_RESOURCES) &&
97297c478bd9Sstevel@tonic-gate 	    (pp->pp_cur_periodic_req_cnt == 0)) {
97307c478bd9Sstevel@tonic-gate 
97317c478bd9Sstevel@tonic-gate 		/* Reset pipe error to zero */
97327c478bd9Sstevel@tonic-gate 		pp->pp_error = 0;
97337c478bd9Sstevel@tonic-gate 
97347c478bd9Sstevel@tonic-gate 		/* Do callback for original request */
97357c478bd9Sstevel@tonic-gate 		ohci_do_client_periodic_in_req_callback(
97367c478bd9Sstevel@tonic-gate 		    ohcip, pp, USB_CR_NO_RESOURCES);
97377c478bd9Sstevel@tonic-gate 	}
97387c478bd9Sstevel@tonic-gate 
97397c478bd9Sstevel@tonic-gate 	if (pp->pp_count_done_tds) {
97407c478bd9Sstevel@tonic-gate 
97417c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
97427c478bd9Sstevel@tonic-gate 		    "ohci_check_for_transfers_completion:"
97437c478bd9Sstevel@tonic-gate 		    "count_done_tds = 0x%x", pp->pp_count_done_tds);
97447c478bd9Sstevel@tonic-gate 
97457c478bd9Sstevel@tonic-gate 		/* Decrement the done td count */
97467c478bd9Sstevel@tonic-gate 		pp->pp_count_done_tds--;
97477c478bd9Sstevel@tonic-gate 
97487c478bd9Sstevel@tonic-gate 		if (!pp->pp_count_done_tds) {
97497c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
97507c478bd9Sstevel@tonic-gate 			    "ohci_check_for_transfers_completion:"
97517c478bd9Sstevel@tonic-gate 			    "Sent transfers completion event pp = 0x%p", pp);
97527c478bd9Sstevel@tonic-gate 
97537c478bd9Sstevel@tonic-gate 			/* Send the transfer completion signal */
97547c478bd9Sstevel@tonic-gate 			cv_signal(&pp->pp_xfer_cmpl_cv);
97557c478bd9Sstevel@tonic-gate 		}
97567c478bd9Sstevel@tonic-gate 	}
97577c478bd9Sstevel@tonic-gate }
97587c478bd9Sstevel@tonic-gate 
97597c478bd9Sstevel@tonic-gate 
97607c478bd9Sstevel@tonic-gate /*
97617c478bd9Sstevel@tonic-gate  * ohci_save_data_toggle:
97627c478bd9Sstevel@tonic-gate  *
97637c478bd9Sstevel@tonic-gate  * Save the data toggle information.
97647c478bd9Sstevel@tonic-gate  */
97657c478bd9Sstevel@tonic-gate static void
97667c478bd9Sstevel@tonic-gate ohci_save_data_toggle(
97677c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
97687c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
97697c478bd9Sstevel@tonic-gate {
97707c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
97717c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
97727c478bd9Sstevel@tonic-gate 	uint_t			data_toggle;
97737c478bd9Sstevel@tonic-gate 	usb_cr_t		error = pp->pp_error;
97747c478bd9Sstevel@tonic-gate 	ohci_ed_t		*ed = pp->pp_ept;
97757c478bd9Sstevel@tonic-gate 	ohci_td_t		*headp, *tailp;
97767c478bd9Sstevel@tonic-gate 
97777c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
97787c478bd9Sstevel@tonic-gate 	    "ohci_save_data_toggle: ph = 0x%p", ph);
97797c478bd9Sstevel@tonic-gate 
97807c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
97817c478bd9Sstevel@tonic-gate 
97827c478bd9Sstevel@tonic-gate 	/* Reset the pipe error value */
97837c478bd9Sstevel@tonic-gate 	pp->pp_error = USB_CR_OK;
97847c478bd9Sstevel@tonic-gate 
97857c478bd9Sstevel@tonic-gate 	/* Return immediately if it is a control or isoc pipe */
97867c478bd9Sstevel@tonic-gate 	if (((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
97877c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_CONTROL) || ((eptd->bmAttributes &
97887c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH)) {
97897c478bd9Sstevel@tonic-gate 
97907c478bd9Sstevel@tonic-gate 		return;
97917c478bd9Sstevel@tonic-gate 	}
97927c478bd9Sstevel@tonic-gate 
97937c478bd9Sstevel@tonic-gate 	headp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip,
97947c478bd9Sstevel@tonic-gate 	    Get_ED(ed->hced_headp) & (uint32_t)HC_EPT_TD_HEAD));
97957c478bd9Sstevel@tonic-gate 
97967c478bd9Sstevel@tonic-gate 	tailp = (ohci_td_t *)(ohci_td_iommu_to_cpu(ohcip,
97977c478bd9Sstevel@tonic-gate 	    Get_ED(ed->hced_tailp) & (uint32_t)HC_EPT_TD_TAIL));
97987c478bd9Sstevel@tonic-gate 
97997c478bd9Sstevel@tonic-gate 	/*
98007c478bd9Sstevel@tonic-gate 	 * Retrieve the data toggle information either from the endpoint
98017c478bd9Sstevel@tonic-gate 	 * (ED) or from the transfer descriptor (TD) depending on the
98027c478bd9Sstevel@tonic-gate 	 * situation.
98037c478bd9Sstevel@tonic-gate 	 */
98047c478bd9Sstevel@tonic-gate 	if ((Get_ED(ed->hced_headp) & HC_EPT_Halt) || (headp == tailp)) {
98057c478bd9Sstevel@tonic-gate 
98067c478bd9Sstevel@tonic-gate 		/* Get the data toggle information from the endpoint */
98077c478bd9Sstevel@tonic-gate 		data_toggle = (Get_ED(ed->hced_headp) &
98087c478bd9Sstevel@tonic-gate 		    HC_EPT_Carry)? DATA1:DATA0;
98097c478bd9Sstevel@tonic-gate 	} else {
98107c478bd9Sstevel@tonic-gate 		/*
98117c478bd9Sstevel@tonic-gate 		 * Retrieve the data toggle information depending on the
98127c478bd9Sstevel@tonic-gate 		 * master data toggle information saved in  the transfer
98137c478bd9Sstevel@tonic-gate 		 * descriptor (TD) at the head of the endpoint (ED).
98147c478bd9Sstevel@tonic-gate 		 *
98157c478bd9Sstevel@tonic-gate 		 * Check for master data toggle information .
98167c478bd9Sstevel@tonic-gate 		 */
98177c478bd9Sstevel@tonic-gate 		if (Get_TD(headp->hctd_ctrl) & HC_TD_MS_DT) {
98187c478bd9Sstevel@tonic-gate 			/* Get the data toggle information from td */
98197c478bd9Sstevel@tonic-gate 			data_toggle = (Get_TD(headp->hctd_ctrl) &
98207c478bd9Sstevel@tonic-gate 			    HC_TD_DT_1) ? DATA1:DATA0;
98217c478bd9Sstevel@tonic-gate 		} else {
98227c478bd9Sstevel@tonic-gate 			/* Get the data toggle information from the endpoint */
98237c478bd9Sstevel@tonic-gate 			data_toggle = (Get_ED(ed->hced_headp) &
98247c478bd9Sstevel@tonic-gate 			    HC_EPT_Carry)? DATA1:DATA0;
98257c478bd9Sstevel@tonic-gate 		}
98267c478bd9Sstevel@tonic-gate 	}
98277c478bd9Sstevel@tonic-gate 
98287c478bd9Sstevel@tonic-gate 	/*
98297c478bd9Sstevel@tonic-gate 	 * If error is STALL, then, set
98307c478bd9Sstevel@tonic-gate 	 * data toggle to zero.
98317c478bd9Sstevel@tonic-gate 	 */
98327c478bd9Sstevel@tonic-gate 	if (error == USB_CR_STALL) {
98337c478bd9Sstevel@tonic-gate 		data_toggle = DATA0;
98347c478bd9Sstevel@tonic-gate 	}
98357c478bd9Sstevel@tonic-gate 
98367c478bd9Sstevel@tonic-gate 	/*
98377c478bd9Sstevel@tonic-gate 	 * Save the data toggle information
98387c478bd9Sstevel@tonic-gate 	 * in the usb device structure.
98397c478bd9Sstevel@tonic-gate 	 */
98407c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
98417c478bd9Sstevel@tonic-gate 	usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
98427c478bd9Sstevel@tonic-gate 	    data_toggle);
98437c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
98447c478bd9Sstevel@tonic-gate }
98457c478bd9Sstevel@tonic-gate 
98467c478bd9Sstevel@tonic-gate 
98477c478bd9Sstevel@tonic-gate /*
98487c478bd9Sstevel@tonic-gate  * ohci_restore_data_toggle:
98497c478bd9Sstevel@tonic-gate  *
98507c478bd9Sstevel@tonic-gate  * Restore the data toggle information.
98517c478bd9Sstevel@tonic-gate  */
98527c478bd9Sstevel@tonic-gate static void
98537c478bd9Sstevel@tonic-gate ohci_restore_data_toggle(
98547c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
98557c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
98567c478bd9Sstevel@tonic-gate {
98577c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
98587c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
98597c478bd9Sstevel@tonic-gate 	uint_t			data_toggle = 0;
98607c478bd9Sstevel@tonic-gate 
98617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
98627c478bd9Sstevel@tonic-gate 	    "ohci_restore_data_toggle: ph = 0x%p", ph);
98637c478bd9Sstevel@tonic-gate 
98647c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
98657c478bd9Sstevel@tonic-gate 
98667c478bd9Sstevel@tonic-gate 	/*
98677c478bd9Sstevel@tonic-gate 	 * Return immediately if it is a control or isoc pipe.
98687c478bd9Sstevel@tonic-gate 	 */
98697c478bd9Sstevel@tonic-gate 	if (((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
98707c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_CONTROL) || ((eptd->bmAttributes &
98717c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH)) {
98727c478bd9Sstevel@tonic-gate 
98737c478bd9Sstevel@tonic-gate 		return;
98747c478bd9Sstevel@tonic-gate 	}
98757c478bd9Sstevel@tonic-gate 
98767c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
98777c478bd9Sstevel@tonic-gate 
98787c478bd9Sstevel@tonic-gate 	data_toggle = usba_hcdi_get_data_toggle(ph->p_usba_device,
98797c478bd9Sstevel@tonic-gate 	    ph->p_ep.bEndpointAddress);
98807c478bd9Sstevel@tonic-gate 	usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
98817c478bd9Sstevel@tonic-gate 	    0);
98827c478bd9Sstevel@tonic-gate 
98837c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
98847c478bd9Sstevel@tonic-gate 
98857c478bd9Sstevel@tonic-gate 	/*
98867c478bd9Sstevel@tonic-gate 	 * Restore the data toggle bit depending on the
98877c478bd9Sstevel@tonic-gate 	 * previous data toggle information.
98887c478bd9Sstevel@tonic-gate 	 */
98897c478bd9Sstevel@tonic-gate 	if (data_toggle) {
98907c478bd9Sstevel@tonic-gate 		Set_ED(pp->pp_ept->hced_headp,
98917c478bd9Sstevel@tonic-gate 		    Get_ED(pp->pp_ept->hced_headp) | HC_EPT_Carry);
98927c478bd9Sstevel@tonic-gate 	} else {
98937c478bd9Sstevel@tonic-gate 		Set_ED(pp->pp_ept->hced_headp,
98947c478bd9Sstevel@tonic-gate 		    Get_ED(pp->pp_ept->hced_headp) & (~HC_EPT_Carry));
98957c478bd9Sstevel@tonic-gate 	}
98967c478bd9Sstevel@tonic-gate }
98977c478bd9Sstevel@tonic-gate 
98987c478bd9Sstevel@tonic-gate 
98997c478bd9Sstevel@tonic-gate /*
99007c478bd9Sstevel@tonic-gate  * ohci_handle_outstanding_requests
99017c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
99027c478bd9Sstevel@tonic-gate  *
99037c478bd9Sstevel@tonic-gate  * Deallocate interrupt/isochronous request structure for the
99047c478bd9Sstevel@tonic-gate  * interrupt/isochronous IN transfer. Do the callbacks for all
99057c478bd9Sstevel@tonic-gate  * unfinished requests.
99067c478bd9Sstevel@tonic-gate  */
99077c478bd9Sstevel@tonic-gate void
99087c478bd9Sstevel@tonic-gate ohci_handle_outstanding_requests(
99097c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
99107c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp)
99117c478bd9Sstevel@tonic-gate {
99127c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
99137c478bd9Sstevel@tonic-gate 	usb_ep_descr_t	*eptd = &ph->p_ep;
99147c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*curr_tw;
99157c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*next_tw;
99167c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp;
99177c478bd9Sstevel@tonic-gate 
99187c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
99197c478bd9Sstevel@tonic-gate 	    "ohci_handle_outstanding_requests: pp = 0x%p", pp);
99207c478bd9Sstevel@tonic-gate 
99217c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
99227c478bd9Sstevel@tonic-gate 
99237c478bd9Sstevel@tonic-gate 	/*
99247c478bd9Sstevel@tonic-gate 	 * Deallocate all the pre-allocated interrupt requests
99257c478bd9Sstevel@tonic-gate 	 */
99267c478bd9Sstevel@tonic-gate 	next_tw = pp->pp_tw_head;
99277c478bd9Sstevel@tonic-gate 
99287c478bd9Sstevel@tonic-gate 	while (next_tw) {
99297c478bd9Sstevel@tonic-gate 		curr_tw = next_tw;
99307c478bd9Sstevel@tonic-gate 		next_tw = curr_tw->tw_next;
99317c478bd9Sstevel@tonic-gate 
99327c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = curr_tw->tw_curr_xfer_reqp;
99337c478bd9Sstevel@tonic-gate 
99347c478bd9Sstevel@tonic-gate 		/* Deallocate current interrupt request */
99357c478bd9Sstevel@tonic-gate 		if (curr_xfer_reqp) {
99367c478bd9Sstevel@tonic-gate 
99377c478bd9Sstevel@tonic-gate 			if ((OHCI_PERIODIC_ENDPOINT(eptd)) &&
99387c478bd9Sstevel@tonic-gate 			    (curr_tw->tw_direction == HC_TD_IN)) {
99397c478bd9Sstevel@tonic-gate 
99407c478bd9Sstevel@tonic-gate 				/* Decrement periodic in request count */
99417c478bd9Sstevel@tonic-gate 				pp->pp_cur_periodic_req_cnt--;
99427c478bd9Sstevel@tonic-gate 
99437c478bd9Sstevel@tonic-gate 				ohci_deallocate_periodic_in_resource(
99447c478bd9Sstevel@tonic-gate 				    ohcip, pp, curr_tw);
99457c478bd9Sstevel@tonic-gate 			} else {
99467c478bd9Sstevel@tonic-gate 				ohci_hcdi_callback(ph,
99477c478bd9Sstevel@tonic-gate 				    curr_tw, USB_CR_FLUSHED);
99487c478bd9Sstevel@tonic-gate 			}
99497c478bd9Sstevel@tonic-gate 		}
99507c478bd9Sstevel@tonic-gate 	}
99517c478bd9Sstevel@tonic-gate }
99527c478bd9Sstevel@tonic-gate 
99537c478bd9Sstevel@tonic-gate 
99547c478bd9Sstevel@tonic-gate /*
99557c478bd9Sstevel@tonic-gate  * ohci_deallocate_periodic_in_resource
99567c478bd9Sstevel@tonic-gate  *
99577c478bd9Sstevel@tonic-gate  * Deallocate interrupt/isochronous request structure for the
99587c478bd9Sstevel@tonic-gate  * interrupt/isochronous IN transfer.
99597c478bd9Sstevel@tonic-gate  */
99607c478bd9Sstevel@tonic-gate static void
99617c478bd9Sstevel@tonic-gate ohci_deallocate_periodic_in_resource(
99627c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
99637c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
99647c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
99657c478bd9Sstevel@tonic-gate {
99667c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
99677c478bd9Sstevel@tonic-gate 	uchar_t			ep_attr = ph->p_ep.bmAttributes;
99687c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp;
99697c478bd9Sstevel@tonic-gate 
99707c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
99717c478bd9Sstevel@tonic-gate 	    "ohci_deallocate_periodic_in_resource: "
99727c478bd9Sstevel@tonic-gate 	    "pp = 0x%p tw = 0x%p", pp, tw);
99737c478bd9Sstevel@tonic-gate 
99747c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
99757c478bd9Sstevel@tonic-gate 
99767c478bd9Sstevel@tonic-gate 	curr_xfer_reqp = tw->tw_curr_xfer_reqp;
99777c478bd9Sstevel@tonic-gate 
99787c478bd9Sstevel@tonic-gate 	/* Check the current periodic in request pointer */
99797c478bd9Sstevel@tonic-gate 	if (curr_xfer_reqp) {
99807c478bd9Sstevel@tonic-gate 		/*
99817c478bd9Sstevel@tonic-gate 		 * Reset periodic in request usb isoch
99827c478bd9Sstevel@tonic-gate 		 * packet request pointers to null.
99837c478bd9Sstevel@tonic-gate 		 */
99847c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = NULL;
99857c478bd9Sstevel@tonic-gate 		tw->tw_curr_isoc_pktp = NULL;
99867c478bd9Sstevel@tonic-gate 
99877c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
99887c478bd9Sstevel@tonic-gate 		ph->p_req_count--;
99897c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
99907c478bd9Sstevel@tonic-gate 
99917c478bd9Sstevel@tonic-gate 		/*
99927c478bd9Sstevel@tonic-gate 		 * Free pre-allocated interrupt
99937c478bd9Sstevel@tonic-gate 		 * or isochronous requests.
99947c478bd9Sstevel@tonic-gate 		 */
99957c478bd9Sstevel@tonic-gate 		switch (ep_attr & USB_EP_ATTR_MASK) {
99967c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
99977c478bd9Sstevel@tonic-gate 			usb_free_intr_req(
99987c478bd9Sstevel@tonic-gate 			    (usb_intr_req_t *)curr_xfer_reqp);
99997c478bd9Sstevel@tonic-gate 			break;
100007c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
100017c478bd9Sstevel@tonic-gate 			usb_free_isoc_req(
100027c478bd9Sstevel@tonic-gate 			    (usb_isoc_req_t *)curr_xfer_reqp);
100037c478bd9Sstevel@tonic-gate 			break;
100047c478bd9Sstevel@tonic-gate 		}
100057c478bd9Sstevel@tonic-gate 	}
100067c478bd9Sstevel@tonic-gate }
100077c478bd9Sstevel@tonic-gate 
100087c478bd9Sstevel@tonic-gate 
100097c478bd9Sstevel@tonic-gate /*
100107c478bd9Sstevel@tonic-gate  * ohci_do_client_periodic_in_req_callback
100117c478bd9Sstevel@tonic-gate  *
100127c478bd9Sstevel@tonic-gate  * Do callback for the original client periodic IN request.
100137c478bd9Sstevel@tonic-gate  */
100147c478bd9Sstevel@tonic-gate static void
100157c478bd9Sstevel@tonic-gate ohci_do_client_periodic_in_req_callback(
100167c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip,
100177c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp,
100187c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason)
100197c478bd9Sstevel@tonic-gate {
100207c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
100217c478bd9Sstevel@tonic-gate 
100227c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
100237c478bd9Sstevel@tonic-gate 	    "ohci_do_client_periodic_in_req_callback: "
100247c478bd9Sstevel@tonic-gate 	    "pp = 0x%p cc = 0x%x", pp, completion_reason);
100257c478bd9Sstevel@tonic-gate 
100267c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
100277c478bd9Sstevel@tonic-gate 
100287c478bd9Sstevel@tonic-gate 	/*
100297c478bd9Sstevel@tonic-gate 	 * Check for Interrupt/Isochronous IN, whether we need to do
100307c478bd9Sstevel@tonic-gate 	 * callback for the original client's periodic IN request.
100317c478bd9Sstevel@tonic-gate 	 */
100327c478bd9Sstevel@tonic-gate 	if (pp->pp_client_periodic_in_reqp) {
100337c478bd9Sstevel@tonic-gate 		ASSERT(pp->pp_cur_periodic_req_cnt == 0);
100347c478bd9Sstevel@tonic-gate 		ohci_hcdi_callback(ph, NULL, completion_reason);
100357c478bd9Sstevel@tonic-gate 	}
100367c478bd9Sstevel@tonic-gate }
100377c478bd9Sstevel@tonic-gate 
100387c478bd9Sstevel@tonic-gate 
100397c478bd9Sstevel@tonic-gate /*
100407c478bd9Sstevel@tonic-gate  * ohci_hcdi_callback()
100417c478bd9Sstevel@tonic-gate  *
100427c478bd9Sstevel@tonic-gate  * Convenience wrapper around usba_hcdi_cb() other than root hub.
100437c478bd9Sstevel@tonic-gate  */
100447c478bd9Sstevel@tonic-gate static void
100457c478bd9Sstevel@tonic-gate ohci_hcdi_callback(
100467c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
100477c478bd9Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
100487c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason)
100497c478bd9Sstevel@tonic-gate {
100507c478bd9Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_obtain_state(
100517c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
100527c478bd9Sstevel@tonic-gate 	uchar_t			attributes = ph->p_ep.bmAttributes &
100537c478bd9Sstevel@tonic-gate 							USB_EP_ATTR_MASK;
100547c478bd9Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = (ohci_pipe_private_t *)ph->p_hcd_private;
100557c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp;
100567c478bd9Sstevel@tonic-gate 	uint_t			pipe_state = 0;
100577c478bd9Sstevel@tonic-gate 
100587c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
100597c478bd9Sstevel@tonic-gate 	    "ohci_hcdi_callback: ph = 0x%p, tw = 0x%p, cr = 0x%x",
100607c478bd9Sstevel@tonic-gate 	    ph, tw, completion_reason);
100617c478bd9Sstevel@tonic-gate 
100627c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
100637c478bd9Sstevel@tonic-gate 
100647c478bd9Sstevel@tonic-gate 	/* Set the pipe state as per completion reason */
100657c478bd9Sstevel@tonic-gate 	switch (completion_reason) {
100667c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
100677c478bd9Sstevel@tonic-gate 		pipe_state = pp->pp_state;
100687c478bd9Sstevel@tonic-gate 		break;
100697c478bd9Sstevel@tonic-gate 	case USB_CR_NO_RESOURCES:
100707c478bd9Sstevel@tonic-gate 	case USB_CR_NOT_SUPPORTED:
100717c478bd9Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
100727c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
100737c478bd9Sstevel@tonic-gate 		pipe_state = OHCI_PIPE_STATE_IDLE;
100747c478bd9Sstevel@tonic-gate 		break;
100757c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_CLOSING:
100767c478bd9Sstevel@tonic-gate 		break;
100777c478bd9Sstevel@tonic-gate 	default:
100787c478bd9Sstevel@tonic-gate 		/*
100797c478bd9Sstevel@tonic-gate 		 * Set the pipe state to error
100807c478bd9Sstevel@tonic-gate 		 * except for the isoc pipe.
100817c478bd9Sstevel@tonic-gate 		 */
100827c478bd9Sstevel@tonic-gate 		if (attributes != USB_EP_ATTR_ISOCH) {
100837c478bd9Sstevel@tonic-gate 			pipe_state = OHCI_PIPE_STATE_ERROR;
100847c478bd9Sstevel@tonic-gate 			pp->pp_error = completion_reason;
100857c478bd9Sstevel@tonic-gate 		}
100867c478bd9Sstevel@tonic-gate 		break;
100877c478bd9Sstevel@tonic-gate 
100887c478bd9Sstevel@tonic-gate 	}
100897c478bd9Sstevel@tonic-gate 
100907c478bd9Sstevel@tonic-gate 	pp->pp_state = pipe_state;
100917c478bd9Sstevel@tonic-gate 
100927c478bd9Sstevel@tonic-gate 	if (tw && tw->tw_curr_xfer_reqp) {
100937c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = tw->tw_curr_xfer_reqp;
100947c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = NULL;
100957c478bd9Sstevel@tonic-gate 		tw->tw_curr_isoc_pktp = NULL;
100967c478bd9Sstevel@tonic-gate 	} else {
100977c478bd9Sstevel@tonic-gate 		ASSERT(pp->pp_client_periodic_in_reqp != NULL);
100987c478bd9Sstevel@tonic-gate 
100997c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = pp->pp_client_periodic_in_reqp;
101007c478bd9Sstevel@tonic-gate 		pp->pp_client_periodic_in_reqp = NULL;
101017c478bd9Sstevel@tonic-gate 	}
101027c478bd9Sstevel@tonic-gate 
101037c478bd9Sstevel@tonic-gate 	ASSERT(curr_xfer_reqp != NULL);
101047c478bd9Sstevel@tonic-gate 
101057c478bd9Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
101067c478bd9Sstevel@tonic-gate 
101077c478bd9Sstevel@tonic-gate 	usba_hcdi_cb(ph, curr_xfer_reqp, completion_reason);
101087c478bd9Sstevel@tonic-gate 
101097c478bd9Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
101107c478bd9Sstevel@tonic-gate }
101117c478bd9Sstevel@tonic-gate 
101127c478bd9Sstevel@tonic-gate 
101137c478bd9Sstevel@tonic-gate /*
101147c478bd9Sstevel@tonic-gate  * ohci kstat functions
101157c478bd9Sstevel@tonic-gate  */
101167c478bd9Sstevel@tonic-gate 
101177c478bd9Sstevel@tonic-gate /*
101187c478bd9Sstevel@tonic-gate  * ohci_create_stats:
101197c478bd9Sstevel@tonic-gate  *
101207c478bd9Sstevel@tonic-gate  * Allocate and initialize the ohci kstat structures
101217c478bd9Sstevel@tonic-gate  */
101227c478bd9Sstevel@tonic-gate static void
101237c478bd9Sstevel@tonic-gate ohci_create_stats(ohci_state_t	*ohcip)
101247c478bd9Sstevel@tonic-gate {
101257c478bd9Sstevel@tonic-gate 	char			kstatname[KSTAT_STRLEN];
101267c478bd9Sstevel@tonic-gate 	const char		*dname = ddi_driver_name(ohcip->ohci_dip);
101277c478bd9Sstevel@tonic-gate 	char			*usbtypes[USB_N_COUNT_KSTATS] =
101287c478bd9Sstevel@tonic-gate 				    {"ctrl", "isoch", "bulk", "intr"};
101297c478bd9Sstevel@tonic-gate 	uint_t			instance = ohcip->ohci_instance;
101307c478bd9Sstevel@tonic-gate 	ohci_intrs_stats_t	*isp;
101317c478bd9Sstevel@tonic-gate 	int			i;
101327c478bd9Sstevel@tonic-gate 
101337c478bd9Sstevel@tonic-gate 	if (OHCI_INTRS_STATS(ohcip) == NULL) {
101347c478bd9Sstevel@tonic-gate 		(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,intrs",
101357c478bd9Sstevel@tonic-gate 		    dname, instance);
101367c478bd9Sstevel@tonic-gate 		OHCI_INTRS_STATS(ohcip) = kstat_create("usba", instance,
101377c478bd9Sstevel@tonic-gate 		    kstatname, "usb_interrupts", KSTAT_TYPE_NAMED,
101387c478bd9Sstevel@tonic-gate 		    sizeof (ohci_intrs_stats_t) / sizeof (kstat_named_t),
101397c478bd9Sstevel@tonic-gate 		    KSTAT_FLAG_PERSISTENT);
101407c478bd9Sstevel@tonic-gate 
101417c478bd9Sstevel@tonic-gate 		if (OHCI_INTRS_STATS(ohcip)) {
101427c478bd9Sstevel@tonic-gate 			isp = OHCI_INTRS_STATS_DATA(ohcip);
101437c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->ohci_hcr_intr_total,
101447c478bd9Sstevel@tonic-gate 			"Interrupts Total", KSTAT_DATA_UINT64);
101457c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->ohci_hcr_intr_not_claimed,
101467c478bd9Sstevel@tonic-gate 			"Not Claimed", KSTAT_DATA_UINT64);
101477c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->ohci_hcr_intr_so,
101487c478bd9Sstevel@tonic-gate 			"Schedule Overruns", KSTAT_DATA_UINT64);
101497c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->ohci_hcr_intr_wdh,
101507c478bd9Sstevel@tonic-gate 			"Writeback Done Head", KSTAT_DATA_UINT64);
101517c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->ohci_hcr_intr_sof,
101527c478bd9Sstevel@tonic-gate 			"Start Of Frame", KSTAT_DATA_UINT64);
101537c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->ohci_hcr_intr_rd,
101547c478bd9Sstevel@tonic-gate 			"Resume Detected", KSTAT_DATA_UINT64);
101557c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->ohci_hcr_intr_ue,
101567c478bd9Sstevel@tonic-gate 			"Unrecoverable Error", KSTAT_DATA_UINT64);
101577c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->ohci_hcr_intr_fno,
101587c478bd9Sstevel@tonic-gate 			"Frame No. Overflow", KSTAT_DATA_UINT64);
101597c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->ohci_hcr_intr_rhsc,
101607c478bd9Sstevel@tonic-gate 			"Root Hub Status Change", KSTAT_DATA_UINT64);
101617c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->ohci_hcr_intr_oc,
101627c478bd9Sstevel@tonic-gate 			"Change In Ownership", KSTAT_DATA_UINT64);
101637c478bd9Sstevel@tonic-gate 
101647c478bd9Sstevel@tonic-gate 			OHCI_INTRS_STATS(ohcip)->ks_private = ohcip;
101657c478bd9Sstevel@tonic-gate 			OHCI_INTRS_STATS(ohcip)->ks_update = nulldev;
101667c478bd9Sstevel@tonic-gate 			kstat_install(OHCI_INTRS_STATS(ohcip));
101677c478bd9Sstevel@tonic-gate 		}
101687c478bd9Sstevel@tonic-gate 	}
101697c478bd9Sstevel@tonic-gate 
101707c478bd9Sstevel@tonic-gate 	if (OHCI_TOTAL_STATS(ohcip) == NULL) {
101717c478bd9Sstevel@tonic-gate 		(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,total",
101727c478bd9Sstevel@tonic-gate 		    dname, instance);
101737c478bd9Sstevel@tonic-gate 		OHCI_TOTAL_STATS(ohcip) = kstat_create("usba", instance,
101747c478bd9Sstevel@tonic-gate 		    kstatname, "usb_byte_count", KSTAT_TYPE_IO, 1,
101757c478bd9Sstevel@tonic-gate 		    KSTAT_FLAG_PERSISTENT);
101767c478bd9Sstevel@tonic-gate 
101777c478bd9Sstevel@tonic-gate 		if (OHCI_TOTAL_STATS(ohcip)) {
101787c478bd9Sstevel@tonic-gate 			kstat_install(OHCI_TOTAL_STATS(ohcip));
101797c478bd9Sstevel@tonic-gate 		}
101807c478bd9Sstevel@tonic-gate 	}
101817c478bd9Sstevel@tonic-gate 
101827c478bd9Sstevel@tonic-gate 	for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
101837c478bd9Sstevel@tonic-gate 		if (ohcip->ohci_count_stats[i] == NULL) {
101847c478bd9Sstevel@tonic-gate 			(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,%s",
101857c478bd9Sstevel@tonic-gate 			    dname, instance, usbtypes[i]);
101867c478bd9Sstevel@tonic-gate 			ohcip->ohci_count_stats[i] = kstat_create("usba",
101877c478bd9Sstevel@tonic-gate 			    instance, kstatname, "usb_byte_count",
101887c478bd9Sstevel@tonic-gate 			    KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
101897c478bd9Sstevel@tonic-gate 
101907c478bd9Sstevel@tonic-gate 			if (ohcip->ohci_count_stats[i]) {
101917c478bd9Sstevel@tonic-gate 				kstat_install(ohcip->ohci_count_stats[i]);
101927c478bd9Sstevel@tonic-gate 			}
101937c478bd9Sstevel@tonic-gate 		}
101947c478bd9Sstevel@tonic-gate 	}
101957c478bd9Sstevel@tonic-gate }
101967c478bd9Sstevel@tonic-gate 
101977c478bd9Sstevel@tonic-gate 
101987c478bd9Sstevel@tonic-gate /*
101997c478bd9Sstevel@tonic-gate  * ohci_destroy_stats:
102007c478bd9Sstevel@tonic-gate  *
102017c478bd9Sstevel@tonic-gate  * Clean up ohci kstat structures
102027c478bd9Sstevel@tonic-gate  */
102037c478bd9Sstevel@tonic-gate static void
102047c478bd9Sstevel@tonic-gate ohci_destroy_stats(ohci_state_t	*ohcip)
102057c478bd9Sstevel@tonic-gate {
102067c478bd9Sstevel@tonic-gate 	int	i;
102077c478bd9Sstevel@tonic-gate 
102087c478bd9Sstevel@tonic-gate 	if (OHCI_INTRS_STATS(ohcip)) {
102097c478bd9Sstevel@tonic-gate 		kstat_delete(OHCI_INTRS_STATS(ohcip));
102107c478bd9Sstevel@tonic-gate 		OHCI_INTRS_STATS(ohcip) = NULL;
102117c478bd9Sstevel@tonic-gate 	}
102127c478bd9Sstevel@tonic-gate 
102137c478bd9Sstevel@tonic-gate 	if (OHCI_TOTAL_STATS(ohcip)) {
102147c478bd9Sstevel@tonic-gate 		kstat_delete(OHCI_TOTAL_STATS(ohcip));
102157c478bd9Sstevel@tonic-gate 		OHCI_TOTAL_STATS(ohcip) = NULL;
102167c478bd9Sstevel@tonic-gate 	}
102177c478bd9Sstevel@tonic-gate 
102187c478bd9Sstevel@tonic-gate 	for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
102197c478bd9Sstevel@tonic-gate 		if (ohcip->ohci_count_stats[i]) {
102207c478bd9Sstevel@tonic-gate 			kstat_delete(ohcip->ohci_count_stats[i]);
102217c478bd9Sstevel@tonic-gate 			ohcip->ohci_count_stats[i] = NULL;
102227c478bd9Sstevel@tonic-gate 		}
102237c478bd9Sstevel@tonic-gate 	}
102247c478bd9Sstevel@tonic-gate }
102257c478bd9Sstevel@tonic-gate 
102267c478bd9Sstevel@tonic-gate 
102277c478bd9Sstevel@tonic-gate /*
102287c478bd9Sstevel@tonic-gate  * ohci_do_intrs_stats:
102297c478bd9Sstevel@tonic-gate  *
102307c478bd9Sstevel@tonic-gate  * ohci status information
102317c478bd9Sstevel@tonic-gate  */
102327c478bd9Sstevel@tonic-gate static void
102337c478bd9Sstevel@tonic-gate ohci_do_intrs_stats(
102347c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
102357c478bd9Sstevel@tonic-gate 	int		val)
102367c478bd9Sstevel@tonic-gate {
102377c478bd9Sstevel@tonic-gate 	if (OHCI_INTRS_STATS(ohcip)) {
102387c478bd9Sstevel@tonic-gate 		OHCI_INTRS_STATS_DATA(ohcip)->ohci_hcr_intr_total.value.ui64++;
102397c478bd9Sstevel@tonic-gate 		switch (val) {
102407c478bd9Sstevel@tonic-gate 			case HCR_INTR_SO:
102417c478bd9Sstevel@tonic-gate 				OHCI_INTRS_STATS_DATA(ohcip)->
102427c478bd9Sstevel@tonic-gate 				    ohci_hcr_intr_so.value.ui64++;
102437c478bd9Sstevel@tonic-gate 				break;
102447c478bd9Sstevel@tonic-gate 			case HCR_INTR_WDH:
102457c478bd9Sstevel@tonic-gate 				OHCI_INTRS_STATS_DATA(ohcip)->
102467c478bd9Sstevel@tonic-gate 				    ohci_hcr_intr_wdh.value.ui64++;
102477c478bd9Sstevel@tonic-gate 				break;
102487c478bd9Sstevel@tonic-gate 			case HCR_INTR_SOF:
102497c478bd9Sstevel@tonic-gate 				OHCI_INTRS_STATS_DATA(ohcip)->
102507c478bd9Sstevel@tonic-gate 				    ohci_hcr_intr_sof.value.ui64++;
102517c478bd9Sstevel@tonic-gate 				break;
102527c478bd9Sstevel@tonic-gate 			case HCR_INTR_RD:
102537c478bd9Sstevel@tonic-gate 				OHCI_INTRS_STATS_DATA(ohcip)->
102547c478bd9Sstevel@tonic-gate 				    ohci_hcr_intr_rd.value.ui64++;
102557c478bd9Sstevel@tonic-gate 				break;
102567c478bd9Sstevel@tonic-gate 			case HCR_INTR_UE:
102577c478bd9Sstevel@tonic-gate 				OHCI_INTRS_STATS_DATA(ohcip)->
102587c478bd9Sstevel@tonic-gate 				    ohci_hcr_intr_ue.value.ui64++;
102597c478bd9Sstevel@tonic-gate 				break;
102607c478bd9Sstevel@tonic-gate 			case HCR_INTR_FNO:
102617c478bd9Sstevel@tonic-gate 				OHCI_INTRS_STATS_DATA(ohcip)->
102627c478bd9Sstevel@tonic-gate 				    ohci_hcr_intr_fno.value.ui64++;
102637c478bd9Sstevel@tonic-gate 				break;
102647c478bd9Sstevel@tonic-gate 			case HCR_INTR_RHSC:
102657c478bd9Sstevel@tonic-gate 				OHCI_INTRS_STATS_DATA(ohcip)->
102667c478bd9Sstevel@tonic-gate 				    ohci_hcr_intr_rhsc.value.ui64++;
102677c478bd9Sstevel@tonic-gate 				break;
102687c478bd9Sstevel@tonic-gate 			case HCR_INTR_OC:
102697c478bd9Sstevel@tonic-gate 				OHCI_INTRS_STATS_DATA(ohcip)->
102707c478bd9Sstevel@tonic-gate 				    ohci_hcr_intr_oc.value.ui64++;
102717c478bd9Sstevel@tonic-gate 				break;
102727c478bd9Sstevel@tonic-gate 			default:
102737c478bd9Sstevel@tonic-gate 				OHCI_INTRS_STATS_DATA(ohcip)->
102747c478bd9Sstevel@tonic-gate 				    ohci_hcr_intr_not_claimed.value.ui64++;
102757c478bd9Sstevel@tonic-gate 				    break;
102767c478bd9Sstevel@tonic-gate 		}
102777c478bd9Sstevel@tonic-gate 	}
102787c478bd9Sstevel@tonic-gate }
102797c478bd9Sstevel@tonic-gate 
102807c478bd9Sstevel@tonic-gate 
102817c478bd9Sstevel@tonic-gate /*
102827c478bd9Sstevel@tonic-gate  * ohci_do_byte_stats:
102837c478bd9Sstevel@tonic-gate  *
102847c478bd9Sstevel@tonic-gate  * ohci data xfer information
102857c478bd9Sstevel@tonic-gate  */
102867c478bd9Sstevel@tonic-gate static void
102877c478bd9Sstevel@tonic-gate ohci_do_byte_stats(
102887c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
102897c478bd9Sstevel@tonic-gate 	size_t		len,
102907c478bd9Sstevel@tonic-gate 	uint8_t		attr,
102917c478bd9Sstevel@tonic-gate 	uint8_t		addr)
102927c478bd9Sstevel@tonic-gate {
102937c478bd9Sstevel@tonic-gate 	uint8_t 	type = attr & USB_EP_ATTR_MASK;
102947c478bd9Sstevel@tonic-gate 	uint8_t 	dir = addr & USB_EP_DIR_MASK;
102957c478bd9Sstevel@tonic-gate 
102967c478bd9Sstevel@tonic-gate 	if (dir == USB_EP_DIR_IN) {
102977c478bd9Sstevel@tonic-gate 		OHCI_TOTAL_STATS_DATA(ohcip)->reads++;
102987c478bd9Sstevel@tonic-gate 		OHCI_TOTAL_STATS_DATA(ohcip)->nread += len;
102997c478bd9Sstevel@tonic-gate 		switch (type) {
103007c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_CONTROL:
103017c478bd9Sstevel@tonic-gate 				OHCI_CTRL_STATS(ohcip)->reads++;
103027c478bd9Sstevel@tonic-gate 				OHCI_CTRL_STATS(ohcip)->nread += len;
103037c478bd9Sstevel@tonic-gate 				break;
103047c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_BULK:
103057c478bd9Sstevel@tonic-gate 				OHCI_BULK_STATS(ohcip)->reads++;
103067c478bd9Sstevel@tonic-gate 				OHCI_BULK_STATS(ohcip)->nread += len;
103077c478bd9Sstevel@tonic-gate 				break;
103087c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_INTR:
103097c478bd9Sstevel@tonic-gate 				OHCI_INTR_STATS(ohcip)->reads++;
103107c478bd9Sstevel@tonic-gate 				OHCI_INTR_STATS(ohcip)->nread += len;
103117c478bd9Sstevel@tonic-gate 				break;
103127c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_ISOCH:
103137c478bd9Sstevel@tonic-gate 				OHCI_ISOC_STATS(ohcip)->reads++;
103147c478bd9Sstevel@tonic-gate 				OHCI_ISOC_STATS(ohcip)->nread += len;
103157c478bd9Sstevel@tonic-gate 				break;
103167c478bd9Sstevel@tonic-gate 		}
103177c478bd9Sstevel@tonic-gate 	} else if (dir == USB_EP_DIR_OUT) {
103187c478bd9Sstevel@tonic-gate 		OHCI_TOTAL_STATS_DATA(ohcip)->writes++;
103197c478bd9Sstevel@tonic-gate 		OHCI_TOTAL_STATS_DATA(ohcip)->nwritten += len;
103207c478bd9Sstevel@tonic-gate 		switch (type) {
103217c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_CONTROL:
103227c478bd9Sstevel@tonic-gate 				OHCI_CTRL_STATS(ohcip)->writes++;
103237c478bd9Sstevel@tonic-gate 				OHCI_CTRL_STATS(ohcip)->nwritten += len;
103247c478bd9Sstevel@tonic-gate 				break;
103257c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_BULK:
103267c478bd9Sstevel@tonic-gate 				OHCI_BULK_STATS(ohcip)->writes++;
103277c478bd9Sstevel@tonic-gate 				OHCI_BULK_STATS(ohcip)->nwritten += len;
103287c478bd9Sstevel@tonic-gate 				break;
103297c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_INTR:
103307c478bd9Sstevel@tonic-gate 				OHCI_INTR_STATS(ohcip)->writes++;
103317c478bd9Sstevel@tonic-gate 				OHCI_INTR_STATS(ohcip)->nwritten += len;
103327c478bd9Sstevel@tonic-gate 				break;
103337c478bd9Sstevel@tonic-gate 			case USB_EP_ATTR_ISOCH:
103347c478bd9Sstevel@tonic-gate 				OHCI_ISOC_STATS(ohcip)->writes++;
103357c478bd9Sstevel@tonic-gate 				OHCI_ISOC_STATS(ohcip)->nwritten += len;
103367c478bd9Sstevel@tonic-gate 				break;
103377c478bd9Sstevel@tonic-gate 		}
103387c478bd9Sstevel@tonic-gate 	}
103397c478bd9Sstevel@tonic-gate }
103407c478bd9Sstevel@tonic-gate 
103417c478bd9Sstevel@tonic-gate 
103427c478bd9Sstevel@tonic-gate /*
103437c478bd9Sstevel@tonic-gate  * ohci_print_op_regs:
103447c478bd9Sstevel@tonic-gate  *
103457c478bd9Sstevel@tonic-gate  * Print Host Controller's (HC) Operational registers.
103467c478bd9Sstevel@tonic-gate  */
103477c478bd9Sstevel@tonic-gate static void
103487c478bd9Sstevel@tonic-gate ohci_print_op_regs(ohci_state_t *ohcip)
103497c478bd9Sstevel@tonic-gate {
103507c478bd9Sstevel@tonic-gate 	uint_t			i;
103517c478bd9Sstevel@tonic-gate 
103527c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103537c478bd9Sstevel@tonic-gate 	    "\n\tOHCI%d Operational Registers\n",
103547c478bd9Sstevel@tonic-gate 	    ddi_get_instance(ohcip->ohci_dip));
103557c478bd9Sstevel@tonic-gate 
103567c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103577c478bd9Sstevel@tonic-gate 	    "\thcr_revision: 0x%x \t\thcr_control: 0x%x",
103587c478bd9Sstevel@tonic-gate 	    Get_OpReg(hcr_revision), Get_OpReg(hcr_control));
103597c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103607c478bd9Sstevel@tonic-gate 	    "\thcr_cmd_status: 0x%x \t\thcr_intr_enable: 0x%x",
103617c478bd9Sstevel@tonic-gate 	    Get_OpReg(hcr_cmd_status), Get_OpReg(hcr_intr_enable));
103627c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103637c478bd9Sstevel@tonic-gate 	    "\thcr_intr_disable: 0x%x \thcr_HCCA: 0x%x",
103647c478bd9Sstevel@tonic-gate 	    Get_OpReg(hcr_intr_disable), Get_OpReg(hcr_HCCA));
103657c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103667c478bd9Sstevel@tonic-gate 	    "\thcr_periodic_curr: 0x%x \t\thcr_ctrl_head: 0x%x",
103677c478bd9Sstevel@tonic-gate 	    Get_OpReg(hcr_periodic_curr), Get_OpReg(hcr_ctrl_head));
103687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103697c478bd9Sstevel@tonic-gate 	    "\thcr_ctrl_curr: 0x%x  \t\thcr_bulk_head: 0x%x",
103707c478bd9Sstevel@tonic-gate 	    Get_OpReg(hcr_ctrl_curr), Get_OpReg(hcr_bulk_head));
103717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103727c478bd9Sstevel@tonic-gate 	    "\thcr_bulk_curr: 0x%x \t\thcr_done_head: 0x%x",
103737c478bd9Sstevel@tonic-gate 	    Get_OpReg(hcr_bulk_curr), Get_OpReg(hcr_done_head));
103747c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103757c478bd9Sstevel@tonic-gate 	    "\thcr_frame_interval: 0x%x "
103767c478bd9Sstevel@tonic-gate 	    "\thcr_frame_remaining: 0x%x", Get_OpReg(hcr_frame_interval),
103777c478bd9Sstevel@tonic-gate 	    Get_OpReg(hcr_frame_remaining));
103787c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103797c478bd9Sstevel@tonic-gate 	    "\thcr_frame_number: 0x%x  \thcr_periodic_strt: 0x%x",
103807c478bd9Sstevel@tonic-gate 	    Get_OpReg(hcr_frame_number), Get_OpReg(hcr_periodic_strt));
103817c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103827c478bd9Sstevel@tonic-gate 	    "\thcr_transfer_ls: 0x%x \t\thcr_rh_descriptorA: 0x%x",
103837c478bd9Sstevel@tonic-gate 	    Get_OpReg(hcr_transfer_ls), Get_OpReg(hcr_rh_descriptorA));
103847c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103857c478bd9Sstevel@tonic-gate 	    "\thcr_rh_descriptorB: 0x%x \thcr_rh_status: 0x%x",
103867c478bd9Sstevel@tonic-gate 	    Get_OpReg(hcr_rh_descriptorB), Get_OpReg(hcr_rh_status));
103877c478bd9Sstevel@tonic-gate 
103887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103897c478bd9Sstevel@tonic-gate 	    "\tRoot hub port status");
103907c478bd9Sstevel@tonic-gate 
103917c478bd9Sstevel@tonic-gate 	for (i = 0; i < (Get_OpReg(hcr_rh_descriptorA) & HCR_RHA_NDP); i++) {
103927c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ATTA, ohcip->ohci_log_hdl,
103937c478bd9Sstevel@tonic-gate 		    "\thcr_rh_portstatus 0x%x: 0x%x ", i,
103947c478bd9Sstevel@tonic-gate 		    Get_OpReg(hcr_rh_portstatus[i]));
103957c478bd9Sstevel@tonic-gate 	}
103967c478bd9Sstevel@tonic-gate }
103977c478bd9Sstevel@tonic-gate 
103987c478bd9Sstevel@tonic-gate 
103997c478bd9Sstevel@tonic-gate /*
104007c478bd9Sstevel@tonic-gate  * ohci_print_ed:
104017c478bd9Sstevel@tonic-gate  */
104027c478bd9Sstevel@tonic-gate static void
104037c478bd9Sstevel@tonic-gate ohci_print_ed(
104047c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
104057c478bd9Sstevel@tonic-gate 	ohci_ed_t	*ed)
104067c478bd9Sstevel@tonic-gate {
104077c478bd9Sstevel@tonic-gate 	uint_t 		ctrl = Get_ED(ed->hced_ctrl);
104087c478bd9Sstevel@tonic-gate 
104097c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104107c478bd9Sstevel@tonic-gate 	    "ohci_print_ed: ed = 0x%p", (void *)ed);
104117c478bd9Sstevel@tonic-gate 
104127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104137c478bd9Sstevel@tonic-gate 	    "\thced_ctrl: 0x%x %s", ctrl,
104147c478bd9Sstevel@tonic-gate 	    ((Get_ED(ed->hced_headp) & HC_EPT_Halt) ? "halted": ""));
104157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104167c478bd9Sstevel@tonic-gate 	    "\ttoggle carry: 0x%x", Get_ED(ed->hced_headp) & HC_EPT_Carry);
104177c478bd9Sstevel@tonic-gate 
104187c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104197c478bd9Sstevel@tonic-gate 	    "\tctrl: 0x%x", Get_ED(ed->hced_ctrl));
104207c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104217c478bd9Sstevel@tonic-gate 	    "\ttailp: 0x%x", Get_ED(ed->hced_tailp));
104227c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104237c478bd9Sstevel@tonic-gate 	    "\theadp: 0x%x", Get_ED(ed->hced_headp));
104247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104257c478bd9Sstevel@tonic-gate 	    "\tnext: 0x%x", Get_ED(ed->hced_next));
104267c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104277c478bd9Sstevel@tonic-gate 	    "\tprev: 0x%x", Get_ED(ed->hced_prev));
104287c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104297c478bd9Sstevel@tonic-gate 	    "\tnode: 0x%x", Get_ED(ed->hced_node));
104307c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104317c478bd9Sstevel@tonic-gate 	    "\treclaim_next: 0x%x", Get_ED(ed->hced_reclaim_next));
104327c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104337c478bd9Sstevel@tonic-gate 	    "\treclaim_frame: 0x%x", Get_ED(ed->hced_reclaim_frame));
104347c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104357c478bd9Sstevel@tonic-gate 	    "\tstate: 0x%x", Get_ED(ed->hced_state));
104367c478bd9Sstevel@tonic-gate }
104377c478bd9Sstevel@tonic-gate 
104387c478bd9Sstevel@tonic-gate 
104397c478bd9Sstevel@tonic-gate /*
104407c478bd9Sstevel@tonic-gate  * ohci_print_td:
104417c478bd9Sstevel@tonic-gate  */
104427c478bd9Sstevel@tonic-gate static void
104437c478bd9Sstevel@tonic-gate ohci_print_td(
104447c478bd9Sstevel@tonic-gate 	ohci_state_t	*ohcip,
104457c478bd9Sstevel@tonic-gate 	ohci_td_t	*td)
104467c478bd9Sstevel@tonic-gate {
104477c478bd9Sstevel@tonic-gate 	uint_t		i;
104487c478bd9Sstevel@tonic-gate 	uint_t		ctrl = Get_TD(td->hctd_ctrl);
104497c478bd9Sstevel@tonic-gate 
104507c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104517c478bd9Sstevel@tonic-gate 	    "ohci_print_td: td = 0x%p", (void *)td);
104527c478bd9Sstevel@tonic-gate 
104537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104547c478bd9Sstevel@tonic-gate 	    "\tPID: 0x%x ", ctrl & HC_TD_PID);
104557c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104567c478bd9Sstevel@tonic-gate 	    "\tDelay Intr: 0x%x ", ctrl & HC_TD_DI);
104577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104587c478bd9Sstevel@tonic-gate 	    "\tData Toggle: 0x%x ", ctrl & HC_TD_DT);
104597c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104607c478bd9Sstevel@tonic-gate 	    "\tError Count: 0x%x ", ctrl & HC_TD_EC);
104617c478bd9Sstevel@tonic-gate 
104627c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104637c478bd9Sstevel@tonic-gate 	    "\tctrl: 0x%x ", Get_TD(td->hctd_ctrl));
104647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104657c478bd9Sstevel@tonic-gate 	    "\tcbp: 0x%x ", Get_TD(td->hctd_cbp));
104667c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104677c478bd9Sstevel@tonic-gate 	    "\tnext_td: 0x%x ", Get_TD(td->hctd_next_td));
104687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104697c478bd9Sstevel@tonic-gate 	    "\tbuf_end: 0x%x ", Get_TD(td->hctd_buf_end));
104707c478bd9Sstevel@tonic-gate 
104717c478bd9Sstevel@tonic-gate 	for (i = 0; i < 4; i++) {
104727c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104737c478bd9Sstevel@tonic-gate 		    "\toffset[%d]: 0x%x ", i, Get_TD(td->hctd_offsets[i]));
104747c478bd9Sstevel@tonic-gate 	}
104757c478bd9Sstevel@tonic-gate 
104767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104777c478bd9Sstevel@tonic-gate 	    "\ttrans_wrapper: 0x%x ", Get_TD(td->hctd_trans_wrapper));
104787c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104797c478bd9Sstevel@tonic-gate 	    "\tstate: 0x%x ", Get_TD(td->hctd_state));
104807c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104817c478bd9Sstevel@tonic-gate 	    "\ttw_next_td: 0x%x ", Get_TD(td->hctd_tw_next_td));
104827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ohcip->ohci_log_hdl,
104837c478bd9Sstevel@tonic-gate 	    "\tctrl_phase: 0x%x ", Get_TD(td->hctd_ctrl_phase));
104847c478bd9Sstevel@tonic-gate }
10485