xref: /onnv-gate/usr/src/uts/common/io/usb/hcd/openhci/ohci_polled.c (revision 9095:ef4ae1685182)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52125Ssl147100  * Common Development and Distribution License (the "License").
62125Ssl147100  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*9095SZhigang.Lu@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * Open Host Controller Driver (OHCI)
280Sstevel@tonic-gate  *
290Sstevel@tonic-gate  * The USB Open Host Controller driver is a software driver which interfaces
300Sstevel@tonic-gate  * to the Universal Serial Bus layer (USBA) and the USB Open Host Controller.
310Sstevel@tonic-gate  * The interface to USB Open Host Controller is defined by the OpenHCI	Host
320Sstevel@tonic-gate  * Controller Interface.
330Sstevel@tonic-gate  *
340Sstevel@tonic-gate  * This module contains the specific ohci code used in POLLED mode and this
350Sstevel@tonic-gate  * code is in a separate file since it will never become part of ohci driver.
360Sstevel@tonic-gate  */
370Sstevel@tonic-gate #include <sys/usb/hcd/openhci/ohcid.h>
380Sstevel@tonic-gate #include <sys/usb/hcd/openhci/ohci_polled.h>
390Sstevel@tonic-gate 
400Sstevel@tonic-gate /*
410Sstevel@tonic-gate  * Internal Function Prototypes
420Sstevel@tonic-gate  */
430Sstevel@tonic-gate 
440Sstevel@tonic-gate /* Polled initialization routines */
450Sstevel@tonic-gate static int	ohci_polled_init(
460Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
470Sstevel@tonic-gate 				ohci_state_t		*ohcip,
480Sstevel@tonic-gate 				usb_console_info_impl_t	*console_input_info);
490Sstevel@tonic-gate 
500Sstevel@tonic-gate /* Polled deinitialization routines */
510Sstevel@tonic-gate static int	ohci_polled_fini(ohci_polled_t		*ohci_polledp);
520Sstevel@tonic-gate 
530Sstevel@tonic-gate /* Polled save state routines */
540Sstevel@tonic-gate static void	ohci_polled_save_state(ohci_polled_t	*ohci_polledp);
550Sstevel@tonic-gate static void	ohci_polled_stop_processing(
560Sstevel@tonic-gate 				ohci_polled_t		*ohci_polledp);
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /* Polled restore state routines */
590Sstevel@tonic-gate static void	ohci_polled_restore_state(ohci_polled_t	*ohci_polledp);
600Sstevel@tonic-gate static void	ohci_polled_start_processing(
610Sstevel@tonic-gate 				ohci_polled_t		*ohci_polledp);
620Sstevel@tonic-gate 
630Sstevel@tonic-gate /* Polled read routines */
640Sstevel@tonic-gate static ohci_td_t *ohci_polled_pickup_done_list(
650Sstevel@tonic-gate 				ohci_polled_t		*ohci_polledp,
660Sstevel@tonic-gate 				ohci_td_t		*done_head);
670Sstevel@tonic-gate static int	ohci_polled_check_done_list(
680Sstevel@tonic-gate 				ohci_polled_t		*ohci_polledp);
690Sstevel@tonic-gate static void	ohci_polled_create_input_list(
700Sstevel@tonic-gate 				ohci_polled_t		*ohci_polledp,
710Sstevel@tonic-gate 				ohci_td_t		*head_done_list);
720Sstevel@tonic-gate static int	ohci_polled_process_input_list(
730Sstevel@tonic-gate 				ohci_polled_t		*ohci_polledp);
740Sstevel@tonic-gate static int	ohci_polled_handle_normal_td(
750Sstevel@tonic-gate 				ohci_polled_t		*ohci_polledp,
760Sstevel@tonic-gate 				ohci_td_t		*td);
770Sstevel@tonic-gate static void	ohci_polled_insert_td(ohci_state_t	*ohcip,
780Sstevel@tonic-gate 				ohci_td_t		*td);
790Sstevel@tonic-gate static void	ohci_polled_fill_in_td(ohci_state_t	*ohcip,
800Sstevel@tonic-gate 				ohci_td_t		*td,
810Sstevel@tonic-gate 				ohci_td_t		*new_dummy,
820Sstevel@tonic-gate 				uint_t			hctd_ctrl,
830Sstevel@tonic-gate 				uint32_t		hctd_iommu_cbp,
840Sstevel@tonic-gate 				size_t			hctd_length,
850Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw);
860Sstevel@tonic-gate static void	ohci_polled_insert_td_on_tw(
870Sstevel@tonic-gate 				ohci_state_t		*ohcip,
880Sstevel@tonic-gate 				ohci_trans_wrapper_t	*tw,
890Sstevel@tonic-gate 				ohci_td_t		*td);
900Sstevel@tonic-gate static void	ohci_polled_handle_frame_number_overflow(
910Sstevel@tonic-gate 				ohci_state_t		*ohcip);
920Sstevel@tonic-gate static void	ohci_polled_finish_interrupt(
930Sstevel@tonic-gate 				ohci_state_t		*ohcip,
940Sstevel@tonic-gate 				uint_t			intr);
95*9095SZhigang.Lu@Sun.COM static void	ohci_polled_insert_bulk_td(
96*9095SZhigang.Lu@Sun.COM 				ohci_polled_t		*ohci_polledp);
97*9095SZhigang.Lu@Sun.COM static int 	ohci_polled_create_tw(
98*9095SZhigang.Lu@Sun.COM 				ohci_state_t		*ohcip,
99*9095SZhigang.Lu@Sun.COM 				usba_pipe_handle_data_t	*ph,
100*9095SZhigang.Lu@Sun.COM 				usb_flags_t		usb_flags);
101*9095SZhigang.Lu@Sun.COM static int	ohci_polled_insert_hc_td(
102*9095SZhigang.Lu@Sun.COM 				ohci_state_t		*ohcip,
103*9095SZhigang.Lu@Sun.COM 				uint_t			hctd_ctrl,
104*9095SZhigang.Lu@Sun.COM 				uint32_t		hctd_dma_offs,
105*9095SZhigang.Lu@Sun.COM 				size_t			hctd_length,
106*9095SZhigang.Lu@Sun.COM 				ohci_pipe_private_t	*pp,
107*9095SZhigang.Lu@Sun.COM 				ohci_trans_wrapper_t	*tw);
1080Sstevel@tonic-gate /*
1090Sstevel@tonic-gate  * POLLED entry points
1100Sstevel@tonic-gate  *
1110Sstevel@tonic-gate  * These functions are entry points into the POLLED code.
1120Sstevel@tonic-gate  */
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate /*
1150Sstevel@tonic-gate  * ohci_hcdi_polled_input_init:
1160Sstevel@tonic-gate  *
117*9095SZhigang.Lu@Sun.COM  * This is the initialization routine for handling the USB input device
1180Sstevel@tonic-gate  * in POLLED mode.  This routine is not called from POLLED mode, so
1190Sstevel@tonic-gate  * it is OK to acquire mutexes.
1200Sstevel@tonic-gate  */
1210Sstevel@tonic-gate int
ohci_hcdi_polled_input_init(usba_pipe_handle_data_t * ph,uchar_t ** polled_buf,usb_console_info_impl_t * console_input_info)1220Sstevel@tonic-gate ohci_hcdi_polled_input_init(
1230Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
1240Sstevel@tonic-gate 	uchar_t			**polled_buf,
1250Sstevel@tonic-gate 	usb_console_info_impl_t	*console_input_info)
1260Sstevel@tonic-gate {
1270Sstevel@tonic-gate 	ohci_polled_t		*ohci_polledp;
1280Sstevel@tonic-gate 	ohci_state_t		*ohcip;
129*9095SZhigang.Lu@Sun.COM 	int			pipe_attr, ret;
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 	ohcip = ohci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate 	/*
1340Sstevel@tonic-gate 	 * Grab the ohci_int_mutex so that things don't change on us
1350Sstevel@tonic-gate 	 * if an interrupt comes in.
1360Sstevel@tonic-gate 	 */
1370Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 	ret = ohci_polled_init(ph, ohcip, console_input_info);
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	if (ret != USB_SUCCESS) {
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 		/* Allow interrupts to continue */
1440Sstevel@tonic-gate 		mutex_exit(&ohcip->ohci_int_mutex);
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 		return (ret);
1470Sstevel@tonic-gate 	}
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate 	ohci_polledp = (ohci_polled_t *)console_input_info->uci_private;
1500Sstevel@tonic-gate 	/*
1510Sstevel@tonic-gate 	 * Mark the structure so that if we are using it, we don't free
1520Sstevel@tonic-gate 	 * the structures if one of them is unplugged.
1530Sstevel@tonic-gate 	 */
1540Sstevel@tonic-gate 	ohci_polledp->ohci_polled_flags |= POLLED_INPUT_MODE;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	/* increase the polled kbd counter for keyboard connected */
1570Sstevel@tonic-gate 	ohcip->ohci_polled_kbd_count ++;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	/*
1600Sstevel@tonic-gate 	 * This is the buffer we will copy characters into. It will be
1610Sstevel@tonic-gate 	 * copied into at this layer, so we need to keep track of it.
1620Sstevel@tonic-gate 	 */
1630Sstevel@tonic-gate 	ohci_polledp->ohci_polled_buf =
1647425SGongtian.Zhao@Sun.COM 	    (uchar_t *)kmem_zalloc(POLLED_RAW_BUF_SIZE, KM_SLEEP);
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 	*polled_buf = ohci_polledp->ohci_polled_buf;
1670Sstevel@tonic-gate 
168*9095SZhigang.Lu@Sun.COM 	/* Insert bulkin td into endpoint's tds list */
169*9095SZhigang.Lu@Sun.COM 	pipe_attr = ohci_polledp->ohci_polled_input_pipe_handle->
170*9095SZhigang.Lu@Sun.COM 	    p_ep.bmAttributes & USB_EP_ATTR_MASK;
171*9095SZhigang.Lu@Sun.COM 
172*9095SZhigang.Lu@Sun.COM 	if (pipe_attr == USB_EP_ATTR_BULK) {
173*9095SZhigang.Lu@Sun.COM 		ohci_polled_insert_bulk_td(ohci_polledp);
174*9095SZhigang.Lu@Sun.COM 	}
1750Sstevel@tonic-gate 	/*
1760Sstevel@tonic-gate 	 * This is a software workaround to fix schizo hardware bug.
1770Sstevel@tonic-gate 	 * Existence of "no-prom-cdma-sync"  property means consistent
1780Sstevel@tonic-gate 	 * dma sync should not be done while in prom or polled mode.
1790Sstevel@tonic-gate 	 */
1800Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, ohcip->ohci_dip,
1810Sstevel@tonic-gate 	    DDI_PROP_NOTPROM, "no-prom-cdma-sync")) {
1820Sstevel@tonic-gate 		ohci_polledp->ohci_polled_no_sync_flag = B_TRUE;
1830Sstevel@tonic-gate 	}
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 	/* Allow interrupts to continue */
1860Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	return (USB_SUCCESS);
1890Sstevel@tonic-gate }
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate /*
1930Sstevel@tonic-gate  * ohci_hcdi_polled_input_fini:
1940Sstevel@tonic-gate  */
1950Sstevel@tonic-gate int
ohci_hcdi_polled_input_fini(usb_console_info_impl_t * info)1960Sstevel@tonic-gate ohci_hcdi_polled_input_fini(usb_console_info_impl_t *info)
1970Sstevel@tonic-gate {
1980Sstevel@tonic-gate 	ohci_polled_t		*ohci_polledp;
1990Sstevel@tonic-gate 	ohci_state_t		*ohcip;
2000Sstevel@tonic-gate 	int			ret;
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	ohci_polledp = (ohci_polled_t *)info->uci_private;
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	ohcip = ohci_polledp->ohci_polled_ohcip;
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	mutex_enter(&ohcip->ohci_int_mutex);
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	/*
2090Sstevel@tonic-gate 	 * Reset the POLLED_INPUT_MODE flag so that we can tell if
2100Sstevel@tonic-gate 	 * this structure is in use in the ohci_polled_fini routine.
2110Sstevel@tonic-gate 	 */
2120Sstevel@tonic-gate 	ohci_polledp->ohci_polled_flags &= ~POLLED_INPUT_MODE;
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	/* Decrease the polled kbd counter for keyboard disconnected */
2150Sstevel@tonic-gate 	ohcip->ohci_polled_kbd_count --;
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	/* Free the buffer that we copied data into */
2180Sstevel@tonic-gate 	kmem_free(ohci_polledp->ohci_polled_buf, POLLED_RAW_BUF_SIZE);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	ret = ohci_polled_fini(ohci_polledp);
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	mutex_exit(&ohcip->ohci_int_mutex);
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	return (ret);
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate /*
2290Sstevel@tonic-gate  * ohci_hcdi_polled_input_enter:
2300Sstevel@tonic-gate  *
2310Sstevel@tonic-gate  * This is where we enter into POLLED mode.  This routine sets up
2320Sstevel@tonic-gate  * everything so that calls to	ohci_hcdi_polled_read will return
2330Sstevel@tonic-gate  * characters.
2340Sstevel@tonic-gate  */
2350Sstevel@tonic-gate int
ohci_hcdi_polled_input_enter(usb_console_info_impl_t * info)2360Sstevel@tonic-gate ohci_hcdi_polled_input_enter(usb_console_info_impl_t *info)
2370Sstevel@tonic-gate {
2380Sstevel@tonic-gate 	ohci_polled_t		*ohci_polledp;
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	ohci_polledp = (ohci_polled_t *)info->uci_private;
2410Sstevel@tonic-gate 	ohci_polledp->ohci_polled_entry++;
2420Sstevel@tonic-gate 	/*
2430Sstevel@tonic-gate 	 * If the controller is already switched over, just return
2440Sstevel@tonic-gate 	 */
2450Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_entry > 1) {
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 		return (USB_SUCCESS);
2480Sstevel@tonic-gate 	}
2490Sstevel@tonic-gate 	ohci_polled_save_state(ohci_polledp);
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	ohci_polledp->ohci_polled_flags |= POLLED_INPUT_MODE_INUSE;
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	return (USB_SUCCESS);
2540Sstevel@tonic-gate }
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate /*
2580Sstevel@tonic-gate  * ohci_hcdi_polled_input_exit:
2590Sstevel@tonic-gate  *
2600Sstevel@tonic-gate  * This is where we exit POLLED mode. This routine restores
2610Sstevel@tonic-gate  * everything that is needed to continue operation.
2620Sstevel@tonic-gate  */
2630Sstevel@tonic-gate int
ohci_hcdi_polled_input_exit(usb_console_info_impl_t * info)2640Sstevel@tonic-gate ohci_hcdi_polled_input_exit(usb_console_info_impl_t *info)
2650Sstevel@tonic-gate {
2660Sstevel@tonic-gate 	ohci_polled_t		*ohci_polledp;
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 	ohci_polledp = (ohci_polled_t *)info->uci_private;
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	ohci_polledp->ohci_polled_entry--;
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	/*
2730Sstevel@tonic-gate 	 * If there are still outstanding "enters", just return
2740Sstevel@tonic-gate 	 */
2750Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_entry > 0)
2760Sstevel@tonic-gate 		return (USB_SUCCESS);
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 	ohci_polledp->ohci_polled_flags &= ~POLLED_INPUT_MODE_INUSE;
2790Sstevel@tonic-gate 	ohci_polled_restore_state(ohci_polledp);
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	return (USB_SUCCESS);
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate /*
2860Sstevel@tonic-gate  * ohci_hcdi_polled_read:
2870Sstevel@tonic-gate  *
2880Sstevel@tonic-gate  * Get a key character
2890Sstevel@tonic-gate  */
2900Sstevel@tonic-gate int
ohci_hcdi_polled_read(usb_console_info_impl_t * info,uint_t * num_characters)2910Sstevel@tonic-gate ohci_hcdi_polled_read(
2920Sstevel@tonic-gate 	usb_console_info_impl_t	*info,
2930Sstevel@tonic-gate 	uint_t			*num_characters)
2940Sstevel@tonic-gate {
2950Sstevel@tonic-gate 	ohci_state_t		*ohcip;
2960Sstevel@tonic-gate 	ohci_polled_t		*ohci_polledp;
2970Sstevel@tonic-gate 	uint_t			intr;
2980Sstevel@tonic-gate 	ohci_polledp = (ohci_polled_t *)info->uci_private;
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	ohcip = ohci_polledp->ohci_polled_ohcip;
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate #ifndef lint
3030Sstevel@tonic-gate 	_NOTE(NO_COMPETING_THREADS_NOW);
3040Sstevel@tonic-gate #endif
3050Sstevel@tonic-gate 	*num_characters = 0;
3060Sstevel@tonic-gate 	intr = (Get_OpReg(hcr_intr_status) & Get_OpReg(hcr_intr_enable));
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 	/*
3090Sstevel@tonic-gate 	 * Check whether any Frame Number Overflow interrupt is pending
3100Sstevel@tonic-gate 	 * and if it is pending, process this interrupt.
3110Sstevel@tonic-gate 	 */
3120Sstevel@tonic-gate 	if (intr & HCR_INTR_FNO) {
3130Sstevel@tonic-gate 		ohci_handle_frame_number_overflow(ohcip);
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 		/* Acknowledge the FNO interrupt */
3160Sstevel@tonic-gate 		ohci_polled_finish_interrupt(ohcip, HCR_INTR_FNO);
3170Sstevel@tonic-gate 	}
318*9095SZhigang.Lu@Sun.COM 
319*9095SZhigang.Lu@Sun.COM 	/* Check to see if there are any TD's for this input device */
320*9095SZhigang.Lu@Sun.COM 	if (ohci_polled_check_done_list(ohci_polledp) == USB_SUCCESS) {
321*9095SZhigang.Lu@Sun.COM 
322*9095SZhigang.Lu@Sun.COM 		/* Process any TD's on the input done list */
323*9095SZhigang.Lu@Sun.COM 		*num_characters =
324*9095SZhigang.Lu@Sun.COM 		    ohci_polled_process_input_list(ohci_polledp);
325*9095SZhigang.Lu@Sun.COM 	}
326*9095SZhigang.Lu@Sun.COM 
327*9095SZhigang.Lu@Sun.COM 	/*
328*9095SZhigang.Lu@Sun.COM 	 * To make sure after we get the done list from DoneHead,
329*9095SZhigang.Lu@Sun.COM 	 * every input device get his own TD's in the
330*9095SZhigang.Lu@Sun.COM 	 * ohci_polled_done_list and then clear the interrupt status.
331*9095SZhigang.Lu@Sun.COM 	 */
3320Sstevel@tonic-gate 	if (intr & HCR_INTR_WDH) {
3330Sstevel@tonic-gate 
334*9095SZhigang.Lu@Sun.COM 		/* Acknowledge the WDH interrupt */
335*9095SZhigang.Lu@Sun.COM 		ohci_polled_finish_interrupt(ohcip, HCR_INTR_WDH);
3360Sstevel@tonic-gate 	}
3370Sstevel@tonic-gate #ifndef lint
3380Sstevel@tonic-gate 	_NOTE(COMPETING_THREADS_NOW);
3390Sstevel@tonic-gate #endif
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	return (USB_SUCCESS);
3420Sstevel@tonic-gate }
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate /*
346*9095SZhigang.Lu@Sun.COM  * ohci_hcdi_polled_output_init:
347*9095SZhigang.Lu@Sun.COM  *
348*9095SZhigang.Lu@Sun.COM  * This is the initialization routine for handling the USB serial output
349*9095SZhigang.Lu@Sun.COM  * in POLLED mode.  This routine is not called from POLLED mode, so
350*9095SZhigang.Lu@Sun.COM  * it is OK to acquire mutexes.
351*9095SZhigang.Lu@Sun.COM  */
352*9095SZhigang.Lu@Sun.COM int
ohci_hcdi_polled_output_init(usba_pipe_handle_data_t * ph,usb_console_info_impl_t * console_output_info)353*9095SZhigang.Lu@Sun.COM ohci_hcdi_polled_output_init(
354*9095SZhigang.Lu@Sun.COM 	usba_pipe_handle_data_t	*ph,
355*9095SZhigang.Lu@Sun.COM 	usb_console_info_impl_t	*console_output_info)
356*9095SZhigang.Lu@Sun.COM {
357*9095SZhigang.Lu@Sun.COM 	ohci_polled_t		*ohci_polledp;
358*9095SZhigang.Lu@Sun.COM 	ohci_state_t		*ohcip;
359*9095SZhigang.Lu@Sun.COM 	int			ret;
360*9095SZhigang.Lu@Sun.COM 
361*9095SZhigang.Lu@Sun.COM 	ohcip = ohci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
362*9095SZhigang.Lu@Sun.COM 
363*9095SZhigang.Lu@Sun.COM 	/*
364*9095SZhigang.Lu@Sun.COM 	 * Grab the ohci_int_mutex so that things don't change on us
365*9095SZhigang.Lu@Sun.COM 	 * if an interrupt comes in.
366*9095SZhigang.Lu@Sun.COM 	 */
367*9095SZhigang.Lu@Sun.COM 	mutex_enter(&ohcip->ohci_int_mutex);
368*9095SZhigang.Lu@Sun.COM 
369*9095SZhigang.Lu@Sun.COM 	ret = ohci_polled_init(ph, ohcip, console_output_info);
370*9095SZhigang.Lu@Sun.COM 
371*9095SZhigang.Lu@Sun.COM 	if (ret != USB_SUCCESS) {
372*9095SZhigang.Lu@Sun.COM 
373*9095SZhigang.Lu@Sun.COM 		/* Allow interrupts to continue */
374*9095SZhigang.Lu@Sun.COM 		mutex_exit(&ohcip->ohci_int_mutex);
375*9095SZhigang.Lu@Sun.COM 
376*9095SZhigang.Lu@Sun.COM 		return (ret);
377*9095SZhigang.Lu@Sun.COM 	}
378*9095SZhigang.Lu@Sun.COM 
379*9095SZhigang.Lu@Sun.COM 	ohci_polledp = (ohci_polled_t *)console_output_info->uci_private;
380*9095SZhigang.Lu@Sun.COM 	/*
381*9095SZhigang.Lu@Sun.COM 	 * Mark the structure so that if we are using it, we don't free
382*9095SZhigang.Lu@Sun.COM 	 * the structures if one of them is unplugged.
383*9095SZhigang.Lu@Sun.COM 	 */
384*9095SZhigang.Lu@Sun.COM 	ohci_polledp->ohci_polled_flags |= POLLED_OUTPUT_MODE;
385*9095SZhigang.Lu@Sun.COM 
386*9095SZhigang.Lu@Sun.COM 	/*
387*9095SZhigang.Lu@Sun.COM 	 * This is a software workaround to fix schizo hardware bug.
388*9095SZhigang.Lu@Sun.COM 	 * Existence of "no-prom-cdma-sync"  property means consistent
389*9095SZhigang.Lu@Sun.COM 	 * dma sync should not be done while in prom or polled mode.
390*9095SZhigang.Lu@Sun.COM 	 */
391*9095SZhigang.Lu@Sun.COM 	if (ddi_prop_exists(DDI_DEV_T_ANY, ohcip->ohci_dip,
392*9095SZhigang.Lu@Sun.COM 	    DDI_PROP_NOTPROM, "no-prom-cdma-sync")) {
393*9095SZhigang.Lu@Sun.COM 		ohci_polledp->ohci_polled_no_sync_flag = B_TRUE;
394*9095SZhigang.Lu@Sun.COM 	}
395*9095SZhigang.Lu@Sun.COM 
396*9095SZhigang.Lu@Sun.COM 	/* Allow interrupts to continue */
397*9095SZhigang.Lu@Sun.COM 	mutex_exit(&ohcip->ohci_int_mutex);
398*9095SZhigang.Lu@Sun.COM 
399*9095SZhigang.Lu@Sun.COM 	return (USB_SUCCESS);
400*9095SZhigang.Lu@Sun.COM }
401*9095SZhigang.Lu@Sun.COM 
402*9095SZhigang.Lu@Sun.COM /*
403*9095SZhigang.Lu@Sun.COM  * ohci_hcdi_polled_output_fini:
404*9095SZhigang.Lu@Sun.COM  */
405*9095SZhigang.Lu@Sun.COM int
ohci_hcdi_polled_output_fini(usb_console_info_impl_t * info)406*9095SZhigang.Lu@Sun.COM ohci_hcdi_polled_output_fini(usb_console_info_impl_t *info)
407*9095SZhigang.Lu@Sun.COM {
408*9095SZhigang.Lu@Sun.COM 	ohci_polled_t		*ohci_polledp;
409*9095SZhigang.Lu@Sun.COM 	ohci_state_t		*ohcip;
410*9095SZhigang.Lu@Sun.COM 	int			ret;
411*9095SZhigang.Lu@Sun.COM 
412*9095SZhigang.Lu@Sun.COM 	ohci_polledp = (ohci_polled_t *)info->uci_private;
413*9095SZhigang.Lu@Sun.COM 
414*9095SZhigang.Lu@Sun.COM 	ohcip = ohci_polledp->ohci_polled_ohcip;
415*9095SZhigang.Lu@Sun.COM 
416*9095SZhigang.Lu@Sun.COM 	mutex_enter(&ohcip->ohci_int_mutex);
417*9095SZhigang.Lu@Sun.COM 
418*9095SZhigang.Lu@Sun.COM 	/*
419*9095SZhigang.Lu@Sun.COM 	 * Reset the POLLED_INPUT_MODE flag so that we can tell if
420*9095SZhigang.Lu@Sun.COM 	 * this structure is in use in the ohci_polled_fini routine.
421*9095SZhigang.Lu@Sun.COM 	 */
422*9095SZhigang.Lu@Sun.COM 	ohci_polledp->ohci_polled_flags &= ~POLLED_OUTPUT_MODE;
423*9095SZhigang.Lu@Sun.COM 
424*9095SZhigang.Lu@Sun.COM 	ret = ohci_polled_fini(ohci_polledp);
425*9095SZhigang.Lu@Sun.COM 
426*9095SZhigang.Lu@Sun.COM 	info->uci_private = NULL;
427*9095SZhigang.Lu@Sun.COM 
428*9095SZhigang.Lu@Sun.COM 	mutex_exit(&ohcip->ohci_int_mutex);
429*9095SZhigang.Lu@Sun.COM 
430*9095SZhigang.Lu@Sun.COM 	return (ret);
431*9095SZhigang.Lu@Sun.COM }
432*9095SZhigang.Lu@Sun.COM 
433*9095SZhigang.Lu@Sun.COM 
434*9095SZhigang.Lu@Sun.COM /*
435*9095SZhigang.Lu@Sun.COM  * ohci_hcdi_polled_output_enter:
436*9095SZhigang.Lu@Sun.COM  *
437*9095SZhigang.Lu@Sun.COM  * everything is done in input enter
438*9095SZhigang.Lu@Sun.COM  */
439*9095SZhigang.Lu@Sun.COM /*ARGSUSED*/
440*9095SZhigang.Lu@Sun.COM int
ohci_hcdi_polled_output_enter(usb_console_info_impl_t * info)441*9095SZhigang.Lu@Sun.COM ohci_hcdi_polled_output_enter(usb_console_info_impl_t *info)
442*9095SZhigang.Lu@Sun.COM {
443*9095SZhigang.Lu@Sun.COM 	return (USB_SUCCESS);
444*9095SZhigang.Lu@Sun.COM }
445*9095SZhigang.Lu@Sun.COM 
446*9095SZhigang.Lu@Sun.COM 
447*9095SZhigang.Lu@Sun.COM /*
448*9095SZhigang.Lu@Sun.COM  * ohci_hcdi_polled_output_exit:
449*9095SZhigang.Lu@Sun.COM  *
450*9095SZhigang.Lu@Sun.COM  * everything is done in input exit
451*9095SZhigang.Lu@Sun.COM  */
452*9095SZhigang.Lu@Sun.COM /*ARGSUSED*/
453*9095SZhigang.Lu@Sun.COM int
ohci_hcdi_polled_output_exit(usb_console_info_impl_t * info)454*9095SZhigang.Lu@Sun.COM ohci_hcdi_polled_output_exit(usb_console_info_impl_t *info)
455*9095SZhigang.Lu@Sun.COM {
456*9095SZhigang.Lu@Sun.COM 	return (USB_SUCCESS);
457*9095SZhigang.Lu@Sun.COM }
458*9095SZhigang.Lu@Sun.COM 
459*9095SZhigang.Lu@Sun.COM 
460*9095SZhigang.Lu@Sun.COM /*
461*9095SZhigang.Lu@Sun.COM  * ohci_hcdi_polled_write:
462*9095SZhigang.Lu@Sun.COM  *	Put a key character -- rewrite this!
463*9095SZhigang.Lu@Sun.COM  */
464*9095SZhigang.Lu@Sun.COM int
ohci_hcdi_polled_write(usb_console_info_impl_t * info,uchar_t * buf,uint_t num_characters,uint_t * num_characters_written)465*9095SZhigang.Lu@Sun.COM ohci_hcdi_polled_write(usb_console_info_impl_t *info, uchar_t *buf,
466*9095SZhigang.Lu@Sun.COM     uint_t num_characters, uint_t *num_characters_written)
467*9095SZhigang.Lu@Sun.COM {
468*9095SZhigang.Lu@Sun.COM 	ohci_state_t		*ohcip;
469*9095SZhigang.Lu@Sun.COM 	ohci_polled_t		*ohci_polledp;
470*9095SZhigang.Lu@Sun.COM 	ohci_trans_wrapper_t	*tw;
471*9095SZhigang.Lu@Sun.COM 	ohci_pipe_private_t	*pp;
472*9095SZhigang.Lu@Sun.COM 	usba_pipe_handle_data_t	*ph;
473*9095SZhigang.Lu@Sun.COM 	uint32_t		ctrl;
474*9095SZhigang.Lu@Sun.COM 	uint_t			intr, bulk_pkg_size;
475*9095SZhigang.Lu@Sun.COM 	int			i;
476*9095SZhigang.Lu@Sun.COM 
477*9095SZhigang.Lu@Sun.COM #ifndef lint
478*9095SZhigang.Lu@Sun.COM 	_NOTE(NO_COMPETING_THREADS_NOW);
479*9095SZhigang.Lu@Sun.COM #endif
480*9095SZhigang.Lu@Sun.COM 
481*9095SZhigang.Lu@Sun.COM 	ohci_polledp = (ohci_polled_t *)info->uci_private;
482*9095SZhigang.Lu@Sun.COM 	ohcip = ohci_polledp->ohci_polled_ohcip;
483*9095SZhigang.Lu@Sun.COM 
484*9095SZhigang.Lu@Sun.COM 	/* Disable periodic list processing */
485*9095SZhigang.Lu@Sun.COM 	Set_OpReg(hcr_control,
486*9095SZhigang.Lu@Sun.COM 	    (Get_OpReg(hcr_control) & (~HCR_CONTROL_PLE)));
487*9095SZhigang.Lu@Sun.COM 
488*9095SZhigang.Lu@Sun.COM 	/* Add the endpoint to the lattice */
489*9095SZhigang.Lu@Sun.COM 	for (i = ohcip->ohci_polled_enter_count; i < NUM_INTR_ED_LISTS;
490*9095SZhigang.Lu@Sun.COM 	    i = i + MIN_LOW_SPEED_POLL_INTERVAL) {
491*9095SZhigang.Lu@Sun.COM 		Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
492*9095SZhigang.Lu@Sun.COM 		    ohci_ed_cpu_to_iommu(ohcip,
493*9095SZhigang.Lu@Sun.COM 		    ohci_polledp->ohci_polled_ed));
494*9095SZhigang.Lu@Sun.COM 	}
495*9095SZhigang.Lu@Sun.COM 
496*9095SZhigang.Lu@Sun.COM 	ph = ohci_polledp->ohci_polled_input_pipe_handle;
497*9095SZhigang.Lu@Sun.COM 	pp = (ohci_pipe_private_t *)ph->p_hcd_private;
498*9095SZhigang.Lu@Sun.COM 	tw = pp->pp_tw_head;
499*9095SZhigang.Lu@Sun.COM 
500*9095SZhigang.Lu@Sun.COM 	ASSERT(tw != NULL);
501*9095SZhigang.Lu@Sun.COM 	if (tw->tw_hctd_free_list == NULL) {
502*9095SZhigang.Lu@Sun.COM #ifndef lint
503*9095SZhigang.Lu@Sun.COM 	_NOTE(COMPETING_THREADS_NOW);
504*9095SZhigang.Lu@Sun.COM #endif
505*9095SZhigang.Lu@Sun.COM 		return (USB_SUCCESS);
506*9095SZhigang.Lu@Sun.COM 	}
507*9095SZhigang.Lu@Sun.COM 
508*9095SZhigang.Lu@Sun.COM 	/* Copy transmit buffer */
509*9095SZhigang.Lu@Sun.COM 	if (num_characters > POLLED_RAW_BUF_SIZE) {
510*9095SZhigang.Lu@Sun.COM 		cmn_err(CE_NOTE, "polled write size %d bigger than %d",
511*9095SZhigang.Lu@Sun.COM 		    num_characters, POLLED_RAW_BUF_SIZE);
512*9095SZhigang.Lu@Sun.COM 		num_characters = POLLED_RAW_BUF_SIZE;
513*9095SZhigang.Lu@Sun.COM 	}
514*9095SZhigang.Lu@Sun.COM 	tw->tw_length = num_characters;
515*9095SZhigang.Lu@Sun.COM 
516*9095SZhigang.Lu@Sun.COM 	ddi_rep_put8(tw->tw_accesshandle,
517*9095SZhigang.Lu@Sun.COM 	    buf, (uint8_t *)tw->tw_buf,
518*9095SZhigang.Lu@Sun.COM 	    tw->tw_length, DDI_DEV_AUTOINCR);
519*9095SZhigang.Lu@Sun.COM 	Sync_IO_Buffer_for_device(tw->tw_dmahandle, tw->tw_length);
520*9095SZhigang.Lu@Sun.COM 
521*9095SZhigang.Lu@Sun.COM 	/* Insert td into endpoint's tds list */
522*9095SZhigang.Lu@Sun.COM 	ctrl = tw->tw_direction | HC_TD_DT_0|HC_TD_1I | HC_TD_R;
523*9095SZhigang.Lu@Sun.COM 	bulk_pkg_size = min(tw->tw_length, OHCI_MAX_TD_XFER_SIZE);
524*9095SZhigang.Lu@Sun.COM 
525*9095SZhigang.Lu@Sun.COM 	(void) ohci_polled_insert_hc_td(ohcip, ctrl, 0, bulk_pkg_size, pp, tw);
526*9095SZhigang.Lu@Sun.COM 
527*9095SZhigang.Lu@Sun.COM 	/* Enable periodic list processing */
528*9095SZhigang.Lu@Sun.COM 	Set_OpReg(hcr_control,
529*9095SZhigang.Lu@Sun.COM 	    (Get_OpReg(hcr_control) | HCR_CONTROL_PLE));
530*9095SZhigang.Lu@Sun.COM 
531*9095SZhigang.Lu@Sun.COM 	/* Wait for bulk out tds transfer completion */
532*9095SZhigang.Lu@Sun.COM 	for (;;) {
533*9095SZhigang.Lu@Sun.COM 		intr = Get_OpReg(hcr_intr_status);
534*9095SZhigang.Lu@Sun.COM 
535*9095SZhigang.Lu@Sun.COM 		if (intr & HCR_INTR_FNO) {
536*9095SZhigang.Lu@Sun.COM 			ohci_handle_frame_number_overflow(ohcip);
537*9095SZhigang.Lu@Sun.COM 			ohci_polled_finish_interrupt(ohcip, HCR_INTR_FNO);
538*9095SZhigang.Lu@Sun.COM 		}
539*9095SZhigang.Lu@Sun.COM 
540*9095SZhigang.Lu@Sun.COM 		if (intr & HCR_INTR_WDH) {
541*9095SZhigang.Lu@Sun.COM 			if (ohci_polled_check_done_list(ohci_polledp) ==
542*9095SZhigang.Lu@Sun.COM 			    USB_SUCCESS) {
543*9095SZhigang.Lu@Sun.COM 				*num_characters_written =
544*9095SZhigang.Lu@Sun.COM 				    ohci_polled_process_input_list(
545*9095SZhigang.Lu@Sun.COM 				    ohci_polledp);
546*9095SZhigang.Lu@Sun.COM 				break;
547*9095SZhigang.Lu@Sun.COM 			}
548*9095SZhigang.Lu@Sun.COM 		}
549*9095SZhigang.Lu@Sun.COM 
550*9095SZhigang.Lu@Sun.COM 		Set_OpReg(hcr_intr_status, intr);
551*9095SZhigang.Lu@Sun.COM 		(void) Get_OpReg(hcr_intr_status);
552*9095SZhigang.Lu@Sun.COM 	}
553*9095SZhigang.Lu@Sun.COM 
554*9095SZhigang.Lu@Sun.COM 	/* Remove the endpoint from the lattice */
555*9095SZhigang.Lu@Sun.COM 	for (i = ohcip->ohci_polled_enter_count; i < NUM_INTR_ED_LISTS;
556*9095SZhigang.Lu@Sun.COM 	    i = i + MIN_LOW_SPEED_POLL_INTERVAL) {
557*9095SZhigang.Lu@Sun.COM 		Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
558*9095SZhigang.Lu@Sun.COM 		    ohci_ed_cpu_to_iommu(ohcip,
559*9095SZhigang.Lu@Sun.COM 		    ohci_polledp->ohci_polled_dummy_ed));
560*9095SZhigang.Lu@Sun.COM 	}
561*9095SZhigang.Lu@Sun.COM 
562*9095SZhigang.Lu@Sun.COM 	Set_OpReg(hcr_intr_status, intr);
563*9095SZhigang.Lu@Sun.COM 	(void) Get_OpReg(hcr_intr_status);
564*9095SZhigang.Lu@Sun.COM #ifndef lint
565*9095SZhigang.Lu@Sun.COM 	_NOTE(COMPETING_THREADS_NOW);
566*9095SZhigang.Lu@Sun.COM #endif
567*9095SZhigang.Lu@Sun.COM 	return (USB_SUCCESS);
568*9095SZhigang.Lu@Sun.COM }
569*9095SZhigang.Lu@Sun.COM 
570*9095SZhigang.Lu@Sun.COM 
571*9095SZhigang.Lu@Sun.COM /*
5720Sstevel@tonic-gate  * Internal Functions
5730Sstevel@tonic-gate  */
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate /*
5760Sstevel@tonic-gate  * Polled initialization routines
5770Sstevel@tonic-gate  */
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate /*
5810Sstevel@tonic-gate  * ohci_polled_init:
5820Sstevel@tonic-gate  *
5830Sstevel@tonic-gate  * Initialize generic information Uthat is needed to provide USB/POLLED
5840Sstevel@tonic-gate  * support.
5850Sstevel@tonic-gate  */
5860Sstevel@tonic-gate static int
ohci_polled_init(usba_pipe_handle_data_t * ph,ohci_state_t * ohcip,usb_console_info_impl_t * console_info)5870Sstevel@tonic-gate ohci_polled_init(
5880Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
5890Sstevel@tonic-gate 	ohci_state_t		*ohcip,
5900Sstevel@tonic-gate 	usb_console_info_impl_t	*console_info)
5910Sstevel@tonic-gate {
5920Sstevel@tonic-gate 	ohci_polled_t		*ohci_polledp;
5930Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;
594*9095SZhigang.Lu@Sun.COM 	int			pipe_attr;
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	/*
5990Sstevel@tonic-gate 	 * We have already initialized this structure. If the structure
6000Sstevel@tonic-gate 	 * has already been initialized, then we don't need to redo it.
6010Sstevel@tonic-gate 	 */
6020Sstevel@tonic-gate 	if (console_info->uci_private) {
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate 		return (USB_SUCCESS);
6050Sstevel@tonic-gate 	}
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	/* Allocate and intitialize a state structure */
6080Sstevel@tonic-gate 	ohci_polledp = (ohci_polled_t *)
6090Sstevel@tonic-gate 	    kmem_zalloc(sizeof (ohci_polled_t), KM_SLEEP);
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 	console_info->uci_private = (usb_console_info_private_t)ohci_polledp;
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 	/*
6140Sstevel@tonic-gate 	 * Store away the ohcip so that we can get to it when we are in
6150Sstevel@tonic-gate 	 * POLLED mode. We don't want to have to call ohci_obtain_state
6160Sstevel@tonic-gate 	 * every time we want to access this structure. Also save ohci
6170Sstevel@tonic-gate 	 * polled state information in ohcip.
6180Sstevel@tonic-gate 	 */
6190Sstevel@tonic-gate 	ohci_polledp->ohci_polled_ohcip = ohcip;
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 	/*
6220Sstevel@tonic-gate 	 * Save usb device and endpoint number information from the usb
6230Sstevel@tonic-gate 	 * pipe handle.
6240Sstevel@tonic-gate 	 */
6250Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
6260Sstevel@tonic-gate 	ohci_polledp->ohci_polled_usb_dev = ph->p_usba_device;
6270Sstevel@tonic-gate 	ohci_polledp->ohci_polled_ep_addr = ph->p_ep.bEndpointAddress;
6280Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 	/*
6310Sstevel@tonic-gate 	 * Allocate memory to make duplicate of original usb pipe handle.
6320Sstevel@tonic-gate 	 */
6330Sstevel@tonic-gate 	ohci_polledp->ohci_polled_input_pipe_handle =
6340Sstevel@tonic-gate 	    kmem_zalloc(sizeof (usba_pipe_handle_data_t), KM_SLEEP);
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 	/*
6370Sstevel@tonic-gate 	 * Copy the USB handle into the new pipe handle. Also
6380Sstevel@tonic-gate 	 * create new lock for the new pipe handle.
6390Sstevel@tonic-gate 	 */
6400Sstevel@tonic-gate 	bcopy((void *)ph,
6410Sstevel@tonic-gate 	    (void *)ohci_polledp->ohci_polled_input_pipe_handle,
6420Sstevel@tonic-gate 	    sizeof (usba_pipe_handle_data_t));
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	/*
6450Sstevel@tonic-gate 	 * uint64_t typecast to make sure amd64 can compile
6460Sstevel@tonic-gate 	 */
6470Sstevel@tonic-gate 	mutex_init(&ohci_polledp->ohci_polled_input_pipe_handle->p_mutex,
648693Sgovinda 	    NULL, MUTEX_DRIVER, DDI_INTR_PRI(ohcip->ohci_intr_pri));
6490Sstevel@tonic-gate 
6500Sstevel@tonic-gate 	/* Create a new ohci pipe private structure */
6510Sstevel@tonic-gate 	pp = (ohci_pipe_private_t *)
6520Sstevel@tonic-gate 	    kmem_zalloc(sizeof (ohci_pipe_private_t), KM_SLEEP);
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 	/*
6550Sstevel@tonic-gate 	 * Store the pointer in the pipe handle. This structure was also
6560Sstevel@tonic-gate 	 * just allocated.
6570Sstevel@tonic-gate 	 */
6580Sstevel@tonic-gate 	mutex_enter(&ohci_polledp->ohci_polled_input_pipe_handle->p_mutex);
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 	ohci_polledp->ohci_polled_input_pipe_handle->
6610Sstevel@tonic-gate 	    p_hcd_private = (usb_opaque_t)pp;
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	mutex_exit(&ohci_polledp->ohci_polled_input_pipe_handle->p_mutex);
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	/*
6660Sstevel@tonic-gate 	 * Store a pointer to the pipe handle. This structure was  just
6670Sstevel@tonic-gate 	 * allocated and it is not in use yet.	The locking is there to
6680Sstevel@tonic-gate 	 * satisfy warlock.
6690Sstevel@tonic-gate 	 */
6700Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate 	bcopy(&ph->p_policy, &pp->pp_policy, sizeof (usb_pipe_policy_t));
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 	pp->pp_pipe_handle = ohci_polledp->ohci_polled_input_pipe_handle;
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 	/*
6790Sstevel@tonic-gate 	 * Allocate a dummy for the interrupt table. This dummy will be
6800Sstevel@tonic-gate 	 * put into the action when we	switch interrupt  tables during
6810Sstevel@tonic-gate 	 * ohci_hcdi_polled_enter. Dummy is placed on the unused lattice
6820Sstevel@tonic-gate 	 * entries. When the ED is allocated we will replace dummy ED by
6830Sstevel@tonic-gate 	 * valid interrupt ED in one or more locations in the interrupt
6840Sstevel@tonic-gate 	 * lattice depending on the requested polling interval. Also we
6850Sstevel@tonic-gate 	 * will hang a dummy TD to the ED & dummy TD is used to indicate
6860Sstevel@tonic-gate 	 * the end of the TD chain.
6870Sstevel@tonic-gate 	 */
6880Sstevel@tonic-gate 	ohci_polledp->ohci_polled_dummy_ed = ohci_alloc_hc_ed(ohcip, NULL);
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_dummy_ed == NULL) {
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
6930Sstevel@tonic-gate 	}
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate 	/*
696*9095SZhigang.Lu@Sun.COM 	 * Allocate the endpoint. This ED will be inserted in
697*9095SZhigang.Lu@Sun.COM 	 * to the lattice chain for the device. This endpoint
6980Sstevel@tonic-gate 	 * will have the TDs hanging off of it for the processing.
6990Sstevel@tonic-gate 	 */
7000Sstevel@tonic-gate 	ohci_polledp->ohci_polled_ed = ohci_alloc_hc_ed(ohcip,
7010Sstevel@tonic-gate 	    ohci_polledp->ohci_polled_input_pipe_handle);
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_ed == NULL) {
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
7060Sstevel@tonic-gate 	}
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 	/* Set the state of pipe as idle */
7090Sstevel@tonic-gate 	pp->pp_state = OHCI_PIPE_STATE_IDLE;
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 	/* Insert the endpoint onto the pipe handle */
7120Sstevel@tonic-gate 	pp->pp_ept = ohci_polledp->ohci_polled_ed;
7130Sstevel@tonic-gate 
714*9095SZhigang.Lu@Sun.COM 	pipe_attr = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
715*9095SZhigang.Lu@Sun.COM 
716*9095SZhigang.Lu@Sun.COM 	switch (pipe_attr) {
717*9095SZhigang.Lu@Sun.COM 	case USB_EP_ATTR_INTR:
718*9095SZhigang.Lu@Sun.COM 		/*
719*9095SZhigang.Lu@Sun.COM 		 * Set soft interrupt handler flag in the normal mode usb
720*9095SZhigang.Lu@Sun.COM 		 * pipe handle.
721*9095SZhigang.Lu@Sun.COM 		 */
722*9095SZhigang.Lu@Sun.COM 		mutex_enter(&ph->p_mutex);
723*9095SZhigang.Lu@Sun.COM 		ph->p_spec_flag |= USBA_PH_FLAG_USE_SOFT_INTR;
724*9095SZhigang.Lu@Sun.COM 		mutex_exit(&ph->p_mutex);
7250Sstevel@tonic-gate 
726*9095SZhigang.Lu@Sun.COM 		/*
727*9095SZhigang.Lu@Sun.COM 		 * Insert a Interrupt polling request onto the endpoint.
728*9095SZhigang.Lu@Sun.COM 		 *
729*9095SZhigang.Lu@Sun.COM 		 * There will now be two TDs on the ED, one is the dummy TD
730*9095SZhigang.Lu@Sun.COM 		 * that was allocated above in the ohci_alloc_hc_ed and
731*9095SZhigang.Lu@Sun.COM 		 * this new one.
732*9095SZhigang.Lu@Sun.COM 		 */
733*9095SZhigang.Lu@Sun.COM 		if ((ohci_start_periodic_pipe_polling(ohcip,
734*9095SZhigang.Lu@Sun.COM 		    ohci_polledp->ohci_polled_input_pipe_handle,
735*9095SZhigang.Lu@Sun.COM 		    NULL, USB_FLAGS_SLEEP)) != USB_SUCCESS) {
736*9095SZhigang.Lu@Sun.COM 			return (USB_NO_RESOURCES);
737*9095SZhigang.Lu@Sun.COM 		}
738*9095SZhigang.Lu@Sun.COM 		break;
739*9095SZhigang.Lu@Sun.COM 	case USB_EP_ATTR_BULK:
740*9095SZhigang.Lu@Sun.COM 		if ((ohci_polled_create_tw(ohcip,
741*9095SZhigang.Lu@Sun.COM 		    ohci_polledp->ohci_polled_input_pipe_handle,
742*9095SZhigang.Lu@Sun.COM 		    USB_FLAGS_SLEEP)) != USB_SUCCESS) {
743*9095SZhigang.Lu@Sun.COM 			return (USB_NO_RESOURCES);
744*9095SZhigang.Lu@Sun.COM 		}
745*9095SZhigang.Lu@Sun.COM 		break;
746*9095SZhigang.Lu@Sun.COM 	default:
747*9095SZhigang.Lu@Sun.COM 		return (USB_FAILURE);
7480Sstevel@tonic-gate 	}
7490Sstevel@tonic-gate 	return (USB_SUCCESS);
7500Sstevel@tonic-gate }
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate /*
7540Sstevel@tonic-gate  * Polled deinitialization routines
7550Sstevel@tonic-gate  */
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate /*
7590Sstevel@tonic-gate  * ohci_polled_fini:
7600Sstevel@tonic-gate  */
7610Sstevel@tonic-gate static int
ohci_polled_fini(ohci_polled_t * ohci_polledp)7620Sstevel@tonic-gate ohci_polled_fini(ohci_polled_t	*ohci_polledp)
7630Sstevel@tonic-gate {
7640Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_polledp->ohci_polled_ohcip;
7650Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;
7660Sstevel@tonic-gate 	ohci_td_t		*curr_td, *next_td;
7670Sstevel@tonic-gate 	ohci_trans_wrapper_t	*curr_tw, *next_tw;
7680Sstevel@tonic-gate 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 	/*
7710Sstevel@tonic-gate 	 * If the structure is already in use, then don't free it.
7720Sstevel@tonic-gate 	 */
7730Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_flags & POLLED_INPUT_MODE) {
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate 		return (USB_SUCCESS);
7760Sstevel@tonic-gate 	}
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate 	pp = (ohci_pipe_private_t *)
7790Sstevel@tonic-gate 	    ohci_polledp->ohci_polled_input_pipe_handle->p_hcd_private;
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	/*
7820Sstevel@tonic-gate 	 * Deallocate all the pre-allocated interrupt requests
7830Sstevel@tonic-gate 	 */
7840Sstevel@tonic-gate 	ohci_handle_outstanding_requests(ohcip, pp);
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate 	/*
7870Sstevel@tonic-gate 	 * Traverse the list of TD's on this endpoint and these TD's
7880Sstevel@tonic-gate 	 * have outstanding transfer requests. Since list processing
7890Sstevel@tonic-gate 	 * is stopped, these TDs can be deallocated.
7900Sstevel@tonic-gate 	 */
7910Sstevel@tonic-gate 	ohci_traverse_tds(ohcip, pp->pp_pipe_handle);
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	/*
7940Sstevel@tonic-gate 	 * For each transfer wrapper on this pipe, free the TD and
7950Sstevel@tonic-gate 	 * free the TW.  We don't free the last TD in the chain
7960Sstevel@tonic-gate 	 * because it will be freed by ohci_deallocate_ed.  All TD's
7970Sstevel@tonic-gate 	 * on this TW are also on the end point associated with this
7980Sstevel@tonic-gate 	 * pipe.
7990Sstevel@tonic-gate 	 */
8000Sstevel@tonic-gate 	next_tw = pp->pp_tw_head;
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 	while (next_tw) {
8030Sstevel@tonic-gate 		next_td = (ohci_td_t *)next_tw->tw_hctd_head;
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 		/*
8060Sstevel@tonic-gate 		 * Walk through each TD for this transfer
8070Sstevel@tonic-gate 		 * wrapper and free that TD.
8080Sstevel@tonic-gate 		 */
8090Sstevel@tonic-gate 		while (next_td) {
8100Sstevel@tonic-gate 			curr_td = next_td;
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 			next_td = ohci_td_iommu_to_cpu(ohcip,
8130Sstevel@tonic-gate 			    Get_TD(next_td->hctd_tw_next_td));
8140Sstevel@tonic-gate 
8150Sstevel@tonic-gate 			ohci_deallocate_td(ohcip, curr_td);
8160Sstevel@tonic-gate 		}
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 		curr_tw = next_tw;
8190Sstevel@tonic-gate 		next_tw = curr_tw->tw_next;
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 		/* Free the transfer wrapper */
8220Sstevel@tonic-gate 		ohci_deallocate_tw_resources(ohcip, pp, curr_tw);
8230Sstevel@tonic-gate 	}
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate 	/*
8260Sstevel@tonic-gate 	 * Deallocate the endpoint descriptors that we allocated
8270Sstevel@tonic-gate 	 * with ohci_alloc_hc_ed.
8280Sstevel@tonic-gate 	 */
8290Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_dummy_ed) {
8300Sstevel@tonic-gate 		ohci_deallocate_ed(ohcip, ohci_polledp->ohci_polled_dummy_ed);
8310Sstevel@tonic-gate 	}
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_ed) {
8340Sstevel@tonic-gate 		ohci_deallocate_ed(ohcip, ohci_polledp->ohci_polled_ed);
8350Sstevel@tonic-gate 	}
8360Sstevel@tonic-gate 
8370Sstevel@tonic-gate 	mutex_destroy(&ohci_polledp->ohci_polled_input_pipe_handle->p_mutex);
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	/*
8400Sstevel@tonic-gate 	 * Destroy everything about the pipe that we allocated in
8410Sstevel@tonic-gate 	 * ohci_polled_duplicate_pipe_handle
8420Sstevel@tonic-gate 	 */
8430Sstevel@tonic-gate 	kmem_free(pp, sizeof (ohci_pipe_private_t));
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 	kmem_free(ohci_polledp->ohci_polled_input_pipe_handle,
8460Sstevel@tonic-gate 	    sizeof (usba_pipe_handle_data_t));
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	/*
8490Sstevel@tonic-gate 	 * We use this field to determine if a TD is for input or not,
8500Sstevel@tonic-gate 	 * so NULL the pointer so we don't check deallocated data.
8510Sstevel@tonic-gate 	 */
8520Sstevel@tonic-gate 	ohci_polledp->ohci_polled_input_pipe_handle = NULL;
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 	/*
8550Sstevel@tonic-gate 	 * Finally, free off the structure that we use to keep track
8560Sstevel@tonic-gate 	 * of all this.
8570Sstevel@tonic-gate 	 */
8580Sstevel@tonic-gate 	kmem_free(ohci_polledp, sizeof (ohci_polled_t));
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate 	return (USB_SUCCESS);
8610Sstevel@tonic-gate }
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 
8640Sstevel@tonic-gate /*
8650Sstevel@tonic-gate  * Polled save state routines
8660Sstevel@tonic-gate  */
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate /*
8700Sstevel@tonic-gate  * ohci_polled_save_state:
8710Sstevel@tonic-gate  */
8720Sstevel@tonic-gate static void
ohci_polled_save_state(ohci_polled_t * ohci_polledp)8730Sstevel@tonic-gate ohci_polled_save_state(ohci_polled_t	*ohci_polledp)
8740Sstevel@tonic-gate {
8750Sstevel@tonic-gate 	ohci_state_t		*ohcip;
8760Sstevel@tonic-gate 	int			i;
8770Sstevel@tonic-gate 	uint_t			polled_toggle;
8780Sstevel@tonic-gate 	uint_t			real_toggle;
8790Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = NULL;	/* Normal mode Pipe */
8800Sstevel@tonic-gate 	ohci_pipe_private_t	*polled_pp;	/* Polled mode Pipe */
8810Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
8820Sstevel@tonic-gate 	uint8_t			ep_addr;
8830Sstevel@tonic-gate 	ohci_save_intr_sts_t	*ohci_intr_sts;
8840Sstevel@tonic-gate 	ohci_regs_t		*ohci_polled_regsp;
8850Sstevel@tonic-gate 	ohci_td_t		*td, *prev_td;
8860Sstevel@tonic-gate 	ohci_td_t		*done_head, **done_list;
8870Sstevel@tonic-gate 
8880Sstevel@tonic-gate #ifndef lint
8890Sstevel@tonic-gate 	_NOTE(NO_COMPETING_THREADS_NOW);
8900Sstevel@tonic-gate #endif
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 	/*
8930Sstevel@tonic-gate 	 * If either of these two flags are set, then we have already
8940Sstevel@tonic-gate 	 * saved off the state information and setup the controller.
8950Sstevel@tonic-gate 	 */
8960Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_flags & POLLED_INPUT_MODE_INUSE) {
8970Sstevel@tonic-gate #ifndef lint
8980Sstevel@tonic-gate 		_NOTE(COMPETING_THREADS_NOW);
8990Sstevel@tonic-gate #endif
9000Sstevel@tonic-gate 		return;
9010Sstevel@tonic-gate 	}
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	ohcip = ohci_polledp->ohci_polled_ohcip;
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 	/*
9060Sstevel@tonic-gate 	 * Check if the number of keyboard reach the max number we can
9070Sstevel@tonic-gate 	 * support in polled mode
9080Sstevel@tonic-gate 	 */
9090Sstevel@tonic-gate 	if (++ ohcip->ohci_polled_enter_count > MAX_NUM_FOR_KEYBOARD) {
9100Sstevel@tonic-gate #ifndef lint
9110Sstevel@tonic-gate 		_NOTE(COMPETING_THREADS_NOW);
9120Sstevel@tonic-gate #endif
9130Sstevel@tonic-gate 		return;
9140Sstevel@tonic-gate 	}
9150Sstevel@tonic-gate 	/* Get the endpoint addr. */
9160Sstevel@tonic-gate 	ep_addr = ohci_polledp->ohci_polled_ep_addr;
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate 	/* Get the normal mode usb pipe handle */
9190Sstevel@tonic-gate 	ph = usba_hcdi_get_ph_data(ohci_polledp->ohci_polled_usb_dev, ep_addr);
9200Sstevel@tonic-gate 	ohci_intr_sts = &ohcip->ohci_save_intr_sts;
9210Sstevel@tonic-gate 	ohci_polled_regsp = &ohcip->ohci_polled_save_regs;
9220Sstevel@tonic-gate 
9230Sstevel@tonic-gate 	/*
9240Sstevel@tonic-gate 	 * Only the first enter keyboard entry disable the interrupt, save the
9250Sstevel@tonic-gate 	 * information of normal mode, stop the processing, initialize the
9260Sstevel@tonic-gate 	 * frame list table.
9270Sstevel@tonic-gate 	 */
9280Sstevel@tonic-gate 	if (ohcip->ohci_polled_enter_count == 1) {
9290Sstevel@tonic-gate 		/*
9300Sstevel@tonic-gate 		 * Prevent the ohci interrupt handler from handling interrupt.
9310Sstevel@tonic-gate 		 * We will turn off interrupts. This  keeps us from generating
9320Sstevel@tonic-gate 		 * a hardware interrupt.This is the useful for testing because
9330Sstevel@tonic-gate 		 * in POLLED  mode we can't get interrupts anyway. We can test
9340Sstevel@tonic-gate 		 * this code by shutting off hardware interrupt generation and
9350Sstevel@tonic-gate 		 * polling  for the interrupts.
9360Sstevel@tonic-gate 		 */
9370Sstevel@tonic-gate 		Set_OpReg(hcr_intr_disable, HCR_INTR_MIE);
9380Sstevel@tonic-gate 		/*
9390Sstevel@tonic-gate 		 * Save the current normal mode ohci registers	and later this
9400Sstevel@tonic-gate 		 * saved register copy is used to replace some of required ohci
9410Sstevel@tonic-gate 		 * registers before switching from polled mode to normal mode.
9420Sstevel@tonic-gate 		 */
9430Sstevel@tonic-gate 		bzero((void *)ohci_polled_regsp, sizeof (ohci_regs_t));
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 		ohci_polled_regsp->hcr_control = Get_OpReg(hcr_control);
9460Sstevel@tonic-gate 		ohci_polled_regsp->hcr_cmd_status = Get_OpReg(hcr_cmd_status);
9470Sstevel@tonic-gate 		ohci_polled_regsp->hcr_intr_enable = Get_OpReg(hcr_intr_enable);
9480Sstevel@tonic-gate 		ohci_polled_regsp->hcr_HCCA = Get_OpReg(hcr_HCCA);
9490Sstevel@tonic-gate 		ohci_polled_regsp->hcr_done_head = Get_OpReg(hcr_done_head);
9500Sstevel@tonic-gate 		ohci_polled_regsp->hcr_bulk_head = Get_OpReg(hcr_bulk_head);
9510Sstevel@tonic-gate 		ohci_polled_regsp->hcr_ctrl_head = Get_OpReg(hcr_ctrl_head);
9520Sstevel@tonic-gate 
9530Sstevel@tonic-gate 		/*
9540Sstevel@tonic-gate 		 * The functionality &	importance of critical code section in
9550Sstevel@tonic-gate 		 * the normal mode ohci interrupt handler and its usage in the
9560Sstevel@tonic-gate 		 * polled mode is explained below.
9570Sstevel@tonic-gate 		 *
9580Sstevel@tonic-gate 		 * (a) Normal mode:
9590Sstevel@tonic-gate 		 *
9600Sstevel@tonic-gate 		 *	- Set the flag indicating that processing critical code
9610Sstevel@tonic-gate 		 *	  in ohci interrupt handler.
9620Sstevel@tonic-gate 		 *
9630Sstevel@tonic-gate 		 *	- Process the missed ohci interrupts by copying missed
9640Sstevel@tonic-gate 		 *	  interrupt events & done head list fields information
9650Sstevel@tonic-gate 		 *	  to the critical interrupt events & done list fields.
9660Sstevel@tonic-gate 		 *
9670Sstevel@tonic-gate 		 *	- Reset the missed ohci interrupt events and done head
9680Sstevel@tonic-gate 		 *	  list fields so that the new missed  interrupt events
9690Sstevel@tonic-gate 		 *	  and done head list information can be saved.
9700Sstevel@tonic-gate 		 *
9710Sstevel@tonic-gate 		 *	- All above steps will be executed within the critical
9720Sstevel@tonic-gate 		 *	  section of the  interrupt handler.  Then ohci missed
9730Sstevel@tonic-gate 		 *	  interrupt handler will be called to service the ohci
9740Sstevel@tonic-gate 		 *	  missed interrupts.
9750Sstevel@tonic-gate 		 *
9760Sstevel@tonic-gate 		 * (b) Polled mode:
9770Sstevel@tonic-gate 		 *
9780Sstevel@tonic-gate 		 *	- On entering the polled code, checks for the critical
9790Sstevel@tonic-gate 		 *	  section code execution within normal	mode interrupt
9800Sstevel@tonic-gate 		 *	  handler.
9810Sstevel@tonic-gate 		 *
9820Sstevel@tonic-gate 		 *	- If critical section code is  executing in the normal
9830Sstevel@tonic-gate 		 *	  mode ohci interrupt handler & if copying of the ohci
9840Sstevel@tonic-gate 		 *	  missed interrupt events and done head list fields to
9850Sstevel@tonic-gate 		 *	  the critical fields is finished then, save the  "any
9860Sstevel@tonic-gate 		 *	  missed interrupt events and done head list"  because
9870Sstevel@tonic-gate 		 *	  of current polled mode switch into "critical	missed
9880Sstevel@tonic-gate 		 *	  interrupt events & done list fields" instead	actual
9890Sstevel@tonic-gate 		 *	  missed events and done list fields.
9900Sstevel@tonic-gate 		 *
9910Sstevel@tonic-gate 		 *	- Otherwise save "any missed interrupt events and done
9920Sstevel@tonic-gate 		 *	  list" because of this  current polled mode switch in
9930Sstevel@tonic-gate 		 *	  the actual missed  interrupt events & done head list
9940Sstevel@tonic-gate 		 *	  fields.
9950Sstevel@tonic-gate 		 */
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 		/*
9980Sstevel@tonic-gate 		 * Check and save the pending SOF interrupt  condition for the
9990Sstevel@tonic-gate 		 * ohci normal mode. This information will be  saved either in
10000Sstevel@tonic-gate 		 * the critical missed event fields or in actual  missed event
10010Sstevel@tonic-gate 		 * fields depending on the whether the critical code section's
10020Sstevel@tonic-gate 		 * execution flag was set or not when switched to  polled mode
10030Sstevel@tonic-gate 		 * from normal mode.
10040Sstevel@tonic-gate 		 */
10050Sstevel@tonic-gate 		if ((ohci_intr_sts->ohci_intr_flag & OHCI_INTR_CRITICAL) &&
10060Sstevel@tonic-gate 		    (ohci_intr_sts->ohci_critical_intr_sts != 0)) {
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate 			ohci_intr_sts->ohci_critical_intr_sts |=
10090Sstevel@tonic-gate 			    ((Get_OpReg(hcr_intr_status) &
10100Sstevel@tonic-gate 			    Get_OpReg(hcr_intr_enable)) & HCR_INTR_SOF);
10110Sstevel@tonic-gate 		} else {
10120Sstevel@tonic-gate 			ohci_intr_sts->ohci_missed_intr_sts |=
10130Sstevel@tonic-gate 			    ((Get_OpReg(hcr_intr_status) &
10140Sstevel@tonic-gate 			    Get_OpReg(hcr_intr_enable)) & HCR_INTR_SOF);
10150Sstevel@tonic-gate 		}
10160Sstevel@tonic-gate 		ohci_polled_stop_processing(ohci_polledp);
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 		/* Process any missed Frame Number Overflow (FNO) interrupt */
10190Sstevel@tonic-gate 		ohci_polled_handle_frame_number_overflow(ohcip);
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 		/*
10220Sstevel@tonic-gate 		 * By this time all list processing has been stopped.Now check
10230Sstevel@tonic-gate 		 * and save the information about the pending HCCA done  list,
10240Sstevel@tonic-gate 		 * done head ohci register and WDH bit in the interrupt status
10250Sstevel@tonic-gate 		 * register. This information will be saved either in critical
10260Sstevel@tonic-gate 		 * missed event fields or in actual missed event fields depend
10270Sstevel@tonic-gate 		 * on the whether the  critical code section's	execution flag
10280Sstevel@tonic-gate 		 * was set or not when switched to polled mode from the normal
10290Sstevel@tonic-gate 		 * mode.
10300Sstevel@tonic-gate 		 */
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate 		/* Read and Save the HCCA DoneHead value */
10330Sstevel@tonic-gate 		done_head = (ohci_td_t *)(uintptr_t)(Get_HCCA(
10340Sstevel@tonic-gate 		    ohcip->ohci_hccap->HccaDoneHead) & HCCA_DONE_HEAD_MASK);
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 		if ((done_head) &&
10370Sstevel@tonic-gate 		    (done_head != ohci_intr_sts->ohci_curr_done_lst)) {
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 			if ((ohci_intr_sts->ohci_intr_flag &
10400Sstevel@tonic-gate 			    OHCI_INTR_CRITICAL) &&
10410Sstevel@tonic-gate 			    ((ohci_intr_sts->ohci_critical_done_lst) ||
10420Sstevel@tonic-gate 			    (ohci_intr_sts->ohci_missed_done_lst == NULL))) {
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate 				done_list =
10450Sstevel@tonic-gate 				    &ohci_intr_sts->ohci_critical_done_lst;
10460Sstevel@tonic-gate 				ohci_intr_sts->ohci_critical_intr_sts |=
10470Sstevel@tonic-gate 				    HCR_INTR_WDH;
10480Sstevel@tonic-gate 			} else {
10490Sstevel@tonic-gate 				done_list =
10500Sstevel@tonic-gate 				    &ohci_intr_sts->ohci_missed_done_lst;
10510Sstevel@tonic-gate 				ohci_intr_sts->ohci_missed_intr_sts |=
10520Sstevel@tonic-gate 				    HCR_INTR_WDH;
10530Sstevel@tonic-gate 			}
10540Sstevel@tonic-gate 
10550Sstevel@tonic-gate 			if (*done_list) {
10560Sstevel@tonic-gate 				td = (ohci_td_t *)
10570Sstevel@tonic-gate 				    ohci_td_iommu_to_cpu(ohcip,
10580Sstevel@tonic-gate 				    (uintptr_t)done_head);
10590Sstevel@tonic-gate 
10600Sstevel@tonic-gate 				while (td) {
10610Sstevel@tonic-gate 					prev_td = td;
10620Sstevel@tonic-gate 					td = ohci_td_iommu_to_cpu(ohcip,
10630Sstevel@tonic-gate 					    Get_TD(td->hctd_next_td));
10640Sstevel@tonic-gate 				}
10650Sstevel@tonic-gate 
10660Sstevel@tonic-gate 				Set_TD(prev_td->hctd_next_td, *done_list);
10670Sstevel@tonic-gate 
10680Sstevel@tonic-gate 				*done_list = done_head;
10690Sstevel@tonic-gate 			} else {
10700Sstevel@tonic-gate 				*done_list = (ohci_td_t *)done_head;
10710Sstevel@tonic-gate 			}
10720Sstevel@tonic-gate 		}
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate 		/*
10750Sstevel@tonic-gate 		 * Save the latest hcr_done_head ohci register value,  so that
10760Sstevel@tonic-gate 		 * this value can be replaced  when exit from the POLLED mode.
10770Sstevel@tonic-gate 		 */
10780Sstevel@tonic-gate 		ohci_polled_regsp->hcr_done_head = Get_OpReg(hcr_done_head);
10790Sstevel@tonic-gate 		/*
10800Sstevel@tonic-gate 		 * Reset the HCCA done head and ohci done head register.
10810Sstevel@tonic-gate 		 */
10820Sstevel@tonic-gate 		Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, NULL);
10830Sstevel@tonic-gate 		Set_OpReg(hcr_done_head, (uint32_t)0x0);
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 		/*
10860Sstevel@tonic-gate 		 * Clear the  WriteDoneHead interrupt bit in the ohci interrupt
10870Sstevel@tonic-gate 		 * status register.
10880Sstevel@tonic-gate 		 */
10890Sstevel@tonic-gate 		Set_OpReg(hcr_intr_status, HCR_INTR_WDH);
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 		/*
10920Sstevel@tonic-gate 		 * Save the current interrupt lattice and  replace this lattice
10930Sstevel@tonic-gate 		 * with an lattice used in POLLED mode. We will restore lattice
10940Sstevel@tonic-gate 		 * back when we exit from the POLLED mode.
10950Sstevel@tonic-gate 		 */
10960Sstevel@tonic-gate 		for (i = 0; i < NUM_INTR_ED_LISTS; i++) {
10970Sstevel@tonic-gate 			ohcip->ohci_polled_save_IntTble[i] =
10980Sstevel@tonic-gate 			    (ohci_ed_t *)(uintptr_t)Get_HCCA(
10990Sstevel@tonic-gate 			    ohcip->ohci_hccap->HccaIntTble[i]);
11000Sstevel@tonic-gate 		}
11010Sstevel@tonic-gate 		/*
11020Sstevel@tonic-gate 		 * Fill in the lattice with dummy EDs. These EDs are used so the
11030Sstevel@tonic-gate 		 * controller can tell that it is at the end of the ED list.
11040Sstevel@tonic-gate 		 */
11050Sstevel@tonic-gate 		for (i = 0; i < NUM_INTR_ED_LISTS; i++) {
11060Sstevel@tonic-gate 			Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
11070Sstevel@tonic-gate 			    ohci_ed_cpu_to_iommu(ohcip,
11080Sstevel@tonic-gate 			    ohci_polledp->ohci_polled_dummy_ed));
11090Sstevel@tonic-gate 		}
11100Sstevel@tonic-gate 	}
11110Sstevel@tonic-gate 	/* Get the polled mode ohci pipe private structure */
11120Sstevel@tonic-gate 	polled_pp = (ohci_pipe_private_t *)
11130Sstevel@tonic-gate 	    ohci_polledp->ohci_polled_input_pipe_handle->p_hcd_private;
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 	/*
11160Sstevel@tonic-gate 	 * Before replacing the lattice, adjust the data togggle on the
11170Sstevel@tonic-gate 	 * on the ohci's interrupt ed
11180Sstevel@tonic-gate 	 */
11190Sstevel@tonic-gate 	polled_toggle = (Get_ED(polled_pp->pp_ept->hced_headp) &
11207425SGongtian.Zhao@Sun.COM 	    HC_EPT_Carry) ? DATA1:DATA0;
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 	/*
11230Sstevel@tonic-gate 	 * If normal mode interrupt pipe endpoint is active, get the data
11240Sstevel@tonic-gate 	 * toggle from the this interrupt endpoint through the corresponding
11250Sstevel@tonic-gate 	 * interrupt pipe handle. Else get the data toggle information from
11260Sstevel@tonic-gate 	 * the usb device structure and this information is saved during the
11270Sstevel@tonic-gate 	 * normal mode interrupt pipe close. Use this data toggle information
11280Sstevel@tonic-gate 	 * to fix the data toggle of polled mode interrupt endpoint.
11290Sstevel@tonic-gate 	 */
11300Sstevel@tonic-gate 	if (ph) {
11310Sstevel@tonic-gate 		/* Get the normal mode ohci pipe private structure */
11320Sstevel@tonic-gate 		pp = (ohci_pipe_private_t *)ph->p_hcd_private;
11330Sstevel@tonic-gate 
11340Sstevel@tonic-gate 		real_toggle = (Get_ED(pp->pp_ept->hced_headp) &
11350Sstevel@tonic-gate 		    HC_EPT_Carry) ? DATA1:DATA0;
11360Sstevel@tonic-gate 	} else {
11370Sstevel@tonic-gate 		real_toggle = usba_hcdi_get_data_toggle(
11380Sstevel@tonic-gate 		    ohci_polledp->ohci_polled_usb_dev, ep_addr);
11390Sstevel@tonic-gate 	}
11400Sstevel@tonic-gate 
11410Sstevel@tonic-gate 	if (polled_toggle != real_toggle) {
11420Sstevel@tonic-gate 		if (real_toggle == DATA0) {
11430Sstevel@tonic-gate 			Set_ED(polled_pp->pp_ept->hced_headp,
11440Sstevel@tonic-gate 			    Get_ED(polled_pp->pp_ept->hced_headp) &
11450Sstevel@tonic-gate 			    ~HC_EPT_Carry);
11460Sstevel@tonic-gate 		} else {
11470Sstevel@tonic-gate 			Set_ED(polled_pp->pp_ept->hced_headp,
11480Sstevel@tonic-gate 			    Get_ED(polled_pp->pp_ept->hced_headp) |
11490Sstevel@tonic-gate 			    HC_EPT_Carry);
11500Sstevel@tonic-gate 		}
11510Sstevel@tonic-gate 	}
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	/*
11540Sstevel@tonic-gate 	 * Check whether Halt bit is set in the ED and if so  clear the
11550Sstevel@tonic-gate 	 * halt bit.
11560Sstevel@tonic-gate 	 */
11570Sstevel@tonic-gate 	if (polled_pp->pp_ept->hced_headp & HC_EPT_Halt) {
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate 		/* Clear the halt bit */
11600Sstevel@tonic-gate 		Set_ED(polled_pp->pp_ept->hced_headp,
11610Sstevel@tonic-gate 		    (Get_ED(polled_pp->pp_ept->hced_headp) & ~HC_EPT_Halt));
11620Sstevel@tonic-gate 	}
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 	/*
11650Sstevel@tonic-gate 	 * Now, add the endpoint to the lattice that we will  hang  our
11660Sstevel@tonic-gate 	 * TD's off of.  We need to poll this device at  every 8 ms and
11670Sstevel@tonic-gate 	 * hence add this ED needs 4 entries in interrupt lattice.
11680Sstevel@tonic-gate 	 */
11690Sstevel@tonic-gate 	for (i = (ohcip->ohci_polled_enter_count -1); i < NUM_INTR_ED_LISTS;
11707425SGongtian.Zhao@Sun.COM 	    i = i + MIN_LOW_SPEED_POLL_INTERVAL) {
11710Sstevel@tonic-gate 		Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
11720Sstevel@tonic-gate 		    ohci_ed_cpu_to_iommu(ohcip,
11730Sstevel@tonic-gate 		    ohci_polledp->ohci_polled_ed));
11740Sstevel@tonic-gate 	}
11750Sstevel@tonic-gate 	/*
11760Sstevel@tonic-gate 	 * Only the first enter keyboard entry clear the contents of
11770Sstevel@tonic-gate 	 * periodic ED register and enable the WDH interrupt and
11780Sstevel@tonic-gate 	 * start process the periodic list.
11790Sstevel@tonic-gate 	 */
11800Sstevel@tonic-gate 	if (ohcip->ohci_polled_enter_count == 1) {
11810Sstevel@tonic-gate 		/*
11820Sstevel@tonic-gate 		 * Clear the contents of current ohci periodic ED register that
11830Sstevel@tonic-gate 		 * is physical address of current Isochronous or Interrupt ED.
11840Sstevel@tonic-gate 		 */
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate 		Set_OpReg(hcr_periodic_curr, (uint32_t)0x0);
11870Sstevel@tonic-gate 
11880Sstevel@tonic-gate 		/* Make sure WriteDoneHead interrupt is enabled */
11890Sstevel@tonic-gate 		Set_OpReg(hcr_intr_enable, HCR_INTR_WDH);
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate 		/*
11920Sstevel@tonic-gate 		 * Enable the periodic list. We will now start processing EDs &
11930Sstevel@tonic-gate 		 * TDs again.
11940Sstevel@tonic-gate 		 */
11950Sstevel@tonic-gate 		Set_OpReg(hcr_control,
11960Sstevel@tonic-gate 		    (Get_OpReg(hcr_control) | HCR_CONTROL_PLE));
11970Sstevel@tonic-gate 	}
11980Sstevel@tonic-gate #ifndef lint
11990Sstevel@tonic-gate 	_NOTE(COMPETING_THREADS_NOW);
12000Sstevel@tonic-gate #endif
12010Sstevel@tonic-gate }
12020Sstevel@tonic-gate 
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate /*
12050Sstevel@tonic-gate  * ohci_polled_stop_processing:
12060Sstevel@tonic-gate  */
12070Sstevel@tonic-gate static void
ohci_polled_stop_processing(ohci_polled_t * ohci_polledp)12080Sstevel@tonic-gate ohci_polled_stop_processing(ohci_polled_t	*ohci_polledp)
12090Sstevel@tonic-gate {
12100Sstevel@tonic-gate 	ohci_state_t		*ohcip;
12110Sstevel@tonic-gate 	uint_t			count;
12120Sstevel@tonic-gate 	ohci_regs_t		*ohci_polled_regsp;
12130Sstevel@tonic-gate 
12140Sstevel@tonic-gate 	ohcip = ohci_polledp->ohci_polled_ohcip;
12150Sstevel@tonic-gate 	ohci_polled_regsp = &ohcip->ohci_polled_save_regs;
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 	/*
12180Sstevel@tonic-gate 	 * Turn off all list processing. This will take place starting
12190Sstevel@tonic-gate 	 * at the next frame.
12200Sstevel@tonic-gate 	 */
12210Sstevel@tonic-gate 	Set_OpReg(hcr_control,
12220Sstevel@tonic-gate 	    (ohci_polled_regsp->hcr_control) & ~(HCR_CONTROL_CLE|
12230Sstevel@tonic-gate 	    HCR_CONTROL_PLE| HCR_CONTROL_BLE|HCR_CONTROL_IE));
12240Sstevel@tonic-gate 
12250Sstevel@tonic-gate 	/*
12260Sstevel@tonic-gate 	 * Make sure that the  SOF interrupt bit is cleared in the ohci
12270Sstevel@tonic-gate 	 * interrupt status register.
12280Sstevel@tonic-gate 	 */
12290Sstevel@tonic-gate 	Set_OpReg(hcr_intr_status, HCR_INTR_SOF);
12300Sstevel@tonic-gate 
12310Sstevel@tonic-gate 	/* Enable SOF interrupt */
12320Sstevel@tonic-gate 	Set_OpReg(hcr_intr_enable, HCR_INTR_SOF);
12330Sstevel@tonic-gate 
12340Sstevel@tonic-gate 	/*
12350Sstevel@tonic-gate 	 * According to  OHCI Specification,  we have to wait for eight
12360Sstevel@tonic-gate 	 * start of frames to make sure that the Host Controller writes
12370Sstevel@tonic-gate 	 * contents of done head register to done head filed of HCCA.
12380Sstevel@tonic-gate 	 */
12390Sstevel@tonic-gate 	for (count = 0; count <= DONE_QUEUE_INTR_COUNTER; count++) {
12400Sstevel@tonic-gate 		while (!((Get_OpReg(hcr_intr_status)) & HCR_INTR_SOF)) {
12410Sstevel@tonic-gate 			continue;
12420Sstevel@tonic-gate 		}
12430Sstevel@tonic-gate 
12440Sstevel@tonic-gate 		/* Acknowledge the SOF interrupt */
12450Sstevel@tonic-gate 		ohci_polled_finish_interrupt(ohcip, HCR_INTR_SOF);
12460Sstevel@tonic-gate 	}
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate 	Set_OpReg(hcr_intr_disable, HCR_INTR_SOF);
12490Sstevel@tonic-gate }
12500Sstevel@tonic-gate 
12510Sstevel@tonic-gate 
12520Sstevel@tonic-gate /*
12530Sstevel@tonic-gate  * Polled restore state routines
12540Sstevel@tonic-gate  */
12550Sstevel@tonic-gate 
12560Sstevel@tonic-gate /*
12570Sstevel@tonic-gate  * ohci_polled_restore_state:
12580Sstevel@tonic-gate  */
12590Sstevel@tonic-gate static void
ohci_polled_restore_state(ohci_polled_t * ohci_polledp)12600Sstevel@tonic-gate ohci_polled_restore_state(ohci_polled_t	*ohci_polledp)
12610Sstevel@tonic-gate {
12620Sstevel@tonic-gate 	ohci_state_t		*ohcip;
12630Sstevel@tonic-gate 	int			i;
12640Sstevel@tonic-gate 	uint_t			polled_toggle;
12650Sstevel@tonic-gate 	uint_t			real_toggle;
12660Sstevel@tonic-gate 	ohci_pipe_private_t	*pp = NULL;	/* Normal mode Pipe */
12670Sstevel@tonic-gate 	ohci_pipe_private_t	*polled_pp;	/* Polled mode Pipe */
12680Sstevel@tonic-gate 	ohci_td_t		*td;
12690Sstevel@tonic-gate 	ohci_td_t		*next_td;	/* TD pointers */
12700Sstevel@tonic-gate 	uint_t			count;
12710Sstevel@tonic-gate 	ohci_save_intr_sts_t	*ohci_intr_sts;
12720Sstevel@tonic-gate 	ohci_regs_t		*ohci_polled_regsp;
12730Sstevel@tonic-gate 	uint32_t		mask;
12740Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
12750Sstevel@tonic-gate 	uint8_t			ep_addr;
12760Sstevel@tonic-gate 
12770Sstevel@tonic-gate #ifndef lint
12780Sstevel@tonic-gate 	_NOTE(NO_COMPETING_THREADS_NOW);
12790Sstevel@tonic-gate #endif
12800Sstevel@tonic-gate 
12810Sstevel@tonic-gate 	/*
12820Sstevel@tonic-gate 	 * If this flag is set, then we are still using this structure,
12830Sstevel@tonic-gate 	 * so don't restore any controller state information yet.
12840Sstevel@tonic-gate 	 */
12850Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_flags & POLLED_INPUT_MODE_INUSE) {
12860Sstevel@tonic-gate 
12870Sstevel@tonic-gate #ifndef lint
12880Sstevel@tonic-gate 		_NOTE(COMPETING_THREADS_NOW);
12890Sstevel@tonic-gate #endif
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 		return;
12920Sstevel@tonic-gate 	}
12930Sstevel@tonic-gate 
12940Sstevel@tonic-gate 	ohcip = ohci_polledp->ohci_polled_ohcip;
12950Sstevel@tonic-gate 	ohci_intr_sts = &ohcip->ohci_save_intr_sts;
12960Sstevel@tonic-gate 	ohci_polled_regsp = &ohcip->ohci_polled_save_regs;
12970Sstevel@tonic-gate 	ohcip->ohci_polled_enter_count --;
12980Sstevel@tonic-gate 
12990Sstevel@tonic-gate 	/* Get the endpoint addr. */
13000Sstevel@tonic-gate 	ep_addr = ohci_polledp->ohci_polled_ep_addr;
13010Sstevel@tonic-gate 	/* Get the normal mode usb pipe handle */
13020Sstevel@tonic-gate 	ph = usba_hcdi_get_ph_data(ohci_polledp->ohci_polled_usb_dev, ep_addr);
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate 	/*
13050Sstevel@tonic-gate 	 * Only the first leave keyboard entry turn off all list processing.
13060Sstevel@tonic-gate 	 * This will take place starting at the next frame.
13070Sstevel@tonic-gate 	 */
13080Sstevel@tonic-gate 	if (Get_OpReg(hcr_control) & HCR_CONTROL_PLE) {
13090Sstevel@tonic-gate 		Set_OpReg(hcr_control,
13100Sstevel@tonic-gate 		    (Get_OpReg(hcr_control) & ~HCR_CONTROL_PLE));
13110Sstevel@tonic-gate 	}
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate 	/*
13140Sstevel@tonic-gate 	 * Only the last leave keyboard entry restore the info for
13150Sstevel@tonic-gate 	 * normal mode.
13160Sstevel@tonic-gate 	 */
13170Sstevel@tonic-gate 	if (ohcip->ohci_polled_enter_count == 0) {
13180Sstevel@tonic-gate 		Set_OpReg(hcr_intr_enable, HCR_INTR_SOF);
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 		/*
13210Sstevel@tonic-gate 		 * According to  OHCI Specification,  we have to wait for eight
13220Sstevel@tonic-gate 		 * start of frames to make sure that the Host Controller writes
13230Sstevel@tonic-gate 		 * contents of done head register to done head filed of HCCA.
13240Sstevel@tonic-gate 		 */
13250Sstevel@tonic-gate 		for (count = 0; count <= DONE_QUEUE_INTR_COUNTER; count++) {
13260Sstevel@tonic-gate 			while (!((Get_OpReg(hcr_intr_status)) & HCR_INTR_SOF)) {
13270Sstevel@tonic-gate 				continue;
13280Sstevel@tonic-gate 			}
13290Sstevel@tonic-gate 			/* Acknowledge the SOF interrupt */
13300Sstevel@tonic-gate 			ohci_polled_finish_interrupt(ohcip, HCR_INTR_SOF);
13310Sstevel@tonic-gate 		}
13320Sstevel@tonic-gate 
13330Sstevel@tonic-gate 		/*
13340Sstevel@tonic-gate 		 * Check any Frame Number Overflow interrupt (FNO) is pending.
13350Sstevel@tonic-gate 		 */
13360Sstevel@tonic-gate 		ohci_polled_handle_frame_number_overflow(ohcip);
13370Sstevel@tonic-gate 
13380Sstevel@tonic-gate 		/*
13390Sstevel@tonic-gate 		 * Before switching back, we have to process last TD in the
13400Sstevel@tonic-gate 		 * POLLED mode. It may be in the hcr_done_head register or
13410Sstevel@tonic-gate 		 * in done list or in the lattice. If it is either on the
13420Sstevel@tonic-gate 		 * hcr_done_head register or in the done list, just re-inserted
13430Sstevel@tonic-gate 		 * into the ED's TD list.
13440Sstevel@tonic-gate 		 *
13450Sstevel@tonic-gate 		 * First look up at the TD's that are in the hcr_done_head
13460Sstevel@tonic-gate 		 * register and re-insert them back into the ED's TD list.
13470Sstevel@tonic-gate 		 */
13480Sstevel@tonic-gate 		td = ohci_td_iommu_to_cpu(ohcip,
13490Sstevel@tonic-gate 		    (uintptr_t)Get_OpReg(hcr_done_head));
13500Sstevel@tonic-gate 
13510Sstevel@tonic-gate 		while (td) {
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 		next_td = ohci_td_iommu_to_cpu(ohcip, Get_TD(td->hctd_next_td));
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 			/*
13560Sstevel@tonic-gate 			 * Insert valid interrupt TD back into ED's
13570Sstevel@tonic-gate 			 * TD list. No periodic TD's will be processed
13580Sstevel@tonic-gate 			 * since all processing has been stopped.
13590Sstevel@tonic-gate 			 */
13600Sstevel@tonic-gate 			ohci_polled_insert_td(ohcip, td);
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate 			td = next_td;
13630Sstevel@tonic-gate 		}
13640Sstevel@tonic-gate 
13650Sstevel@tonic-gate 		/*
13660Sstevel@tonic-gate 		 * Now look up at the TD's that are in the HCCA done head list &
13670Sstevel@tonic-gate 		 * re-insert them back into the ED's TD list.
13680Sstevel@tonic-gate 		 */
13690Sstevel@tonic-gate 		td = ohci_td_iommu_to_cpu(ohcip, (Get_HCCA(
13700Sstevel@tonic-gate 		    ohcip->ohci_hccap->HccaDoneHead) & HCCA_DONE_HEAD_MASK));
13710Sstevel@tonic-gate 
13720Sstevel@tonic-gate 		while (td) {
13730Sstevel@tonic-gate 
13740Sstevel@tonic-gate 			next_td = ohci_td_iommu_to_cpu(ohcip,
13750Sstevel@tonic-gate 			    Get_TD(td->hctd_next_td));
13760Sstevel@tonic-gate 
13770Sstevel@tonic-gate 			/*
13780Sstevel@tonic-gate 			 * Insert valid interrupt TD back into ED's
13790Sstevel@tonic-gate 			 * TD list. No periodic TD's will be processed
13800Sstevel@tonic-gate 			 * since all processing has been stopped.
13810Sstevel@tonic-gate 			 */
13820Sstevel@tonic-gate 			ohci_polled_insert_td(ohcip, td);
13830Sstevel@tonic-gate 
13840Sstevel@tonic-gate 			td = next_td;
13850Sstevel@tonic-gate 		}
13860Sstevel@tonic-gate 		/* Reset the HCCA done head list to NULL */
13870Sstevel@tonic-gate 		Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, NULL);
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate 		/*
13900Sstevel@tonic-gate 		 * Replace the hcr_done_head register field with the saved copy
13910Sstevel@tonic-gate 		 * of current normal mode hcr_done_head register contents.
13920Sstevel@tonic-gate 		 */
13930Sstevel@tonic-gate 		Set_OpReg(hcr_done_head,
13940Sstevel@tonic-gate 		    (uint32_t)ohci_polled_regsp->hcr_done_head);
13950Sstevel@tonic-gate 
13960Sstevel@tonic-gate 		/*
13970Sstevel@tonic-gate 		 * Clear the WriteDoneHead and SOF interrupt bits in the ohci
13980Sstevel@tonic-gate 		 * interrupt status register.
13990Sstevel@tonic-gate 		 */
14000Sstevel@tonic-gate 		Set_OpReg(hcr_intr_status, (HCR_INTR_WDH | HCR_INTR_SOF));
14010Sstevel@tonic-gate 	}
14020Sstevel@tonic-gate 
14030Sstevel@tonic-gate 	/* Get the polled mode ohci pipe private structure */
14040Sstevel@tonic-gate 	polled_pp = (ohci_pipe_private_t *)
14050Sstevel@tonic-gate 	    ohci_polledp->ohci_polled_input_pipe_handle->p_hcd_private;
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate 	/*
14080Sstevel@tonic-gate 	 * Before replacing the lattice, adjust the data togggle
14090Sstevel@tonic-gate 	 * on the on the ohci's interrupt ed
14100Sstevel@tonic-gate 	 */
14110Sstevel@tonic-gate 	polled_toggle = (Get_ED(polled_pp->pp_ept->hced_headp) &
14127425SGongtian.Zhao@Sun.COM 	    HC_EPT_Carry) ? DATA1:DATA0;
14130Sstevel@tonic-gate 
14140Sstevel@tonic-gate 	/*
14150Sstevel@tonic-gate 	 * If normal mode interrupt pipe endpoint is active, fix the
14160Sstevel@tonic-gate 	 * data toggle for this interrupt endpoint by getting the data
14170Sstevel@tonic-gate 	 * toggle information from the polled interrupt endpoint. Else
14180Sstevel@tonic-gate 	 * save the data toggle information in usb device structure.
14190Sstevel@tonic-gate 	 */
14200Sstevel@tonic-gate 	if (ph) {
14210Sstevel@tonic-gate 		/* Get the normal mode ohci pipe private structure */
14220Sstevel@tonic-gate 		pp = (ohci_pipe_private_t *)ph->p_hcd_private;
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate 		real_toggle = (Get_ED(pp->pp_ept->hced_headp) &
14250Sstevel@tonic-gate 		    HC_EPT_Carry) ? DATA1:DATA0;
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 		if (polled_toggle != real_toggle) {
14280Sstevel@tonic-gate 			if (polled_toggle == DATA0) {
14290Sstevel@tonic-gate 				Set_ED(pp->pp_ept->hced_headp,
14300Sstevel@tonic-gate 				    Get_ED(pp->pp_ept->hced_headp) &
14310Sstevel@tonic-gate 				    ~HC_EPT_Carry);
14320Sstevel@tonic-gate 			} else {
14330Sstevel@tonic-gate 				Set_ED(pp->pp_ept->hced_headp,
14340Sstevel@tonic-gate 				    Get_ED(pp->pp_ept->hced_headp) |
14350Sstevel@tonic-gate 				    HC_EPT_Carry);
14360Sstevel@tonic-gate 			}
14370Sstevel@tonic-gate 		}
14380Sstevel@tonic-gate 	} else {
14390Sstevel@tonic-gate 		usba_hcdi_set_data_toggle(ohci_polledp->ohci_polled_usb_dev,
14400Sstevel@tonic-gate 		    ep_addr, polled_toggle);
14410Sstevel@tonic-gate 	}
14420Sstevel@tonic-gate 	/*
14430Sstevel@tonic-gate 	 * Only the last leave keyboard entry restore the Interrupt table,
14440Sstevel@tonic-gate 	 * start processing and enable the interrupt.
14450Sstevel@tonic-gate 	 */
14460Sstevel@tonic-gate 	if (ohcip->ohci_polled_enter_count == 0) {
14470Sstevel@tonic-gate 		/* Replace the lattice */
14480Sstevel@tonic-gate 		for (i = 0; i < NUM_INTR_ED_LISTS; i++) {
14490Sstevel@tonic-gate 			Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
14500Sstevel@tonic-gate 			    (uintptr_t)ohcip->ohci_polled_save_IntTble[i]);
14510Sstevel@tonic-gate 		}
14520Sstevel@tonic-gate 
14530Sstevel@tonic-gate 		/*
14540Sstevel@tonic-gate 		 * Clear the contents of current ohci periodic ED register that
14550Sstevel@tonic-gate 		 * is physical address of current Isochronous or Interrupt ED.
14560Sstevel@tonic-gate 		 */
14570Sstevel@tonic-gate 		Set_OpReg(hcr_periodic_curr, (uint32_t)0x0);
14580Sstevel@tonic-gate 
14590Sstevel@tonic-gate 		ohci_polled_start_processing(ohci_polledp);
14600Sstevel@tonic-gate 
14610Sstevel@tonic-gate 		/*
14620Sstevel@tonic-gate 		 * Check and enable required ohci  interrupts before switching
14630Sstevel@tonic-gate 		 * back to normal mode from the POLLED mode.
14640Sstevel@tonic-gate 		 */
14650Sstevel@tonic-gate 		mask = (uint32_t)ohci_polled_regsp->hcr_intr_enable &
14660Sstevel@tonic-gate 		    (HCR_INTR_SOF | HCR_INTR_WDH);
14670Sstevel@tonic-gate 
14680Sstevel@tonic-gate 		if (ohci_intr_sts->ohci_intr_flag & OHCI_INTR_HANDLING) {
14690Sstevel@tonic-gate 			Set_OpReg(hcr_intr_enable, mask);
14700Sstevel@tonic-gate 		} else {
14710Sstevel@tonic-gate 			Set_OpReg(hcr_intr_enable, mask | HCR_INTR_MIE);
14720Sstevel@tonic-gate 		}
14730Sstevel@tonic-gate 	}
14740Sstevel@tonic-gate #ifndef lint
14750Sstevel@tonic-gate 	_NOTE(COMPETING_THREADS_NOW);
14760Sstevel@tonic-gate #endif
14770Sstevel@tonic-gate }
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate /*
14800Sstevel@tonic-gate  * ohci_polled_start_processing:
14810Sstevel@tonic-gate  */
14820Sstevel@tonic-gate static void
ohci_polled_start_processing(ohci_polled_t * ohci_polledp)14830Sstevel@tonic-gate ohci_polled_start_processing(ohci_polled_t	*ohci_polledp)
14840Sstevel@tonic-gate {
14850Sstevel@tonic-gate 	ohci_state_t		*ohcip;
14860Sstevel@tonic-gate 	uint32_t		control;
14870Sstevel@tonic-gate 	uint32_t		mask;
14880Sstevel@tonic-gate 	ohci_regs_t		*ohci_polled_regsp;
14890Sstevel@tonic-gate 
14900Sstevel@tonic-gate 	ohcip = ohci_polledp->ohci_polled_ohcip;
14910Sstevel@tonic-gate 	ohci_polled_regsp = &ohcip->ohci_polled_save_regs;
14920Sstevel@tonic-gate 
14930Sstevel@tonic-gate 	mask = ((uint32_t)ohci_polled_regsp->hcr_control) & (HCR_CONTROL_CLE |
14940Sstevel@tonic-gate 	    HCR_CONTROL_PLE | HCR_CONTROL_BLE | HCR_CONTROL_IE);
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate 	control = Get_OpReg(hcr_control) & ~(HCR_CONTROL_CLE |
14970Sstevel@tonic-gate 	    HCR_CONTROL_PLE | HCR_CONTROL_BLE | HCR_CONTROL_IE);
14980Sstevel@tonic-gate 
14990Sstevel@tonic-gate 	Set_OpReg(hcr_control, (control | mask));
15000Sstevel@tonic-gate }
15010Sstevel@tonic-gate 
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate /*
15040Sstevel@tonic-gate  * Polled read routines
15050Sstevel@tonic-gate  */
15060Sstevel@tonic-gate /*
15070Sstevel@tonic-gate  * ohci_polled_check_done_list:
15080Sstevel@tonic-gate  *
15090Sstevel@tonic-gate  * Check to see it there are any TD's on the done head.  If there are
15100Sstevel@tonic-gate  * then reverse the done list and put the TD's on the appropriated list.
15110Sstevel@tonic-gate  */
15120Sstevel@tonic-gate static int
ohci_polled_check_done_list(ohci_polled_t * ohci_polledp)15130Sstevel@tonic-gate ohci_polled_check_done_list(ohci_polled_t	*ohci_polledp)
15140Sstevel@tonic-gate {
15150Sstevel@tonic-gate 	ohci_state_t	*ohcip = ohci_polledp->ohci_polled_ohcip;
15160Sstevel@tonic-gate 	ohci_td_t	*done_head, *done_list;
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate 	/* Sync HCCA area */
15190Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_no_sync_flag == B_FALSE) {
15200Sstevel@tonic-gate 		Sync_HCCA(ohcip);
15210Sstevel@tonic-gate 	}
15220Sstevel@tonic-gate 
15230Sstevel@tonic-gate 	/* Read and Save the HCCA DoneHead value */
15240Sstevel@tonic-gate 	done_head = (ohci_td_t *)(uintptr_t)
15250Sstevel@tonic-gate 	    (Get_HCCA(ohcip->ohci_hccap->HccaDoneHead) & HCCA_DONE_HEAD_MASK);
15260Sstevel@tonic-gate 
15270Sstevel@tonic-gate 	/*
15280Sstevel@tonic-gate 	 * Look at the Done Head and if it is NULL and ohci done list is NULL,
15290Sstevel@tonic-gate 	 * just return; else if ohci done list is not NULL, should check it.
15300Sstevel@tonic-gate 	 */
15310Sstevel@tonic-gate 	if (done_head == NULL) {
15320Sstevel@tonic-gate 		if (ohcip->ohci_polled_done_list) {
15330Sstevel@tonic-gate 			done_head = ohcip->ohci_polled_done_list;
1534*9095SZhigang.Lu@Sun.COM 			ohcip->ohci_polled_done_list = NULL;
15350Sstevel@tonic-gate 		} else {
15360Sstevel@tonic-gate 
15370Sstevel@tonic-gate 			return (USB_FAILURE);
15380Sstevel@tonic-gate 		}
15390Sstevel@tonic-gate 	} else {
15400Sstevel@tonic-gate 		/* Reset the done head to NULL */
15410Sstevel@tonic-gate 		Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, NULL);
15420Sstevel@tonic-gate 	}
15430Sstevel@tonic-gate 
15440Sstevel@tonic-gate 	/* Sync ED and TD pool */
15450Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_no_sync_flag == B_FALSE) {
15460Sstevel@tonic-gate 		Sync_ED_TD_Pool(ohcip);
15470Sstevel@tonic-gate 	}
15480Sstevel@tonic-gate 
15490Sstevel@tonic-gate 	/* Pickup own tds in the done head */
15500Sstevel@tonic-gate 	done_list = ohci_polled_pickup_done_list(ohci_polledp, done_head);
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate 	/*
15530Sstevel@tonic-gate 	 * Look at the own done list which is pickup'ed
15540Sstevel@tonic-gate 	 * and if it is NULL, just return.
15550Sstevel@tonic-gate 	 */
15560Sstevel@tonic-gate 	if (done_list == NULL) {
15570Sstevel@tonic-gate 
15580Sstevel@tonic-gate 		return (USB_FAILURE);
15590Sstevel@tonic-gate 	}
15600Sstevel@tonic-gate 	/* Create the input done list */
15610Sstevel@tonic-gate 	ohci_polled_create_input_list(ohci_polledp, done_list);
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate 	return (USB_SUCCESS);
15640Sstevel@tonic-gate }
15650Sstevel@tonic-gate 
1566*9095SZhigang.Lu@Sun.COM 
15670Sstevel@tonic-gate /*
15680Sstevel@tonic-gate  * ohci_polled_pickup_done_list:
15690Sstevel@tonic-gate  *
15700Sstevel@tonic-gate  * Pickup the TDs of own in the Done Head List
15710Sstevel@tonic-gate  */
15720Sstevel@tonic-gate static ohci_td_t *
ohci_polled_pickup_done_list(ohci_polled_t * ohci_polledp,ohci_td_t * done_head)15730Sstevel@tonic-gate ohci_polled_pickup_done_list(
15740Sstevel@tonic-gate 	ohci_polled_t	*ohci_polledp,
15750Sstevel@tonic-gate 	ohci_td_t	*done_head)
15760Sstevel@tonic-gate {
15770Sstevel@tonic-gate 	ohci_state_t	*ohcip = ohci_polledp->ohci_polled_ohcip;
15780Sstevel@tonic-gate 	ohci_td_t	*create_head = NULL, *current_td, *td;
15790Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
15800Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;
15810Sstevel@tonic-gate 
15820Sstevel@tonic-gate 	/*
15830Sstevel@tonic-gate 	 * Current_td pointers point to the done head.
15840Sstevel@tonic-gate 	 */
15850Sstevel@tonic-gate 	current_td = (ohci_td_t *)
15860Sstevel@tonic-gate 	    ohci_td_iommu_to_cpu(ohcip, (uintptr_t)done_head);
15870Sstevel@tonic-gate 	while (current_td) {
15880Sstevel@tonic-gate 		td = (ohci_td_t *)ohci_td_iommu_to_cpu(ohcip,
15890Sstevel@tonic-gate 		    Get_TD(current_td->hctd_next_td));
15900Sstevel@tonic-gate 
15910Sstevel@tonic-gate 		Set_TD(current_td->hctd_next_td, NULL);
15920Sstevel@tonic-gate 
15930Sstevel@tonic-gate 		/* Obtain the transfer wrapper from the TD */
15940Sstevel@tonic-gate 		tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
15950Sstevel@tonic-gate 		    (uint32_t)Get_TD(current_td->hctd_trans_wrapper));
15960Sstevel@tonic-gate 
15970Sstevel@tonic-gate 		/* Get the pipe handle for this transfer wrapper. */
15980Sstevel@tonic-gate 		pp = tw->tw_pipe_private;
15990Sstevel@tonic-gate 
16000Sstevel@tonic-gate 		/*
1601*9095SZhigang.Lu@Sun.COM 		 * Figure  out  which  done list to put this TD on and put it
1602*9095SZhigang.Lu@Sun.COM 		 * there.   If  the  pipe handle  of the TD matches the pipe
16030Sstevel@tonic-gate 		 * handle  we  are  using for the input device, then this must
16040Sstevel@tonic-gate 		 * be an input TD, reverse the order and link to the list for
16050Sstevel@tonic-gate 		 * this input device. Else put the TD to the reserve done list
16060Sstevel@tonic-gate 		 * for other input devices.
16070Sstevel@tonic-gate 		 */
16080Sstevel@tonic-gate 
16090Sstevel@tonic-gate 		if (pp->pp_pipe_handle ==
16100Sstevel@tonic-gate 		    ohci_polledp->ohci_polled_input_pipe_handle) {
16110Sstevel@tonic-gate 			if (create_head == NULL) {
16120Sstevel@tonic-gate 				create_head = current_td;
16130Sstevel@tonic-gate 			} else {
16140Sstevel@tonic-gate 				Set_TD(current_td->hctd_next_td,
16150Sstevel@tonic-gate 				    ohci_td_cpu_to_iommu(ohcip, create_head));
16160Sstevel@tonic-gate 				create_head = current_td;
16170Sstevel@tonic-gate 			}
16180Sstevel@tonic-gate 		} else {
1619*9095SZhigang.Lu@Sun.COM 			if (ohcip->ohci_polled_done_list == NULL) {
1620*9095SZhigang.Lu@Sun.COM 				ohcip->ohci_polled_done_list = (ohci_td_t *)
1621*9095SZhigang.Lu@Sun.COM 				    (uintptr_t)ohci_td_cpu_to_iommu(ohcip,
1622*9095SZhigang.Lu@Sun.COM 				    current_td);
16230Sstevel@tonic-gate 			} else {
1624*9095SZhigang.Lu@Sun.COM 				Set_TD(current_td->hctd_next_td,
1625*9095SZhigang.Lu@Sun.COM 				    ohcip->ohci_polled_done_list);
1626*9095SZhigang.Lu@Sun.COM 				ohcip->ohci_polled_done_list = (ohci_td_t *)
1627*9095SZhigang.Lu@Sun.COM 				    (uintptr_t)ohci_td_cpu_to_iommu(ohcip,
1628*9095SZhigang.Lu@Sun.COM 				    current_td);
16290Sstevel@tonic-gate 			}
16300Sstevel@tonic-gate 		}
16310Sstevel@tonic-gate 		current_td = td;
16320Sstevel@tonic-gate 	}
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 	return (create_head);
16350Sstevel@tonic-gate }
16360Sstevel@tonic-gate 
1637*9095SZhigang.Lu@Sun.COM 
16380Sstevel@tonic-gate /*
16390Sstevel@tonic-gate  * ohci_polled_create_input_list:
16400Sstevel@tonic-gate  *
16410Sstevel@tonic-gate  * Create the input done list from the actual done head list.
16420Sstevel@tonic-gate  */
16430Sstevel@tonic-gate static void
ohci_polled_create_input_list(ohci_polled_t * ohci_polledp,ohci_td_t * head_done_list)16440Sstevel@tonic-gate ohci_polled_create_input_list(
16450Sstevel@tonic-gate 	ohci_polled_t		*ohci_polledp,
16460Sstevel@tonic-gate 	ohci_td_t		*head_done_list)
16470Sstevel@tonic-gate {
16480Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_polledp->ohci_polled_ohcip;
16490Sstevel@tonic-gate 	ohci_td_t		*cpu_save, *td;
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate 	ASSERT(head_done_list != NULL);
16520Sstevel@tonic-gate 
16530Sstevel@tonic-gate 	/* Get the done head list */
16540Sstevel@tonic-gate 	td = (ohci_td_t *)head_done_list;
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate 	/*
16570Sstevel@tonic-gate 	 * Traverse the done list and create the input done list.
16580Sstevel@tonic-gate 	 */
16590Sstevel@tonic-gate 	while (td) {
16600Sstevel@tonic-gate 
16610Sstevel@tonic-gate 		/*
16620Sstevel@tonic-gate 		 * Convert the iommu pointer to a cpu pointer. No point
16630Sstevel@tonic-gate 		 * in doing this over and over, might as well do it once.
16640Sstevel@tonic-gate 		 */
16650Sstevel@tonic-gate 		cpu_save = ohci_td_iommu_to_cpu(ohcip,
16660Sstevel@tonic-gate 		    Get_TD(td->hctd_next_td));
16670Sstevel@tonic-gate 
16680Sstevel@tonic-gate 		/*
16690Sstevel@tonic-gate 		 * Terminate this TD by setting its next pointer to NULL.
16700Sstevel@tonic-gate 		 */
16710Sstevel@tonic-gate 		Set_TD(td->hctd_next_td, NULL);
16720Sstevel@tonic-gate 
16730Sstevel@tonic-gate 		/* This is an input TD, so put it on the input done list */
16740Sstevel@tonic-gate 		if (ohci_polledp->ohci_polled_input_done_head == NULL) {
16750Sstevel@tonic-gate 
16760Sstevel@tonic-gate 			/*
16770Sstevel@tonic-gate 			 * There is nothing on the input done list,
16780Sstevel@tonic-gate 			 * so put this TD on the head.
16790Sstevel@tonic-gate 			 */
16800Sstevel@tonic-gate 			ohci_polledp->ohci_polled_input_done_head = td;
16810Sstevel@tonic-gate 		} else {
16820Sstevel@tonic-gate 			Set_TD(ohci_polledp->
16830Sstevel@tonic-gate 			    ohci_polled_input_done_tail->hctd_next_td,
16840Sstevel@tonic-gate 			    ohci_td_cpu_to_iommu(ohcip, td));
16850Sstevel@tonic-gate 		}
16860Sstevel@tonic-gate 
16870Sstevel@tonic-gate 		/* The tail points to the new TD */
16880Sstevel@tonic-gate 		ohci_polledp->ohci_polled_input_done_tail = td;
16890Sstevel@tonic-gate 		td = cpu_save;
16900Sstevel@tonic-gate 	}
16910Sstevel@tonic-gate }
16920Sstevel@tonic-gate 
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate /*
16950Sstevel@tonic-gate  * ohci_polled_process_input_list:
16960Sstevel@tonic-gate  *
16970Sstevel@tonic-gate  * This routine takes the TD's off of the input done head and processes
16980Sstevel@tonic-gate  * them.  It returns the number of characters that have been copied for
16990Sstevel@tonic-gate  * input.
17000Sstevel@tonic-gate  */
17010Sstevel@tonic-gate static int
ohci_polled_process_input_list(ohci_polled_t * ohci_polledp)17020Sstevel@tonic-gate ohci_polled_process_input_list(ohci_polled_t	*ohci_polledp)
17030Sstevel@tonic-gate {
17040Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_polledp->ohci_polled_ohcip;
17050Sstevel@tonic-gate 	ohci_td_t		*td, *next_td;
17060Sstevel@tonic-gate 	uint_t			ctrl;
17070Sstevel@tonic-gate 	uint_t			num_characters;
17080Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
17090Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;
1710*9095SZhigang.Lu@Sun.COM 	int 			pipe_dir;
17110Sstevel@tonic-gate 
17120Sstevel@tonic-gate 	/*
17130Sstevel@tonic-gate 	 * Get the first TD on the input done head.
17140Sstevel@tonic-gate 	 */
17150Sstevel@tonic-gate 	td = ohci_polledp->ohci_polled_input_done_head;
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate 	ohci_polledp->ohci_polled_input_done_head = NULL;
17180Sstevel@tonic-gate 
17190Sstevel@tonic-gate 	num_characters = 0;
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate 	/*
17220Sstevel@tonic-gate 	 * Traverse the list of transfer descriptors. We can't destroy
17230Sstevel@tonic-gate 	 * hctd_next_td pointers of these  TDs because we are using it
17240Sstevel@tonic-gate 	 * to traverse the done list.  Therefore, we can not put these
17250Sstevel@tonic-gate 	 * TDs back on the ED until we are done processing all of them.
17260Sstevel@tonic-gate 	 */
17270Sstevel@tonic-gate 	while (td) {
17280Sstevel@tonic-gate 
17290Sstevel@tonic-gate 		/* Get the next TD from the input done list */
17300Sstevel@tonic-gate 		next_td = (ohci_td_t *)
17310Sstevel@tonic-gate 		    ohci_td_iommu_to_cpu(ohcip, Get_TD(td->hctd_next_td));
17320Sstevel@tonic-gate 
17330Sstevel@tonic-gate 		/* Look at the status */
17340Sstevel@tonic-gate 		ctrl = (uint_t)Get_TD(td->hctd_ctrl) & (uint32_t)HC_TD_CC;
17350Sstevel@tonic-gate 
17360Sstevel@tonic-gate 		/*
17370Sstevel@tonic-gate 		 * Check to see if there is an error. If there is error
17380Sstevel@tonic-gate 		 * clear the halt condition in the Endpoint  Descriptor
17390Sstevel@tonic-gate 		 * (ED) associated with this Transfer  Descriptor (TD).
17400Sstevel@tonic-gate 		 */
17410Sstevel@tonic-gate 		if (ctrl != HC_TD_CC_NO_E) {
17420Sstevel@tonic-gate 			/* Obtain the transfer wrapper from the TD */
17430Sstevel@tonic-gate 			tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
17440Sstevel@tonic-gate 			    (uint32_t)Get_TD(td->hctd_trans_wrapper));
17450Sstevel@tonic-gate 
17460Sstevel@tonic-gate 			/* Get the pipe handle for this transfer wrapper */
17470Sstevel@tonic-gate 			pp = tw->tw_pipe_private;
17480Sstevel@tonic-gate 
17490Sstevel@tonic-gate 			/* Clear the halt bit */
17500Sstevel@tonic-gate 			Set_ED(pp->pp_ept->hced_headp,
17510Sstevel@tonic-gate 			    (Get_ED(pp->pp_ept->hced_headp) & ~HC_EPT_Halt));
17520Sstevel@tonic-gate 		}
17530Sstevel@tonic-gate 
1754*9095SZhigang.Lu@Sun.COM 		/* Obtain the transfer wrapper from the TD */
1755*9095SZhigang.Lu@Sun.COM 		tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
1756*9095SZhigang.Lu@Sun.COM 		    (uint32_t)Get_TD(td->hctd_trans_wrapper));
1757*9095SZhigang.Lu@Sun.COM 
1758*9095SZhigang.Lu@Sun.COM 		/* Get the pipe direction for this transfer wrapper */
1759*9095SZhigang.Lu@Sun.COM 		pipe_dir = tw->tw_pipe_private->pp_pipe_handle->
1760*9095SZhigang.Lu@Sun.COM 		    p_ep.bEndpointAddress & USB_EP_DIR_MASK;
1761*9095SZhigang.Lu@Sun.COM 
1762*9095SZhigang.Lu@Sun.COM 		switch (pipe_dir) {
1763*9095SZhigang.Lu@Sun.COM 		case USB_EP_DIR_IN:
1764*9095SZhigang.Lu@Sun.COM 			num_characters +=
1765*9095SZhigang.Lu@Sun.COM 			    ohci_polled_handle_normal_td(ohci_polledp,
1766*9095SZhigang.Lu@Sun.COM 			    td);
1767*9095SZhigang.Lu@Sun.COM 
1768*9095SZhigang.Lu@Sun.COM 			/*
1769*9095SZhigang.Lu@Sun.COM 			 * Insert this TD back
1770*9095SZhigang.Lu@Sun.COM 			 * onto the ED's TD list
1771*9095SZhigang.Lu@Sun.COM 			 */
1772*9095SZhigang.Lu@Sun.COM 			ohci_polled_insert_td(ohcip, td);
1773*9095SZhigang.Lu@Sun.COM 			break;
1774*9095SZhigang.Lu@Sun.COM 		case USB_EP_DIR_OUT:
1775*9095SZhigang.Lu@Sun.COM 			ASSERT((ohci_td_t *)tw->tw_hctd_head == td);
1776*9095SZhigang.Lu@Sun.COM 
1777*9095SZhigang.Lu@Sun.COM 			tw->tw_hctd_head = (ohci_td_t *)
1778*9095SZhigang.Lu@Sun.COM 			    ohci_td_iommu_to_cpu(ohcip,
1779*9095SZhigang.Lu@Sun.COM 			    Get_TD(td->hctd_tw_next_td));
1780*9095SZhigang.Lu@Sun.COM 			Set_TD(td->hctd_state, HC_TD_DUMMY);
1781*9095SZhigang.Lu@Sun.COM 
1782*9095SZhigang.Lu@Sun.COM 			if (tw->tw_hctd_head == NULL) {
1783*9095SZhigang.Lu@Sun.COM 				tw->tw_hctd_tail = NULL;
1784*9095SZhigang.Lu@Sun.COM 			}
1785*9095SZhigang.Lu@Sun.COM 
1786*9095SZhigang.Lu@Sun.COM 			if (tw->tw_hctd_free_list != NULL) {
1787*9095SZhigang.Lu@Sun.COM 				uint32_t	td_addr;
1788*9095SZhigang.Lu@Sun.COM 				td_addr = ohci_td_cpu_to_iommu(ohcip,
1789*9095SZhigang.Lu@Sun.COM 				    tw->tw_hctd_free_list);
1790*9095SZhigang.Lu@Sun.COM 				Set_TD(td->hctd_tw_next_td, td_addr);
1791*9095SZhigang.Lu@Sun.COM 				tw->tw_hctd_free_list = td;
1792*9095SZhigang.Lu@Sun.COM 			} else {
1793*9095SZhigang.Lu@Sun.COM 				tw->tw_hctd_free_list = td;
1794*9095SZhigang.Lu@Sun.COM 				Set_TD(td->hctd_tw_next_td, NULL);
1795*9095SZhigang.Lu@Sun.COM 			}
1796*9095SZhigang.Lu@Sun.COM 			break;
1797*9095SZhigang.Lu@Sun.COM 		}
17980Sstevel@tonic-gate 
17990Sstevel@tonic-gate 		td = next_td;
18000Sstevel@tonic-gate 	}
18010Sstevel@tonic-gate 
18020Sstevel@tonic-gate 	return (num_characters);
18030Sstevel@tonic-gate }
18040Sstevel@tonic-gate 
18050Sstevel@tonic-gate 
18060Sstevel@tonic-gate /*
18070Sstevel@tonic-gate  * ohci_polled_handle_normal_td:
18080Sstevel@tonic-gate  */
18090Sstevel@tonic-gate static int
ohci_polled_handle_normal_td(ohci_polled_t * ohci_polledp,ohci_td_t * td)18100Sstevel@tonic-gate ohci_polled_handle_normal_td(
18110Sstevel@tonic-gate 	ohci_polled_t		*ohci_polledp,
18120Sstevel@tonic-gate 	ohci_td_t		*td)
18130Sstevel@tonic-gate {
18140Sstevel@tonic-gate 	ohci_state_t		*ohcip = ohci_polledp->ohci_polled_ohcip;
18150Sstevel@tonic-gate 	uchar_t			*buf;
18160Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
18172125Ssl147100 	size_t			length, residue;
18180Sstevel@tonic-gate 
18190Sstevel@tonic-gate 	/* Obtain the transfer wrapper from the TD */
18200Sstevel@tonic-gate 	tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID((uint32_t)
18210Sstevel@tonic-gate 	    Get_TD(td->hctd_trans_wrapper));
18220Sstevel@tonic-gate 
18230Sstevel@tonic-gate 	ASSERT(tw != NULL);
18240Sstevel@tonic-gate 
18250Sstevel@tonic-gate 	buf = (uchar_t *)tw->tw_buf;
18260Sstevel@tonic-gate 
18270Sstevel@tonic-gate 	length = tw->tw_length;
18280Sstevel@tonic-gate 	/*
18290Sstevel@tonic-gate 	 * If "CurrentBufferPointer" of Transfer Descriptor (TD) is
18300Sstevel@tonic-gate 	 * not equal to zero, then we  received less data  from the
18310Sstevel@tonic-gate 	 * device than requested by us. In that  case, get the actual
18320Sstevel@tonic-gate 	 * received data size.
18330Sstevel@tonic-gate 	 */
18340Sstevel@tonic-gate 	if (Get_TD(td->hctd_cbp)) {
18350Sstevel@tonic-gate 
18362125Ssl147100 		residue = ohci_get_td_residue(ohcip, td);
18372125Ssl147100 		length = Get_TD(td->hctd_xfer_offs) +
18382125Ssl147100 		    Get_TD(td->hctd_xfer_len) - residue;
18390Sstevel@tonic-gate 	}
18400Sstevel@tonic-gate 
18410Sstevel@tonic-gate 	/* Sync IO buffer */
18420Sstevel@tonic-gate 	if (ohci_polledp->ohci_polled_no_sync_flag == B_FALSE) {
18430Sstevel@tonic-gate 		Sync_IO_Buffer(tw->tw_dmahandle, length);
18440Sstevel@tonic-gate 	}
18450Sstevel@tonic-gate 
1846*9095SZhigang.Lu@Sun.COM 		/* Copy the data into the message */
18470Sstevel@tonic-gate 	ddi_rep_get8(tw->tw_accesshandle,
18480Sstevel@tonic-gate 	    (uint8_t *)ohci_polledp->ohci_polled_buf,
18490Sstevel@tonic-gate 	    (uint8_t *)buf, length, DDI_DEV_AUTOINCR);
18500Sstevel@tonic-gate 
18517492SZhigang.Lu@Sun.COM 	return ((int)length);
18520Sstevel@tonic-gate }
18530Sstevel@tonic-gate 
18540Sstevel@tonic-gate 
18550Sstevel@tonic-gate /*
18560Sstevel@tonic-gate  * ohci_polled_insert_td:
18570Sstevel@tonic-gate  *
18580Sstevel@tonic-gate  * Insert a Transfer Descriptor (TD) on an Endpoint Descriptor (ED).
18590Sstevel@tonic-gate  */
18600Sstevel@tonic-gate static void
ohci_polled_insert_td(ohci_state_t * ohcip,ohci_td_t * td)18610Sstevel@tonic-gate ohci_polled_insert_td(
18620Sstevel@tonic-gate 	ohci_state_t		*ohcip,
18630Sstevel@tonic-gate 	ohci_td_t		*td)
18640Sstevel@tonic-gate {
18650Sstevel@tonic-gate 	ohci_pipe_private_t	*pp;
18660Sstevel@tonic-gate 	ohci_ed_t		*ept;
18670Sstevel@tonic-gate 	uint_t			td_control;
18680Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw;
18690Sstevel@tonic-gate 	ohci_td_t		*cpu_current_dummy;
18700Sstevel@tonic-gate 	usb_intr_req_t		*intr_req;
1871*9095SZhigang.Lu@Sun.COM 	usba_pipe_handle_data_t	*ph;
1872*9095SZhigang.Lu@Sun.COM 	int			pipe_attr;
18730Sstevel@tonic-gate 
18740Sstevel@tonic-gate 	/* Obtain the transfer wrapper from the TD */
18750Sstevel@tonic-gate 	tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
18760Sstevel@tonic-gate 	    (uint32_t)Get_TD(td->hctd_trans_wrapper));
18770Sstevel@tonic-gate 
18782125Ssl147100 	/* Ensure the DMA cookie is valid for reuse */
18792125Ssl147100 	ASSERT((tw->tw_cookie_idx == 0) && (tw->tw_dma_offs == 0));
18802125Ssl147100 
18810Sstevel@tonic-gate 	/*
18820Sstevel@tonic-gate 	 * Take this TD off the transfer wrapper's list since
18830Sstevel@tonic-gate 	 * the pipe is FIFO, this must be the first TD on the
18840Sstevel@tonic-gate 	 * list.
18850Sstevel@tonic-gate 	 */
18860Sstevel@tonic-gate 	ASSERT((ohci_td_t *)tw->tw_hctd_head == td);
18870Sstevel@tonic-gate 
18880Sstevel@tonic-gate 	tw->tw_hctd_head = (ohci_td_t *)
18890Sstevel@tonic-gate 	    ohci_td_iommu_to_cpu(ohcip, Get_TD(td->hctd_tw_next_td));
18900Sstevel@tonic-gate 
18910Sstevel@tonic-gate 	/*
18920Sstevel@tonic-gate 	 * If the head becomes NULL, then there are no more
18930Sstevel@tonic-gate 	 * active TD's for this transfer wrapper. Also	set
18940Sstevel@tonic-gate 	 * the tail to NULL.
18950Sstevel@tonic-gate 	 */
18960Sstevel@tonic-gate 	if (tw->tw_hctd_head == NULL) {
18970Sstevel@tonic-gate 		tw->tw_hctd_tail = NULL;
18980Sstevel@tonic-gate 	}
18990Sstevel@tonic-gate 
19000Sstevel@tonic-gate 	/* Convert current valid TD as new dummy TD */
19010Sstevel@tonic-gate 	bzero((char *)td, sizeof (ohci_td_t));
19020Sstevel@tonic-gate 	Set_TD(td->hctd_state, HC_TD_DUMMY);
19030Sstevel@tonic-gate 
19040Sstevel@tonic-gate 	pp = tw->tw_pipe_private;
1905*9095SZhigang.Lu@Sun.COM 	ph = pp->pp_pipe_handle;
19060Sstevel@tonic-gate 
1907*9095SZhigang.Lu@Sun.COM 	/* Obtain the endpoint and the request */
19080Sstevel@tonic-gate 	ept = pp->pp_ept;
19090Sstevel@tonic-gate 
1910*9095SZhigang.Lu@Sun.COM 	/* Get the pipe attribute */
1911*9095SZhigang.Lu@Sun.COM 	pipe_attr = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
1912*9095SZhigang.Lu@Sun.COM 
1913*9095SZhigang.Lu@Sun.COM 	switch (pipe_attr) {
1914*9095SZhigang.Lu@Sun.COM 	case USB_EP_ATTR_INTR:
1915*9095SZhigang.Lu@Sun.COM 		intr_req = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
19160Sstevel@tonic-gate 
1917*9095SZhigang.Lu@Sun.COM 		if (intr_req->intr_attributes & USB_ATTRS_SHORT_XFER_OK) {
1918*9095SZhigang.Lu@Sun.COM 			td_control = HC_TD_IN|HC_TD_1I|HC_TD_R;
1919*9095SZhigang.Lu@Sun.COM 		} else {
1920*9095SZhigang.Lu@Sun.COM 			td_control = HC_TD_IN|HC_TD_1I;
1921*9095SZhigang.Lu@Sun.COM 		}
1922*9095SZhigang.Lu@Sun.COM 		break;
1923*9095SZhigang.Lu@Sun.COM 	case USB_EP_ATTR_BULK:
1924*9095SZhigang.Lu@Sun.COM 		td_control = tw->tw_direction|HC_TD_DT_0|HC_TD_1I|HC_TD_R;
1925*9095SZhigang.Lu@Sun.COM 		break;
19260Sstevel@tonic-gate 	}
19270Sstevel@tonic-gate 
19280Sstevel@tonic-gate 	/* Get the current dummy */
19290Sstevel@tonic-gate 	cpu_current_dummy = (ohci_td_t *)
19300Sstevel@tonic-gate 	    (ohci_td_iommu_to_cpu(ohcip, Get_ED(ept->hced_tailp)));
19310Sstevel@tonic-gate 
19320Sstevel@tonic-gate 	/*
19330Sstevel@tonic-gate 	 * Fill in the current dummy td and
19340Sstevel@tonic-gate 	 * add the new dummy to the end.
19350Sstevel@tonic-gate 	 */
19360Sstevel@tonic-gate 	ohci_polled_fill_in_td(ohcip, cpu_current_dummy, td,
19372125Ssl147100 	    td_control, 0, tw->tw_length, tw);
19380Sstevel@tonic-gate 
19390Sstevel@tonic-gate 	/* Insert this td onto the tw */
19400Sstevel@tonic-gate 	ohci_polled_insert_td_on_tw(ohcip, tw, cpu_current_dummy);
19410Sstevel@tonic-gate 
19420Sstevel@tonic-gate 	/*
19430Sstevel@tonic-gate 	 * Add the new dummy to the ED's list.	When this occurs,
19440Sstevel@tonic-gate 	 * the Host Controller will see the newly filled in dummy
19450Sstevel@tonic-gate 	 * TD.
19460Sstevel@tonic-gate 	 */
19470Sstevel@tonic-gate 	Set_ED(ept->hced_tailp, (ohci_td_cpu_to_iommu(ohcip, td)));
19480Sstevel@tonic-gate }
19490Sstevel@tonic-gate 
19500Sstevel@tonic-gate 
19510Sstevel@tonic-gate /*
19520Sstevel@tonic-gate  * ohci_polled_fill_in_td:
19530Sstevel@tonic-gate  *
19540Sstevel@tonic-gate  * Fill in the fields of a Transfer Descriptor (TD).
19550Sstevel@tonic-gate  */
19560Sstevel@tonic-gate static void
ohci_polled_fill_in_td(ohci_state_t * ohcip,ohci_td_t * td,ohci_td_t * new_dummy,uint_t hctd_ctrl,uint32_t hctd_dma_offs,size_t hctd_length,ohci_trans_wrapper_t * tw)19570Sstevel@tonic-gate ohci_polled_fill_in_td(
19580Sstevel@tonic-gate 	ohci_state_t		*ohcip,
19590Sstevel@tonic-gate 	ohci_td_t		*td,
19600Sstevel@tonic-gate 	ohci_td_t		*new_dummy,
19610Sstevel@tonic-gate 	uint_t			hctd_ctrl,
19622125Ssl147100 	uint32_t		hctd_dma_offs,
19630Sstevel@tonic-gate 	size_t			hctd_length,
19640Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw)
19650Sstevel@tonic-gate {
19660Sstevel@tonic-gate 	/* Assert that the td to be filled in is a dummy */
19670Sstevel@tonic-gate 	ASSERT(Get_TD(td->hctd_state) == HC_TD_DUMMY);
19680Sstevel@tonic-gate 
19690Sstevel@tonic-gate 	/* Clear the TD */
19700Sstevel@tonic-gate 	bzero((char *)td, sizeof (ohci_td_t));
19710Sstevel@tonic-gate 
19720Sstevel@tonic-gate 	/* Update the dummy with control information */
19730Sstevel@tonic-gate 	Set_TD(td->hctd_ctrl, (hctd_ctrl | HC_TD_CC_NA));
19740Sstevel@tonic-gate 
19752125Ssl147100 	/* Update the beginning and end of the buffer */
19762125Ssl147100 	ohci_init_td(ohcip, tw, hctd_dma_offs, hctd_length, td);
19770Sstevel@tonic-gate 
19780Sstevel@tonic-gate 	/* The current dummy now points to the new dummy */
19790Sstevel@tonic-gate 	Set_TD(td->hctd_next_td, (ohci_td_cpu_to_iommu(ohcip, new_dummy)));
19800Sstevel@tonic-gate 
19810Sstevel@tonic-gate 	/* Fill in the wrapper portion of the TD */
19820Sstevel@tonic-gate 	Set_TD(td->hctd_trans_wrapper, (uint32_t)tw->tw_id);
19830Sstevel@tonic-gate 	Set_TD(td->hctd_tw_next_td, NULL);
19840Sstevel@tonic-gate }
19850Sstevel@tonic-gate 
19860Sstevel@tonic-gate 
19870Sstevel@tonic-gate /*
19880Sstevel@tonic-gate  * ohci_polled_insert_td_on_tw:
19890Sstevel@tonic-gate  *
19900Sstevel@tonic-gate  * The transfer wrapper keeps a list of all Transfer Descriptors (TD) that
19910Sstevel@tonic-gate  * are allocated for this transfer. Insert a TD  onto this list. The  list
19920Sstevel@tonic-gate  * of TD's does not include the dummy TD that is at the end of the list of
19930Sstevel@tonic-gate  * TD's for the endpoint.
19940Sstevel@tonic-gate  */
19950Sstevel@tonic-gate static void
ohci_polled_insert_td_on_tw(ohci_state_t * ohcip,ohci_trans_wrapper_t * tw,ohci_td_t * td)19960Sstevel@tonic-gate ohci_polled_insert_td_on_tw(
19970Sstevel@tonic-gate 	ohci_state_t		*ohcip,
19980Sstevel@tonic-gate 	ohci_trans_wrapper_t	*tw,
19990Sstevel@tonic-gate 	ohci_td_t		*td)
20000Sstevel@tonic-gate {
20010Sstevel@tonic-gate 
20020Sstevel@tonic-gate 	/*
20030Sstevel@tonic-gate 	 * Set the next pointer to NULL because
20040Sstevel@tonic-gate 	 * this is the last TD on list.
20050Sstevel@tonic-gate 	 */
20060Sstevel@tonic-gate 	Set_TD(td->hctd_tw_next_td, NULL);
20070Sstevel@tonic-gate 
20080Sstevel@tonic-gate 	if (tw->tw_hctd_head == NULL) {
20090Sstevel@tonic-gate 		ASSERT(tw->tw_hctd_tail == NULL);
20100Sstevel@tonic-gate 		tw->tw_hctd_head = td;
20110Sstevel@tonic-gate 		tw->tw_hctd_tail = td;
20120Sstevel@tonic-gate 	} else {
20130Sstevel@tonic-gate 		ohci_td_t *dummy = (ohci_td_t *)tw->tw_hctd_tail;
20140Sstevel@tonic-gate 
20150Sstevel@tonic-gate 		ASSERT(dummy != NULL);
20160Sstevel@tonic-gate 		ASSERT(dummy != td);
20170Sstevel@tonic-gate 		ASSERT(Get_TD(td->hctd_state) == HC_TD_DUMMY);
20180Sstevel@tonic-gate 
20190Sstevel@tonic-gate 		/* Add the td to the end of the list */
20200Sstevel@tonic-gate 		Set_TD(dummy->hctd_tw_next_td, ohci_td_cpu_to_iommu(ohcip, td));
20210Sstevel@tonic-gate 		tw->tw_hctd_tail = td;
20220Sstevel@tonic-gate 
20230Sstevel@tonic-gate 		ASSERT(Get_TD(td->hctd_tw_next_td) == NULL);
20240Sstevel@tonic-gate 	}
20250Sstevel@tonic-gate }
20260Sstevel@tonic-gate 
20270Sstevel@tonic-gate 
20280Sstevel@tonic-gate /*
20290Sstevel@tonic-gate  * ohci_polled_handle_frame_number_overflow:
20300Sstevel@tonic-gate  *
20310Sstevel@tonic-gate  * Process Frame Number Overflow (FNO) interrupt in polled mode.
20320Sstevel@tonic-gate  */
20330Sstevel@tonic-gate static void
ohci_polled_handle_frame_number_overflow(ohci_state_t * ohcip)20340Sstevel@tonic-gate ohci_polled_handle_frame_number_overflow(ohci_state_t	*ohcip)
20350Sstevel@tonic-gate {
20360Sstevel@tonic-gate 	uint_t			intr;
20370Sstevel@tonic-gate 
20380Sstevel@tonic-gate 	/* Read the Interrupt Status & Interrupt enable register */
20390Sstevel@tonic-gate 	intr = (Get_OpReg(hcr_intr_status) & Get_OpReg(hcr_intr_enable));
20400Sstevel@tonic-gate 
20410Sstevel@tonic-gate 	/*
20420Sstevel@tonic-gate 	 * Check whether any Frame Number Overflow interrupt is pending
20430Sstevel@tonic-gate 	 * and if it is pending, process this interrupt.
20440Sstevel@tonic-gate 	 */
20450Sstevel@tonic-gate 	if (intr & HCR_INTR_FNO) {
20460Sstevel@tonic-gate 		ohci_handle_frame_number_overflow(ohcip);
20470Sstevel@tonic-gate 
20480Sstevel@tonic-gate 		/* Acknowledge the FNO interrupt */
20490Sstevel@tonic-gate 		ohci_polled_finish_interrupt(ohcip, HCR_INTR_FNO);
20500Sstevel@tonic-gate 	}
20510Sstevel@tonic-gate }
20520Sstevel@tonic-gate 
20530Sstevel@tonic-gate 
20540Sstevel@tonic-gate /*
20550Sstevel@tonic-gate  * ohci_polled_finish_interrupt:
20560Sstevel@tonic-gate  */
20570Sstevel@tonic-gate static void
ohci_polled_finish_interrupt(ohci_state_t * ohcip,uint_t intr)20580Sstevel@tonic-gate ohci_polled_finish_interrupt(
20590Sstevel@tonic-gate 	ohci_state_t	*ohcip,
20600Sstevel@tonic-gate 	uint_t		intr)
20610Sstevel@tonic-gate {
20620Sstevel@tonic-gate 	/* Acknowledge the interrupt */
20630Sstevel@tonic-gate 	Set_OpReg(hcr_intr_status, intr);
20640Sstevel@tonic-gate 
20650Sstevel@tonic-gate 	/*
20660Sstevel@tonic-gate 	 * Read interrupt status register to make sure that any PIO
20670Sstevel@tonic-gate 	 * store to clear the ISR has made it on the PCI bus before
20680Sstevel@tonic-gate 	 * returning from its interrupt handler.
20690Sstevel@tonic-gate 	 */
20700Sstevel@tonic-gate 	(void) Get_OpReg(hcr_intr_status);
20710Sstevel@tonic-gate }
2072*9095SZhigang.Lu@Sun.COM 
2073*9095SZhigang.Lu@Sun.COM 
2074*9095SZhigang.Lu@Sun.COM /*
2075*9095SZhigang.Lu@Sun.COM  * ohci_polled_buikin_start:
2076*9095SZhigang.Lu@Sun.COM  * 	Insert bulkin td into endpoint's td list.
2077*9095SZhigang.Lu@Sun.COM  */
2078*9095SZhigang.Lu@Sun.COM static void
ohci_polled_insert_bulk_td(ohci_polled_t * ohci_polledp)2079*9095SZhigang.Lu@Sun.COM ohci_polled_insert_bulk_td(
2080*9095SZhigang.Lu@Sun.COM 	ohci_polled_t	*ohci_polledp)
2081*9095SZhigang.Lu@Sun.COM {
2082*9095SZhigang.Lu@Sun.COM 	ohci_state_t		*ohcip;
2083*9095SZhigang.Lu@Sun.COM 	ohci_trans_wrapper_t	*tw;
2084*9095SZhigang.Lu@Sun.COM 	ohci_pipe_private_t	*pp;
2085*9095SZhigang.Lu@Sun.COM 	usba_pipe_handle_data_t	*ph;
2086*9095SZhigang.Lu@Sun.COM 	uint32_t		ctrl;
2087*9095SZhigang.Lu@Sun.COM 	uint_t			bulk_pkg_size;
2088*9095SZhigang.Lu@Sun.COM 
2089*9095SZhigang.Lu@Sun.COM 	ohcip = ohci_polledp->ohci_polled_ohcip;
2090*9095SZhigang.Lu@Sun.COM 	ph = ohci_polledp->ohci_polled_input_pipe_handle;
2091*9095SZhigang.Lu@Sun.COM 	pp = (ohci_pipe_private_t *)ph->p_hcd_private;
2092*9095SZhigang.Lu@Sun.COM 
2093*9095SZhigang.Lu@Sun.COM 	tw = pp->pp_tw_head;
2094*9095SZhigang.Lu@Sun.COM 	ASSERT(tw != NULL);
2095*9095SZhigang.Lu@Sun.COM 
2096*9095SZhigang.Lu@Sun.COM 	ctrl = tw->tw_direction | HC_TD_DT_0 | HC_TD_1I | HC_TD_R;
2097*9095SZhigang.Lu@Sun.COM 	bulk_pkg_size = min(POLLED_RAW_BUF_SIZE, OHCI_MAX_TD_XFER_SIZE);
2098*9095SZhigang.Lu@Sun.COM 
2099*9095SZhigang.Lu@Sun.COM 	(void) ohci_polled_insert_hc_td(ohcip, ctrl, 0, bulk_pkg_size, pp, tw);
2100*9095SZhigang.Lu@Sun.COM }
2101*9095SZhigang.Lu@Sun.COM 
2102*9095SZhigang.Lu@Sun.COM 
2103*9095SZhigang.Lu@Sun.COM /*
2104*9095SZhigang.Lu@Sun.COM  * ohci_polled_create_tw:
2105*9095SZhigang.Lu@Sun.COM  *	Create the transfer wrapper used in polled mode.
2106*9095SZhigang.Lu@Sun.COM  */
2107*9095SZhigang.Lu@Sun.COM static int
ohci_polled_create_tw(ohci_state_t * ohcip,usba_pipe_handle_data_t * ph,usb_flags_t usb_flags)2108*9095SZhigang.Lu@Sun.COM ohci_polled_create_tw(
2109*9095SZhigang.Lu@Sun.COM 	ohci_state_t	*ohcip,
2110*9095SZhigang.Lu@Sun.COM 	usba_pipe_handle_data_t	*ph,
2111*9095SZhigang.Lu@Sun.COM 	usb_flags_t	usb_flags)
2112*9095SZhigang.Lu@Sun.COM {
2113*9095SZhigang.Lu@Sun.COM 	uint_t			ccount;
2114*9095SZhigang.Lu@Sun.COM 	ohci_trans_wrapper_t	*tw;
2115*9095SZhigang.Lu@Sun.COM 	ddi_device_acc_attr_t	dev_attr;
2116*9095SZhigang.Lu@Sun.COM 	ddi_dma_attr_t		dma_attr;
2117*9095SZhigang.Lu@Sun.COM 	ohci_pipe_private_t	*pp;
2118*9095SZhigang.Lu@Sun.COM 	int			result, pipe_dir, td_count;
2119*9095SZhigang.Lu@Sun.COM 	size_t			real_length;
2120*9095SZhigang.Lu@Sun.COM 
2121*9095SZhigang.Lu@Sun.COM 	pp = (ohci_pipe_private_t *)ph->p_hcd_private;
2122*9095SZhigang.Lu@Sun.COM 	td_count = (POLLED_RAW_BUF_SIZE - 1) / OHCI_MAX_TD_XFER_SIZE + 1;
2123*9095SZhigang.Lu@Sun.COM 
2124*9095SZhigang.Lu@Sun.COM 	if ((tw = kmem_zalloc(sizeof (ohci_trans_wrapper_t),
2125*9095SZhigang.Lu@Sun.COM 	    KM_NOSLEEP)) == NULL) {
2126*9095SZhigang.Lu@Sun.COM 		return (USB_FAILURE);
2127*9095SZhigang.Lu@Sun.COM 	}
2128*9095SZhigang.Lu@Sun.COM 
2129*9095SZhigang.Lu@Sun.COM 	/* allow sg lists for transfer wrapper dma memory */
2130*9095SZhigang.Lu@Sun.COM 	bcopy(&ohcip->ohci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
2131*9095SZhigang.Lu@Sun.COM 	dma_attr.dma_attr_sgllen = OHCI_DMA_ATTR_TW_SGLLEN;
2132*9095SZhigang.Lu@Sun.COM 	dma_attr.dma_attr_align = OHCI_DMA_ATTR_ALIGNMENT;
2133*9095SZhigang.Lu@Sun.COM 
2134*9095SZhigang.Lu@Sun.COM 	/* Allocate the DMA handle */
2135*9095SZhigang.Lu@Sun.COM 	if ((result = ddi_dma_alloc_handle(ohcip->ohci_dip,
2136*9095SZhigang.Lu@Sun.COM 	    &dma_attr, DDI_DMA_DONTWAIT, 0, &tw->tw_dmahandle)) !=
2137*9095SZhigang.Lu@Sun.COM 	    DDI_SUCCESS) {
2138*9095SZhigang.Lu@Sun.COM 		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
2139*9095SZhigang.Lu@Sun.COM 
2140*9095SZhigang.Lu@Sun.COM 		return (USB_FAILURE);
2141*9095SZhigang.Lu@Sun.COM 	}
2142*9095SZhigang.Lu@Sun.COM 
2143*9095SZhigang.Lu@Sun.COM 	dev_attr.devacc_attr_version		= DDI_DEVICE_ATTR_V0;
2144*9095SZhigang.Lu@Sun.COM 	dev_attr.devacc_attr_endian_flags	= DDI_STRUCTURE_LE_ACC;
2145*9095SZhigang.Lu@Sun.COM 	dev_attr.devacc_attr_dataorder		= DDI_STRICTORDER_ACC;
2146*9095SZhigang.Lu@Sun.COM 
2147*9095SZhigang.Lu@Sun.COM 	/* Allocate the memory */
2148*9095SZhigang.Lu@Sun.COM 	if ((result = ddi_dma_mem_alloc(tw->tw_dmahandle, POLLED_RAW_BUF_SIZE,
2149*9095SZhigang.Lu@Sun.COM 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
2150*9095SZhigang.Lu@Sun.COM 	    &tw->tw_buf, &real_length, &tw->tw_accesshandle)) !=
2151*9095SZhigang.Lu@Sun.COM 	    DDI_SUCCESS) {
2152*9095SZhigang.Lu@Sun.COM 		ddi_dma_free_handle(&tw->tw_dmahandle);
2153*9095SZhigang.Lu@Sun.COM 		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
2154*9095SZhigang.Lu@Sun.COM 
2155*9095SZhigang.Lu@Sun.COM 		return (USB_FAILURE);
2156*9095SZhigang.Lu@Sun.COM 	}
2157*9095SZhigang.Lu@Sun.COM 
2158*9095SZhigang.Lu@Sun.COM 	/* Bind the handle */
2159*9095SZhigang.Lu@Sun.COM 	if ((result = ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
2160*9095SZhigang.Lu@Sun.COM 	    tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
2161*9095SZhigang.Lu@Sun.COM 	    DDI_DMA_DONTWAIT, NULL, &tw->tw_cookie, &ccount)) !=
2162*9095SZhigang.Lu@Sun.COM 	    DDI_DMA_MAPPED) {
2163*9095SZhigang.Lu@Sun.COM 		ddi_dma_mem_free(&tw->tw_accesshandle);
2164*9095SZhigang.Lu@Sun.COM 		ddi_dma_free_handle(&tw->tw_dmahandle);
2165*9095SZhigang.Lu@Sun.COM 		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
2166*9095SZhigang.Lu@Sun.COM 
2167*9095SZhigang.Lu@Sun.COM 		return (USB_FAILURE);
2168*9095SZhigang.Lu@Sun.COM 	}
2169*9095SZhigang.Lu@Sun.COM 
2170*9095SZhigang.Lu@Sun.COM 	/* The cookie count should be 1 */
2171*9095SZhigang.Lu@Sun.COM 	if (ccount != 1) {
2172*9095SZhigang.Lu@Sun.COM 		result = ddi_dma_unbind_handle(tw->tw_dmahandle);
2173*9095SZhigang.Lu@Sun.COM 		ASSERT(result == DDI_SUCCESS);
2174*9095SZhigang.Lu@Sun.COM 
2175*9095SZhigang.Lu@Sun.COM 		ddi_dma_mem_free(&tw->tw_accesshandle);
2176*9095SZhigang.Lu@Sun.COM 		ddi_dma_free_handle(&tw->tw_dmahandle);
2177*9095SZhigang.Lu@Sun.COM 		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
2178*9095SZhigang.Lu@Sun.COM 
2179*9095SZhigang.Lu@Sun.COM 		return (USB_FAILURE);
2180*9095SZhigang.Lu@Sun.COM 	}
2181*9095SZhigang.Lu@Sun.COM 
2182*9095SZhigang.Lu@Sun.COM 	if (ohci_allocate_tds_for_tw(ohcip, tw, td_count) == USB_SUCCESS) {
2183*9095SZhigang.Lu@Sun.COM 		tw->tw_num_tds = td_count;
2184*9095SZhigang.Lu@Sun.COM 	} else {
2185*9095SZhigang.Lu@Sun.COM 		ohci_deallocate_tw_resources(ohcip, pp, tw);
2186*9095SZhigang.Lu@Sun.COM 		return (USB_FAILURE);
2187*9095SZhigang.Lu@Sun.COM 	}
2188*9095SZhigang.Lu@Sun.COM 	tw->tw_cookie_idx = 0;
2189*9095SZhigang.Lu@Sun.COM 	tw->tw_dma_offs = 0;
2190*9095SZhigang.Lu@Sun.COM 
2191*9095SZhigang.Lu@Sun.COM 	/*
2192*9095SZhigang.Lu@Sun.COM 	 * Only allow one wrapper to be added at a time. Insert the
2193*9095SZhigang.Lu@Sun.COM 	 * new transaction wrapper into the list for this pipe.
2194*9095SZhigang.Lu@Sun.COM 	 */
2195*9095SZhigang.Lu@Sun.COM 	if (pp->pp_tw_head == NULL) {
2196*9095SZhigang.Lu@Sun.COM 		pp->pp_tw_head = tw;
2197*9095SZhigang.Lu@Sun.COM 		pp->pp_tw_tail = tw;
2198*9095SZhigang.Lu@Sun.COM 	} else {
2199*9095SZhigang.Lu@Sun.COM 		pp->pp_tw_tail->tw_next = tw;
2200*9095SZhigang.Lu@Sun.COM 		pp->pp_tw_tail = tw;
2201*9095SZhigang.Lu@Sun.COM 	}
2202*9095SZhigang.Lu@Sun.COM 
2203*9095SZhigang.Lu@Sun.COM 	/* Store the transfer length */
2204*9095SZhigang.Lu@Sun.COM 	tw->tw_length = POLLED_RAW_BUF_SIZE;
2205*9095SZhigang.Lu@Sun.COM 
2206*9095SZhigang.Lu@Sun.COM 	/* Store a back pointer to the pipe private structure */
2207*9095SZhigang.Lu@Sun.COM 	tw->tw_pipe_private = pp;
2208*9095SZhigang.Lu@Sun.COM 
2209*9095SZhigang.Lu@Sun.COM 	/* Store the transfer type - synchronous or asynchronous */
2210*9095SZhigang.Lu@Sun.COM 	tw->tw_flags = usb_flags;
2211*9095SZhigang.Lu@Sun.COM 
2212*9095SZhigang.Lu@Sun.COM 	/* Get and Store 32bit ID */
2213*9095SZhigang.Lu@Sun.COM 	tw->tw_id = OHCI_GET_ID((void *)tw);
2214*9095SZhigang.Lu@Sun.COM 
2215*9095SZhigang.Lu@Sun.COM 	ASSERT(tw->tw_id != NULL);
2216*9095SZhigang.Lu@Sun.COM 
2217*9095SZhigang.Lu@Sun.COM 	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
2218*9095SZhigang.Lu@Sun.COM 	tw->tw_direction = (pipe_dir == USB_EP_DIR_IN) ? HC_TD_IN : HC_TD_OUT;
2219*9095SZhigang.Lu@Sun.COM 
2220*9095SZhigang.Lu@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
2221*9095SZhigang.Lu@Sun.COM 	    "ohci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
2222*9095SZhigang.Lu@Sun.COM 	    (void *)tw, tw->tw_ncookies);
2223*9095SZhigang.Lu@Sun.COM 
2224*9095SZhigang.Lu@Sun.COM 	return (USB_SUCCESS);
2225*9095SZhigang.Lu@Sun.COM }
2226*9095SZhigang.Lu@Sun.COM 
2227*9095SZhigang.Lu@Sun.COM 
2228*9095SZhigang.Lu@Sun.COM /*
2229*9095SZhigang.Lu@Sun.COM  * ohci_polled_insert_hc_td:
2230*9095SZhigang.Lu@Sun.COM  *
2231*9095SZhigang.Lu@Sun.COM  * Insert a Transfer Descriptor (TD) on an Endpoint Descriptor (ED).
2232*9095SZhigang.Lu@Sun.COM  */
2233*9095SZhigang.Lu@Sun.COM int
ohci_polled_insert_hc_td(ohci_state_t * ohcip,uint_t hctd_ctrl,uint32_t hctd_dma_offs,size_t hctd_length,ohci_pipe_private_t * pp,ohci_trans_wrapper_t * tw)2234*9095SZhigang.Lu@Sun.COM ohci_polled_insert_hc_td(
2235*9095SZhigang.Lu@Sun.COM 	ohci_state_t		*ohcip,
2236*9095SZhigang.Lu@Sun.COM 	uint_t			hctd_ctrl,
2237*9095SZhigang.Lu@Sun.COM 	uint32_t		hctd_dma_offs,
2238*9095SZhigang.Lu@Sun.COM 	size_t			hctd_length,
2239*9095SZhigang.Lu@Sun.COM 	ohci_pipe_private_t	*pp,
2240*9095SZhigang.Lu@Sun.COM 	ohci_trans_wrapper_t	*tw)
2241*9095SZhigang.Lu@Sun.COM {
2242*9095SZhigang.Lu@Sun.COM 	ohci_td_t		*new_dummy;
2243*9095SZhigang.Lu@Sun.COM 	ohci_td_t		*cpu_current_dummy;
2244*9095SZhigang.Lu@Sun.COM 	ohci_ed_t		*ept = pp->pp_ept;
2245*9095SZhigang.Lu@Sun.COM 
2246*9095SZhigang.Lu@Sun.COM 	/* Retrieve preallocated td from the TW */
2247*9095SZhigang.Lu@Sun.COM 	new_dummy = tw->tw_hctd_free_list;
2248*9095SZhigang.Lu@Sun.COM 
2249*9095SZhigang.Lu@Sun.COM 	ASSERT(new_dummy != NULL);
2250*9095SZhigang.Lu@Sun.COM 
2251*9095SZhigang.Lu@Sun.COM 	tw->tw_hctd_free_list = ohci_td_iommu_to_cpu(ohcip,
2252*9095SZhigang.Lu@Sun.COM 	    Get_TD(new_dummy->hctd_tw_next_td));
2253*9095SZhigang.Lu@Sun.COM 	Set_TD(new_dummy->hctd_tw_next_td, NULL);
2254*9095SZhigang.Lu@Sun.COM 
2255*9095SZhigang.Lu@Sun.COM 	/* Fill in the current dummy */
2256*9095SZhigang.Lu@Sun.COM 	cpu_current_dummy = (ohci_td_t *)
2257*9095SZhigang.Lu@Sun.COM 	    (ohci_td_iommu_to_cpu(ohcip, Get_ED(ept->hced_tailp)));
2258*9095SZhigang.Lu@Sun.COM 
2259*9095SZhigang.Lu@Sun.COM 	/*
2260*9095SZhigang.Lu@Sun.COM 	 * Fill in the current dummy td and
2261*9095SZhigang.Lu@Sun.COM 	 * add the new dummy to the end.
2262*9095SZhigang.Lu@Sun.COM 	 */
2263*9095SZhigang.Lu@Sun.COM 	ohci_polled_fill_in_td(ohcip, cpu_current_dummy, new_dummy,
2264*9095SZhigang.Lu@Sun.COM 	    hctd_ctrl, hctd_dma_offs, hctd_length, tw);
2265*9095SZhigang.Lu@Sun.COM 
2266*9095SZhigang.Lu@Sun.COM 	/*
2267*9095SZhigang.Lu@Sun.COM 	 * add the new dummy to the ED's list. When
2268*9095SZhigang.Lu@Sun.COM 	 * this occurs, the Host Controller will see
2269*9095SZhigang.Lu@Sun.COM 	 * the newly filled in dummy TD.
2270*9095SZhigang.Lu@Sun.COM 	 */
2271*9095SZhigang.Lu@Sun.COM 	Set_ED(ept->hced_tailp,
2272*9095SZhigang.Lu@Sun.COM 	    (ohci_td_cpu_to_iommu(ohcip, new_dummy)));
2273*9095SZhigang.Lu@Sun.COM 
2274*9095SZhigang.Lu@Sun.COM 	/* Insert this td onto the tw */
2275*9095SZhigang.Lu@Sun.COM 	ohci_polled_insert_td_on_tw(ohcip, tw, cpu_current_dummy);
2276*9095SZhigang.Lu@Sun.COM 
2277*9095SZhigang.Lu@Sun.COM 	return (USB_SUCCESS);
2278*9095SZhigang.Lu@Sun.COM }
2279