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
51500Ssl147100 * Common Development and Distribution License (the "License").
61500Ssl147100 * 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 * EHCI Host Controller Driver (EHCI)
280Sstevel@tonic-gate *
290Sstevel@tonic-gate * The EHCI driver is a software driver which interfaces to the Universal
300Sstevel@tonic-gate * Serial Bus layer (USBA) and the Host Controller (HC). The interface to
310Sstevel@tonic-gate * the Host Controller is defined by the EHCI Host Controller Interface.
320Sstevel@tonic-gate *
330Sstevel@tonic-gate * This module contains the specific EHCI code used in POLLED mode. This
340Sstevel@tonic-gate * code is in a separate file since it will never become part of the EHCI
350Sstevel@tonic-gate * driver.
360Sstevel@tonic-gate */
370Sstevel@tonic-gate
380Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
390Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehcid.h>
400Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_xfer.h>
410Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_intr.h>
420Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_util.h>
430Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_polled.h>
440Sstevel@tonic-gate
45*9095SZhigang.Lu@Sun.COM #ifndef __sparc
46*9095SZhigang.Lu@Sun.COM extern void invalidate_cache();
47*9095SZhigang.Lu@Sun.COM #endif
48*9095SZhigang.Lu@Sun.COM
490Sstevel@tonic-gate /*
500Sstevel@tonic-gate * Internal Function Prototypes
510Sstevel@tonic-gate */
520Sstevel@tonic-gate
530Sstevel@tonic-gate /* Polled initialization routines */
540Sstevel@tonic-gate static int ehci_polled_init(
550Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
560Sstevel@tonic-gate ehci_state_t *ehcip,
570Sstevel@tonic-gate usb_console_info_impl_t *console_input_info);
580Sstevel@tonic-gate
590Sstevel@tonic-gate /* Polled deinitialization routines */
600Sstevel@tonic-gate static int ehci_polled_fini(ehci_polled_t *ehci_polledp);
610Sstevel@tonic-gate
620Sstevel@tonic-gate /* Polled save state routines */
630Sstevel@tonic-gate static void ehci_polled_save_state(ehci_polled_t *ehci_polledp);
640Sstevel@tonic-gate
650Sstevel@tonic-gate /* Polled restore state routines */
660Sstevel@tonic-gate static void ehci_polled_restore_state(ehci_polled_t *ehci_polledp);
670Sstevel@tonic-gate static void ehci_polled_stop_processing(
680Sstevel@tonic-gate ehci_polled_t *ehci_polledp);
690Sstevel@tonic-gate static void ehci_polled_start_processing(
700Sstevel@tonic-gate ehci_polled_t *ehci_polledp);
710Sstevel@tonic-gate
720Sstevel@tonic-gate /* Polled read routines */
730Sstevel@tonic-gate static int ehci_polled_process_active_intr_qtd_list(
740Sstevel@tonic-gate ehci_polled_t *ehci_polledp);
750Sstevel@tonic-gate static int ehci_polled_handle_normal_qtd(
760Sstevel@tonic-gate ehci_polled_t *ehci_polledp,
770Sstevel@tonic-gate ehci_qtd_t *qtd);
78*9095SZhigang.Lu@Sun.COM static void ehci_polled_insert_intr_qtd(
790Sstevel@tonic-gate ehci_polled_t *ehci_polledp,
800Sstevel@tonic-gate ehci_qtd_t *qtd);
81*9095SZhigang.Lu@Sun.COM static void ehci_polled_insert_bulk_qtd(
82*9095SZhigang.Lu@Sun.COM ehci_polled_t *ehci_polledp);
830Sstevel@tonic-gate static void ehci_polled_fill_in_qtd(
840Sstevel@tonic-gate ehci_state_t *ehcip,
850Sstevel@tonic-gate ehci_qtd_t *qtd,
860Sstevel@tonic-gate uint_t qtd_ctrl,
871500Ssl147100 size_t qtd_dma_offs,
880Sstevel@tonic-gate size_t qtd_length,
890Sstevel@tonic-gate ehci_trans_wrapper_t *tw);
900Sstevel@tonic-gate static void ehci_polled_insert_qtd_on_tw(
910Sstevel@tonic-gate ehci_state_t *ehcip,
920Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
930Sstevel@tonic-gate ehci_qtd_t *qtd);
940Sstevel@tonic-gate static ehci_qtd_t *ehci_polled_create_done_qtd_list(
950Sstevel@tonic-gate ehci_polled_t *ehci_polledp);
960Sstevel@tonic-gate static void ehci_polled_insert_qtd_into_active_intr_qtd_list(
970Sstevel@tonic-gate ehci_polled_t *ehci_polledp,
980Sstevel@tonic-gate ehci_qtd_t *curr_qtd);
990Sstevel@tonic-gate static void ehci_polled_remove_qtd_from_active_intr_qtd_list(
1000Sstevel@tonic-gate ehci_polled_t *ehci_polledp,
1010Sstevel@tonic-gate ehci_qtd_t *curr_qtd);
1020Sstevel@tonic-gate static void ehci_polled_traverse_qtds(
1030Sstevel@tonic-gate ehci_polled_t *ehci_polledp,
1040Sstevel@tonic-gate usba_pipe_handle_data_t *ph);
1050Sstevel@tonic-gate static void ehci_polled_finish_interrupt(
1060Sstevel@tonic-gate ehci_state_t *ehcip,
1070Sstevel@tonic-gate uint_t intr);
108*9095SZhigang.Lu@Sun.COM static int ehci_polled_create_tw(
109*9095SZhigang.Lu@Sun.COM ehci_polled_t *ehci_polledp,
110*9095SZhigang.Lu@Sun.COM usba_pipe_handle_data_t *ph,
111*9095SZhigang.Lu@Sun.COM usb_flags_t usb_flags);
112*9095SZhigang.Lu@Sun.COM static void ehci_polled_insert_async_qh(
113*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip,
114*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp);
115*9095SZhigang.Lu@Sun.COM static void ehci_polled_remove_async_qh(
116*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip,
117*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp);
1180Sstevel@tonic-gate
1190Sstevel@tonic-gate /*
1200Sstevel@tonic-gate * POLLED entry points
1210Sstevel@tonic-gate *
1220Sstevel@tonic-gate * These functions are entry points into the POLLED code.
1230Sstevel@tonic-gate */
1240Sstevel@tonic-gate
1250Sstevel@tonic-gate /*
1260Sstevel@tonic-gate * ehci_hcdi_polled_input_init:
1270Sstevel@tonic-gate *
128*9095SZhigang.Lu@Sun.COM * This is the initialization routine for handling the USB input device
1290Sstevel@tonic-gate * in POLLED mode. This routine is not called from POLLED mode, so
1300Sstevel@tonic-gate * it is OK to acquire mutexes.
1310Sstevel@tonic-gate */
1320Sstevel@tonic-gate int
ehci_hcdi_polled_input_init(usba_pipe_handle_data_t * ph,uchar_t ** polled_buf,usb_console_info_impl_t * console_input_info)1330Sstevel@tonic-gate ehci_hcdi_polled_input_init(
1340Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
1350Sstevel@tonic-gate uchar_t **polled_buf,
1360Sstevel@tonic-gate usb_console_info_impl_t *console_input_info)
1370Sstevel@tonic-gate {
1380Sstevel@tonic-gate ehci_polled_t *ehci_polledp;
1390Sstevel@tonic-gate ehci_state_t *ehcip;
1400Sstevel@tonic-gate int ret;
1410Sstevel@tonic-gate
1420Sstevel@tonic-gate ehcip = ehci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
1430Sstevel@tonic-gate
1440Sstevel@tonic-gate /*
1450Sstevel@tonic-gate * Grab the ehci_int_mutex so that things don't change on us
1460Sstevel@tonic-gate * if an interrupt comes in.
1470Sstevel@tonic-gate */
1480Sstevel@tonic-gate mutex_enter(&ehcip->ehci_int_mutex);
1490Sstevel@tonic-gate
1500Sstevel@tonic-gate ret = ehci_polled_init(ph, ehcip, console_input_info);
1510Sstevel@tonic-gate
1520Sstevel@tonic-gate if (ret != USB_SUCCESS) {
1530Sstevel@tonic-gate
1540Sstevel@tonic-gate /* Allow interrupts to continue */
1550Sstevel@tonic-gate mutex_exit(&ehcip->ehci_int_mutex);
1560Sstevel@tonic-gate return (ret);
1570Sstevel@tonic-gate }
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate ehci_polledp = (ehci_polled_t *)console_input_info->uci_private;
1600Sstevel@tonic-gate
1610Sstevel@tonic-gate /*
1620Sstevel@tonic-gate * Mark the structure so that if we are using it, we don't free
1630Sstevel@tonic-gate * the structures if one of them is unplugged.
1640Sstevel@tonic-gate */
1650Sstevel@tonic-gate ehci_polledp->ehci_polled_flags |= POLLED_INPUT_MODE;
1660Sstevel@tonic-gate
1670Sstevel@tonic-gate /* increase the counter for keyboard connected */
1680Sstevel@tonic-gate ehcip->ehci_polled_kbd_count ++;
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate /*
1710Sstevel@tonic-gate * This is the buffer we will copy characters into. It will be
1720Sstevel@tonic-gate * copied into at this layer, so we need to keep track of it.
1730Sstevel@tonic-gate */
1740Sstevel@tonic-gate ehci_polledp->ehci_polled_buf =
1750Sstevel@tonic-gate (uchar_t *)kmem_zalloc(POLLED_RAW_BUF_SIZE, KM_SLEEP);
1760Sstevel@tonic-gate
1770Sstevel@tonic-gate *polled_buf = ehci_polledp->ehci_polled_buf;
1780Sstevel@tonic-gate
1790Sstevel@tonic-gate /*
1800Sstevel@tonic-gate * This is a software workaround to fix schizo hardware bug.
1810Sstevel@tonic-gate * Existence of "no-prom-cdma-sync" property means consistent
1820Sstevel@tonic-gate * dma sync should not be done while in prom or polled mode.
1830Sstevel@tonic-gate */
1840Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, ehcip->ehci_dip,
1850Sstevel@tonic-gate DDI_PROP_NOTPROM, "no-prom-cdma-sync")) {
1860Sstevel@tonic-gate ehci_polledp->ehci_polled_no_sync_flag = B_TRUE;
1870Sstevel@tonic-gate }
1880Sstevel@tonic-gate
1890Sstevel@tonic-gate /* Allow interrupts to continue */
1900Sstevel@tonic-gate mutex_exit(&ehcip->ehci_int_mutex);
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate return (USB_SUCCESS);
1930Sstevel@tonic-gate }
1940Sstevel@tonic-gate
1950Sstevel@tonic-gate
1960Sstevel@tonic-gate /*
1970Sstevel@tonic-gate * ehci_hcdi_polled_input_fini:
1980Sstevel@tonic-gate */
1990Sstevel@tonic-gate int
ehci_hcdi_polled_input_fini(usb_console_info_impl_t * info)2000Sstevel@tonic-gate ehci_hcdi_polled_input_fini(usb_console_info_impl_t *info)
2010Sstevel@tonic-gate {
2020Sstevel@tonic-gate ehci_polled_t *ehci_polledp;
2030Sstevel@tonic-gate ehci_state_t *ehcip;
2040Sstevel@tonic-gate int ret;
2050Sstevel@tonic-gate
2060Sstevel@tonic-gate ehci_polledp = (ehci_polled_t *)info->uci_private;
2070Sstevel@tonic-gate
2080Sstevel@tonic-gate ehcip = ehci_polledp->ehci_polled_ehcip;
2090Sstevel@tonic-gate
2100Sstevel@tonic-gate mutex_enter(&ehcip->ehci_int_mutex);
2110Sstevel@tonic-gate
2120Sstevel@tonic-gate /*
2130Sstevel@tonic-gate * Reset the POLLED_INPUT_MODE flag so that we can tell if
2140Sstevel@tonic-gate * this structure is in use in the ehci_polled_fini routine.
2150Sstevel@tonic-gate */
2160Sstevel@tonic-gate ehci_polledp->ehci_polled_flags &= ~POLLED_INPUT_MODE;
2170Sstevel@tonic-gate
2180Sstevel@tonic-gate /* decrease the counter for keyboard disconnected */
2190Sstevel@tonic-gate ehcip->ehci_polled_kbd_count --;
2200Sstevel@tonic-gate
2210Sstevel@tonic-gate /* Free the buffer that we copied data into */
2220Sstevel@tonic-gate kmem_free(ehci_polledp->ehci_polled_buf, POLLED_RAW_BUF_SIZE);
2230Sstevel@tonic-gate
2240Sstevel@tonic-gate ret = ehci_polled_fini(ehci_polledp);
2250Sstevel@tonic-gate
2260Sstevel@tonic-gate mutex_exit(&ehcip->ehci_int_mutex);
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate return (ret);
2290Sstevel@tonic-gate }
2300Sstevel@tonic-gate
2310Sstevel@tonic-gate
2320Sstevel@tonic-gate /*
2330Sstevel@tonic-gate * ehci_hcdi_polled_input_enter:
2340Sstevel@tonic-gate *
2350Sstevel@tonic-gate * This is where we enter into POLLED mode. This routine sets up
2360Sstevel@tonic-gate * everything so that calls to ehci_hcdi_polled_read will return
2370Sstevel@tonic-gate * characters.
2380Sstevel@tonic-gate */
2390Sstevel@tonic-gate int
ehci_hcdi_polled_input_enter(usb_console_info_impl_t * info)2400Sstevel@tonic-gate ehci_hcdi_polled_input_enter(usb_console_info_impl_t *info)
2410Sstevel@tonic-gate {
2420Sstevel@tonic-gate ehci_polled_t *ehci_polledp;
243*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip;
244*9095SZhigang.Lu@Sun.COM usba_pipe_handle_data_t *ph;
245*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp;
246*9095SZhigang.Lu@Sun.COM int pipe_attr;
2470Sstevel@tonic-gate
248*9095SZhigang.Lu@Sun.COM #ifndef lint
249*9095SZhigang.Lu@Sun.COM _NOTE(NO_COMPETING_THREADS_NOW);
250*9095SZhigang.Lu@Sun.COM #endif
2510Sstevel@tonic-gate ehci_polledp = (ehci_polled_t *)info->uci_private;
252*9095SZhigang.Lu@Sun.COM ehcip = ehci_polledp->ehci_polled_ehcip;
253*9095SZhigang.Lu@Sun.COM ph = ehci_polledp->ehci_polled_input_pipe_handle;
254*9095SZhigang.Lu@Sun.COM pp = (ehci_pipe_private_t *)ph->p_hcd_private;
255*9095SZhigang.Lu@Sun.COM
256*9095SZhigang.Lu@Sun.COM pipe_attr = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
257*9095SZhigang.Lu@Sun.COM #ifndef lint
258*9095SZhigang.Lu@Sun.COM _NOTE(COMPETING_THREADS_NOW);
259*9095SZhigang.Lu@Sun.COM #endif
2600Sstevel@tonic-gate
2610Sstevel@tonic-gate ehci_polledp->ehci_polled_entry++;
2620Sstevel@tonic-gate /*
2630Sstevel@tonic-gate * If the controller is already switched over, just return
2640Sstevel@tonic-gate */
2650Sstevel@tonic-gate if (ehci_polledp->ehci_polled_entry > 1) {
2660Sstevel@tonic-gate
2670Sstevel@tonic-gate return (USB_SUCCESS);
2680Sstevel@tonic-gate }
2690Sstevel@tonic-gate
270*9095SZhigang.Lu@Sun.COM switch (pipe_attr) {
271*9095SZhigang.Lu@Sun.COM case USB_EP_ATTR_INTR:
272*9095SZhigang.Lu@Sun.COM ehci_polled_save_state(ehci_polledp);
273*9095SZhigang.Lu@Sun.COM break;
274*9095SZhigang.Lu@Sun.COM case USB_EP_ATTR_BULK:
275*9095SZhigang.Lu@Sun.COM #ifndef lint
276*9095SZhigang.Lu@Sun.COM _NOTE(NO_COMPETING_THREADS_NOW);
277*9095SZhigang.Lu@Sun.COM #endif
278*9095SZhigang.Lu@Sun.COM Set_OpReg(ehci_command, (Get_OpReg(ehci_command) &
279*9095SZhigang.Lu@Sun.COM ~(EHCI_CMD_PERIODIC_SCHED_ENABLE |
280*9095SZhigang.Lu@Sun.COM EHCI_CMD_ASYNC_SCHED_ENABLE)));
281*9095SZhigang.Lu@Sun.COM /* Wait for few milliseconds */
282*9095SZhigang.Lu@Sun.COM drv_usecwait(EHCI_POLLED_TIMEWAIT);
283*9095SZhigang.Lu@Sun.COM
284*9095SZhigang.Lu@Sun.COM ehci_polled_insert_async_qh(ehcip, pp);
285*9095SZhigang.Lu@Sun.COM
286*9095SZhigang.Lu@Sun.COM Set_OpReg(ehci_command,
287*9095SZhigang.Lu@Sun.COM (Get_OpReg(ehci_command) | EHCI_CMD_ASYNC_SCHED_ENABLE));
288*9095SZhigang.Lu@Sun.COM #ifndef lint
289*9095SZhigang.Lu@Sun.COM _NOTE(COMPETING_THREADS_NOW);
290*9095SZhigang.Lu@Sun.COM #endif
291*9095SZhigang.Lu@Sun.COM /* Wait for few milliseconds */
292*9095SZhigang.Lu@Sun.COM drv_usecwait(EHCI_POLLED_TIMEWAIT);
293*9095SZhigang.Lu@Sun.COM break;
294*9095SZhigang.Lu@Sun.COM default:
295*9095SZhigang.Lu@Sun.COM return (USB_FAILURE);
296*9095SZhigang.Lu@Sun.COM }
2970Sstevel@tonic-gate
2980Sstevel@tonic-gate ehci_polledp->ehci_polled_flags |= POLLED_INPUT_MODE_INUSE;
2990Sstevel@tonic-gate
3000Sstevel@tonic-gate return (USB_SUCCESS);
3010Sstevel@tonic-gate }
3020Sstevel@tonic-gate
3030Sstevel@tonic-gate
3040Sstevel@tonic-gate /*
3050Sstevel@tonic-gate * ehci_hcdi_polled_input_exit:
3060Sstevel@tonic-gate *
3070Sstevel@tonic-gate * This is where we exit POLLED mode. This routine restores
3080Sstevel@tonic-gate * everything that is needed to continue operation.
3090Sstevel@tonic-gate */
3100Sstevel@tonic-gate int
ehci_hcdi_polled_input_exit(usb_console_info_impl_t * info)3110Sstevel@tonic-gate ehci_hcdi_polled_input_exit(usb_console_info_impl_t *info)
3120Sstevel@tonic-gate {
3130Sstevel@tonic-gate ehci_polled_t *ehci_polledp;
314*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip;
315*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp;
316*9095SZhigang.Lu@Sun.COM int pipe_attr;
3170Sstevel@tonic-gate
318*9095SZhigang.Lu@Sun.COM #ifndef lint
319*9095SZhigang.Lu@Sun.COM _NOTE(NO_COMPETING_THREADS_NOW);
320*9095SZhigang.Lu@Sun.COM #endif
3210Sstevel@tonic-gate ehci_polledp = (ehci_polled_t *)info->uci_private;
322*9095SZhigang.Lu@Sun.COM ehcip = ehci_polledp->ehci_polled_ehcip;
323*9095SZhigang.Lu@Sun.COM pp = (ehci_pipe_private_t *)ehci_polledp->
324*9095SZhigang.Lu@Sun.COM ehci_polled_input_pipe_handle->p_hcd_private;
325*9095SZhigang.Lu@Sun.COM
326*9095SZhigang.Lu@Sun.COM pipe_attr = ehci_polledp->ehci_polled_input_pipe_handle->
327*9095SZhigang.Lu@Sun.COM p_ep.bmAttributes & USB_EP_ATTR_MASK;
328*9095SZhigang.Lu@Sun.COM #ifndef lint
329*9095SZhigang.Lu@Sun.COM _NOTE(COMPETING_THREADS_NOW);
330*9095SZhigang.Lu@Sun.COM #endif
3310Sstevel@tonic-gate
3320Sstevel@tonic-gate ehci_polledp->ehci_polled_entry--;
3330Sstevel@tonic-gate
3340Sstevel@tonic-gate /*
3350Sstevel@tonic-gate * If there are still outstanding "enters", just return
3360Sstevel@tonic-gate */
3370Sstevel@tonic-gate if (ehci_polledp->ehci_polled_entry > 0) {
3380Sstevel@tonic-gate
3390Sstevel@tonic-gate return (USB_SUCCESS);
3400Sstevel@tonic-gate }
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate ehci_polledp->ehci_polled_flags &= ~POLLED_INPUT_MODE_INUSE;
3430Sstevel@tonic-gate
344*9095SZhigang.Lu@Sun.COM switch (pipe_attr & USB_EP_ATTR_MASK) {
345*9095SZhigang.Lu@Sun.COM case USB_EP_ATTR_INTR:
346*9095SZhigang.Lu@Sun.COM ehci_polled_restore_state(ehci_polledp);
347*9095SZhigang.Lu@Sun.COM break;
348*9095SZhigang.Lu@Sun.COM case USB_EP_ATTR_BULK:
349*9095SZhigang.Lu@Sun.COM #ifndef lint
350*9095SZhigang.Lu@Sun.COM _NOTE(NO_COMPETING_THREADS_NOW);
351*9095SZhigang.Lu@Sun.COM #endif
352*9095SZhigang.Lu@Sun.COM Set_OpReg(ehci_command, (Get_OpReg(ehci_command) &
353*9095SZhigang.Lu@Sun.COM ~(EHCI_CMD_PERIODIC_SCHED_ENABLE |
354*9095SZhigang.Lu@Sun.COM EHCI_CMD_ASYNC_SCHED_ENABLE)));
355*9095SZhigang.Lu@Sun.COM /* Wait for few milliseconds */
356*9095SZhigang.Lu@Sun.COM drv_usecwait(EHCI_POLLED_TIMEWAIT);
357*9095SZhigang.Lu@Sun.COM
358*9095SZhigang.Lu@Sun.COM ehci_polled_remove_async_qh(ehcip, pp);
359*9095SZhigang.Lu@Sun.COM
360*9095SZhigang.Lu@Sun.COM Set_OpReg(ehci_command,
361*9095SZhigang.Lu@Sun.COM (Get_OpReg(ehci_command) | EHCI_CMD_ASYNC_SCHED_ENABLE |
362*9095SZhigang.Lu@Sun.COM EHCI_CMD_ASYNC_SCHED_ENABLE));
363*9095SZhigang.Lu@Sun.COM #ifndef lint
364*9095SZhigang.Lu@Sun.COM _NOTE(COMPETING_THREADS_NOW);
365*9095SZhigang.Lu@Sun.COM #endif
366*9095SZhigang.Lu@Sun.COM /* Wait for few milliseconds */
367*9095SZhigang.Lu@Sun.COM drv_usecwait(EHCI_POLLED_TIMEWAIT);
368*9095SZhigang.Lu@Sun.COM break;
369*9095SZhigang.Lu@Sun.COM default:
370*9095SZhigang.Lu@Sun.COM return (USB_FAILURE);
371*9095SZhigang.Lu@Sun.COM
372*9095SZhigang.Lu@Sun.COM }
3730Sstevel@tonic-gate
3740Sstevel@tonic-gate return (USB_SUCCESS);
3750Sstevel@tonic-gate }
3760Sstevel@tonic-gate
3770Sstevel@tonic-gate
3780Sstevel@tonic-gate /*
3790Sstevel@tonic-gate * ehci_hcdi_polled_read:
3800Sstevel@tonic-gate *
3810Sstevel@tonic-gate * Get a key character
3820Sstevel@tonic-gate */
3830Sstevel@tonic-gate int
ehci_hcdi_polled_read(usb_console_info_impl_t * info,uint_t * num_characters)3840Sstevel@tonic-gate ehci_hcdi_polled_read(
3850Sstevel@tonic-gate usb_console_info_impl_t *info,
3860Sstevel@tonic-gate uint_t *num_characters)
3870Sstevel@tonic-gate {
3880Sstevel@tonic-gate ehci_state_t *ehcip;
3890Sstevel@tonic-gate ehci_polled_t *ehci_polledp;
3900Sstevel@tonic-gate uint_t intr;
391*9095SZhigang.Lu@Sun.COM int pipe_attr;
3920Sstevel@tonic-gate
3930Sstevel@tonic-gate ehci_polledp = (ehci_polled_t *)info->uci_private;
3940Sstevel@tonic-gate
3950Sstevel@tonic-gate ehcip = ehci_polledp->ehci_polled_ehcip;
3960Sstevel@tonic-gate
3970Sstevel@tonic-gate #ifndef lint
3980Sstevel@tonic-gate _NOTE(NO_COMPETING_THREADS_NOW);
3990Sstevel@tonic-gate #endif
4000Sstevel@tonic-gate
4010Sstevel@tonic-gate *num_characters = 0;
4020Sstevel@tonic-gate
403*9095SZhigang.Lu@Sun.COM pipe_attr = ehci_polledp->ehci_polled_input_pipe_handle->
404*9095SZhigang.Lu@Sun.COM p_ep.bmAttributes & USB_EP_ATTR_MASK;
405*9095SZhigang.Lu@Sun.COM
406*9095SZhigang.Lu@Sun.COM if (pipe_attr == USB_EP_ATTR_BULK) {
407*9095SZhigang.Lu@Sun.COM ehci_polled_insert_bulk_qtd(ehci_polledp);
408*9095SZhigang.Lu@Sun.COM }
409*9095SZhigang.Lu@Sun.COM
4100Sstevel@tonic-gate intr = ((Get_OpReg(ehci_status) & Get_OpReg(ehci_interrupt)) &
4110Sstevel@tonic-gate (EHCI_INTR_FRAME_LIST_ROLLOVER |
4120Sstevel@tonic-gate EHCI_INTR_USB | EHCI_INTR_USB_ERROR));
4130Sstevel@tonic-gate
4140Sstevel@tonic-gate /*
4150Sstevel@tonic-gate * Check whether any frame list rollover interrupt is pending
4160Sstevel@tonic-gate * and if it is pending, process this interrupt.
4170Sstevel@tonic-gate */
4180Sstevel@tonic-gate if (intr & EHCI_INTR_FRAME_LIST_ROLLOVER) {
4190Sstevel@tonic-gate /* Check any frame list rollover interrupt is pending */
4200Sstevel@tonic-gate ehci_handle_frame_list_rollover(ehcip);
4210Sstevel@tonic-gate ehci_polled_finish_interrupt(ehcip,
4220Sstevel@tonic-gate EHCI_INTR_FRAME_LIST_ROLLOVER);
4230Sstevel@tonic-gate }
4240Sstevel@tonic-gate
425*9095SZhigang.Lu@Sun.COM /* Process any QTD's on the active interrupt qtd list */
426*9095SZhigang.Lu@Sun.COM *num_characters =
427*9095SZhigang.Lu@Sun.COM ehci_polled_process_active_intr_qtd_list(ehci_polledp);
428*9095SZhigang.Lu@Sun.COM
429*9095SZhigang.Lu@Sun.COM #ifndef lint
430*9095SZhigang.Lu@Sun.COM _NOTE(COMPETING_THREADS_NOW);
431*9095SZhigang.Lu@Sun.COM #endif
432*9095SZhigang.Lu@Sun.COM
433*9095SZhigang.Lu@Sun.COM return (USB_SUCCESS);
434*9095SZhigang.Lu@Sun.COM }
435*9095SZhigang.Lu@Sun.COM
436*9095SZhigang.Lu@Sun.COM
437*9095SZhigang.Lu@Sun.COM /*
438*9095SZhigang.Lu@Sun.COM * ehci_hcdi_polled_output_init:
439*9095SZhigang.Lu@Sun.COM *
440*9095SZhigang.Lu@Sun.COM * This is the initialization routine for handling the USB serial output
441*9095SZhigang.Lu@Sun.COM * in POLLED mode. This routine is not called from POLLED mode, so
442*9095SZhigang.Lu@Sun.COM * it is OK to acquire mutexes.
443*9095SZhigang.Lu@Sun.COM */
444*9095SZhigang.Lu@Sun.COM int
ehci_hcdi_polled_output_init(usba_pipe_handle_data_t * ph,usb_console_info_impl_t * console_output_info)445*9095SZhigang.Lu@Sun.COM ehci_hcdi_polled_output_init(
446*9095SZhigang.Lu@Sun.COM usba_pipe_handle_data_t *ph,
447*9095SZhigang.Lu@Sun.COM usb_console_info_impl_t *console_output_info)
448*9095SZhigang.Lu@Sun.COM {
449*9095SZhigang.Lu@Sun.COM ehci_polled_t *ehci_polledp;
450*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip;
451*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp;
452*9095SZhigang.Lu@Sun.COM int ret;
453*9095SZhigang.Lu@Sun.COM
454*9095SZhigang.Lu@Sun.COM ehcip = ehci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
455*9095SZhigang.Lu@Sun.COM
456*9095SZhigang.Lu@Sun.COM /*
457*9095SZhigang.Lu@Sun.COM * Grab the ehci_int_mutex so that things don't change on us
458*9095SZhigang.Lu@Sun.COM * if an interrupt comes in.
459*9095SZhigang.Lu@Sun.COM */
460*9095SZhigang.Lu@Sun.COM mutex_enter(&ehcip->ehci_int_mutex);
461*9095SZhigang.Lu@Sun.COM
462*9095SZhigang.Lu@Sun.COM ret = ehci_polled_init(ph, ehcip, console_output_info);
463*9095SZhigang.Lu@Sun.COM
464*9095SZhigang.Lu@Sun.COM if (ret != USB_SUCCESS) {
465*9095SZhigang.Lu@Sun.COM
466*9095SZhigang.Lu@Sun.COM /* Allow interrupts to continue */
467*9095SZhigang.Lu@Sun.COM mutex_exit(&ehcip->ehci_int_mutex);
468*9095SZhigang.Lu@Sun.COM
469*9095SZhigang.Lu@Sun.COM return (ret);
470*9095SZhigang.Lu@Sun.COM }
471*9095SZhigang.Lu@Sun.COM
472*9095SZhigang.Lu@Sun.COM ehci_polledp = (ehci_polled_t *)console_output_info->uci_private;
473*9095SZhigang.Lu@Sun.COM /*
474*9095SZhigang.Lu@Sun.COM * Mark the structure so that if we are using it, we don't free
475*9095SZhigang.Lu@Sun.COM * the structures if one of them is unplugged.
476*9095SZhigang.Lu@Sun.COM */
477*9095SZhigang.Lu@Sun.COM ehci_polledp->ehci_polled_flags |= POLLED_OUTPUT_MODE;
478*9095SZhigang.Lu@Sun.COM
479*9095SZhigang.Lu@Sun.COM /*
480*9095SZhigang.Lu@Sun.COM * Insert the Endpoint Descriptor to appropriate endpoint list.
481*9095SZhigang.Lu@Sun.COM */
482*9095SZhigang.Lu@Sun.COM pp = (ehci_pipe_private_t *)ehci_polledp->
483*9095SZhigang.Lu@Sun.COM ehci_polled_input_pipe_handle->p_hcd_private;
484*9095SZhigang.Lu@Sun.COM ehci_polled_insert_async_qh(ehcip, pp);
485*9095SZhigang.Lu@Sun.COM
486*9095SZhigang.Lu@Sun.COM /*
487*9095SZhigang.Lu@Sun.COM * This is a software workaround to fix schizo hardware bug.
488*9095SZhigang.Lu@Sun.COM * Existence of "no-prom-cdma-sync" property means consistent
489*9095SZhigang.Lu@Sun.COM * dma sync should not be done while in prom or polled mode.
490*9095SZhigang.Lu@Sun.COM */
491*9095SZhigang.Lu@Sun.COM if (ddi_prop_exists(DDI_DEV_T_ANY, ehcip->ehci_dip,
492*9095SZhigang.Lu@Sun.COM DDI_PROP_NOTPROM, "no-prom-cdma-sync")) {
493*9095SZhigang.Lu@Sun.COM ehci_polledp->ehci_polled_no_sync_flag = B_TRUE;
494*9095SZhigang.Lu@Sun.COM }
495*9095SZhigang.Lu@Sun.COM
496*9095SZhigang.Lu@Sun.COM /* Allow interrupts to continue */
497*9095SZhigang.Lu@Sun.COM mutex_exit(&ehcip->ehci_int_mutex);
498*9095SZhigang.Lu@Sun.COM
499*9095SZhigang.Lu@Sun.COM return (USB_SUCCESS);
500*9095SZhigang.Lu@Sun.COM }
501*9095SZhigang.Lu@Sun.COM
502*9095SZhigang.Lu@Sun.COM
503*9095SZhigang.Lu@Sun.COM /*
504*9095SZhigang.Lu@Sun.COM * ehci_hcdi_polled_output_fini:
505*9095SZhigang.Lu@Sun.COM */
506*9095SZhigang.Lu@Sun.COM int
ehci_hcdi_polled_output_fini(usb_console_info_impl_t * info)507*9095SZhigang.Lu@Sun.COM ehci_hcdi_polled_output_fini(usb_console_info_impl_t *info)
508*9095SZhigang.Lu@Sun.COM {
509*9095SZhigang.Lu@Sun.COM ehci_polled_t *ehci_polledp;
510*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip;
511*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp;
512*9095SZhigang.Lu@Sun.COM int ret;
513*9095SZhigang.Lu@Sun.COM
514*9095SZhigang.Lu@Sun.COM ehci_polledp = (ehci_polled_t *)info->uci_private;
515*9095SZhigang.Lu@Sun.COM
516*9095SZhigang.Lu@Sun.COM ehcip = ehci_polledp->ehci_polled_ehcip;
517*9095SZhigang.Lu@Sun.COM
518*9095SZhigang.Lu@Sun.COM mutex_enter(&ehcip->ehci_int_mutex);
519*9095SZhigang.Lu@Sun.COM
520*9095SZhigang.Lu@Sun.COM /* Remove the Endpoint Descriptor. */
521*9095SZhigang.Lu@Sun.COM pp = (ehci_pipe_private_t *)ehci_polledp->
522*9095SZhigang.Lu@Sun.COM ehci_polled_input_pipe_handle->p_hcd_private;
523*9095SZhigang.Lu@Sun.COM ehci_polled_remove_async_qh(ehcip, pp);
524*9095SZhigang.Lu@Sun.COM
525*9095SZhigang.Lu@Sun.COM /*
526*9095SZhigang.Lu@Sun.COM * Reset the POLLED_INPUT_MODE flag so that we can tell if
527*9095SZhigang.Lu@Sun.COM * this structure is in use in the ehci_polled_fini routine.
528*9095SZhigang.Lu@Sun.COM */
529*9095SZhigang.Lu@Sun.COM ehci_polledp->ehci_polled_flags &= ~POLLED_OUTPUT_MODE;
530*9095SZhigang.Lu@Sun.COM
531*9095SZhigang.Lu@Sun.COM ret = ehci_polled_fini(ehci_polledp);
532*9095SZhigang.Lu@Sun.COM
533*9095SZhigang.Lu@Sun.COM info->uci_private = NULL;
534*9095SZhigang.Lu@Sun.COM
535*9095SZhigang.Lu@Sun.COM mutex_exit(&ehcip->ehci_int_mutex);
536*9095SZhigang.Lu@Sun.COM
537*9095SZhigang.Lu@Sun.COM return (ret);
538*9095SZhigang.Lu@Sun.COM }
539*9095SZhigang.Lu@Sun.COM
540*9095SZhigang.Lu@Sun.COM
541*9095SZhigang.Lu@Sun.COM /*
542*9095SZhigang.Lu@Sun.COM * ehci_hcdi_polled_output_enter:
543*9095SZhigang.Lu@Sun.COM *
544*9095SZhigang.Lu@Sun.COM * everything is done in input enter
545*9095SZhigang.Lu@Sun.COM */
546*9095SZhigang.Lu@Sun.COM /*ARGSUSED*/
547*9095SZhigang.Lu@Sun.COM int
ehci_hcdi_polled_output_enter(usb_console_info_impl_t * info)548*9095SZhigang.Lu@Sun.COM ehci_hcdi_polled_output_enter(usb_console_info_impl_t *info)
549*9095SZhigang.Lu@Sun.COM {
550*9095SZhigang.Lu@Sun.COM return (USB_SUCCESS);
551*9095SZhigang.Lu@Sun.COM }
552*9095SZhigang.Lu@Sun.COM
553*9095SZhigang.Lu@Sun.COM
554*9095SZhigang.Lu@Sun.COM /*
555*9095SZhigang.Lu@Sun.COM * ehci_hcdi_polled_output_exit:
556*9095SZhigang.Lu@Sun.COM *
557*9095SZhigang.Lu@Sun.COM * everything is done in input exit
558*9095SZhigang.Lu@Sun.COM */
559*9095SZhigang.Lu@Sun.COM /*ARGSUSED*/
560*9095SZhigang.Lu@Sun.COM int
ehci_hcdi_polled_output_exit(usb_console_info_impl_t * info)561*9095SZhigang.Lu@Sun.COM ehci_hcdi_polled_output_exit(usb_console_info_impl_t *info)
562*9095SZhigang.Lu@Sun.COM {
563*9095SZhigang.Lu@Sun.COM return (USB_SUCCESS);
564*9095SZhigang.Lu@Sun.COM }
565*9095SZhigang.Lu@Sun.COM
566*9095SZhigang.Lu@Sun.COM
567*9095SZhigang.Lu@Sun.COM /*
568*9095SZhigang.Lu@Sun.COM * ehci_hcdi_polled_write:
569*9095SZhigang.Lu@Sun.COM * Put a key character.
570*9095SZhigang.Lu@Sun.COM */
571*9095SZhigang.Lu@Sun.COM int
ehci_hcdi_polled_write(usb_console_info_impl_t * info,uchar_t * buf,uint_t num_characters,uint_t * num_characters_written)572*9095SZhigang.Lu@Sun.COM ehci_hcdi_polled_write(usb_console_info_impl_t *info, uchar_t *buf,
573*9095SZhigang.Lu@Sun.COM uint_t num_characters, uint_t *num_characters_written)
574*9095SZhigang.Lu@Sun.COM {
575*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip;
576*9095SZhigang.Lu@Sun.COM ehci_polled_t *ehci_polledp;
577*9095SZhigang.Lu@Sun.COM ehci_trans_wrapper_t *tw;
578*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp;
579*9095SZhigang.Lu@Sun.COM usba_pipe_handle_data_t *ph;
580*9095SZhigang.Lu@Sun.COM int intr;
581*9095SZhigang.Lu@Sun.COM
582*9095SZhigang.Lu@Sun.COM #ifndef lint
583*9095SZhigang.Lu@Sun.COM _NOTE(NO_COMPETING_THREADS_NOW);
584*9095SZhigang.Lu@Sun.COM #endif
585*9095SZhigang.Lu@Sun.COM ehci_polledp = (ehci_polled_t *)info->uci_private;
586*9095SZhigang.Lu@Sun.COM ehcip = ehci_polledp->ehci_polled_ehcip;
587*9095SZhigang.Lu@Sun.COM ph = ehci_polledp->ehci_polled_input_pipe_handle;
588*9095SZhigang.Lu@Sun.COM pp = (ehci_pipe_private_t *)ph->p_hcd_private;
589*9095SZhigang.Lu@Sun.COM
590*9095SZhigang.Lu@Sun.COM /* Disable all list processing */
591*9095SZhigang.Lu@Sun.COM Set_OpReg(ehci_command, Get_OpReg(ehci_command) &
592*9095SZhigang.Lu@Sun.COM ~(EHCI_CMD_ASYNC_SCHED_ENABLE |
593*9095SZhigang.Lu@Sun.COM EHCI_CMD_PERIODIC_SCHED_ENABLE));
594*9095SZhigang.Lu@Sun.COM
595*9095SZhigang.Lu@Sun.COM /* Wait for few milliseconds */
596*9095SZhigang.Lu@Sun.COM drv_usecwait(EHCI_POLLED_TIMEWAIT);
597*9095SZhigang.Lu@Sun.COM
598*9095SZhigang.Lu@Sun.COM tw = pp->pp_tw_head;
599*9095SZhigang.Lu@Sun.COM ASSERT(tw != NULL);
600*9095SZhigang.Lu@Sun.COM
601*9095SZhigang.Lu@Sun.COM /* copy transmit buffer */
602*9095SZhigang.Lu@Sun.COM if (num_characters > POLLED_RAW_BUF_SIZE) {
603*9095SZhigang.Lu@Sun.COM cmn_err(CE_NOTE, "polled write size %d bigger than %d",
604*9095SZhigang.Lu@Sun.COM num_characters, POLLED_RAW_BUF_SIZE);
605*9095SZhigang.Lu@Sun.COM num_characters = POLLED_RAW_BUF_SIZE;
606*9095SZhigang.Lu@Sun.COM }
607*9095SZhigang.Lu@Sun.COM tw->tw_length = num_characters;
608*9095SZhigang.Lu@Sun.COM ddi_rep_put8(tw->tw_accesshandle,
609*9095SZhigang.Lu@Sun.COM buf, (uint8_t *)tw->tw_buf,
610*9095SZhigang.Lu@Sun.COM tw->tw_length, DDI_DEV_AUTOINCR);
611*9095SZhigang.Lu@Sun.COM Sync_IO_Buffer_for_device(tw->tw_dmahandle, tw->tw_length);
612*9095SZhigang.Lu@Sun.COM
613*9095SZhigang.Lu@Sun.COM ehci_polled_insert_bulk_qtd(ehci_polledp);
614*9095SZhigang.Lu@Sun.COM
615*9095SZhigang.Lu@Sun.COM /* Enable async list processing */
616*9095SZhigang.Lu@Sun.COM Set_OpReg(ehci_command, (Get_OpReg(ehci_command) |
617*9095SZhigang.Lu@Sun.COM EHCI_CMD_ASYNC_SCHED_ENABLE));
618*9095SZhigang.Lu@Sun.COM
619*9095SZhigang.Lu@Sun.COM /* Wait for few milliseconds */
620*9095SZhigang.Lu@Sun.COM drv_usecwait(EHCI_POLLED_TIMEWAIT);
621*9095SZhigang.Lu@Sun.COM
622*9095SZhigang.Lu@Sun.COM while (!((Get_OpReg(ehci_status)) & (EHCI_INTR_USB
623*9095SZhigang.Lu@Sun.COM |EHCI_INTR_FRAME_LIST_ROLLOVER | EHCI_INTR_USB_ERROR))) {
624*9095SZhigang.Lu@Sun.COM #ifndef __sparc
625*9095SZhigang.Lu@Sun.COM invalidate_cache();
626*9095SZhigang.Lu@Sun.COM #else
627*9095SZhigang.Lu@Sun.COM ;
628*9095SZhigang.Lu@Sun.COM #endif
629*9095SZhigang.Lu@Sun.COM }
630*9095SZhigang.Lu@Sun.COM
631*9095SZhigang.Lu@Sun.COM intr = (Get_OpReg(ehci_status)) &
632*9095SZhigang.Lu@Sun.COM (EHCI_INTR_FRAME_LIST_ROLLOVER |
633*9095SZhigang.Lu@Sun.COM EHCI_INTR_USB | EHCI_INTR_USB_ERROR);
634*9095SZhigang.Lu@Sun.COM
635*9095SZhigang.Lu@Sun.COM /*
636*9095SZhigang.Lu@Sun.COM * Check whether any frame list rollover interrupt is pending
637*9095SZhigang.Lu@Sun.COM * and if it is pending, process this interrupt.
638*9095SZhigang.Lu@Sun.COM */
639*9095SZhigang.Lu@Sun.COM if (intr & EHCI_INTR_FRAME_LIST_ROLLOVER) {
640*9095SZhigang.Lu@Sun.COM
641*9095SZhigang.Lu@Sun.COM ehci_handle_frame_list_rollover(ehcip);
642*9095SZhigang.Lu@Sun.COM ehci_polled_finish_interrupt(ehcip,
643*9095SZhigang.Lu@Sun.COM EHCI_INTR_FRAME_LIST_ROLLOVER);
644*9095SZhigang.Lu@Sun.COM }
645*9095SZhigang.Lu@Sun.COM
6460Sstevel@tonic-gate /* Check for any USB transaction completion notification */
6470Sstevel@tonic-gate if (intr & (EHCI_INTR_USB | EHCI_INTR_USB_ERROR)) {
648*9095SZhigang.Lu@Sun.COM
649*9095SZhigang.Lu@Sun.COM (void) ehci_polled_process_active_intr_qtd_list(ehci_polledp);
6500Sstevel@tonic-gate
651*9095SZhigang.Lu@Sun.COM /* Acknowledge the USB and USB error interrupt */
652*9095SZhigang.Lu@Sun.COM ehci_polled_finish_interrupt(ehcip,
653*9095SZhigang.Lu@Sun.COM intr & (EHCI_INTR_USB | EHCI_INTR_USB_ERROR));
654*9095SZhigang.Lu@Sun.COM
6550Sstevel@tonic-gate }
6560Sstevel@tonic-gate
657*9095SZhigang.Lu@Sun.COM *num_characters_written = num_characters;
658*9095SZhigang.Lu@Sun.COM
6590Sstevel@tonic-gate #ifndef lint
6600Sstevel@tonic-gate _NOTE(COMPETING_THREADS_NOW);
6610Sstevel@tonic-gate #endif
6620Sstevel@tonic-gate
6630Sstevel@tonic-gate return (USB_SUCCESS);
6640Sstevel@tonic-gate }
6650Sstevel@tonic-gate
6660Sstevel@tonic-gate
6670Sstevel@tonic-gate /*
6680Sstevel@tonic-gate * Internal Functions
6690Sstevel@tonic-gate */
6700Sstevel@tonic-gate
6710Sstevel@tonic-gate /*
6720Sstevel@tonic-gate * Polled initialization routines
6730Sstevel@tonic-gate */
6740Sstevel@tonic-gate
6750Sstevel@tonic-gate
6760Sstevel@tonic-gate /*
6770Sstevel@tonic-gate * ehci_polled_init:
6780Sstevel@tonic-gate *
6790Sstevel@tonic-gate * Initialize generic information Uthat is needed to provide USB/POLLED
6800Sstevel@tonic-gate * support.
6810Sstevel@tonic-gate */
6820Sstevel@tonic-gate static int
ehci_polled_init(usba_pipe_handle_data_t * ph,ehci_state_t * ehcip,usb_console_info_impl_t * console_info)6830Sstevel@tonic-gate ehci_polled_init(
6840Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
6850Sstevel@tonic-gate ehci_state_t *ehcip,
6860Sstevel@tonic-gate usb_console_info_impl_t *console_info)
6870Sstevel@tonic-gate {
6880Sstevel@tonic-gate ehci_polled_t *ehci_polledp;
6890Sstevel@tonic-gate ehci_pipe_private_t *pp;
6900Sstevel@tonic-gate ehci_qtd_t *qtd;
691*9095SZhigang.Lu@Sun.COM int pipe_attr;
6920Sstevel@tonic-gate
6930Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
6940Sstevel@tonic-gate
6950Sstevel@tonic-gate /*
6960Sstevel@tonic-gate * We have already initialized this structure. If the structure
6970Sstevel@tonic-gate * has already been initialized, then we don't need to redo it.
6980Sstevel@tonic-gate */
6990Sstevel@tonic-gate if (console_info->uci_private) {
7000Sstevel@tonic-gate
7010Sstevel@tonic-gate return (USB_SUCCESS);
7020Sstevel@tonic-gate }
7030Sstevel@tonic-gate
7040Sstevel@tonic-gate /* Allocate and intitialize a state structure */
7050Sstevel@tonic-gate ehci_polledp = (ehci_polled_t *)
7060Sstevel@tonic-gate kmem_zalloc(sizeof (ehci_polled_t), KM_SLEEP);
7070Sstevel@tonic-gate
7080Sstevel@tonic-gate console_info->uci_private = (usb_console_info_private_t)ehci_polledp;
7090Sstevel@tonic-gate
7100Sstevel@tonic-gate /*
7110Sstevel@tonic-gate * Store away the ehcip so that we can get to it when we are in
7120Sstevel@tonic-gate * POLLED mode. We don't want to have to call ehci_obtain_state
7130Sstevel@tonic-gate * every time we want to access this structure.
7140Sstevel@tonic-gate */
7150Sstevel@tonic-gate ehci_polledp->ehci_polled_ehcip = ehcip;
7160Sstevel@tonic-gate /*
7170Sstevel@tonic-gate * Save usb device and endpoint number information from the usb
7180Sstevel@tonic-gate * pipe handle.
7190Sstevel@tonic-gate */
7200Sstevel@tonic-gate mutex_enter(&ph->p_mutex);
7210Sstevel@tonic-gate ehci_polledp->ehci_polled_usb_dev = ph->p_usba_device;
7220Sstevel@tonic-gate ehci_polledp->ehci_polled_ep_addr = ph->p_ep.bEndpointAddress;
7230Sstevel@tonic-gate mutex_exit(&ph->p_mutex);
7240Sstevel@tonic-gate
7250Sstevel@tonic-gate /*
7260Sstevel@tonic-gate * Allocate memory to make duplicate of original usb pipe handle.
7270Sstevel@tonic-gate */
7280Sstevel@tonic-gate ehci_polledp->ehci_polled_input_pipe_handle =
7290Sstevel@tonic-gate kmem_zalloc(sizeof (usba_pipe_handle_data_t), KM_SLEEP);
7300Sstevel@tonic-gate
7310Sstevel@tonic-gate /*
7320Sstevel@tonic-gate * Copy the USB handle into the new pipe handle. Also
7330Sstevel@tonic-gate * create new lock for the new pipe handle.
7340Sstevel@tonic-gate */
7350Sstevel@tonic-gate bcopy((void *)ph,
7360Sstevel@tonic-gate (void *)ehci_polledp->ehci_polled_input_pipe_handle,
7370Sstevel@tonic-gate sizeof (usba_pipe_handle_data_t));
7380Sstevel@tonic-gate
7390Sstevel@tonic-gate /*
7400Sstevel@tonic-gate * uint64_t typecast to make sure amd64 can compile
7410Sstevel@tonic-gate */
7420Sstevel@tonic-gate mutex_init(&ehci_polledp->ehci_polled_input_pipe_handle->p_mutex,
743693Sgovinda NULL, MUTEX_DRIVER, DDI_INTR_PRI(ehcip->ehci_intr_pri));
7440Sstevel@tonic-gate
7450Sstevel@tonic-gate /*
7460Sstevel@tonic-gate * Create a new ehci pipe private structure
7470Sstevel@tonic-gate */
7480Sstevel@tonic-gate pp = (ehci_pipe_private_t *)
7490Sstevel@tonic-gate kmem_zalloc(sizeof (ehci_pipe_private_t), KM_SLEEP);
7500Sstevel@tonic-gate
7510Sstevel@tonic-gate /*
7520Sstevel@tonic-gate * Store the pointer in the pipe handle. This structure was also
7530Sstevel@tonic-gate * just allocated.
7540Sstevel@tonic-gate */
7550Sstevel@tonic-gate mutex_enter(&ehci_polledp->ehci_polled_input_pipe_handle->p_mutex);
7560Sstevel@tonic-gate
7570Sstevel@tonic-gate ehci_polledp->ehci_polled_input_pipe_handle->
7580Sstevel@tonic-gate p_hcd_private = (usb_opaque_t)pp;
7590Sstevel@tonic-gate
7600Sstevel@tonic-gate mutex_exit(&ehci_polledp->ehci_polled_input_pipe_handle->p_mutex);
7610Sstevel@tonic-gate
7620Sstevel@tonic-gate /*
7630Sstevel@tonic-gate * Store a pointer to the pipe handle. This structure was just
7640Sstevel@tonic-gate * allocated and it is not in use yet. The locking is there to
7650Sstevel@tonic-gate * satisfy warlock.
7660Sstevel@tonic-gate */
7670Sstevel@tonic-gate mutex_enter(&ph->p_mutex);
7680Sstevel@tonic-gate
7690Sstevel@tonic-gate bcopy(&ph->p_policy, &pp->pp_policy, sizeof (usb_pipe_policy_t));
7700Sstevel@tonic-gate
7710Sstevel@tonic-gate mutex_exit(&ph->p_mutex);
7720Sstevel@tonic-gate
7730Sstevel@tonic-gate pp->pp_pipe_handle = ehci_polledp->ehci_polled_input_pipe_handle;
7740Sstevel@tonic-gate
7750Sstevel@tonic-gate /*
7760Sstevel@tonic-gate * Allocate a dummy for the interrupt table. This dummy will be
7770Sstevel@tonic-gate * put into the action when we switch interrupt tables during
7780Sstevel@tonic-gate * ehci_hcdi_polled_enter. Dummy is placed on the unused lattice
7790Sstevel@tonic-gate * entries. When the QH is allocated we will replace dummy QH by
7800Sstevel@tonic-gate * valid interrupt QH in one or more locations in the interrupt
7810Sstevel@tonic-gate * lattice depending on the requested polling interval. Also we
7820Sstevel@tonic-gate * will hang a dummy QTD to the QH & dummy QTD is used to indicate
7830Sstevel@tonic-gate * the end of the QTD chain.
7840Sstevel@tonic-gate */
7850Sstevel@tonic-gate ehci_polledp->ehci_polled_dummy_qh =
7860Sstevel@tonic-gate ehci_alloc_qh(ehcip, NULL, EHCI_POLLED_MODE_FLAG);
7870Sstevel@tonic-gate
7880Sstevel@tonic-gate if (ehci_polledp->ehci_polled_dummy_qh == NULL) {
7890Sstevel@tonic-gate
7900Sstevel@tonic-gate return (USB_NO_RESOURCES);
7910Sstevel@tonic-gate }
7920Sstevel@tonic-gate
7930Sstevel@tonic-gate /*
794*9095SZhigang.Lu@Sun.COM * Allocate the endpoint. This QH will be inserted in
795*9095SZhigang.Lu@Sun.COM * to the lattice chain for the device. This endpoint
7960Sstevel@tonic-gate * will have the QTDs hanging off of it for the processing.
7970Sstevel@tonic-gate */
7980Sstevel@tonic-gate ehci_polledp->ehci_polled_qh = ehci_alloc_qh(
7990Sstevel@tonic-gate ehcip, ph, EHCI_POLLED_MODE_FLAG);
8000Sstevel@tonic-gate
8010Sstevel@tonic-gate if (ehci_polledp->ehci_polled_qh == NULL) {
8020Sstevel@tonic-gate
8030Sstevel@tonic-gate return (USB_NO_RESOURCES);
8040Sstevel@tonic-gate }
8050Sstevel@tonic-gate
8060Sstevel@tonic-gate /* Set the state of pipe as idle */
8070Sstevel@tonic-gate pp->pp_state = EHCI_PIPE_STATE_IDLE;
8080Sstevel@tonic-gate
8090Sstevel@tonic-gate /* Set polled mode flag */
8100Sstevel@tonic-gate pp->pp_flag = EHCI_POLLED_MODE_FLAG;
8110Sstevel@tonic-gate
8120Sstevel@tonic-gate /* Insert the endpoint onto the pipe handle */
8130Sstevel@tonic-gate pp->pp_qh = ehci_polledp->ehci_polled_qh;
8140Sstevel@tonic-gate
815*9095SZhigang.Lu@Sun.COM pipe_attr = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
816*9095SZhigang.Lu@Sun.COM
817*9095SZhigang.Lu@Sun.COM switch (pipe_attr) {
818*9095SZhigang.Lu@Sun.COM case USB_EP_ATTR_INTR:
819*9095SZhigang.Lu@Sun.COM /*
820*9095SZhigang.Lu@Sun.COM * Set soft interrupt handler flag in the normal mode usb
821*9095SZhigang.Lu@Sun.COM * pipe handle.
822*9095SZhigang.Lu@Sun.COM */
823*9095SZhigang.Lu@Sun.COM mutex_enter(&ph->p_mutex);
824*9095SZhigang.Lu@Sun.COM ph->p_spec_flag |= USBA_PH_FLAG_USE_SOFT_INTR;
825*9095SZhigang.Lu@Sun.COM mutex_exit(&ph->p_mutex);
8260Sstevel@tonic-gate
827*9095SZhigang.Lu@Sun.COM /*
828*9095SZhigang.Lu@Sun.COM * Insert a Interrupt polling request onto the endpoint.
829*9095SZhigang.Lu@Sun.COM *
830*9095SZhigang.Lu@Sun.COM * There will now be two QTDs on the QH, one is the dummy QTD
831*9095SZhigang.Lu@Sun.COM * that was allocated above in the ehci_alloc_qh and this
832*9095SZhigang.Lu@Sun.COM * new one.
833*9095SZhigang.Lu@Sun.COM */
834*9095SZhigang.Lu@Sun.COM if ((ehci_start_periodic_pipe_polling(ehcip,
835*9095SZhigang.Lu@Sun.COM ehci_polledp->ehci_polled_input_pipe_handle,
836*9095SZhigang.Lu@Sun.COM NULL, USB_FLAGS_SLEEP)) != USB_SUCCESS) {
8370Sstevel@tonic-gate
838*9095SZhigang.Lu@Sun.COM return (USB_NO_RESOURCES);
839*9095SZhigang.Lu@Sun.COM }
840*9095SZhigang.Lu@Sun.COM /* Get the given new interrupt qtd */
841*9095SZhigang.Lu@Sun.COM qtd = (ehci_qtd_t *)(ehci_qtd_iommu_to_cpu(ehcip,
842*9095SZhigang.Lu@Sun.COM (Get_QH(pp->pp_qh->qh_next_qtd) & EHCI_QH_NEXT_QTD_PTR)));
8430Sstevel@tonic-gate
844*9095SZhigang.Lu@Sun.COM /* Insert this qtd into active interrupt QTD list */
845*9095SZhigang.Lu@Sun.COM ehci_polled_insert_qtd_into_active_intr_qtd_list(ehci_polledp,
846*9095SZhigang.Lu@Sun.COM qtd);
847*9095SZhigang.Lu@Sun.COM break;
848*9095SZhigang.Lu@Sun.COM case USB_EP_ATTR_BULK:
849*9095SZhigang.Lu@Sun.COM if ((ehci_polled_create_tw(ehci_polledp,
850*9095SZhigang.Lu@Sun.COM ehci_polledp->ehci_polled_input_pipe_handle,
851*9095SZhigang.Lu@Sun.COM USB_FLAGS_SLEEP)) != USB_SUCCESS) {
8520Sstevel@tonic-gate
853*9095SZhigang.Lu@Sun.COM return (USB_NO_RESOURCES);
854*9095SZhigang.Lu@Sun.COM }
855*9095SZhigang.Lu@Sun.COM break;
856*9095SZhigang.Lu@Sun.COM default:
857*9095SZhigang.Lu@Sun.COM return (USB_FAILURE);
858*9095SZhigang.Lu@Sun.COM }
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 deinitialization routines
8660Sstevel@tonic-gate */
8670Sstevel@tonic-gate
8680Sstevel@tonic-gate
8690Sstevel@tonic-gate /*
8700Sstevel@tonic-gate * ehci_polled_fini:
8710Sstevel@tonic-gate */
8720Sstevel@tonic-gate static int
ehci_polled_fini(ehci_polled_t * ehci_polledp)8730Sstevel@tonic-gate ehci_polled_fini(ehci_polled_t *ehci_polledp)
8740Sstevel@tonic-gate {
8750Sstevel@tonic-gate ehci_state_t *ehcip = ehci_polledp->ehci_polled_ehcip;
8760Sstevel@tonic-gate ehci_pipe_private_t *pp;
8770Sstevel@tonic-gate
8780Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
8790Sstevel@tonic-gate
8800Sstevel@tonic-gate /* If the structure is already in use, then don't free it */
8810Sstevel@tonic-gate if (ehci_polledp->ehci_polled_flags & POLLED_INPUT_MODE) {
8820Sstevel@tonic-gate
8830Sstevel@tonic-gate return (USB_SUCCESS);
8840Sstevel@tonic-gate }
8850Sstevel@tonic-gate
8860Sstevel@tonic-gate pp = (ehci_pipe_private_t *)
8870Sstevel@tonic-gate ehci_polledp->ehci_polled_input_pipe_handle->p_hcd_private;
8880Sstevel@tonic-gate
8890Sstevel@tonic-gate /* Deallocate all the pre-allocated interrupt requests */
8900Sstevel@tonic-gate ehci_handle_outstanding_requests(ehcip, pp);
8910Sstevel@tonic-gate
8920Sstevel@tonic-gate /*
8930Sstevel@tonic-gate * Traverse the list of QTD's on this endpoint and these QTD's
8940Sstevel@tonic-gate * have outstanding transfer requests. Since list processing
8950Sstevel@tonic-gate * is stopped, these QTDs can be deallocated.
8960Sstevel@tonic-gate */
8970Sstevel@tonic-gate ehci_polled_traverse_qtds(ehci_polledp, pp->pp_pipe_handle);
8980Sstevel@tonic-gate
8990Sstevel@tonic-gate /* Free DMA resources */
9000Sstevel@tonic-gate ehci_free_dma_resources(ehcip, pp->pp_pipe_handle);
9010Sstevel@tonic-gate
9020Sstevel@tonic-gate /*
9030Sstevel@tonic-gate * Deallocate the endpoint descriptors that we allocated
9040Sstevel@tonic-gate * with ehci_alloc_qh.
9050Sstevel@tonic-gate */
9060Sstevel@tonic-gate if (ehci_polledp->ehci_polled_dummy_qh) {
9070Sstevel@tonic-gate ehci_deallocate_qh(ehcip, ehci_polledp->ehci_polled_dummy_qh);
9080Sstevel@tonic-gate }
9090Sstevel@tonic-gate
9100Sstevel@tonic-gate if (ehci_polledp->ehci_polled_qh) {
9110Sstevel@tonic-gate ehci_deallocate_qh(ehcip, ehci_polledp->ehci_polled_qh);
9120Sstevel@tonic-gate }
9130Sstevel@tonic-gate
9140Sstevel@tonic-gate mutex_destroy(&ehci_polledp->ehci_polled_input_pipe_handle->p_mutex);
9150Sstevel@tonic-gate
9160Sstevel@tonic-gate /*
9170Sstevel@tonic-gate * Destroy everything about the pipe that we allocated in
9180Sstevel@tonic-gate * ehci_polled_duplicate_pipe_handle
9190Sstevel@tonic-gate */
9200Sstevel@tonic-gate kmem_free(pp, sizeof (ehci_pipe_private_t));
9210Sstevel@tonic-gate
9220Sstevel@tonic-gate kmem_free(ehci_polledp->ehci_polled_input_pipe_handle,
9230Sstevel@tonic-gate sizeof (usba_pipe_handle_data_t));
9240Sstevel@tonic-gate
9250Sstevel@tonic-gate /*
9260Sstevel@tonic-gate * We use this field to determine if a QTD is for input or not,
9270Sstevel@tonic-gate * so NULL the pointer so we don't check deallocated data.
9280Sstevel@tonic-gate */
9290Sstevel@tonic-gate ehci_polledp->ehci_polled_input_pipe_handle = NULL;
9300Sstevel@tonic-gate
9310Sstevel@tonic-gate /*
9320Sstevel@tonic-gate * Finally, free off the structure that we use to keep track
9330Sstevel@tonic-gate * of all this.
9340Sstevel@tonic-gate */
9350Sstevel@tonic-gate kmem_free(ehci_polledp, sizeof (ehci_polled_t));
9360Sstevel@tonic-gate
9370Sstevel@tonic-gate return (USB_SUCCESS);
9380Sstevel@tonic-gate }
9390Sstevel@tonic-gate
9400Sstevel@tonic-gate
9410Sstevel@tonic-gate /*
9420Sstevel@tonic-gate * Polled save state routines
9430Sstevel@tonic-gate */
9440Sstevel@tonic-gate
9450Sstevel@tonic-gate
9460Sstevel@tonic-gate /*
9470Sstevel@tonic-gate * ehci_polled_save_state:
9480Sstevel@tonic-gate */
9490Sstevel@tonic-gate static void
ehci_polled_save_state(ehci_polled_t * ehci_polledp)9500Sstevel@tonic-gate ehci_polled_save_state(ehci_polled_t *ehci_polledp)
9510Sstevel@tonic-gate {
9520Sstevel@tonic-gate int i;
9530Sstevel@tonic-gate ehci_state_t *ehcip;
9540Sstevel@tonic-gate uint_t polled_toggle;
9550Sstevel@tonic-gate uint_t real_toggle;
9560Sstevel@tonic-gate ehci_pipe_private_t *pp = NULL; /* Normal mode Pipe */
9570Sstevel@tonic-gate ehci_pipe_private_t *polled_pp; /* Polled mode Pipe */
9580Sstevel@tonic-gate usba_pipe_handle_data_t *ph;
9590Sstevel@tonic-gate uint8_t ep_addr;
9600Sstevel@tonic-gate ehci_regs_t *ehci_polled_regsp;
9610Sstevel@tonic-gate ehci_qh_t *qh;
9620Sstevel@tonic-gate
9630Sstevel@tonic-gate #ifndef lint
9640Sstevel@tonic-gate _NOTE(NO_COMPETING_THREADS_NOW);
9650Sstevel@tonic-gate #endif
9660Sstevel@tonic-gate
9670Sstevel@tonic-gate /*
9680Sstevel@tonic-gate * If either of these two flags are set, then we have already
9690Sstevel@tonic-gate * saved off the state information and setup the controller.
9700Sstevel@tonic-gate */
9710Sstevel@tonic-gate if (ehci_polledp->ehci_polled_flags & POLLED_INPUT_MODE_INUSE) {
9720Sstevel@tonic-gate #ifndef lint
9730Sstevel@tonic-gate _NOTE(COMPETING_THREADS_NOW);
9740Sstevel@tonic-gate #endif
9750Sstevel@tonic-gate return;
9760Sstevel@tonic-gate }
9770Sstevel@tonic-gate
9780Sstevel@tonic-gate ehcip = ehci_polledp->ehci_polled_ehcip;
9790Sstevel@tonic-gate
9800Sstevel@tonic-gate /*
9810Sstevel@tonic-gate * Check if the number of keyboard reach the max number we can
9820Sstevel@tonic-gate * support in polled mode
9830Sstevel@tonic-gate */
9840Sstevel@tonic-gate if (++ ehcip->ehci_polled_enter_count > MAX_NUM_FOR_KEYBOARD) {
9850Sstevel@tonic-gate #ifndef lint
9860Sstevel@tonic-gate _NOTE(COMPETING_THREADS_NOW);
9870Sstevel@tonic-gate #endif
9880Sstevel@tonic-gate return;
9890Sstevel@tonic-gate }
9900Sstevel@tonic-gate ehci_polled_regsp = &ehcip->ehci_polled_save_regs;
9910Sstevel@tonic-gate
9920Sstevel@tonic-gate /* Get the endpoint addr. */
9930Sstevel@tonic-gate ep_addr = ehci_polledp->ehci_polled_ep_addr;
9940Sstevel@tonic-gate
9950Sstevel@tonic-gate /* Get the normal mode usb pipe handle */
9960Sstevel@tonic-gate ph = usba_hcdi_get_ph_data(ehci_polledp->ehci_polled_usb_dev, ep_addr);
9970Sstevel@tonic-gate
9980Sstevel@tonic-gate /*
9990Sstevel@tonic-gate * The first enter keyboard entry should save info of the normal mode,
10000Sstevel@tonic-gate * disable all list processing and interrupt, initialize the
10010Sstevel@tonic-gate * frame list table with dummy QHs.
10020Sstevel@tonic-gate */
10030Sstevel@tonic-gate if (ehcip->ehci_polled_enter_count == 1) {
10040Sstevel@tonic-gate /*
10050Sstevel@tonic-gate * Save the current normal mode ehci registers and later this
10060Sstevel@tonic-gate * saved register copy is used to replace some of required ehci
10070Sstevel@tonic-gate * registers before switching from polled mode to normal mode.
10080Sstevel@tonic-gate */
10090Sstevel@tonic-gate
10100Sstevel@tonic-gate bzero((void *)ehci_polled_regsp, sizeof (ehci_regs_t));
10110Sstevel@tonic-gate
10120Sstevel@tonic-gate /* Save current ehci registers */
10130Sstevel@tonic-gate ehci_polled_regsp->ehci_command = Get_OpReg(ehci_command);
10140Sstevel@tonic-gate ehci_polled_regsp->ehci_interrupt = Get_OpReg(ehci_interrupt);
10150Sstevel@tonic-gate ehci_polled_regsp->ehci_ctrl_segment =
10160Sstevel@tonic-gate Get_OpReg(ehci_ctrl_segment);
10170Sstevel@tonic-gate ehci_polled_regsp->
10180Sstevel@tonic-gate ehci_async_list_addr = Get_OpReg(ehci_async_list_addr);
10190Sstevel@tonic-gate ehci_polled_regsp->ehci_config_flag =
10200Sstevel@tonic-gate Get_OpReg(ehci_config_flag);
10210Sstevel@tonic-gate ehci_polled_regsp->ehci_periodic_list_base =
10220Sstevel@tonic-gate Get_OpReg(ehci_periodic_list_base);
10230Sstevel@tonic-gate
10240Sstevel@tonic-gate /* Disable all list processing and interrupts */
10250Sstevel@tonic-gate Set_OpReg(ehci_command, Get_OpReg(ehci_command) &
10260Sstevel@tonic-gate ~(EHCI_CMD_ASYNC_SCHED_ENABLE |
10270Sstevel@tonic-gate EHCI_CMD_PERIODIC_SCHED_ENABLE));
10280Sstevel@tonic-gate
10290Sstevel@tonic-gate /* Wait for few milliseconds */
10300Sstevel@tonic-gate drv_usecwait(EHCI_POLLED_TIMEWAIT);
10310Sstevel@tonic-gate
10320Sstevel@tonic-gate /* Save any unprocessed normal mode ehci interrupts */
10330Sstevel@tonic-gate ehcip->ehci_missed_intr_sts = EHCI_INTR_USB;
10340Sstevel@tonic-gate
10350Sstevel@tonic-gate /*
10360Sstevel@tonic-gate * Save the current interrupt lattice and replace this lattice
10370Sstevel@tonic-gate * with an lattice used in POLLED mode. We will restore lattice
10380Sstevel@tonic-gate * back when we exit from the POLLED mode.
10390Sstevel@tonic-gate */
10400Sstevel@tonic-gate for (i = 0; i < EHCI_NUM_PERIODIC_FRAME_LISTS; i++) {
10410Sstevel@tonic-gate ehcip->ehci_polled_frame_list_table[i] =
10420Sstevel@tonic-gate (ehci_qh_t *)(uintptr_t)Get_PFLT(ehcip->
10430Sstevel@tonic-gate ehci_periodic_frame_list_tablep->
10440Sstevel@tonic-gate ehci_periodic_frame_list_table[i]);
10450Sstevel@tonic-gate }
10460Sstevel@tonic-gate
10470Sstevel@tonic-gate /*
10480Sstevel@tonic-gate * Fill in the lattice with dummy QHs. These QHs are used so the
10490Sstevel@tonic-gate * controller can tell that it is at the end of the QH list.
10500Sstevel@tonic-gate */
10510Sstevel@tonic-gate for (i = 0; i < EHCI_NUM_PERIODIC_FRAME_LISTS; i++) {
10520Sstevel@tonic-gate Set_PFLT(ehcip->ehci_periodic_frame_list_tablep->
10530Sstevel@tonic-gate ehci_periodic_frame_list_table[i],
10540Sstevel@tonic-gate ehci_qh_cpu_to_iommu(ehcip,
10550Sstevel@tonic-gate ehci_polledp->ehci_polled_dummy_qh) |
10560Sstevel@tonic-gate (EHCI_QH_LINK_REF_QH | EHCI_QH_LINK_PTR_VALID));
10570Sstevel@tonic-gate }
10580Sstevel@tonic-gate
10590Sstevel@tonic-gate }
10600Sstevel@tonic-gate
10610Sstevel@tonic-gate /* Get the polled mode ehci pipe private structure */
10620Sstevel@tonic-gate polled_pp = (ehci_pipe_private_t *)
10630Sstevel@tonic-gate ehci_polledp->ehci_polled_input_pipe_handle->p_hcd_private;
10640Sstevel@tonic-gate
10650Sstevel@tonic-gate /*
10660Sstevel@tonic-gate * Before replacing the lattice, adjust the data togggle on the
10670Sstevel@tonic-gate * on the ehci's interrupt ed
10680Sstevel@tonic-gate */
10690Sstevel@tonic-gate polled_toggle = (Get_QH(polled_pp->pp_qh->qh_status) &
10700Sstevel@tonic-gate EHCI_QH_STS_DATA_TOGGLE) ? DATA1:DATA0;
10710Sstevel@tonic-gate
10720Sstevel@tonic-gate /*
10730Sstevel@tonic-gate * If normal mode interrupt pipe endpoint is active, get the data
10740Sstevel@tonic-gate * toggle from the this interrupt endpoint through the corresponding
10750Sstevel@tonic-gate * interrupt pipe handle. Else get the data toggle information from
10760Sstevel@tonic-gate * the usb device structure and this information is saved during the
10770Sstevel@tonic-gate * normal mode interrupt pipe close. Use this data toggle information
10780Sstevel@tonic-gate * to fix the data toggle of polled mode interrupt endpoint.
10790Sstevel@tonic-gate */
10800Sstevel@tonic-gate if (ph) {
10810Sstevel@tonic-gate /* Get the normal mode ehci pipe private structure */
10820Sstevel@tonic-gate pp = (ehci_pipe_private_t *)ph->p_hcd_private;
10830Sstevel@tonic-gate
10840Sstevel@tonic-gate real_toggle = (Get_QH(pp->pp_qh->qh_status) &
10850Sstevel@tonic-gate EHCI_QH_STS_DATA_TOGGLE) ? DATA1:DATA0;
10860Sstevel@tonic-gate } else {
10870Sstevel@tonic-gate real_toggle = usba_hcdi_get_data_toggle(
10880Sstevel@tonic-gate ehci_polledp->ehci_polled_usb_dev, ep_addr);
10890Sstevel@tonic-gate }
10900Sstevel@tonic-gate
10910Sstevel@tonic-gate if (polled_toggle != real_toggle) {
10920Sstevel@tonic-gate if (real_toggle == DATA0) {
10930Sstevel@tonic-gate Set_QH(polled_pp->pp_qh->qh_status,
10940Sstevel@tonic-gate Get_QH(polled_pp->pp_qh->qh_status) &
10950Sstevel@tonic-gate ~EHCI_QH_STS_DATA_TOGGLE);
10960Sstevel@tonic-gate } else {
10970Sstevel@tonic-gate Set_QH(polled_pp->pp_qh->qh_status,
10980Sstevel@tonic-gate Get_QH(polled_pp->pp_qh->qh_status) |
10990Sstevel@tonic-gate EHCI_QH_STS_DATA_TOGGLE);
11000Sstevel@tonic-gate }
11010Sstevel@tonic-gate }
11020Sstevel@tonic-gate
11030Sstevel@tonic-gate /*
11040Sstevel@tonic-gate * Check whether Halt bit is set in the QH and if so clear the
11050Sstevel@tonic-gate * halt bit.
11060Sstevel@tonic-gate */
11070Sstevel@tonic-gate if (polled_pp->pp_qh->qh_status & EHCI_QH_STS_HALTED) {
11080Sstevel@tonic-gate
11090Sstevel@tonic-gate /* Clear the halt bit */
11100Sstevel@tonic-gate Set_QH(polled_pp->pp_qh->qh_status,
11110Sstevel@tonic-gate (Get_QH(polled_pp->pp_qh->qh_status) &
11120Sstevel@tonic-gate ~EHCI_QH_STS_HALTED));
11130Sstevel@tonic-gate }
11140Sstevel@tonic-gate
11150Sstevel@tonic-gate /*
11160Sstevel@tonic-gate * Initialize the qh overlay area
11170Sstevel@tonic-gate */
11180Sstevel@tonic-gate qh = ehci_polledp->ehci_polled_qh;
11190Sstevel@tonic-gate for (i = 0; i < 5; i++) {
11200Sstevel@tonic-gate Set_QH(qh->qh_buf[i], NULL);
11210Sstevel@tonic-gate Set_QH(qh->qh_buf_high[i], NULL);
11220Sstevel@tonic-gate }
11230Sstevel@tonic-gate Set_QH(qh->qh_next_qtd, ehci_qtd_cpu_to_iommu(ehcip,
11240Sstevel@tonic-gate ehci_polledp->ehci_polled_active_intr_qtd_list));
11250Sstevel@tonic-gate
11260Sstevel@tonic-gate /*
11270Sstevel@tonic-gate * Now, add the endpoint to the lattice that we will hang our
11280Sstevel@tonic-gate * QTD's off of. We need to poll this device at every 8 ms and
11290Sstevel@tonic-gate * hence add this QH needs 4 entries in interrupt lattice.
11300Sstevel@tonic-gate */
11310Sstevel@tonic-gate for (i = ehcip->ehci_polled_enter_count - 1;
11320Sstevel@tonic-gate i < EHCI_NUM_PERIODIC_FRAME_LISTS;
11330Sstevel@tonic-gate i = i + LS_MIN_POLL_INTERVAL) {
11340Sstevel@tonic-gate Set_PFLT(ehcip->ehci_periodic_frame_list_tablep->
11350Sstevel@tonic-gate ehci_periodic_frame_list_table[i],
11360Sstevel@tonic-gate ehci_qh_cpu_to_iommu(ehcip,
11370Sstevel@tonic-gate ehci_polledp->ehci_polled_qh) | EHCI_QH_LINK_REF_QH);
11380Sstevel@tonic-gate }
11390Sstevel@tonic-gate /* The first enter keyboard entry enable interrupts and periodic list */
11400Sstevel@tonic-gate if (ehcip->ehci_polled_enter_count == 1) {
11410Sstevel@tonic-gate /* Enable USB and Frame list rollover interrupts */
11420Sstevel@tonic-gate Set_OpReg(ehci_interrupt, (EHCI_INTR_USB |
11430Sstevel@tonic-gate EHCI_INTR_USB_ERROR | EHCI_INTR_FRAME_LIST_ROLLOVER));
11440Sstevel@tonic-gate
11450Sstevel@tonic-gate /* Enable the periodic list */
11460Sstevel@tonic-gate Set_OpReg(ehci_command,
11470Sstevel@tonic-gate (Get_OpReg(ehci_command) | EHCI_CMD_PERIODIC_SCHED_ENABLE));
11480Sstevel@tonic-gate
11490Sstevel@tonic-gate /* Wait for few milliseconds */
11500Sstevel@tonic-gate drv_usecwait(EHCI_POLLED_TIMEWAIT);
11510Sstevel@tonic-gate }
11520Sstevel@tonic-gate #ifndef lint
11530Sstevel@tonic-gate _NOTE(COMPETING_THREADS_NOW);
11540Sstevel@tonic-gate #endif
11550Sstevel@tonic-gate }
11560Sstevel@tonic-gate
11570Sstevel@tonic-gate
11580Sstevel@tonic-gate /*
11590Sstevel@tonic-gate * Polled restore state routines
11600Sstevel@tonic-gate */
11610Sstevel@tonic-gate
11620Sstevel@tonic-gate
11630Sstevel@tonic-gate /*
11640Sstevel@tonic-gate * ehci_polled_restore_state:
11650Sstevel@tonic-gate */
11660Sstevel@tonic-gate static void
ehci_polled_restore_state(ehci_polled_t * ehci_polledp)11670Sstevel@tonic-gate ehci_polled_restore_state(ehci_polled_t *ehci_polledp)
11680Sstevel@tonic-gate {
11690Sstevel@tonic-gate ehci_state_t *ehcip;
11700Sstevel@tonic-gate int i;
11710Sstevel@tonic-gate uint_t polled_toggle;
11720Sstevel@tonic-gate uint_t real_toggle;
11730Sstevel@tonic-gate ehci_pipe_private_t *pp = NULL; /* Normal mode Pipe */
11740Sstevel@tonic-gate ehci_pipe_private_t *polled_pp; /* Polled mode Pipe */
11750Sstevel@tonic-gate usba_pipe_handle_data_t *ph;
11760Sstevel@tonic-gate uint8_t ep_addr;
11770Sstevel@tonic-gate
11780Sstevel@tonic-gate #ifndef lint
11790Sstevel@tonic-gate _NOTE(NO_COMPETING_THREADS_NOW);
11800Sstevel@tonic-gate #endif
11810Sstevel@tonic-gate
11820Sstevel@tonic-gate /*
11830Sstevel@tonic-gate * If this flag is set, then we are still using this structure,
11840Sstevel@tonic-gate * so don't restore any controller state information yet.
11850Sstevel@tonic-gate */
11860Sstevel@tonic-gate if (ehci_polledp->ehci_polled_flags & POLLED_INPUT_MODE_INUSE) {
11870Sstevel@tonic-gate
11880Sstevel@tonic-gate #ifndef lint
11890Sstevel@tonic-gate _NOTE(COMPETING_THREADS_NOW);
11900Sstevel@tonic-gate #endif
11910Sstevel@tonic-gate
11920Sstevel@tonic-gate return;
11930Sstevel@tonic-gate }
11940Sstevel@tonic-gate
11950Sstevel@tonic-gate ehcip = ehci_polledp->ehci_polled_ehcip;
11960Sstevel@tonic-gate ehcip->ehci_polled_enter_count --;
11970Sstevel@tonic-gate
11980Sstevel@tonic-gate /* Get the endpoint addr */
11990Sstevel@tonic-gate ep_addr = ehci_polledp->ehci_polled_ep_addr;
12000Sstevel@tonic-gate
12010Sstevel@tonic-gate /* Get the normal mode usb pipe handle */
12020Sstevel@tonic-gate ph = usba_hcdi_get_ph_data(ehci_polledp->ehci_polled_usb_dev, ep_addr);
12030Sstevel@tonic-gate
12040Sstevel@tonic-gate /* Disable list processing and other things */
12050Sstevel@tonic-gate ehci_polled_stop_processing(ehci_polledp);
12060Sstevel@tonic-gate
12070Sstevel@tonic-gate /* Get the polled mode ehci pipe private structure */
12080Sstevel@tonic-gate polled_pp = (ehci_pipe_private_t *)
12090Sstevel@tonic-gate ehci_polledp->ehci_polled_input_pipe_handle->p_hcd_private;
12100Sstevel@tonic-gate
12110Sstevel@tonic-gate /*
12120Sstevel@tonic-gate * Before replacing the lattice, adjust the data togggle
12130Sstevel@tonic-gate * on the on the ehci's interrupt ed
12140Sstevel@tonic-gate */
12150Sstevel@tonic-gate polled_toggle = (Get_QH(polled_pp->pp_qh->qh_status) &
12160Sstevel@tonic-gate EHCI_QH_STS_DATA_TOGGLE) ? DATA1:DATA0;
12170Sstevel@tonic-gate
12180Sstevel@tonic-gate /*
12190Sstevel@tonic-gate * If normal mode interrupt pipe endpoint is active, fix the
12200Sstevel@tonic-gate * data toggle for this interrupt endpoint by getting the data
12210Sstevel@tonic-gate * toggle information from the polled interrupt endpoint. Else
12220Sstevel@tonic-gate * save the data toggle information in usb device structure.
12230Sstevel@tonic-gate */
12240Sstevel@tonic-gate if (ph) {
12250Sstevel@tonic-gate /* Get the normal mode ehci pipe private structure */
12260Sstevel@tonic-gate pp = (ehci_pipe_private_t *)ph->p_hcd_private;
12270Sstevel@tonic-gate
12280Sstevel@tonic-gate real_toggle = (Get_QH(pp->pp_qh->qh_status) &
12290Sstevel@tonic-gate EHCI_QH_STS_DATA_TOGGLE) ? DATA1:DATA0;
12300Sstevel@tonic-gate
12310Sstevel@tonic-gate if (polled_toggle != real_toggle) {
12320Sstevel@tonic-gate if (polled_toggle == DATA0) {
12330Sstevel@tonic-gate Set_QH(pp->pp_qh->qh_status,
12340Sstevel@tonic-gate Get_QH(pp->pp_qh->qh_status) &
12350Sstevel@tonic-gate ~EHCI_QH_STS_DATA_TOGGLE);
12360Sstevel@tonic-gate } else {
12370Sstevel@tonic-gate Set_QH(pp->pp_qh->qh_status,
12380Sstevel@tonic-gate Get_QH(pp->pp_qh->qh_status) |
12390Sstevel@tonic-gate EHCI_QH_STS_DATA_TOGGLE);
12400Sstevel@tonic-gate }
12410Sstevel@tonic-gate }
12420Sstevel@tonic-gate } else {
12430Sstevel@tonic-gate usba_hcdi_set_data_toggle(ehci_polledp->ehci_polled_usb_dev,
12440Sstevel@tonic-gate ep_addr, polled_toggle);
12450Sstevel@tonic-gate }
12460Sstevel@tonic-gate
12470Sstevel@tonic-gate /*
12480Sstevel@tonic-gate * Only the last leave keyboard entry restore the save frame
12490Sstevel@tonic-gate * list table and start processing.
12500Sstevel@tonic-gate */
12510Sstevel@tonic-gate if (ehcip->ehci_polled_enter_count == 0) {
12520Sstevel@tonic-gate
12530Sstevel@tonic-gate /* Replace the lattice */
12540Sstevel@tonic-gate for (i = 0; i < EHCI_NUM_PERIODIC_FRAME_LISTS; i++) {
12550Sstevel@tonic-gate Set_PFLT(ehcip->ehci_periodic_frame_list_tablep->
12560Sstevel@tonic-gate ehci_periodic_frame_list_table[i],
12570Sstevel@tonic-gate ehcip->ehci_polled_frame_list_table[i]);
12580Sstevel@tonic-gate }
12590Sstevel@tonic-gate ehci_polled_start_processing(ehci_polledp);
12600Sstevel@tonic-gate }
12610Sstevel@tonic-gate
12620Sstevel@tonic-gate #ifndef lint
12630Sstevel@tonic-gate _NOTE(COMPETING_THREADS_NOW);
12640Sstevel@tonic-gate #endif
12650Sstevel@tonic-gate }
12660Sstevel@tonic-gate
12670Sstevel@tonic-gate
12680Sstevel@tonic-gate /*
12690Sstevel@tonic-gate * ehci_polled_stop_processing:
12700Sstevel@tonic-gate */
12710Sstevel@tonic-gate static void
ehci_polled_stop_processing(ehci_polled_t * ehci_polledp)12720Sstevel@tonic-gate ehci_polled_stop_processing(ehci_polled_t *ehci_polledp)
12730Sstevel@tonic-gate {
12740Sstevel@tonic-gate ehci_state_t *ehcip;
12750Sstevel@tonic-gate ehci_qh_t *qh = ehci_polledp->ehci_polled_qh;
12760Sstevel@tonic-gate
12770Sstevel@tonic-gate ehcip = ehci_polledp->ehci_polled_ehcip;
12780Sstevel@tonic-gate
12790Sstevel@tonic-gate /* First inactive this QH */
12800Sstevel@tonic-gate Set_QH(qh->qh_ctrl,
12810Sstevel@tonic-gate Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_ED_INACTIVATE);
12820Sstevel@tonic-gate
12830Sstevel@tonic-gate /* Only first leave keyboard entry turn off periodic list processing */
12840Sstevel@tonic-gate if (Get_OpReg(ehci_command) & EHCI_CMD_PERIODIC_SCHED_ENABLE) {
12850Sstevel@tonic-gate Set_OpReg(ehci_command, (Get_OpReg(ehci_command) &
12867425SGongtian.Zhao@Sun.COM ~EHCI_CMD_PERIODIC_SCHED_ENABLE));
12870Sstevel@tonic-gate
12880Sstevel@tonic-gate /* Wait for few milliseconds */
12890Sstevel@tonic-gate drv_usecwait(EHCI_POLLED_TIMEWAIT);
12900Sstevel@tonic-gate }
12910Sstevel@tonic-gate /*
12920Sstevel@tonic-gate * Now clear all required fields of QH
12930Sstevel@tonic-gate * including inactive bit.
12940Sstevel@tonic-gate */
12950Sstevel@tonic-gate Set_QH(qh->qh_ctrl,
12960Sstevel@tonic-gate Get_QH(qh->qh_ctrl) & ~(EHCI_QH_CTRL_ED_INACTIVATE));
12970Sstevel@tonic-gate Set_QH(qh->qh_status,
12980Sstevel@tonic-gate Get_QH(qh->qh_status) & ~(EHCI_QH_STS_XACT_STATUS));
12990Sstevel@tonic-gate Set_QH(qh->qh_curr_qtd, NULL);
13000Sstevel@tonic-gate Set_QH(qh->qh_alt_next_qtd, EHCI_QH_ALT_NEXT_QTD_PTR_VALID);
13010Sstevel@tonic-gate
13020Sstevel@tonic-gate /*
13030Sstevel@tonic-gate * Now look up at the QTD's that are in the active qtd list &
13040Sstevel@tonic-gate * re-insert them back into the QH's QTD list.
13050Sstevel@tonic-gate */
13060Sstevel@tonic-gate (void) ehci_polled_process_active_intr_qtd_list(ehci_polledp);
13070Sstevel@tonic-gate }
13080Sstevel@tonic-gate
13090Sstevel@tonic-gate
13100Sstevel@tonic-gate /*
13110Sstevel@tonic-gate * ehci_polled_start_processing:
13120Sstevel@tonic-gate */
13130Sstevel@tonic-gate static void
ehci_polled_start_processing(ehci_polled_t * ehci_polledp)13140Sstevel@tonic-gate ehci_polled_start_processing(ehci_polled_t *ehci_polledp)
13150Sstevel@tonic-gate {
13160Sstevel@tonic-gate ehci_state_t *ehcip;
13170Sstevel@tonic-gate uint32_t mask;
13180Sstevel@tonic-gate ehci_regs_t *ehci_polled_regsp;
13190Sstevel@tonic-gate
13200Sstevel@tonic-gate ehcip = ehci_polledp->ehci_polled_ehcip;
13210Sstevel@tonic-gate ehci_polled_regsp = &ehcip->ehci_polled_save_regs;
13220Sstevel@tonic-gate
13230Sstevel@tonic-gate mask = ((uint32_t)ehci_polled_regsp->ehci_interrupt &
13240Sstevel@tonic-gate (EHCI_INTR_HOST_SYSTEM_ERROR | EHCI_INTR_FRAME_LIST_ROLLOVER |
13250Sstevel@tonic-gate EHCI_INTR_USB_ERROR | EHCI_INTR_USB | EHCI_INTR_ASYNC_ADVANCE));
13260Sstevel@tonic-gate
13270Sstevel@tonic-gate /* Enable all required EHCI interrupts */
13280Sstevel@tonic-gate Set_OpReg(ehci_interrupt, mask);
13290Sstevel@tonic-gate
13300Sstevel@tonic-gate mask = ((uint32_t)ehci_polled_regsp->ehci_command &
13310Sstevel@tonic-gate (EHCI_CMD_ASYNC_SCHED_ENABLE | EHCI_CMD_PERIODIC_SCHED_ENABLE));
13320Sstevel@tonic-gate
13330Sstevel@tonic-gate /* Enable all reuired list processing */
13340Sstevel@tonic-gate Set_OpReg(ehci_command, (Get_OpReg(ehci_command) | mask));
13350Sstevel@tonic-gate
13360Sstevel@tonic-gate /* Wait for few milliseconds */
13370Sstevel@tonic-gate drv_usecwait(EHCI_POLLED_TIMEWAIT);
13380Sstevel@tonic-gate }
13390Sstevel@tonic-gate
13400Sstevel@tonic-gate
13410Sstevel@tonic-gate /*
13420Sstevel@tonic-gate * Polled read routines
13430Sstevel@tonic-gate */
13440Sstevel@tonic-gate
13450Sstevel@tonic-gate
13460Sstevel@tonic-gate /*
13470Sstevel@tonic-gate * ehci_polled_process_active_intr_qtd_list:
13480Sstevel@tonic-gate *
13490Sstevel@tonic-gate * This routine takes the QTD's off of the input done head and processes
13500Sstevel@tonic-gate * them. It returns the number of characters that have been copied for
13510Sstevel@tonic-gate * input.
13520Sstevel@tonic-gate */
13530Sstevel@tonic-gate static int
ehci_polled_process_active_intr_qtd_list(ehci_polled_t * ehci_polledp)13540Sstevel@tonic-gate ehci_polled_process_active_intr_qtd_list(ehci_polled_t *ehci_polledp)
13550Sstevel@tonic-gate {
13560Sstevel@tonic-gate ehci_state_t *ehcip = ehci_polledp->ehci_polled_ehcip;
13570Sstevel@tonic-gate ehci_qtd_t *qtd, *next_qtd;
13580Sstevel@tonic-gate uint_t num_characters = 0;
13590Sstevel@tonic-gate uint_t ctrl;
13600Sstevel@tonic-gate ehci_trans_wrapper_t *tw;
13610Sstevel@tonic-gate ehci_pipe_private_t *pp;
13627425SGongtian.Zhao@Sun.COM usb_cr_t error;
1363*9095SZhigang.Lu@Sun.COM int pipe_attr, pipe_dir;
13640Sstevel@tonic-gate
13650Sstevel@tonic-gate /* Sync QH and QTD pool */
13660Sstevel@tonic-gate if (ehci_polledp->ehci_polled_no_sync_flag == B_FALSE) {
13670Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
13680Sstevel@tonic-gate }
13690Sstevel@tonic-gate
13700Sstevel@tonic-gate /* Create done qtd list */
13710Sstevel@tonic-gate qtd = ehci_polled_create_done_qtd_list(ehci_polledp);
13720Sstevel@tonic-gate
1373*9095SZhigang.Lu@Sun.COM pipe_attr = ehci_polledp->ehci_polled_input_pipe_handle->
1374*9095SZhigang.Lu@Sun.COM p_ep.bmAttributes & USB_EP_ATTR_MASK;
1375*9095SZhigang.Lu@Sun.COM pipe_dir = ehci_polledp->ehci_polled_input_pipe_handle->
1376*9095SZhigang.Lu@Sun.COM p_ep.bEndpointAddress & USB_EP_DIR_MASK;
13770Sstevel@tonic-gate /*
13780Sstevel@tonic-gate * Traverse the list of transfer descriptors. We can't destroy
13790Sstevel@tonic-gate * the qtd_next pointers of these QTDs because we are using it
13800Sstevel@tonic-gate * to traverse the done list. Therefore, we can not put these
13810Sstevel@tonic-gate * QTD's back on the QH until we are done processing all of them.
13820Sstevel@tonic-gate */
13830Sstevel@tonic-gate while (qtd) {
13840Sstevel@tonic-gate /* Get next active QTD from the active QTD list */
13850Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
13860Sstevel@tonic-gate Get_QTD(qtd->qtd_active_qtd_next));
13870Sstevel@tonic-gate
13880Sstevel@tonic-gate /* Obtain the transfer wrapper from the QTD */
13890Sstevel@tonic-gate tw = (ehci_trans_wrapper_t *)EHCI_LOOKUP_ID(
13900Sstevel@tonic-gate (uint32_t)Get_QTD(qtd->qtd_trans_wrapper));
13910Sstevel@tonic-gate
1392*9095SZhigang.Lu@Sun.COM /* Get ehci pipe from transfer wrapper */
13930Sstevel@tonic-gate pp = tw->tw_pipe_private;
13940Sstevel@tonic-gate
13950Sstevel@tonic-gate /* Look at the status */
13960Sstevel@tonic-gate ctrl = (uint_t)Get_QTD(qtd->qtd_ctrl) &
13970Sstevel@tonic-gate (uint32_t)EHCI_QTD_CTRL_XACT_STATUS;
13980Sstevel@tonic-gate
13990Sstevel@tonic-gate error = ehci_check_for_error(ehcip, pp, tw, qtd, ctrl);
14000Sstevel@tonic-gate
14010Sstevel@tonic-gate /*
14020Sstevel@tonic-gate * Check to see if there is an error. If there is error
14030Sstevel@tonic-gate * clear the halt condition in the Endpoint Descriptor
14040Sstevel@tonic-gate * (QH) associated with this Transfer Descriptor (QTD).
14050Sstevel@tonic-gate */
1406*9095SZhigang.Lu@Sun.COM if (error != USB_CR_OK) {
14070Sstevel@tonic-gate /* Clear the halt bit */
14080Sstevel@tonic-gate Set_QH(pp->pp_qh->qh_status,
14090Sstevel@tonic-gate Get_QH(pp->pp_qh->qh_status) &
14100Sstevel@tonic-gate ~(EHCI_QH_STS_XACT_STATUS));
1411*9095SZhigang.Lu@Sun.COM } else if (pipe_dir == USB_EP_DIR_IN) {
1412*9095SZhigang.Lu@Sun.COM
1413*9095SZhigang.Lu@Sun.COM num_characters +=
1414*9095SZhigang.Lu@Sun.COM ehci_polled_handle_normal_qtd(ehci_polledp,
1415*9095SZhigang.Lu@Sun.COM qtd);
14160Sstevel@tonic-gate }
14170Sstevel@tonic-gate
14180Sstevel@tonic-gate /* Insert this qtd back into QH's qtd list */
1419*9095SZhigang.Lu@Sun.COM switch (pipe_attr) {
1420*9095SZhigang.Lu@Sun.COM case USB_EP_ATTR_INTR:
1421*9095SZhigang.Lu@Sun.COM ehci_polled_insert_intr_qtd(ehci_polledp, qtd);
1422*9095SZhigang.Lu@Sun.COM break;
1423*9095SZhigang.Lu@Sun.COM case USB_EP_ATTR_BULK:
1424*9095SZhigang.Lu@Sun.COM if (tw->tw_qtd_free_list != NULL) {
1425*9095SZhigang.Lu@Sun.COM uint32_t td_addr;
1426*9095SZhigang.Lu@Sun.COM td_addr = ehci_qtd_cpu_to_iommu(ehcip,
1427*9095SZhigang.Lu@Sun.COM tw->tw_qtd_free_list);
1428*9095SZhigang.Lu@Sun.COM Set_QTD(qtd->qtd_tw_next_qtd, td_addr);
1429*9095SZhigang.Lu@Sun.COM Set_QTD(qtd->qtd_state, EHCI_QTD_DUMMY);
1430*9095SZhigang.Lu@Sun.COM tw->tw_qtd_free_list = qtd;
1431*9095SZhigang.Lu@Sun.COM } else {
1432*9095SZhigang.Lu@Sun.COM tw->tw_qtd_free_list = qtd;
1433*9095SZhigang.Lu@Sun.COM Set_QTD(qtd->qtd_tw_next_qtd, NULL);
1434*9095SZhigang.Lu@Sun.COM Set_QTD(qtd->qtd_state, EHCI_QTD_DUMMY);
1435*9095SZhigang.Lu@Sun.COM }
1436*9095SZhigang.Lu@Sun.COM break;
1437*9095SZhigang.Lu@Sun.COM }
14380Sstevel@tonic-gate qtd = next_qtd;
14390Sstevel@tonic-gate }
14400Sstevel@tonic-gate
14410Sstevel@tonic-gate return (num_characters);
14420Sstevel@tonic-gate }
14430Sstevel@tonic-gate
14440Sstevel@tonic-gate
14450Sstevel@tonic-gate /*
14460Sstevel@tonic-gate * ehci_polled_handle_normal_qtd:
14470Sstevel@tonic-gate */
14480Sstevel@tonic-gate static int
ehci_polled_handle_normal_qtd(ehci_polled_t * ehci_polledp,ehci_qtd_t * qtd)14490Sstevel@tonic-gate ehci_polled_handle_normal_qtd(
14500Sstevel@tonic-gate ehci_polled_t *ehci_polledp,
14510Sstevel@tonic-gate ehci_qtd_t *qtd)
14520Sstevel@tonic-gate {
14530Sstevel@tonic-gate ehci_state_t *ehcip = ehci_polledp->ehci_polled_ehcip;
14540Sstevel@tonic-gate uchar_t *buf;
14550Sstevel@tonic-gate ehci_trans_wrapper_t *tw;
14560Sstevel@tonic-gate size_t length;
14570Sstevel@tonic-gate uint32_t residue;
14580Sstevel@tonic-gate
14590Sstevel@tonic-gate /* Obtain the transfer wrapper from the QTD */
14600Sstevel@tonic-gate tw = (ehci_trans_wrapper_t *)EHCI_LOOKUP_ID((uint32_t)
14617425SGongtian.Zhao@Sun.COM Get_QTD(qtd->qtd_trans_wrapper));
14620Sstevel@tonic-gate
14630Sstevel@tonic-gate ASSERT(tw != NULL);
14640Sstevel@tonic-gate
14650Sstevel@tonic-gate buf = (uchar_t *)tw->tw_buf;
14660Sstevel@tonic-gate
14670Sstevel@tonic-gate length = tw->tw_length;
14680Sstevel@tonic-gate
14690Sstevel@tonic-gate /*
14700Sstevel@tonic-gate * If "Total bytes of xfer" in control field of qtd is not equal to 0,
14710Sstevel@tonic-gate * then we received less data from the usb device than requested by us.
14720Sstevel@tonic-gate * In that case, get the actual received data size.
14730Sstevel@tonic-gate */
14740Sstevel@tonic-gate residue = ((Get_QTD(qtd->qtd_ctrl) &
14750Sstevel@tonic-gate EHCI_QTD_CTRL_BYTES_TO_XFER) >> EHCI_QTD_CTRL_BYTES_TO_XFER_SHIFT);
14760Sstevel@tonic-gate
14770Sstevel@tonic-gate if (residue) {
14780Sstevel@tonic-gate
14791500Ssl147100 length = Get_QTD(qtd->qtd_xfer_offs) +
14801500Ssl147100 Get_QTD(qtd->qtd_xfer_len) - residue;
14810Sstevel@tonic-gate }
14820Sstevel@tonic-gate
14830Sstevel@tonic-gate /* Sync IO buffer */
14840Sstevel@tonic-gate if (ehci_polledp->ehci_polled_no_sync_flag == B_FALSE) {
14850Sstevel@tonic-gate Sync_IO_Buffer(tw->tw_dmahandle, length);
14860Sstevel@tonic-gate }
14870Sstevel@tonic-gate
14880Sstevel@tonic-gate /* Copy the data into the message */
14890Sstevel@tonic-gate bcopy(buf, ehci_polledp->ehci_polled_buf, length);
14900Sstevel@tonic-gate
14917492SZhigang.Lu@Sun.COM return ((int)length);
14920Sstevel@tonic-gate }
14930Sstevel@tonic-gate
14940Sstevel@tonic-gate
14950Sstevel@tonic-gate /*
1496*9095SZhigang.Lu@Sun.COM * ehci_polled_insert_intr_qtd:
14970Sstevel@tonic-gate *
14980Sstevel@tonic-gate * Insert a Transfer Descriptor (QTD) on an Endpoint Descriptor (QH).
14990Sstevel@tonic-gate */
15000Sstevel@tonic-gate static void
ehci_polled_insert_intr_qtd(ehci_polled_t * ehci_polledp,ehci_qtd_t * qtd)1501*9095SZhigang.Lu@Sun.COM ehci_polled_insert_intr_qtd(
15020Sstevel@tonic-gate ehci_polled_t *ehci_polledp,
15030Sstevel@tonic-gate ehci_qtd_t *qtd)
15040Sstevel@tonic-gate {
15050Sstevel@tonic-gate ehci_state_t *ehcip = ehci_polledp->ehci_polled_ehcip;
15060Sstevel@tonic-gate ehci_qtd_t *curr_dummy_qtd, *next_dummy_qtd;
15070Sstevel@tonic-gate ehci_qtd_t *new_dummy_qtd;
15080Sstevel@tonic-gate uint_t qtd_control;
15090Sstevel@tonic-gate ehci_pipe_private_t *pp;
15100Sstevel@tonic-gate ehci_qh_t *qh;
15110Sstevel@tonic-gate ehci_trans_wrapper_t *tw;
15120Sstevel@tonic-gate
15130Sstevel@tonic-gate /* Obtain the transfer wrapper from the QTD */
15140Sstevel@tonic-gate tw = (ehci_trans_wrapper_t *)EHCI_LOOKUP_ID(
15150Sstevel@tonic-gate (uint32_t)Get_QTD(qtd->qtd_trans_wrapper));
15160Sstevel@tonic-gate
15170Sstevel@tonic-gate pp = tw->tw_pipe_private;
15180Sstevel@tonic-gate
15190Sstevel@tonic-gate /* Obtain the endpoint and interrupt request */
15200Sstevel@tonic-gate qh = pp->pp_qh;
15210Sstevel@tonic-gate
15220Sstevel@tonic-gate /*
15230Sstevel@tonic-gate * Take this QTD off the transfer wrapper's list since
15240Sstevel@tonic-gate * the pipe is FIFO, this must be the first QTD on the
15250Sstevel@tonic-gate * list.
15260Sstevel@tonic-gate */
15270Sstevel@tonic-gate ASSERT((ehci_qtd_t *)tw->tw_qtd_head == qtd);
15280Sstevel@tonic-gate
15290Sstevel@tonic-gate tw->tw_qtd_head = (ehci_qtd_t *)
15300Sstevel@tonic-gate ehci_qtd_iommu_to_cpu(ehcip, Get_QTD(qtd->qtd_tw_next_qtd));
15310Sstevel@tonic-gate
15320Sstevel@tonic-gate /*
15330Sstevel@tonic-gate * If the head becomes NULL, then there are no more
15340Sstevel@tonic-gate * active QTD's for this transfer wrapper. Also set
15350Sstevel@tonic-gate * the tail to NULL.
15360Sstevel@tonic-gate */
15370Sstevel@tonic-gate if (tw->tw_qtd_head == NULL) {
15380Sstevel@tonic-gate tw->tw_qtd_tail = NULL;
15390Sstevel@tonic-gate }
15400Sstevel@tonic-gate
15410Sstevel@tonic-gate /* Convert current valid QTD as new dummy QTD */
15420Sstevel@tonic-gate bzero((char *)qtd, sizeof (ehci_qtd_t));
15430Sstevel@tonic-gate Set_QTD(qtd->qtd_state, EHCI_QTD_DUMMY);
15440Sstevel@tonic-gate
15450Sstevel@tonic-gate /* Rename qtd as new_dummy_qtd */
15460Sstevel@tonic-gate new_dummy_qtd = qtd;
15470Sstevel@tonic-gate
15480Sstevel@tonic-gate /* Get the current and next dummy QTDs */
15490Sstevel@tonic-gate curr_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
15500Sstevel@tonic-gate Get_QH(qh->qh_dummy_qtd));
15510Sstevel@tonic-gate next_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
15520Sstevel@tonic-gate Get_QTD(curr_dummy_qtd->qtd_next_qtd));
15530Sstevel@tonic-gate
15540Sstevel@tonic-gate /* Update QH's dummy qtd field */
15550Sstevel@tonic-gate Set_QH(qh->qh_dummy_qtd, ehci_qtd_cpu_to_iommu(ehcip, next_dummy_qtd));
15560Sstevel@tonic-gate
15570Sstevel@tonic-gate /* Update next dummy's next qtd pointer */
15580Sstevel@tonic-gate Set_QTD(next_dummy_qtd->qtd_next_qtd,
15590Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(ehcip, new_dummy_qtd));
15600Sstevel@tonic-gate
15610Sstevel@tonic-gate qtd_control = (tw->tw_direction | EHCI_QTD_CTRL_INTR_ON_COMPLETE);
15620Sstevel@tonic-gate
15630Sstevel@tonic-gate /*
15640Sstevel@tonic-gate * Fill in the current dummy qtd and
15650Sstevel@tonic-gate * add the new dummy to the end.
15660Sstevel@tonic-gate */
15670Sstevel@tonic-gate ehci_polled_fill_in_qtd(ehcip, curr_dummy_qtd, qtd_control,
15681500Ssl147100 0, tw->tw_length, tw);
15690Sstevel@tonic-gate
15700Sstevel@tonic-gate /* Insert this qtd onto the tw */
15710Sstevel@tonic-gate ehci_polled_insert_qtd_on_tw(ehcip, tw, curr_dummy_qtd);
15720Sstevel@tonic-gate
15730Sstevel@tonic-gate /* Insert this qtd into active interrupt QTD list */
15740Sstevel@tonic-gate ehci_polled_insert_qtd_into_active_intr_qtd_list(
15750Sstevel@tonic-gate ehci_polledp, curr_dummy_qtd);
15760Sstevel@tonic-gate }
15770Sstevel@tonic-gate
15780Sstevel@tonic-gate
1579*9095SZhigang.Lu@Sun.COM static void
ehci_polled_insert_bulk_qtd(ehci_polled_t * ehci_polledp)1580*9095SZhigang.Lu@Sun.COM ehci_polled_insert_bulk_qtd(
1581*9095SZhigang.Lu@Sun.COM ehci_polled_t *ehci_polledp)
1582*9095SZhigang.Lu@Sun.COM {
1583*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip;
1584*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp;
1585*9095SZhigang.Lu@Sun.COM ehci_trans_wrapper_t *tw;
1586*9095SZhigang.Lu@Sun.COM ehci_qh_t *qh;
1587*9095SZhigang.Lu@Sun.COM ehci_qtd_t *new_dummy_qtd;
1588*9095SZhigang.Lu@Sun.COM ehci_qtd_t *curr_dummy_qtd, *next_dummy_qtd;
1589*9095SZhigang.Lu@Sun.COM uint_t qtd_control;
1590*9095SZhigang.Lu@Sun.COM
1591*9095SZhigang.Lu@Sun.COM ehcip = ehci_polledp->ehci_polled_ehcip;
1592*9095SZhigang.Lu@Sun.COM pp = (ehci_pipe_private_t *)ehci_polledp->
1593*9095SZhigang.Lu@Sun.COM ehci_polled_input_pipe_handle->p_hcd_private;
1594*9095SZhigang.Lu@Sun.COM tw = pp->pp_tw_head;
1595*9095SZhigang.Lu@Sun.COM qh = ehci_polledp->ehci_polled_qh;
1596*9095SZhigang.Lu@Sun.COM new_dummy_qtd = tw->tw_qtd_free_list;
1597*9095SZhigang.Lu@Sun.COM
1598*9095SZhigang.Lu@Sun.COM if (new_dummy_qtd == NULL) {
1599*9095SZhigang.Lu@Sun.COM return;
1600*9095SZhigang.Lu@Sun.COM }
1601*9095SZhigang.Lu@Sun.COM
1602*9095SZhigang.Lu@Sun.COM tw->tw_qtd_free_list = ehci_qtd_iommu_to_cpu(ehcip,
1603*9095SZhigang.Lu@Sun.COM Get_QTD(new_dummy_qtd->qtd_tw_next_qtd));
1604*9095SZhigang.Lu@Sun.COM Set_QTD(new_dummy_qtd->qtd_tw_next_qtd, NULL);
1605*9095SZhigang.Lu@Sun.COM
1606*9095SZhigang.Lu@Sun.COM /* Get the current and next dummy QTDs */
1607*9095SZhigang.Lu@Sun.COM curr_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
1608*9095SZhigang.Lu@Sun.COM Get_QH(qh->qh_dummy_qtd));
1609*9095SZhigang.Lu@Sun.COM next_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
1610*9095SZhigang.Lu@Sun.COM Get_QTD(curr_dummy_qtd->qtd_next_qtd));
1611*9095SZhigang.Lu@Sun.COM
1612*9095SZhigang.Lu@Sun.COM /* Update QH's dummy qtd field */
1613*9095SZhigang.Lu@Sun.COM Set_QH(qh->qh_dummy_qtd, ehci_qtd_cpu_to_iommu(ehcip, next_dummy_qtd));
1614*9095SZhigang.Lu@Sun.COM
1615*9095SZhigang.Lu@Sun.COM /* Update next dummy's next qtd pointer */
1616*9095SZhigang.Lu@Sun.COM Set_QTD(next_dummy_qtd->qtd_next_qtd,
1617*9095SZhigang.Lu@Sun.COM ehci_qtd_cpu_to_iommu(ehcip, new_dummy_qtd));
1618*9095SZhigang.Lu@Sun.COM
1619*9095SZhigang.Lu@Sun.COM qtd_control = (tw->tw_direction | EHCI_QTD_CTRL_INTR_ON_COMPLETE);
1620*9095SZhigang.Lu@Sun.COM
1621*9095SZhigang.Lu@Sun.COM /*
1622*9095SZhigang.Lu@Sun.COM * Fill in the current dummy qtd and
1623*9095SZhigang.Lu@Sun.COM * add the new dummy to the end.
1624*9095SZhigang.Lu@Sun.COM */
1625*9095SZhigang.Lu@Sun.COM ehci_polled_fill_in_qtd(ehcip, curr_dummy_qtd, qtd_control,
1626*9095SZhigang.Lu@Sun.COM 0, tw->tw_length, tw);
1627*9095SZhigang.Lu@Sun.COM
1628*9095SZhigang.Lu@Sun.COM /* Insert this qtd into active interrupt QTD list */
1629*9095SZhigang.Lu@Sun.COM ehci_polled_insert_qtd_into_active_intr_qtd_list(
1630*9095SZhigang.Lu@Sun.COM ehci_polledp, curr_dummy_qtd);
1631*9095SZhigang.Lu@Sun.COM }
1632*9095SZhigang.Lu@Sun.COM
1633*9095SZhigang.Lu@Sun.COM
16340Sstevel@tonic-gate /*
16350Sstevel@tonic-gate * ehci_polled_fill_in_qtd:
16360Sstevel@tonic-gate *
16370Sstevel@tonic-gate * Fill in the fields of a Transfer Descriptor (QTD).
16381500Ssl147100 * The "Buffer Pointer" fields of a QTD are retrieved from the TW
16391500Ssl147100 * it is associated with.
16400Sstevel@tonic-gate *
16410Sstevel@tonic-gate * Unlike the it's ehci_fill_in_qtd counterpart, we do not
16420Sstevel@tonic-gate * set the alternative ptr in polled mode. There is not need
16430Sstevel@tonic-gate * for it in polled mode, because it doesn't need to cleanup
16440Sstevel@tonic-gate * short xfer conditions.
16451500Ssl147100 *
16461500Ssl147100 * Note:
16471500Ssl147100 * qtd_dma_offs - the starting offset into the TW buffer, where the QTD
16487425SGongtian.Zhao@Sun.COM * should transfer from. It should be 4K aligned. And when
16497425SGongtian.Zhao@Sun.COM * a TW has more than one QTDs, the QTDs must be filled in
16507425SGongtian.Zhao@Sun.COM * increasing order.
16511500Ssl147100 * qtd_length - the total bytes to transfer.
16520Sstevel@tonic-gate */
16530Sstevel@tonic-gate static void
ehci_polled_fill_in_qtd(ehci_state_t * ehcip,ehci_qtd_t * qtd,uint_t qtd_ctrl,size_t qtd_dma_offs,size_t qtd_length,ehci_trans_wrapper_t * tw)16540Sstevel@tonic-gate ehci_polled_fill_in_qtd(
16550Sstevel@tonic-gate ehci_state_t *ehcip,
16560Sstevel@tonic-gate ehci_qtd_t *qtd,
16570Sstevel@tonic-gate uint_t qtd_ctrl,
16581500Ssl147100 size_t qtd_dma_offs,
16590Sstevel@tonic-gate size_t qtd_length,
16600Sstevel@tonic-gate ehci_trans_wrapper_t *tw)
16610Sstevel@tonic-gate {
16621500Ssl147100 uint32_t buf_addr;
16630Sstevel@tonic-gate size_t buf_len = qtd_length;
16640Sstevel@tonic-gate uint32_t ctrl = qtd_ctrl;
16650Sstevel@tonic-gate uint_t i = 0;
16661500Ssl147100 int rem_len;
16670Sstevel@tonic-gate
16680Sstevel@tonic-gate /* Assert that the qtd to be filled in is a dummy */
16690Sstevel@tonic-gate ASSERT(Get_QTD(qtd->qtd_state) == EHCI_QTD_DUMMY);
16700Sstevel@tonic-gate
16710Sstevel@tonic-gate /* Change QTD's state Active */
16720Sstevel@tonic-gate Set_QTD(qtd->qtd_state, EHCI_QTD_ACTIVE);
16730Sstevel@tonic-gate
16740Sstevel@tonic-gate /* Set the total length data tarnsfer */
16750Sstevel@tonic-gate ctrl |= (((qtd_length << EHCI_QTD_CTRL_BYTES_TO_XFER_SHIFT)
16760Sstevel@tonic-gate & EHCI_QTD_CTRL_BYTES_TO_XFER) | EHCI_QTD_CTRL_MAX_ERR_COUNTS);
16770Sstevel@tonic-gate
16780Sstevel@tonic-gate /*
16791500Ssl147100 * QTDs must be filled in increasing DMA offset order.
16801500Ssl147100 * tw_dma_offs is initialized to be 0 at TW creation and
16811500Ssl147100 * is only increased in this function.
16821500Ssl147100 */
16831500Ssl147100 ASSERT(buf_len == 0 || qtd_dma_offs >= tw->tw_dma_offs);
16841500Ssl147100
16851500Ssl147100 /*
16861500Ssl147100 * Save the starting dma buffer offset used and
16870Sstevel@tonic-gate * length of data that will be transfered in
16880Sstevel@tonic-gate * the current QTD.
16890Sstevel@tonic-gate */
16901500Ssl147100 Set_QTD(qtd->qtd_xfer_offs, qtd_dma_offs);
16910Sstevel@tonic-gate Set_QTD(qtd->qtd_xfer_len, buf_len);
16920Sstevel@tonic-gate
16930Sstevel@tonic-gate while (buf_len) {
16941500Ssl147100 /*
16951500Ssl147100 * Advance to the next DMA cookie until finding the cookie
16961500Ssl147100 * that qtd_dma_offs falls in.
16971500Ssl147100 * It is very likely this loop will never repeat more than
16981500Ssl147100 * once. It is here just to accommodate the case qtd_dma_offs
16991500Ssl147100 * is increased by multiple cookies during two consecutive
17001500Ssl147100 * calls into this function. In that case, the interim DMA
17011500Ssl147100 * buffer is allowed to be skipped.
17021500Ssl147100 */
17031500Ssl147100 while ((tw->tw_dma_offs + tw->tw_cookie.dmac_size) <=
17041500Ssl147100 qtd_dma_offs) {
17051500Ssl147100 /*
17061500Ssl147100 * tw_dma_offs always points to the starting offset
17071500Ssl147100 * of a cookie
17081500Ssl147100 */
17091500Ssl147100 tw->tw_dma_offs += tw->tw_cookie.dmac_size;
17101500Ssl147100 ddi_dma_nextcookie(tw->tw_dmahandle, &tw->tw_cookie);
17111500Ssl147100 tw->tw_cookie_idx++;
17121500Ssl147100 ASSERT(tw->tw_cookie_idx < tw->tw_ncookies);
17131500Ssl147100 }
17141500Ssl147100
17151500Ssl147100 /*
17161500Ssl147100 * Counting the remained buffer length to be filled in
17171500Ssl147100 * the QTD for current DMA cookie
17181500Ssl147100 */
17191500Ssl147100 rem_len = (tw->tw_dma_offs + tw->tw_cookie.dmac_size) -
17201500Ssl147100 qtd_dma_offs;
17211500Ssl147100
17220Sstevel@tonic-gate /* Update the beginning of the buffer */
17231500Ssl147100 buf_addr = (qtd_dma_offs - tw->tw_dma_offs) +
17241500Ssl147100 tw->tw_cookie.dmac_address;
17251500Ssl147100 ASSERT((buf_addr % EHCI_4K_ALIGN) == 0);
17260Sstevel@tonic-gate Set_QTD(qtd->qtd_buf[i], buf_addr);
17270Sstevel@tonic-gate
17280Sstevel@tonic-gate if (buf_len <= EHCI_MAX_QTD_BUF_SIZE) {
17291500Ssl147100 ASSERT(buf_len <= rem_len);
17300Sstevel@tonic-gate break;
17310Sstevel@tonic-gate } else {
17321500Ssl147100 ASSERT(rem_len >= EHCI_MAX_QTD_BUF_SIZE);
17330Sstevel@tonic-gate buf_len -= EHCI_MAX_QTD_BUF_SIZE;
17341500Ssl147100 qtd_dma_offs += EHCI_MAX_QTD_BUF_SIZE;
17350Sstevel@tonic-gate }
17360Sstevel@tonic-gate
17370Sstevel@tonic-gate i++;
17380Sstevel@tonic-gate }
17390Sstevel@tonic-gate
17400Sstevel@tonic-gate /*
17410Sstevel@tonic-gate * For control, bulk and interrupt QTD, now
17420Sstevel@tonic-gate * enable current QTD by setting active bit.
17430Sstevel@tonic-gate */
17440Sstevel@tonic-gate Set_QTD(qtd->qtd_ctrl, (ctrl | EHCI_QTD_CTRL_ACTIVE_XACT));
17450Sstevel@tonic-gate
17460Sstevel@tonic-gate Set_QTD(qtd->qtd_trans_wrapper, (uint32_t)tw->tw_id);
17470Sstevel@tonic-gate }
17480Sstevel@tonic-gate
17490Sstevel@tonic-gate
17500Sstevel@tonic-gate /*
17510Sstevel@tonic-gate * ehci_polled_insert_qtd_on_tw:
17520Sstevel@tonic-gate *
17530Sstevel@tonic-gate * The transfer wrapper keeps a list of all Transfer Descriptors (QTD) that
17540Sstevel@tonic-gate * are allocated for this transfer. Insert a QTD onto this list. The list
17550Sstevel@tonic-gate * of QTD's does not include the dummy QTD that is at the end of the list of
17560Sstevel@tonic-gate * QTD's for the endpoint.
17570Sstevel@tonic-gate */
17580Sstevel@tonic-gate static void
ehci_polled_insert_qtd_on_tw(ehci_state_t * ehcip,ehci_trans_wrapper_t * tw,ehci_qtd_t * qtd)17590Sstevel@tonic-gate ehci_polled_insert_qtd_on_tw(
17600Sstevel@tonic-gate ehci_state_t *ehcip,
17610Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
17620Sstevel@tonic-gate ehci_qtd_t *qtd)
17630Sstevel@tonic-gate {
17640Sstevel@tonic-gate /*
17650Sstevel@tonic-gate * Set the next pointer to NULL because
17660Sstevel@tonic-gate * this is the last QTD on list.
17670Sstevel@tonic-gate */
17680Sstevel@tonic-gate Set_QTD(qtd->qtd_tw_next_qtd, NULL);
17690Sstevel@tonic-gate
17700Sstevel@tonic-gate if (tw->tw_qtd_head == NULL) {
17710Sstevel@tonic-gate ASSERT(tw->tw_qtd_tail == NULL);
17720Sstevel@tonic-gate tw->tw_qtd_head = qtd;
17730Sstevel@tonic-gate tw->tw_qtd_tail = qtd;
17740Sstevel@tonic-gate } else {
17750Sstevel@tonic-gate ehci_qtd_t *dummy = (ehci_qtd_t *)tw->tw_qtd_tail;
17760Sstevel@tonic-gate
17770Sstevel@tonic-gate ASSERT(dummy != NULL);
17780Sstevel@tonic-gate ASSERT(dummy != qtd);
17790Sstevel@tonic-gate ASSERT(Get_QTD(qtd->qtd_state) != EHCI_QTD_DUMMY);
17800Sstevel@tonic-gate
17810Sstevel@tonic-gate /* Add the qtd to the end of the list */
17820Sstevel@tonic-gate Set_QTD(dummy->qtd_tw_next_qtd,
17830Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(ehcip, qtd));
17840Sstevel@tonic-gate
17850Sstevel@tonic-gate tw->tw_qtd_tail = qtd;
17860Sstevel@tonic-gate
17870Sstevel@tonic-gate ASSERT(Get_QTD(qtd->qtd_tw_next_qtd) == NULL);
17880Sstevel@tonic-gate }
17890Sstevel@tonic-gate }
17900Sstevel@tonic-gate
17910Sstevel@tonic-gate
17920Sstevel@tonic-gate /*
17930Sstevel@tonic-gate * ehci_polled_create_done_qtd_list:
17940Sstevel@tonic-gate *
17950Sstevel@tonic-gate * Create done qtd list from active qtd list.
17960Sstevel@tonic-gate */
17970Sstevel@tonic-gate static ehci_qtd_t *
ehci_polled_create_done_qtd_list(ehci_polled_t * ehci_polledp)17980Sstevel@tonic-gate ehci_polled_create_done_qtd_list(
17990Sstevel@tonic-gate ehci_polled_t *ehci_polledp)
18000Sstevel@tonic-gate {
18010Sstevel@tonic-gate ehci_state_t *ehcip = ehci_polledp->ehci_polled_ehcip;
18020Sstevel@tonic-gate ehci_qtd_t *curr_qtd = NULL, *next_qtd = NULL;
18030Sstevel@tonic-gate ehci_qtd_t *done_qtd_list = NULL, *last_done_qtd = NULL;
18040Sstevel@tonic-gate
18050Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
18060Sstevel@tonic-gate "ehci_polled_create_done_qtd_list:");
18070Sstevel@tonic-gate
18080Sstevel@tonic-gate curr_qtd = ehci_polledp->ehci_polled_active_intr_qtd_list;
18090Sstevel@tonic-gate
18100Sstevel@tonic-gate while (curr_qtd) {
18110Sstevel@tonic-gate
18120Sstevel@tonic-gate /* Get next qtd from the active qtd list */
18130Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
18140Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
18150Sstevel@tonic-gate
18160Sstevel@tonic-gate /* Check this QTD has been processed by Host Controller */
18170Sstevel@tonic-gate if (!(Get_QTD(curr_qtd->qtd_ctrl) &
18180Sstevel@tonic-gate EHCI_QTD_CTRL_ACTIVE_XACT)) {
18190Sstevel@tonic-gate
18200Sstevel@tonic-gate /* Remove this QTD from active QTD list */
18210Sstevel@tonic-gate ehci_polled_remove_qtd_from_active_intr_qtd_list(
18220Sstevel@tonic-gate ehci_polledp, curr_qtd);
18230Sstevel@tonic-gate
18240Sstevel@tonic-gate Set_QTD(curr_qtd->qtd_active_qtd_next, NULL);
18250Sstevel@tonic-gate
18260Sstevel@tonic-gate if (done_qtd_list) {
18270Sstevel@tonic-gate Set_QTD(last_done_qtd->qtd_active_qtd_next,
18280Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(ehcip, curr_qtd));
18290Sstevel@tonic-gate
18300Sstevel@tonic-gate last_done_qtd = curr_qtd;
18310Sstevel@tonic-gate } else {
18320Sstevel@tonic-gate done_qtd_list = curr_qtd;
18330Sstevel@tonic-gate last_done_qtd = curr_qtd;
18340Sstevel@tonic-gate }
18350Sstevel@tonic-gate }
18360Sstevel@tonic-gate
18370Sstevel@tonic-gate curr_qtd = next_qtd;
18380Sstevel@tonic-gate }
18390Sstevel@tonic-gate
18400Sstevel@tonic-gate return (done_qtd_list);
18410Sstevel@tonic-gate }
18420Sstevel@tonic-gate
18430Sstevel@tonic-gate
18440Sstevel@tonic-gate /*
18450Sstevel@tonic-gate * ehci_polled_insert_qtd_into_active_intr_qtd_list:
18460Sstevel@tonic-gate *
18470Sstevel@tonic-gate * Insert current QTD into active interrupt QTD list.
18480Sstevel@tonic-gate */
18490Sstevel@tonic-gate static void
ehci_polled_insert_qtd_into_active_intr_qtd_list(ehci_polled_t * ehci_polledp,ehci_qtd_t * qtd)18500Sstevel@tonic-gate ehci_polled_insert_qtd_into_active_intr_qtd_list(
18510Sstevel@tonic-gate ehci_polled_t *ehci_polledp,
18520Sstevel@tonic-gate ehci_qtd_t *qtd)
18530Sstevel@tonic-gate {
18540Sstevel@tonic-gate ehci_state_t *ehcip = ehci_polledp->ehci_polled_ehcip;
18550Sstevel@tonic-gate ehci_qtd_t *curr_qtd, *next_qtd;
18560Sstevel@tonic-gate
18570Sstevel@tonic-gate curr_qtd = ehci_polledp->ehci_polled_active_intr_qtd_list;
18580Sstevel@tonic-gate
18590Sstevel@tonic-gate /* Insert this qtd into active intr qtd list */
18600Sstevel@tonic-gate if (curr_qtd) {
18610Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
18620Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
18630Sstevel@tonic-gate
18640Sstevel@tonic-gate while (next_qtd) {
18650Sstevel@tonic-gate curr_qtd = next_qtd;
18660Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
18670Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
18680Sstevel@tonic-gate }
18690Sstevel@tonic-gate
18700Sstevel@tonic-gate Set_QTD(qtd->qtd_active_qtd_prev,
18710Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(ehcip, curr_qtd));
18720Sstevel@tonic-gate
18730Sstevel@tonic-gate Set_QTD(curr_qtd->qtd_active_qtd_next,
18740Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(ehcip, qtd));
18750Sstevel@tonic-gate } else {
18760Sstevel@tonic-gate ehci_polledp->ehci_polled_active_intr_qtd_list = qtd;
18770Sstevel@tonic-gate Set_QTD(qtd->qtd_active_qtd_next, NULL);
18780Sstevel@tonic-gate Set_QTD(qtd->qtd_active_qtd_prev, NULL);
18790Sstevel@tonic-gate }
18800Sstevel@tonic-gate }
18810Sstevel@tonic-gate
18820Sstevel@tonic-gate
18830Sstevel@tonic-gate /*
18840Sstevel@tonic-gate * ehci_polled_remove_qtd_from_active_intr_qtd_list:
18850Sstevel@tonic-gate *
18860Sstevel@tonic-gate * Remove current QTD from the active QTD list.
18870Sstevel@tonic-gate */
18880Sstevel@tonic-gate void
ehci_polled_remove_qtd_from_active_intr_qtd_list(ehci_polled_t * ehci_polledp,ehci_qtd_t * qtd)18890Sstevel@tonic-gate ehci_polled_remove_qtd_from_active_intr_qtd_list(
18900Sstevel@tonic-gate ehci_polled_t *ehci_polledp,
18910Sstevel@tonic-gate ehci_qtd_t *qtd)
18920Sstevel@tonic-gate {
18930Sstevel@tonic-gate ehci_state_t *ehcip = ehci_polledp->ehci_polled_ehcip;
18940Sstevel@tonic-gate ehci_qtd_t *curr_qtd, *prev_qtd, *next_qtd;
18950Sstevel@tonic-gate
18960Sstevel@tonic-gate ASSERT(qtd != NULL);
18970Sstevel@tonic-gate
18980Sstevel@tonic-gate curr_qtd = ehci_polledp->ehci_polled_active_intr_qtd_list;
18990Sstevel@tonic-gate
19000Sstevel@tonic-gate while ((curr_qtd) && (curr_qtd != qtd)) {
19010Sstevel@tonic-gate curr_qtd = ehci_qtd_iommu_to_cpu(ehcip,
19020Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
19030Sstevel@tonic-gate }
19040Sstevel@tonic-gate
19050Sstevel@tonic-gate if ((curr_qtd) && (curr_qtd == qtd)) {
19060Sstevel@tonic-gate prev_qtd = ehci_qtd_iommu_to_cpu(ehcip,
19070Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_prev));
19080Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
19090Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
19100Sstevel@tonic-gate
19110Sstevel@tonic-gate if (prev_qtd) {
19120Sstevel@tonic-gate Set_QTD(prev_qtd->qtd_active_qtd_next,
19130Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
19140Sstevel@tonic-gate } else {
19150Sstevel@tonic-gate ehci_polledp->
19160Sstevel@tonic-gate ehci_polled_active_intr_qtd_list = next_qtd;
19170Sstevel@tonic-gate }
19180Sstevel@tonic-gate
19190Sstevel@tonic-gate if (next_qtd) {
19200Sstevel@tonic-gate Set_QTD(next_qtd->qtd_active_qtd_prev,
19210Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_prev));
19220Sstevel@tonic-gate }
19230Sstevel@tonic-gate }
19240Sstevel@tonic-gate }
19250Sstevel@tonic-gate
19260Sstevel@tonic-gate
19270Sstevel@tonic-gate /*
19280Sstevel@tonic-gate * ehci_polled_traverse_qtds:
19290Sstevel@tonic-gate *
19300Sstevel@tonic-gate * Traverse the list of QTDs for given pipe using transfer wrapper. Since
19310Sstevel@tonic-gate * the endpoint is marked as Halted, the Host Controller (HC) is no longer
19320Sstevel@tonic-gate * accessing these QTDs. Remove all the QTDs that are attached to endpoint.
19330Sstevel@tonic-gate */
19340Sstevel@tonic-gate static void
ehci_polled_traverse_qtds(ehci_polled_t * ehci_polledp,usba_pipe_handle_data_t * ph)19350Sstevel@tonic-gate ehci_polled_traverse_qtds(
19360Sstevel@tonic-gate ehci_polled_t *ehci_polledp,
19370Sstevel@tonic-gate usba_pipe_handle_data_t *ph)
19380Sstevel@tonic-gate {
19390Sstevel@tonic-gate ehci_state_t *ehcip = ehci_polledp->ehci_polled_ehcip;
19400Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
19410Sstevel@tonic-gate ehci_trans_wrapper_t *next_tw;
19420Sstevel@tonic-gate ehci_qtd_t *qtd;
19430Sstevel@tonic-gate ehci_qtd_t *next_qtd;
19440Sstevel@tonic-gate
19450Sstevel@tonic-gate /* Process the transfer wrappers for this pipe */
19460Sstevel@tonic-gate next_tw = pp->pp_tw_head;
19470Sstevel@tonic-gate
19480Sstevel@tonic-gate while (next_tw) {
19490Sstevel@tonic-gate qtd = (ehci_qtd_t *)next_tw->tw_qtd_head;
19500Sstevel@tonic-gate
19510Sstevel@tonic-gate /* Walk through each QTD for this transfer wrapper */
19520Sstevel@tonic-gate while (qtd) {
19530Sstevel@tonic-gate /* Remove this QTD from active QTD list */
19540Sstevel@tonic-gate ehci_polled_remove_qtd_from_active_intr_qtd_list(
19550Sstevel@tonic-gate ehci_polledp, qtd);
19560Sstevel@tonic-gate
19570Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
19580Sstevel@tonic-gate Get_QTD(qtd->qtd_tw_next_qtd));
19590Sstevel@tonic-gate
19600Sstevel@tonic-gate /* Deallocate this QTD */
19610Sstevel@tonic-gate ehci_deallocate_qtd(ehcip, qtd);
19620Sstevel@tonic-gate
19630Sstevel@tonic-gate qtd = next_qtd;
19640Sstevel@tonic-gate }
19650Sstevel@tonic-gate
19660Sstevel@tonic-gate next_tw = next_tw->tw_next;
19670Sstevel@tonic-gate }
19680Sstevel@tonic-gate
19690Sstevel@tonic-gate /* Clear current qtd pointer */
19700Sstevel@tonic-gate Set_QH(pp->pp_qh->qh_curr_qtd, (uint32_t)0x00000000);
19710Sstevel@tonic-gate
19720Sstevel@tonic-gate /* Update the next qtd pointer in the QH */
19730Sstevel@tonic-gate Set_QH(pp->pp_qh->qh_next_qtd, Get_QH(pp->pp_qh->qh_dummy_qtd));
19740Sstevel@tonic-gate }
19750Sstevel@tonic-gate
19760Sstevel@tonic-gate
19770Sstevel@tonic-gate /*
19780Sstevel@tonic-gate * ehci_polled_finish_interrupt:
19790Sstevel@tonic-gate */
19800Sstevel@tonic-gate static void
ehci_polled_finish_interrupt(ehci_state_t * ehcip,uint_t intr)19810Sstevel@tonic-gate ehci_polled_finish_interrupt(
19820Sstevel@tonic-gate ehci_state_t *ehcip,
19830Sstevel@tonic-gate uint_t intr)
19840Sstevel@tonic-gate {
19850Sstevel@tonic-gate /* Acknowledge the interrupt */
19860Sstevel@tonic-gate Set_OpReg(ehci_status, intr);
19870Sstevel@tonic-gate
19880Sstevel@tonic-gate /*
19890Sstevel@tonic-gate * Read interrupt status register to make sure that any PIO
19900Sstevel@tonic-gate * store to clear the ISR has made it on the PCI bus before
19910Sstevel@tonic-gate * returning from its interrupt handler.
19920Sstevel@tonic-gate */
19930Sstevel@tonic-gate (void) Get_OpReg(ehci_status);
19940Sstevel@tonic-gate }
1995*9095SZhigang.Lu@Sun.COM
1996*9095SZhigang.Lu@Sun.COM
1997*9095SZhigang.Lu@Sun.COM static int
ehci_polled_create_tw(ehci_polled_t * ehci_polledp,usba_pipe_handle_data_t * ph,usb_flags_t usb_flags)1998*9095SZhigang.Lu@Sun.COM ehci_polled_create_tw(
1999*9095SZhigang.Lu@Sun.COM ehci_polled_t *ehci_polledp,
2000*9095SZhigang.Lu@Sun.COM usba_pipe_handle_data_t *ph,
2001*9095SZhigang.Lu@Sun.COM usb_flags_t usb_flags)
2002*9095SZhigang.Lu@Sun.COM {
2003*9095SZhigang.Lu@Sun.COM uint_t ccount;
2004*9095SZhigang.Lu@Sun.COM size_t real_length;
2005*9095SZhigang.Lu@Sun.COM ehci_trans_wrapper_t *tw;
2006*9095SZhigang.Lu@Sun.COM ddi_device_acc_attr_t dev_attr;
2007*9095SZhigang.Lu@Sun.COM int result, pipe_dir, qtd_count;
2008*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip;
2009*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp;
2010*9095SZhigang.Lu@Sun.COM ddi_dma_attr_t dma_attr;
2011*9095SZhigang.Lu@Sun.COM
2012*9095SZhigang.Lu@Sun.COM ehcip = ehci_polledp->ehci_polled_ehcip;
2013*9095SZhigang.Lu@Sun.COM pp = (ehci_pipe_private_t *)ph->p_hcd_private;
2014*9095SZhigang.Lu@Sun.COM
2015*9095SZhigang.Lu@Sun.COM /* Get the required qtd counts */
2016*9095SZhigang.Lu@Sun.COM qtd_count = (POLLED_RAW_BUF_SIZE - 1) / EHCI_MAX_QTD_XFER_SIZE + 1;
2017*9095SZhigang.Lu@Sun.COM
2018*9095SZhigang.Lu@Sun.COM if ((tw = kmem_zalloc(sizeof (ehci_trans_wrapper_t),
2019*9095SZhigang.Lu@Sun.COM KM_NOSLEEP)) == NULL) {
2020*9095SZhigang.Lu@Sun.COM return (USB_FAILURE);
2021*9095SZhigang.Lu@Sun.COM }
2022*9095SZhigang.Lu@Sun.COM
2023*9095SZhigang.Lu@Sun.COM /* allow sg lists for transfer wrapper dma memory */
2024*9095SZhigang.Lu@Sun.COM bcopy(&ehcip->ehci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
2025*9095SZhigang.Lu@Sun.COM dma_attr.dma_attr_sgllen = EHCI_DMA_ATTR_TW_SGLLEN;
2026*9095SZhigang.Lu@Sun.COM dma_attr.dma_attr_align = EHCI_DMA_ATTR_ALIGNMENT;
2027*9095SZhigang.Lu@Sun.COM
2028*9095SZhigang.Lu@Sun.COM /* Allocate the DMA handle */
2029*9095SZhigang.Lu@Sun.COM if ((result = ddi_dma_alloc_handle(ehcip->ehci_dip,
2030*9095SZhigang.Lu@Sun.COM &dma_attr, DDI_DMA_DONTWAIT, 0, &tw->tw_dmahandle)) !=
2031*9095SZhigang.Lu@Sun.COM DDI_SUCCESS) {
2032*9095SZhigang.Lu@Sun.COM kmem_free(tw, sizeof (ehci_trans_wrapper_t));
2033*9095SZhigang.Lu@Sun.COM
2034*9095SZhigang.Lu@Sun.COM return (USB_FAILURE);
2035*9095SZhigang.Lu@Sun.COM }
2036*9095SZhigang.Lu@Sun.COM
2037*9095SZhigang.Lu@Sun.COM dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
2038*9095SZhigang.Lu@Sun.COM dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
2039*9095SZhigang.Lu@Sun.COM dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
2040*9095SZhigang.Lu@Sun.COM
2041*9095SZhigang.Lu@Sun.COM /* Allocate the memory */
2042*9095SZhigang.Lu@Sun.COM if ((result = ddi_dma_mem_alloc(tw->tw_dmahandle, POLLED_RAW_BUF_SIZE,
2043*9095SZhigang.Lu@Sun.COM &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
2044*9095SZhigang.Lu@Sun.COM &tw->tw_buf, &real_length, &tw->tw_accesshandle)) !=
2045*9095SZhigang.Lu@Sun.COM DDI_SUCCESS) {
2046*9095SZhigang.Lu@Sun.COM ddi_dma_free_handle(&tw->tw_dmahandle);
2047*9095SZhigang.Lu@Sun.COM kmem_free(tw, sizeof (ehci_trans_wrapper_t));
2048*9095SZhigang.Lu@Sun.COM
2049*9095SZhigang.Lu@Sun.COM return (USB_FAILURE);
2050*9095SZhigang.Lu@Sun.COM }
2051*9095SZhigang.Lu@Sun.COM
2052*9095SZhigang.Lu@Sun.COM /* Bind the handle */
2053*9095SZhigang.Lu@Sun.COM if ((result = ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
2054*9095SZhigang.Lu@Sun.COM tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
2055*9095SZhigang.Lu@Sun.COM DDI_DMA_DONTWAIT, NULL, &tw->tw_cookie, &ccount)) !=
2056*9095SZhigang.Lu@Sun.COM DDI_DMA_MAPPED) {
2057*9095SZhigang.Lu@Sun.COM ddi_dma_mem_free(&tw->tw_accesshandle);
2058*9095SZhigang.Lu@Sun.COM ddi_dma_free_handle(&tw->tw_dmahandle);
2059*9095SZhigang.Lu@Sun.COM kmem_free(tw, sizeof (ehci_trans_wrapper_t));
2060*9095SZhigang.Lu@Sun.COM
2061*9095SZhigang.Lu@Sun.COM return (USB_FAILURE);
2062*9095SZhigang.Lu@Sun.COM }
2063*9095SZhigang.Lu@Sun.COM
2064*9095SZhigang.Lu@Sun.COM /* The cookie count should be 1 */
2065*9095SZhigang.Lu@Sun.COM if (ccount != 1) {
2066*9095SZhigang.Lu@Sun.COM result = ddi_dma_unbind_handle(tw->tw_dmahandle);
2067*9095SZhigang.Lu@Sun.COM ASSERT(result == DDI_SUCCESS);
2068*9095SZhigang.Lu@Sun.COM
2069*9095SZhigang.Lu@Sun.COM ddi_dma_mem_free(&tw->tw_accesshandle);
2070*9095SZhigang.Lu@Sun.COM ddi_dma_free_handle(&tw->tw_dmahandle);
2071*9095SZhigang.Lu@Sun.COM kmem_free(tw, sizeof (ehci_trans_wrapper_t));
2072*9095SZhigang.Lu@Sun.COM
2073*9095SZhigang.Lu@Sun.COM return (USB_FAILURE);
2074*9095SZhigang.Lu@Sun.COM }
2075*9095SZhigang.Lu@Sun.COM
2076*9095SZhigang.Lu@Sun.COM if (ehci_allocate_tds_for_tw(ehcip, pp, tw, qtd_count) == USB_SUCCESS) {
2077*9095SZhigang.Lu@Sun.COM tw->tw_num_qtds = qtd_count;
2078*9095SZhigang.Lu@Sun.COM } else {
2079*9095SZhigang.Lu@Sun.COM ehci_deallocate_tw(ehcip, pp, tw);
2080*9095SZhigang.Lu@Sun.COM return (USB_FAILURE);
2081*9095SZhigang.Lu@Sun.COM }
2082*9095SZhigang.Lu@Sun.COM tw->tw_cookie_idx = 0;
2083*9095SZhigang.Lu@Sun.COM tw->tw_dma_offs = 0;
2084*9095SZhigang.Lu@Sun.COM
2085*9095SZhigang.Lu@Sun.COM /*
2086*9095SZhigang.Lu@Sun.COM * Only allow one wrapper to be added at a time. Insert the
2087*9095SZhigang.Lu@Sun.COM * new transaction wrapper into the list for this pipe.
2088*9095SZhigang.Lu@Sun.COM */
2089*9095SZhigang.Lu@Sun.COM if (pp->pp_tw_head == NULL) {
2090*9095SZhigang.Lu@Sun.COM pp->pp_tw_head = tw;
2091*9095SZhigang.Lu@Sun.COM pp->pp_tw_tail = tw;
2092*9095SZhigang.Lu@Sun.COM } else {
2093*9095SZhigang.Lu@Sun.COM pp->pp_tw_tail->tw_next = tw;
2094*9095SZhigang.Lu@Sun.COM pp->pp_tw_tail = tw;
2095*9095SZhigang.Lu@Sun.COM }
2096*9095SZhigang.Lu@Sun.COM
2097*9095SZhigang.Lu@Sun.COM /* Store the transfer length */
2098*9095SZhigang.Lu@Sun.COM tw->tw_length = POLLED_RAW_BUF_SIZE;
2099*9095SZhigang.Lu@Sun.COM
2100*9095SZhigang.Lu@Sun.COM /* Store a back pointer to the pipe private structure */
2101*9095SZhigang.Lu@Sun.COM tw->tw_pipe_private = pp;
2102*9095SZhigang.Lu@Sun.COM
2103*9095SZhigang.Lu@Sun.COM /* Store the transfer type - synchronous or asynchronous */
2104*9095SZhigang.Lu@Sun.COM tw->tw_flags = usb_flags;
2105*9095SZhigang.Lu@Sun.COM
2106*9095SZhigang.Lu@Sun.COM /* Get and Store 32bit ID */
2107*9095SZhigang.Lu@Sun.COM tw->tw_id = EHCI_GET_ID((void *)tw);
2108*9095SZhigang.Lu@Sun.COM
2109*9095SZhigang.Lu@Sun.COM pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
2110*9095SZhigang.Lu@Sun.COM tw->tw_direction = (pipe_dir == USB_EP_DIR_OUT)?
2111*9095SZhigang.Lu@Sun.COM EHCI_QTD_CTRL_OUT_PID : EHCI_QTD_CTRL_IN_PID;
2112*9095SZhigang.Lu@Sun.COM
2113*9095SZhigang.Lu@Sun.COM USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
2114*9095SZhigang.Lu@Sun.COM "ehci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
2115*9095SZhigang.Lu@Sun.COM (void *)tw, tw->tw_ncookies);
2116*9095SZhigang.Lu@Sun.COM
2117*9095SZhigang.Lu@Sun.COM return (USB_SUCCESS);
2118*9095SZhigang.Lu@Sun.COM }
2119*9095SZhigang.Lu@Sun.COM
2120*9095SZhigang.Lu@Sun.COM
2121*9095SZhigang.Lu@Sun.COM /*
2122*9095SZhigang.Lu@Sun.COM * ehci_polled_insert_async_qh:
2123*9095SZhigang.Lu@Sun.COM *
2124*9095SZhigang.Lu@Sun.COM * Insert a bulk endpoint into the Host Controller's (HC)
2125*9095SZhigang.Lu@Sun.COM * Asynchronous schedule endpoint list.
2126*9095SZhigang.Lu@Sun.COM */
2127*9095SZhigang.Lu@Sun.COM static void
ehci_polled_insert_async_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp)2128*9095SZhigang.Lu@Sun.COM ehci_polled_insert_async_qh(
2129*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip,
2130*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp)
2131*9095SZhigang.Lu@Sun.COM {
2132*9095SZhigang.Lu@Sun.COM ehci_qh_t *qh = pp->pp_qh;
2133*9095SZhigang.Lu@Sun.COM ehci_qh_t *async_head_qh;
2134*9095SZhigang.Lu@Sun.COM ehci_qh_t *next_qh;
2135*9095SZhigang.Lu@Sun.COM uintptr_t qh_addr;
2136*9095SZhigang.Lu@Sun.COM
2137*9095SZhigang.Lu@Sun.COM /* Make sure this QH is not already in the list */
2138*9095SZhigang.Lu@Sun.COM ASSERT((Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR) == NULL);
2139*9095SZhigang.Lu@Sun.COM
2140*9095SZhigang.Lu@Sun.COM qh_addr = ehci_qh_cpu_to_iommu(ehcip, qh);
2141*9095SZhigang.Lu@Sun.COM
2142*9095SZhigang.Lu@Sun.COM /* Obtain a ptr to the head of the Async schedule list */
2143*9095SZhigang.Lu@Sun.COM async_head_qh = ehcip->ehci_head_of_async_sched_list;
2144*9095SZhigang.Lu@Sun.COM
2145*9095SZhigang.Lu@Sun.COM if (async_head_qh == NULL) {
2146*9095SZhigang.Lu@Sun.COM /* Set this QH to be the "head" of the circular list */
2147*9095SZhigang.Lu@Sun.COM Set_QH(qh->qh_ctrl,
2148*9095SZhigang.Lu@Sun.COM (Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_RECLAIM_HEAD));
2149*9095SZhigang.Lu@Sun.COM
2150*9095SZhigang.Lu@Sun.COM /* Set new QH's link and previous pointer to itself */
2151*9095SZhigang.Lu@Sun.COM Set_QH(qh->qh_link_ptr, qh_addr | EHCI_QH_LINK_REF_QH);
2152*9095SZhigang.Lu@Sun.COM Set_QH(qh->qh_prev, qh_addr);
2153*9095SZhigang.Lu@Sun.COM
2154*9095SZhigang.Lu@Sun.COM ehcip->ehci_head_of_async_sched_list = qh;
2155*9095SZhigang.Lu@Sun.COM
2156*9095SZhigang.Lu@Sun.COM /* Set the head ptr to the new endpoint */
2157*9095SZhigang.Lu@Sun.COM Set_OpReg(ehci_async_list_addr, qh_addr);
2158*9095SZhigang.Lu@Sun.COM
2159*9095SZhigang.Lu@Sun.COM /*
2160*9095SZhigang.Lu@Sun.COM * For some reason this register might get nulled out by
2161*9095SZhigang.Lu@Sun.COM * the Uli M1575 South Bridge. To workaround the hardware
2162*9095SZhigang.Lu@Sun.COM * problem, check the value after write and retry if the
2163*9095SZhigang.Lu@Sun.COM * last write fails.
2164*9095SZhigang.Lu@Sun.COM *
2165*9095SZhigang.Lu@Sun.COM * If the ASYNCLISTADDR remains "stuck" after
2166*9095SZhigang.Lu@Sun.COM * EHCI_MAX_RETRY retries, then the M1575 is broken
2167*9095SZhigang.Lu@Sun.COM * and is stuck in an inconsistent state and is about
2168*9095SZhigang.Lu@Sun.COM * to crash the machine with a trn_oor panic when it
2169*9095SZhigang.Lu@Sun.COM * does a DMA read from 0x0. It is better to panic
2170*9095SZhigang.Lu@Sun.COM * now rather than wait for the trn_oor crash; this
2171*9095SZhigang.Lu@Sun.COM * way Customer Service will have a clean signature
2172*9095SZhigang.Lu@Sun.COM * that indicts the M1575 chip rather than a
2173*9095SZhigang.Lu@Sun.COM * mysterious and hard-to-diagnose trn_oor panic.
2174*9095SZhigang.Lu@Sun.COM */
2175*9095SZhigang.Lu@Sun.COM if ((ehcip->ehci_vendor_id == PCI_VENDOR_ULi_M1575) &&
2176*9095SZhigang.Lu@Sun.COM (ehcip->ehci_device_id == PCI_DEVICE_ULi_M1575) &&
2177*9095SZhigang.Lu@Sun.COM (qh_addr != Get_OpReg(ehci_async_list_addr))) {
2178*9095SZhigang.Lu@Sun.COM int retry = 0;
2179*9095SZhigang.Lu@Sun.COM
2180*9095SZhigang.Lu@Sun.COM Set_OpRegRetry(ehci_async_list_addr, qh_addr, retry);
2181*9095SZhigang.Lu@Sun.COM if (retry >= EHCI_MAX_RETRY)
2182*9095SZhigang.Lu@Sun.COM cmn_err(CE_PANIC, "ehci_insert_async_qh:"
2183*9095SZhigang.Lu@Sun.COM " ASYNCLISTADDR write failed.");
2184*9095SZhigang.Lu@Sun.COM
2185*9095SZhigang.Lu@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
2186*9095SZhigang.Lu@Sun.COM "ehci_insert_async_qh: ASYNCLISTADDR "
2187*9095SZhigang.Lu@Sun.COM "write failed, retry=%d", retry);
2188*9095SZhigang.Lu@Sun.COM }
2189*9095SZhigang.Lu@Sun.COM } else {
2190*9095SZhigang.Lu@Sun.COM ASSERT(Get_QH(async_head_qh->qh_ctrl) &
2191*9095SZhigang.Lu@Sun.COM EHCI_QH_CTRL_RECLAIM_HEAD);
2192*9095SZhigang.Lu@Sun.COM
2193*9095SZhigang.Lu@Sun.COM /* Ensure this QH's "H" bit is not set */
2194*9095SZhigang.Lu@Sun.COM Set_QH(qh->qh_ctrl,
2195*9095SZhigang.Lu@Sun.COM (Get_QH(qh->qh_ctrl) & ~EHCI_QH_CTRL_RECLAIM_HEAD));
2196*9095SZhigang.Lu@Sun.COM
2197*9095SZhigang.Lu@Sun.COM next_qh = ehci_qh_iommu_to_cpu(ehcip,
2198*9095SZhigang.Lu@Sun.COM Get_QH(async_head_qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
2199*9095SZhigang.Lu@Sun.COM
2200*9095SZhigang.Lu@Sun.COM /* Set new QH's link and previous pointers */
2201*9095SZhigang.Lu@Sun.COM Set_QH(qh->qh_link_ptr,
2202*9095SZhigang.Lu@Sun.COM Get_QH(async_head_qh->qh_link_ptr) | EHCI_QH_LINK_REF_QH);
2203*9095SZhigang.Lu@Sun.COM Set_QH(qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, async_head_qh));
2204*9095SZhigang.Lu@Sun.COM
2205*9095SZhigang.Lu@Sun.COM /* Set next QH's prev pointer */
2206*9095SZhigang.Lu@Sun.COM Set_QH(next_qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, qh));
2207*9095SZhigang.Lu@Sun.COM
2208*9095SZhigang.Lu@Sun.COM /* Set QH Head's link pointer points to new QH */
2209*9095SZhigang.Lu@Sun.COM Set_QH(async_head_qh->qh_link_ptr,
2210*9095SZhigang.Lu@Sun.COM qh_addr | EHCI_QH_LINK_REF_QH);
2211*9095SZhigang.Lu@Sun.COM }
2212*9095SZhigang.Lu@Sun.COM }
2213*9095SZhigang.Lu@Sun.COM
2214*9095SZhigang.Lu@Sun.COM
2215*9095SZhigang.Lu@Sun.COM /*
2216*9095SZhigang.Lu@Sun.COM * ehci_remove_async_qh:
2217*9095SZhigang.Lu@Sun.COM *
2218*9095SZhigang.Lu@Sun.COM * Remove a control/bulk endpoint into the Host Controller's (HC)
2219*9095SZhigang.Lu@Sun.COM * Asynchronous schedule endpoint list.
2220*9095SZhigang.Lu@Sun.COM */
2221*9095SZhigang.Lu@Sun.COM static void
ehci_polled_remove_async_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp)2222*9095SZhigang.Lu@Sun.COM ehci_polled_remove_async_qh(
2223*9095SZhigang.Lu@Sun.COM ehci_state_t *ehcip,
2224*9095SZhigang.Lu@Sun.COM ehci_pipe_private_t *pp)
2225*9095SZhigang.Lu@Sun.COM {
2226*9095SZhigang.Lu@Sun.COM ehci_qh_t *qh = pp->pp_qh; /* qh to be removed */
2227*9095SZhigang.Lu@Sun.COM ehci_qh_t *prev_qh, *next_qh;
2228*9095SZhigang.Lu@Sun.COM
2229*9095SZhigang.Lu@Sun.COM prev_qh = ehci_qh_iommu_to_cpu(ehcip,
2230*9095SZhigang.Lu@Sun.COM Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR);
2231*9095SZhigang.Lu@Sun.COM next_qh = ehci_qh_iommu_to_cpu(ehcip,
2232*9095SZhigang.Lu@Sun.COM Get_QH(qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
2233*9095SZhigang.Lu@Sun.COM
2234*9095SZhigang.Lu@Sun.COM /* Make sure this QH is in the list */
2235*9095SZhigang.Lu@Sun.COM ASSERT(prev_qh != NULL);
2236*9095SZhigang.Lu@Sun.COM
2237*9095SZhigang.Lu@Sun.COM /*
2238*9095SZhigang.Lu@Sun.COM * If next QH and current QH are the same, then this is the last
2239*9095SZhigang.Lu@Sun.COM * QH on the Asynchronous Schedule list.
2240*9095SZhigang.Lu@Sun.COM */
2241*9095SZhigang.Lu@Sun.COM if (qh == next_qh) {
2242*9095SZhigang.Lu@Sun.COM ASSERT(Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_RECLAIM_HEAD);
2243*9095SZhigang.Lu@Sun.COM /*
2244*9095SZhigang.Lu@Sun.COM * Null our pointer to the async sched list, but do not
2245*9095SZhigang.Lu@Sun.COM * touch the host controller's list_addr.
2246*9095SZhigang.Lu@Sun.COM */
2247*9095SZhigang.Lu@Sun.COM ehcip->ehci_head_of_async_sched_list = NULL;
2248*9095SZhigang.Lu@Sun.COM } else {
2249*9095SZhigang.Lu@Sun.COM /* If this QH is the HEAD then find another one to replace it */
2250*9095SZhigang.Lu@Sun.COM if (ehcip->ehci_head_of_async_sched_list == qh) {
2251*9095SZhigang.Lu@Sun.COM
2252*9095SZhigang.Lu@Sun.COM ASSERT(Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_RECLAIM_HEAD);
2253*9095SZhigang.Lu@Sun.COM ehcip->ehci_head_of_async_sched_list = next_qh;
2254*9095SZhigang.Lu@Sun.COM Set_QH(next_qh->qh_ctrl,
2255*9095SZhigang.Lu@Sun.COM Get_QH(next_qh->qh_ctrl) |
2256*9095SZhigang.Lu@Sun.COM EHCI_QH_CTRL_RECLAIM_HEAD);
2257*9095SZhigang.Lu@Sun.COM }
2258*9095SZhigang.Lu@Sun.COM Set_QH(prev_qh->qh_link_ptr, Get_QH(qh->qh_link_ptr));
2259*9095SZhigang.Lu@Sun.COM Set_QH(next_qh->qh_prev, Get_QH(qh->qh_prev));
2260*9095SZhigang.Lu@Sun.COM }
2261*9095SZhigang.Lu@Sun.COM
2262*9095SZhigang.Lu@Sun.COM /* qh_prev to indicate it is no longer in the circular list */
2263*9095SZhigang.Lu@Sun.COM Set_QH(qh->qh_prev, NULL);
2264*9095SZhigang.Lu@Sun.COM
2265*9095SZhigang.Lu@Sun.COM }
2266