1184610Salfred/* $FreeBSD$ */
2184610Salfred/*-
3189002Sed * Copyright (c) 2008 Hans Petter Selasky <hselasky@FreeBSD.org>
4184610Salfred * All rights reserved.
5184610Salfred *
6184610Salfred * Redistribution and use in source and binary forms, with or without
7184610Salfred * modification, are permitted provided that the following conditions
8184610Salfred * are met:
9184610Salfred * 1. Redistributions of source code must retain the above copyright
10184610Salfred *    notice, this list of conditions and the following disclaimer.
11184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
12184610Salfred *    notice, this list of conditions and the following disclaimer in the
13184610Salfred *    documentation and/or other materials provided with the distribution.
14184610Salfred *
15184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25184610Salfred * SUCH DAMAGE.
26184610Salfred */
27184610Salfred
28184610Salfred/*
29184610Salfred * This file contains the driver for the USS820 series USB Device
30184610Salfred * Controller
31184610Salfred *
32190754Sthompsa * NOTE: The datasheet does not document everything.
33184610Salfred */
34184610Salfred
35246122Shselasky#ifdef USB_GLOBAL_INCLUDE_FILE
36246122Shselasky#include USB_GLOBAL_INCLUDE_FILE
37246122Shselasky#else
38194677Sthompsa#include <sys/stdint.h>
39194677Sthompsa#include <sys/stddef.h>
40194677Sthompsa#include <sys/param.h>
41194677Sthompsa#include <sys/queue.h>
42194677Sthompsa#include <sys/types.h>
43194677Sthompsa#include <sys/systm.h>
44194677Sthompsa#include <sys/kernel.h>
45194677Sthompsa#include <sys/bus.h>
46194677Sthompsa#include <sys/module.h>
47194677Sthompsa#include <sys/lock.h>
48194677Sthompsa#include <sys/mutex.h>
49194677Sthompsa#include <sys/condvar.h>
50194677Sthompsa#include <sys/sysctl.h>
51194677Sthompsa#include <sys/sx.h>
52194677Sthompsa#include <sys/unistd.h>
53194677Sthompsa#include <sys/callout.h>
54194677Sthompsa#include <sys/malloc.h>
55194677Sthompsa#include <sys/priv.h>
56194677Sthompsa
57188942Sthompsa#include <dev/usb/usb.h>
58194677Sthompsa#include <dev/usb/usbdi.h>
59184610Salfred
60184610Salfred#define	USB_DEBUG_VAR uss820dcidebug
61184610Salfred
62188942Sthompsa#include <dev/usb/usb_core.h>
63188942Sthompsa#include <dev/usb/usb_debug.h>
64188942Sthompsa#include <dev/usb/usb_busdma.h>
65188942Sthompsa#include <dev/usb/usb_process.h>
66188942Sthompsa#include <dev/usb/usb_transfer.h>
67188942Sthompsa#include <dev/usb/usb_device.h>
68188942Sthompsa#include <dev/usb/usb_hub.h>
69188942Sthompsa#include <dev/usb/usb_util.h>
70184610Salfred
71188942Sthompsa#include <dev/usb/usb_controller.h>
72188942Sthompsa#include <dev/usb/usb_bus.h>
73246122Shselasky#endif			/* USB_GLOBAL_INCLUDE_FILE */
74246122Shselasky
75188942Sthompsa#include <dev/usb/controller/uss820dci.h>
76184610Salfred
77184610Salfred#define	USS820_DCI_BUS2SC(bus) \
78184610Salfred   ((struct uss820dci_softc *)(((uint8_t *)(bus)) - \
79190181Sthompsa    ((uint8_t *)&(((struct uss820dci_softc *)0)->sc_bus))))
80184610Salfred
81184610Salfred#define	USS820_DCI_PC2SC(pc) \
82190180Sthompsa   USS820_DCI_BUS2SC(USB_DMATAG_TO_XROOT((pc)->tag_parent)->bus)
83184610Salfred
84207077Sthompsa#ifdef USB_DEBUG
85184610Salfredstatic int uss820dcidebug = 0;
86184610Salfred
87227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, uss820dci, CTLFLAG_RW, 0,
88227309Sed    "USB uss820dci");
89192502SthompsaSYSCTL_INT(_hw_usb_uss820dci, OID_AUTO, debug, CTLFLAG_RW,
90184610Salfred    &uss820dcidebug, 0, "uss820dci debug level");
91184610Salfred#endif
92184610Salfred
93184610Salfred#define	USS820_DCI_INTR_ENDPT 1
94184610Salfred
95184610Salfred/* prototypes */
96184610Salfred
97192984Sthompsastruct usb_bus_methods uss820dci_bus_methods;
98192984Sthompsastruct usb_pipe_methods uss820dci_device_bulk_methods;
99192984Sthompsastruct usb_pipe_methods uss820dci_device_ctrl_methods;
100192984Sthompsastruct usb_pipe_methods uss820dci_device_intr_methods;
101192984Sthompsastruct usb_pipe_methods uss820dci_device_isoc_fs_methods;
102184610Salfred
103184610Salfredstatic uss820dci_cmd_t uss820dci_setup_rx;
104184610Salfredstatic uss820dci_cmd_t uss820dci_data_rx;
105184610Salfredstatic uss820dci_cmd_t uss820dci_data_tx;
106184610Salfredstatic uss820dci_cmd_t uss820dci_data_tx_sync;
107193045Sthompsastatic void	uss820dci_device_done(struct usb_xfer *, usb_error_t);
108192984Sthompsastatic void	uss820dci_do_poll(struct usb_bus *);
109192984Sthompsastatic void	uss820dci_standard_done(struct usb_xfer *);
110192984Sthompsastatic void	uss820dci_intr_set(struct usb_xfer *, uint8_t);
111185948Sthompsastatic void	uss820dci_update_shared_1(struct uss820dci_softc *, uint8_t,
112185948Sthompsa		    uint8_t, uint8_t);
113190735Sthompsastatic void	uss820dci_root_intr(struct uss820dci_softc *);
114184610Salfred
115184610Salfred/*
116184610Salfred * Here is a list of what the USS820D chip can support. The main
117184610Salfred * limitation is that the sum of the buffer sizes must be less than
118184610Salfred * 1120 bytes.
119184610Salfred */
120192984Sthompsastatic const struct usb_hw_ep_profile
121184610Salfred	uss820dci_ep_profile[] = {
122184610Salfred
123184610Salfred	[0] = {
124184610Salfred		.max_in_frame_size = 32,
125184610Salfred		.max_out_frame_size = 32,
126184610Salfred		.is_simplex = 0,
127184610Salfred		.support_control = 1,
128184610Salfred	},
129184610Salfred	[1] = {
130184610Salfred		.max_in_frame_size = 64,
131184610Salfred		.max_out_frame_size = 64,
132184610Salfred		.is_simplex = 0,
133184610Salfred		.support_multi_buffer = 1,
134184610Salfred		.support_bulk = 1,
135184610Salfred		.support_interrupt = 1,
136184610Salfred		.support_in = 1,
137184610Salfred		.support_out = 1,
138184610Salfred	},
139184610Salfred	[2] = {
140184610Salfred		.max_in_frame_size = 8,
141184610Salfred		.max_out_frame_size = 8,
142184610Salfred		.is_simplex = 0,
143184610Salfred		.support_multi_buffer = 1,
144184610Salfred		.support_bulk = 1,
145184610Salfred		.support_interrupt = 1,
146184610Salfred		.support_in = 1,
147184610Salfred		.support_out = 1,
148184610Salfred	},
149184610Salfred	[3] = {
150184610Salfred		.max_in_frame_size = 256,
151184610Salfred		.max_out_frame_size = 256,
152184610Salfred		.is_simplex = 0,
153184610Salfred		.support_multi_buffer = 1,
154184610Salfred		.support_isochronous = 1,
155184610Salfred		.support_in = 1,
156184610Salfred		.support_out = 1,
157184610Salfred	},
158184610Salfred};
159184610Salfred
160184610Salfredstatic void
161184610Salfreduss820dci_update_shared_1(struct uss820dci_softc *sc, uint8_t reg,
162184610Salfred    uint8_t keep_mask, uint8_t set_mask)
163184610Salfred{
164184610Salfred	uint8_t temp;
165184610Salfred
166184610Salfred	USS820_WRITE_1(sc, USS820_PEND, 1);
167184610Salfred	temp = USS820_READ_1(sc, reg);
168184610Salfred	temp &= (keep_mask);
169184610Salfred	temp |= (set_mask);
170184610Salfred	USS820_WRITE_1(sc, reg, temp);
171184610Salfred	USS820_WRITE_1(sc, USS820_PEND, 0);
172184610Salfred}
173184610Salfred
174184610Salfredstatic void
175192984Sthompsauss820dci_get_hw_ep_profile(struct usb_device *udev,
176192984Sthompsa    const struct usb_hw_ep_profile **ppf, uint8_t ep_addr)
177184610Salfred{
178184610Salfred	if (ep_addr == 0) {
179184610Salfred		*ppf = uss820dci_ep_profile + 0;
180184610Salfred	} else if (ep_addr < 5) {
181184610Salfred		*ppf = uss820dci_ep_profile + 1;
182184610Salfred	} else if (ep_addr < 7) {
183184610Salfred		*ppf = uss820dci_ep_profile + 2;
184184610Salfred	} else if (ep_addr == 7) {
185184610Salfred		*ppf = uss820dci_ep_profile + 3;
186184610Salfred	} else {
187184610Salfred		*ppf = NULL;
188184610Salfred	}
189184610Salfred}
190184610Salfred
191184610Salfredstatic void
192184610Salfreduss820dci_pull_up(struct uss820dci_softc *sc)
193184610Salfred{
194184610Salfred	uint8_t temp;
195184610Salfred
196184610Salfred	/* pullup D+, if possible */
197184610Salfred
198184610Salfred	if (!sc->sc_flags.d_pulled_up &&
199184610Salfred	    sc->sc_flags.port_powered) {
200184610Salfred		sc->sc_flags.d_pulled_up = 1;
201184610Salfred
202184610Salfred		DPRINTF("\n");
203184610Salfred
204184610Salfred		temp = USS820_READ_1(sc, USS820_MCSR);
205184610Salfred		temp |= USS820_MCSR_DPEN;
206184610Salfred		USS820_WRITE_1(sc, USS820_MCSR, temp);
207184610Salfred	}
208184610Salfred}
209184610Salfred
210184610Salfredstatic void
211184610Salfreduss820dci_pull_down(struct uss820dci_softc *sc)
212184610Salfred{
213184610Salfred	uint8_t temp;
214184610Salfred
215184610Salfred	/* pulldown D+, if possible */
216184610Salfred
217184610Salfred	if (sc->sc_flags.d_pulled_up) {
218184610Salfred		sc->sc_flags.d_pulled_up = 0;
219184610Salfred
220184610Salfred		DPRINTF("\n");
221184610Salfred
222184610Salfred		temp = USS820_READ_1(sc, USS820_MCSR);
223184610Salfred		temp &= ~USS820_MCSR_DPEN;
224184610Salfred		USS820_WRITE_1(sc, USS820_MCSR, temp);
225184610Salfred	}
226184610Salfred}
227184610Salfred
228184610Salfredstatic void
229184610Salfreduss820dci_wakeup_peer(struct uss820dci_softc *sc)
230184610Salfred{
231184610Salfred	if (!(sc->sc_flags.status_suspend)) {
232184610Salfred		return;
233184610Salfred	}
234184610Salfred	DPRINTFN(0, "not supported\n");
235184610Salfred}
236184610Salfred
237184610Salfredstatic void
238184610Salfreduss820dci_set_address(struct uss820dci_softc *sc, uint8_t addr)
239184610Salfred{
240184610Salfred	DPRINTFN(5, "addr=%d\n", addr);
241184610Salfred
242184610Salfred	USS820_WRITE_1(sc, USS820_FADDR, addr);
243184610Salfred}
244184610Salfred
245184610Salfredstatic uint8_t
246184610Salfreduss820dci_setup_rx(struct uss820dci_td *td)
247184610Salfred{
248184610Salfred	struct uss820dci_softc *sc;
249192984Sthompsa	struct usb_device_request req;
250184610Salfred	uint16_t count;
251184610Salfred	uint8_t rx_stat;
252184610Salfred	uint8_t temp;
253184610Salfred
254184610Salfred	/* select the correct endpoint */
255184610Salfred	bus_space_write_1(td->io_tag, td->io_hdl,
256192448Sthompsa	    USS820_EPINDEX, td->ep_index);
257184610Salfred
258184610Salfred	/* read out FIFO status */
259184610Salfred	rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
260192448Sthompsa	    USS820_RXSTAT);
261184610Salfred
262184610Salfred	/* get pointer to softc */
263184610Salfred	sc = USS820_DCI_PC2SC(td->pc);
264184610Salfred
265184610Salfred	DPRINTFN(5, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder);
266184610Salfred
267184610Salfred	if (!(rx_stat & USS820_RXSTAT_RXSETUP)) {
268184610Salfred		goto not_complete;
269184610Salfred	}
270190721Sthompsa	/* clear did stall */
271190721Sthompsa	td->did_stall = 0;
272190721Sthompsa
273184610Salfred	/* clear stall and all I/O */
274184610Salfred	uss820dci_update_shared_1(sc, USS820_EPCON,
275184610Salfred	    0xFF ^ (USS820_EPCON_TXSTL |
276184610Salfred	    USS820_EPCON_RXSTL |
277184610Salfred	    USS820_EPCON_RXIE |
278184610Salfred	    USS820_EPCON_TXOE), 0);
279184610Salfred
280184610Salfred	/* clear end overwrite flag */
281184610Salfred	uss820dci_update_shared_1(sc, USS820_RXSTAT,
282184610Salfred	    0xFF ^ USS820_RXSTAT_EDOVW, 0);
283184610Salfred
284184610Salfred	/* get the packet byte count */
285184610Salfred	count = bus_space_read_1(td->io_tag, td->io_hdl,
286192448Sthompsa	    USS820_RXCNTL);
287184610Salfred	count |= (bus_space_read_1(td->io_tag, td->io_hdl,
288192448Sthompsa	    USS820_RXCNTH) << 8);
289184610Salfred	count &= 0x3FF;
290184610Salfred
291184610Salfred	/* verify data length */
292184610Salfred	if (count != td->remainder) {
293184610Salfred		DPRINTFN(0, "Invalid SETUP packet "
294184610Salfred		    "length, %d bytes\n", count);
295190722Sthompsa		goto setup_not_complete;
296184610Salfred	}
297184610Salfred	if (count != sizeof(req)) {
298184610Salfred		DPRINTFN(0, "Unsupported SETUP packet "
299184610Salfred		    "length, %d bytes\n", count);
300190722Sthompsa		goto setup_not_complete;
301184610Salfred	}
302184610Salfred	/* receive data */
303184610Salfred	bus_space_read_multi_1(td->io_tag, td->io_hdl,
304192448Sthompsa	    USS820_RXDAT, (void *)&req, sizeof(req));
305184610Salfred
306184610Salfred	/* read out FIFO status */
307184610Salfred	rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
308192448Sthompsa	    USS820_RXSTAT);
309184610Salfred
310184610Salfred	if (rx_stat & (USS820_RXSTAT_EDOVW |
311184610Salfred	    USS820_RXSTAT_STOVW)) {
312184610Salfred		DPRINTF("new SETUP packet received\n");
313184610Salfred		return (1);		/* not complete */
314184610Salfred	}
315184610Salfred	/* clear receive setup bit */
316184610Salfred	uss820dci_update_shared_1(sc, USS820_RXSTAT,
317184610Salfred	    0xFF ^ (USS820_RXSTAT_RXSETUP |
318184610Salfred	    USS820_RXSTAT_EDOVW |
319184610Salfred	    USS820_RXSTAT_STOVW), 0);
320184610Salfred
321184610Salfred	/* set RXFFRC bit */
322184610Salfred	temp = bus_space_read_1(td->io_tag, td->io_hdl,
323192448Sthompsa	    USS820_RXCON);
324184610Salfred	temp |= USS820_RXCON_RXFFRC;
325184610Salfred	bus_space_write_1(td->io_tag, td->io_hdl,
326192448Sthompsa	    USS820_RXCON, temp);
327184610Salfred
328184610Salfred	/* copy data into real buffer */
329194228Sthompsa	usbd_copy_in(td->pc, 0, &req, sizeof(req));
330184610Salfred
331184610Salfred	td->offset = sizeof(req);
332184610Salfred	td->remainder = 0;
333184610Salfred
334184610Salfred	/* sneak peek the set address */
335184610Salfred	if ((req.bmRequestType == UT_WRITE_DEVICE) &&
336184610Salfred	    (req.bRequest == UR_SET_ADDRESS)) {
337184610Salfred		sc->sc_dv_addr = req.wValue[0] & 0x7F;
338184610Salfred	} else {
339184610Salfred		sc->sc_dv_addr = 0xFF;
340184610Salfred	}
341205034Sthompsa
342205034Sthompsa	/* reset TX FIFO */
343205034Sthompsa	temp = USS820_READ_1(sc, USS820_TXCON);
344205034Sthompsa	temp |= USS820_TXCON_TXCLR;
345205034Sthompsa	USS820_WRITE_1(sc, USS820_TXCON, temp);
346205034Sthompsa	temp &= ~USS820_TXCON_TXCLR;
347205034Sthompsa	USS820_WRITE_1(sc, USS820_TXCON, temp);
348205034Sthompsa
349184610Salfred	return (0);			/* complete */
350184610Salfred
351190722Sthompsasetup_not_complete:
352190722Sthompsa
353190722Sthompsa	/* set RXFFRC bit */
354190722Sthompsa	temp = bus_space_read_1(td->io_tag, td->io_hdl,
355192448Sthompsa	    USS820_RXCON);
356190722Sthompsa	temp |= USS820_RXCON_RXFFRC;
357190722Sthompsa	bus_space_write_1(td->io_tag, td->io_hdl,
358192448Sthompsa	    USS820_RXCON, temp);
359190722Sthompsa
360190722Sthompsa	/* FALLTHROUGH */
361190722Sthompsa
362184610Salfrednot_complete:
363190721Sthompsa	/* abort any ongoing transfer */
364190721Sthompsa	if (!td->did_stall) {
365190721Sthompsa		DPRINTFN(5, "stalling\n");
366190721Sthompsa		/* set stall */
367190721Sthompsa		uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF,
368190721Sthompsa		    (USS820_EPCON_TXSTL | USS820_EPCON_RXSTL));
369190721Sthompsa
370190721Sthompsa		td->did_stall = 1;
371190721Sthompsa	}
372190721Sthompsa
373184610Salfred	/* clear end overwrite flag, if any */
374184610Salfred	if (rx_stat & USS820_RXSTAT_RXSETUP) {
375184610Salfred		uss820dci_update_shared_1(sc, USS820_RXSTAT,
376184610Salfred		    0xFF ^ (USS820_RXSTAT_EDOVW |
377184610Salfred		    USS820_RXSTAT_STOVW |
378184610Salfred		    USS820_RXSTAT_RXSETUP), 0);
379184610Salfred	}
380184610Salfred	return (1);			/* not complete */
381184610Salfred
382184610Salfred}
383184610Salfred
384184610Salfredstatic uint8_t
385184610Salfreduss820dci_data_rx(struct uss820dci_td *td)
386184610Salfred{
387192984Sthompsa	struct usb_page_search buf_res;
388184610Salfred	uint16_t count;
389184610Salfred	uint8_t rx_flag;
390184610Salfred	uint8_t rx_stat;
391184610Salfred	uint8_t rx_cntl;
392184610Salfred	uint8_t to;
393184610Salfred	uint8_t got_short;
394184610Salfred
395184610Salfred	to = 2;				/* don't loop forever! */
396184610Salfred	got_short = 0;
397184610Salfred
398184610Salfred	/* select the correct endpoint */
399192448Sthompsa	bus_space_write_1(td->io_tag, td->io_hdl, USS820_EPINDEX, td->ep_index);
400184610Salfred
401184610Salfred	/* check if any of the FIFO banks have data */
402184610Salfredrepeat:
403184610Salfred	/* read out FIFO flag */
404184610Salfred	rx_flag = bus_space_read_1(td->io_tag, td->io_hdl,
405192448Sthompsa	    USS820_RXFLG);
406184610Salfred	/* read out FIFO status */
407184610Salfred	rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
408192448Sthompsa	    USS820_RXSTAT);
409184610Salfred
410184610Salfred	DPRINTFN(5, "rx_stat=0x%02x rx_flag=0x%02x rem=%u\n",
411184610Salfred	    rx_stat, rx_flag, td->remainder);
412184610Salfred
413184610Salfred	if (rx_stat & (USS820_RXSTAT_RXSETUP |
414184610Salfred	    USS820_RXSTAT_RXSOVW |
415184610Salfred	    USS820_RXSTAT_EDOVW)) {
416184610Salfred		if (td->remainder == 0) {
417184610Salfred			/*
418184610Salfred			 * We are actually complete and have
419184610Salfred			 * received the next SETUP
420184610Salfred			 */
421184610Salfred			DPRINTFN(5, "faking complete\n");
422184610Salfred			return (0);	/* complete */
423184610Salfred		}
424184610Salfred		/*
425184610Salfred	         * USB Host Aborted the transfer.
426184610Salfred	         */
427184610Salfred		td->error = 1;
428184610Salfred		return (0);		/* complete */
429184610Salfred	}
430184610Salfred	/* check for errors */
431184610Salfred	if (rx_flag & (USS820_RXFLG_RXOVF |
432184610Salfred	    USS820_RXFLG_RXURF)) {
433184610Salfred		DPRINTFN(5, "overflow or underflow\n");
434184610Salfred		/* should not happen */
435184610Salfred		td->error = 1;
436184610Salfred		return (0);		/* complete */
437184610Salfred	}
438184610Salfred	/* check status */
439184610Salfred	if (!(rx_flag & (USS820_RXFLG_RXFIF0 |
440184610Salfred	    USS820_RXFLG_RXFIF1))) {
441184610Salfred
442184610Salfred		/* read out EPCON register */
443184610Salfred		/* enable RX input */
444192552Sthompsa		if (!td->did_enable) {
445184610Salfred			uss820dci_update_shared_1(USS820_DCI_PC2SC(td->pc),
446184610Salfred			    USS820_EPCON, 0xFF, USS820_EPCON_RXIE);
447192552Sthompsa			td->did_enable = 1;
448184610Salfred		}
449184610Salfred		return (1);		/* not complete */
450184610Salfred	}
451184610Salfred	/* get the packet byte count */
452184610Salfred	count = bus_space_read_1(td->io_tag, td->io_hdl,
453192448Sthompsa	    USS820_RXCNTL);
454184610Salfred
455184610Salfred	count |= (bus_space_read_1(td->io_tag, td->io_hdl,
456192448Sthompsa	    USS820_RXCNTH) << 8);
457184610Salfred	count &= 0x3FF;
458184610Salfred
459184610Salfred	DPRINTFN(5, "count=0x%04x\n", count);
460184610Salfred
461184610Salfred	/* verify the packet byte count */
462184610Salfred	if (count != td->max_packet_size) {
463184610Salfred		if (count < td->max_packet_size) {
464184610Salfred			/* we have a short packet */
465184610Salfred			td->short_pkt = 1;
466184610Salfred			got_short = 1;
467184610Salfred		} else {
468184610Salfred			/* invalid USB packet */
469184610Salfred			td->error = 1;
470184610Salfred			return (0);	/* we are complete */
471184610Salfred		}
472184610Salfred	}
473184610Salfred	/* verify the packet byte count */
474184610Salfred	if (count > td->remainder) {
475184610Salfred		/* invalid USB packet */
476184610Salfred		td->error = 1;
477184610Salfred		return (0);		/* we are complete */
478184610Salfred	}
479184610Salfred	while (count > 0) {
480194228Sthompsa		usbd_get_page(td->pc, td->offset, &buf_res);
481184610Salfred
482184610Salfred		/* get correct length */
483184610Salfred		if (buf_res.length > count) {
484184610Salfred			buf_res.length = count;
485184610Salfred		}
486184610Salfred		/* receive data */
487184610Salfred		bus_space_read_multi_1(td->io_tag, td->io_hdl,
488192448Sthompsa		    USS820_RXDAT, buf_res.buffer, buf_res.length);
489184610Salfred
490184610Salfred		/* update counters */
491184610Salfred		count -= buf_res.length;
492184610Salfred		td->offset += buf_res.length;
493184610Salfred		td->remainder -= buf_res.length;
494184610Salfred	}
495184610Salfred
496184610Salfred	/* set RXFFRC bit */
497184610Salfred	rx_cntl = bus_space_read_1(td->io_tag, td->io_hdl,
498192448Sthompsa	    USS820_RXCON);
499184610Salfred	rx_cntl |= USS820_RXCON_RXFFRC;
500184610Salfred	bus_space_write_1(td->io_tag, td->io_hdl,
501192448Sthompsa	    USS820_RXCON, rx_cntl);
502184610Salfred
503184610Salfred	/* check if we are complete */
504184610Salfred	if ((td->remainder == 0) || got_short) {
505184610Salfred		if (td->short_pkt) {
506184610Salfred			/* we are complete */
507184610Salfred			return (0);
508184610Salfred		}
509184610Salfred		/* else need to receive a zero length packet */
510184610Salfred	}
511184610Salfred	if (--to) {
512184610Salfred		goto repeat;
513184610Salfred	}
514184610Salfred	return (1);			/* not complete */
515184610Salfred}
516184610Salfred
517184610Salfredstatic uint8_t
518184610Salfreduss820dci_data_tx(struct uss820dci_td *td)
519184610Salfred{
520192984Sthompsa	struct usb_page_search buf_res;
521184610Salfred	uint16_t count;
522184610Salfred	uint16_t count_copy;
523184610Salfred	uint8_t rx_stat;
524184610Salfred	uint8_t tx_flag;
525184610Salfred	uint8_t to;
526184610Salfred
527184610Salfred	/* select the correct endpoint */
528184610Salfred	bus_space_write_1(td->io_tag, td->io_hdl,
529192448Sthompsa	    USS820_EPINDEX, td->ep_index);
530184610Salfred
531184610Salfred	to = 2;				/* don't loop forever! */
532184610Salfred
533184610Salfredrepeat:
534184610Salfred	/* read out TX FIFO flags */
535184610Salfred	tx_flag = bus_space_read_1(td->io_tag, td->io_hdl,
536192448Sthompsa	    USS820_TXFLG);
537184610Salfred
538184610Salfred	/* read out RX FIFO status last */
539184610Salfred	rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
540192448Sthompsa	    USS820_RXSTAT);
541184610Salfred
542184610Salfred	DPRINTFN(5, "rx_stat=0x%02x tx_flag=0x%02x rem=%u\n",
543184610Salfred	    rx_stat, tx_flag, td->remainder);
544184610Salfred
545184610Salfred	if (rx_stat & (USS820_RXSTAT_RXSETUP |
546184610Salfred	    USS820_RXSTAT_RXSOVW |
547184610Salfred	    USS820_RXSTAT_EDOVW)) {
548184610Salfred		/*
549184610Salfred	         * The current transfer was aborted
550184610Salfred	         * by the USB Host
551184610Salfred	         */
552184610Salfred		td->error = 1;
553184610Salfred		return (0);		/* complete */
554184610Salfred	}
555184610Salfred	if (tx_flag & (USS820_TXFLG_TXOVF |
556184610Salfred	    USS820_TXFLG_TXURF)) {
557184610Salfred		td->error = 1;
558184610Salfred		return (0);		/* complete */
559184610Salfred	}
560184610Salfred	if (tx_flag & USS820_TXFLG_TXFIF0) {
561184610Salfred		if (tx_flag & USS820_TXFLG_TXFIF1) {
562184610Salfred			return (1);	/* not complete */
563184610Salfred		}
564184610Salfred	}
565184610Salfred	if ((!td->support_multi_buffer) &&
566184610Salfred	    (tx_flag & (USS820_TXFLG_TXFIF0 |
567184610Salfred	    USS820_TXFLG_TXFIF1))) {
568184610Salfred		return (1);		/* not complete */
569184610Salfred	}
570184610Salfred	count = td->max_packet_size;
571184610Salfred	if (td->remainder < count) {
572184610Salfred		/* we have a short packet */
573184610Salfred		td->short_pkt = 1;
574184610Salfred		count = td->remainder;
575184610Salfred	}
576184610Salfred	count_copy = count;
577184610Salfred	while (count > 0) {
578184610Salfred
579194228Sthompsa		usbd_get_page(td->pc, td->offset, &buf_res);
580184610Salfred
581184610Salfred		/* get correct length */
582184610Salfred		if (buf_res.length > count) {
583184610Salfred			buf_res.length = count;
584184610Salfred		}
585184610Salfred		/* transmit data */
586184610Salfred		bus_space_write_multi_1(td->io_tag, td->io_hdl,
587192448Sthompsa		    USS820_TXDAT, buf_res.buffer, buf_res.length);
588184610Salfred
589184610Salfred		/* update counters */
590184610Salfred		count -= buf_res.length;
591184610Salfred		td->offset += buf_res.length;
592184610Salfred		td->remainder -= buf_res.length;
593184610Salfred	}
594184610Salfred
595184610Salfred	/* post-write high packet byte count first */
596184610Salfred	bus_space_write_1(td->io_tag, td->io_hdl,
597192448Sthompsa	    USS820_TXCNTH, count_copy >> 8);
598184610Salfred
599184610Salfred	/* post-write low packet byte count last */
600184610Salfred	bus_space_write_1(td->io_tag, td->io_hdl,
601192448Sthompsa	    USS820_TXCNTL, count_copy);
602184610Salfred
603184610Salfred	/*
604184610Salfred	 * Enable TX output, which must happen after that we have written
605184610Salfred	 * data into the FIFO. This is undocumented.
606184610Salfred	 */
607192552Sthompsa	if (!td->did_enable) {
608184610Salfred		uss820dci_update_shared_1(USS820_DCI_PC2SC(td->pc),
609184610Salfred		    USS820_EPCON, 0xFF, USS820_EPCON_TXOE);
610192552Sthompsa		td->did_enable = 1;
611184610Salfred	}
612184610Salfred	/* check remainder */
613184610Salfred	if (td->remainder == 0) {
614184610Salfred		if (td->short_pkt) {
615184610Salfred			return (0);	/* complete */
616184610Salfred		}
617184610Salfred		/* else we need to transmit a short packet */
618184610Salfred	}
619184610Salfred	if (--to) {
620184610Salfred		goto repeat;
621184610Salfred	}
622184610Salfred	return (1);			/* not complete */
623184610Salfred}
624184610Salfred
625184610Salfredstatic uint8_t
626184610Salfreduss820dci_data_tx_sync(struct uss820dci_td *td)
627184610Salfred{
628184610Salfred	struct uss820dci_softc *sc;
629184610Salfred	uint8_t rx_stat;
630184610Salfred	uint8_t tx_flag;
631184610Salfred
632184610Salfred	/* select the correct endpoint */
633184610Salfred	bus_space_write_1(td->io_tag, td->io_hdl,
634192448Sthompsa	    USS820_EPINDEX, td->ep_index);
635184610Salfred
636184610Salfred	/* read out TX FIFO flag */
637184610Salfred	tx_flag = bus_space_read_1(td->io_tag, td->io_hdl,
638192448Sthompsa	    USS820_TXFLG);
639184610Salfred
640184610Salfred	/* read out RX FIFO status last */
641184610Salfred	rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
642192448Sthompsa	    USS820_RXSTAT);
643184610Salfred
644184610Salfred	DPRINTFN(5, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder);
645184610Salfred
646184610Salfred	if (rx_stat & (USS820_RXSTAT_RXSETUP |
647184610Salfred	    USS820_RXSTAT_RXSOVW |
648184610Salfred	    USS820_RXSTAT_EDOVW)) {
649184610Salfred		DPRINTFN(5, "faking complete\n");
650184610Salfred		/* Race condition */
651184610Salfred		return (0);		/* complete */
652184610Salfred	}
653184610Salfred	DPRINTFN(5, "tx_flag=0x%02x rem=%u\n",
654184610Salfred	    tx_flag, td->remainder);
655184610Salfred
656184610Salfred	if (tx_flag & (USS820_TXFLG_TXOVF |
657184610Salfred	    USS820_TXFLG_TXURF)) {
658184610Salfred		td->error = 1;
659184610Salfred		return (0);		/* complete */
660184610Salfred	}
661184610Salfred	if (tx_flag & (USS820_TXFLG_TXFIF0 |
662184610Salfred	    USS820_TXFLG_TXFIF1)) {
663184610Salfred		return (1);		/* not complete */
664184610Salfred	}
665184610Salfred	sc = USS820_DCI_PC2SC(td->pc);
666184610Salfred	if (sc->sc_dv_addr != 0xFF) {
667184610Salfred		/* write function address */
668184610Salfred		uss820dci_set_address(sc, sc->sc_dv_addr);
669184610Salfred	}
670184610Salfred	return (0);			/* complete */
671184610Salfred}
672184610Salfred
673184610Salfredstatic uint8_t
674192984Sthompsauss820dci_xfer_do_fifo(struct usb_xfer *xfer)
675184610Salfred{
676184610Salfred	struct uss820dci_td *td;
677184610Salfred
678184610Salfred	DPRINTFN(9, "\n");
679184610Salfred
680184610Salfred	td = xfer->td_transfer_cache;
681184610Salfred	while (1) {
682184610Salfred		if ((td->func) (td)) {
683184610Salfred			/* operation in progress */
684184610Salfred			break;
685184610Salfred		}
686184610Salfred		if (((void *)td) == xfer->td_transfer_last) {
687184610Salfred			goto done;
688184610Salfred		}
689184610Salfred		if (td->error) {
690184610Salfred			goto done;
691184610Salfred		} else if (td->remainder > 0) {
692184610Salfred			/*
693184610Salfred			 * We had a short transfer. If there is no alternate
694184610Salfred			 * next, stop processing !
695184610Salfred			 */
696184610Salfred			if (!td->alt_next) {
697184610Salfred				goto done;
698184610Salfred			}
699184610Salfred		}
700184610Salfred		/*
701184610Salfred		 * Fetch the next transfer descriptor.
702184610Salfred		 */
703184610Salfred		td = td->obj_next;
704184610Salfred		xfer->td_transfer_cache = td;
705184610Salfred	}
706184610Salfred	return (1);			/* not complete */
707184610Salfred
708184610Salfreddone:
709184610Salfred	/* compute all actual lengths */
710184610Salfred
711184610Salfred	uss820dci_standard_done(xfer);
712184610Salfred
713184610Salfred	return (0);			/* complete */
714184610Salfred}
715184610Salfred
716184610Salfredstatic void
717184610Salfreduss820dci_interrupt_poll(struct uss820dci_softc *sc)
718184610Salfred{
719192984Sthompsa	struct usb_xfer *xfer;
720184610Salfred
721184610Salfredrepeat:
722184610Salfred	TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
723184610Salfred		if (!uss820dci_xfer_do_fifo(xfer)) {
724184610Salfred			/* queue has been modified */
725184610Salfred			goto repeat;
726184610Salfred		}
727184610Salfred	}
728184610Salfred}
729184610Salfred
730184610Salfredstatic void
731184610Salfreduss820dci_wait_suspend(struct uss820dci_softc *sc, uint8_t on)
732184610Salfred{
733184610Salfred	uint8_t scr;
734184610Salfred	uint8_t scratch;
735184610Salfred
736184610Salfred	scr = USS820_READ_1(sc, USS820_SCR);
737184610Salfred	scratch = USS820_READ_1(sc, USS820_SCRATCH);
738184610Salfred
739184610Salfred	if (on) {
740184610Salfred		scr |= USS820_SCR_IE_SUSP;
741184610Salfred		scratch &= ~USS820_SCRATCH_IE_RESUME;
742184610Salfred	} else {
743184610Salfred		scr &= ~USS820_SCR_IE_SUSP;
744184610Salfred		scratch |= USS820_SCRATCH_IE_RESUME;
745184610Salfred	}
746184610Salfred
747184610Salfred	USS820_WRITE_1(sc, USS820_SCR, scr);
748184610Salfred	USS820_WRITE_1(sc, USS820_SCRATCH, scratch);
749184610Salfred}
750184610Salfred
751184610Salfredvoid
752184610Salfreduss820dci_interrupt(struct uss820dci_softc *sc)
753184610Salfred{
754184610Salfred	uint8_t ssr;
755184610Salfred	uint8_t event;
756184610Salfred
757184824Sthompsa	USB_BUS_LOCK(&sc->sc_bus);
758184610Salfred
759184610Salfred	ssr = USS820_READ_1(sc, USS820_SSR);
760184610Salfred
761184610Salfred	ssr &= (USS820_SSR_SUSPEND |
762184610Salfred	    USS820_SSR_RESUME |
763184610Salfred	    USS820_SSR_RESET);
764184610Salfred
765184610Salfred	/* acknowledge all interrupts */
766184610Salfred
767184610Salfred	uss820dci_update_shared_1(sc, USS820_SSR, 0, 0);
768184610Salfred
769184610Salfred	/* check for any bus state change interrupts */
770184610Salfred
771184610Salfred	if (ssr) {
772184610Salfred
773184610Salfred		event = 0;
774184610Salfred
775184610Salfred		if (ssr & USS820_SSR_RESET) {
776184610Salfred			sc->sc_flags.status_bus_reset = 1;
777184610Salfred			sc->sc_flags.status_suspend = 0;
778184610Salfred			sc->sc_flags.change_suspend = 0;
779184610Salfred			sc->sc_flags.change_connect = 1;
780184610Salfred
781184610Salfred			/* disable resume interrupt */
782184610Salfred			uss820dci_wait_suspend(sc, 1);
783184610Salfred
784184610Salfred			event = 1;
785184610Salfred		}
786184610Salfred		/*
787184610Salfred	         * If "RESUME" and "SUSPEND" is set at the same time
788184610Salfred	         * we interpret that like "RESUME". Resume is set when
789184610Salfred	         * there is at least 3 milliseconds of inactivity on
790184610Salfred	         * the USB BUS.
791184610Salfred	         */
792184610Salfred		if (ssr & USS820_SSR_RESUME) {
793184610Salfred			if (sc->sc_flags.status_suspend) {
794184610Salfred				sc->sc_flags.status_suspend = 0;
795184610Salfred				sc->sc_flags.change_suspend = 1;
796184610Salfred				/* disable resume interrupt */
797184610Salfred				uss820dci_wait_suspend(sc, 1);
798184610Salfred				event = 1;
799184610Salfred			}
800184610Salfred		} else if (ssr & USS820_SSR_SUSPEND) {
801184610Salfred			if (!sc->sc_flags.status_suspend) {
802184610Salfred				sc->sc_flags.status_suspend = 1;
803184610Salfred				sc->sc_flags.change_suspend = 1;
804184610Salfred				/* enable resume interrupt */
805184610Salfred				uss820dci_wait_suspend(sc, 0);
806184610Salfred				event = 1;
807184610Salfred			}
808184610Salfred		}
809184610Salfred		if (event) {
810184610Salfred
811184610Salfred			DPRINTF("real bus interrupt 0x%02x\n", ssr);
812184610Salfred
813184610Salfred			/* complete root HUB interrupt endpoint */
814190735Sthompsa			uss820dci_root_intr(sc);
815184610Salfred		}
816184610Salfred	}
817184610Salfred	/* acknowledge all SBI interrupts */
818184610Salfred	uss820dci_update_shared_1(sc, USS820_SBI, 0, 0);
819184610Salfred
820184610Salfred	/* acknowledge all SBI1 interrupts */
821184610Salfred	uss820dci_update_shared_1(sc, USS820_SBI1, 0, 0);
822184610Salfred
823184610Salfred	/* poll all active transfers */
824184610Salfred	uss820dci_interrupt_poll(sc);
825184610Salfred
826184824Sthompsa	USB_BUS_UNLOCK(&sc->sc_bus);
827184610Salfred}
828184610Salfred
829184610Salfredstatic void
830184610Salfreduss820dci_setup_standard_chain_sub(struct uss820_std_temp *temp)
831184610Salfred{
832184610Salfred	struct uss820dci_td *td;
833184610Salfred
834184610Salfred	/* get current Transfer Descriptor */
835184610Salfred	td = temp->td_next;
836184610Salfred	temp->td = td;
837184610Salfred
838184610Salfred	/* prepare for next TD */
839184610Salfred	temp->td_next = td->obj_next;
840184610Salfred
841184610Salfred	/* fill out the Transfer Descriptor */
842184610Salfred	td->func = temp->func;
843184610Salfred	td->pc = temp->pc;
844184610Salfred	td->offset = temp->offset;
845184610Salfred	td->remainder = temp->len;
846184610Salfred	td->error = 0;
847192552Sthompsa	td->did_enable = 0;
848192552Sthompsa	td->did_stall = temp->did_stall;
849184610Salfred	td->short_pkt = temp->short_pkt;
850184610Salfred	td->alt_next = temp->setup_alt_next;
851184610Salfred}
852184610Salfred
853184610Salfredstatic void
854192984Sthompsauss820dci_setup_standard_chain(struct usb_xfer *xfer)
855184610Salfred{
856184610Salfred	struct uss820_std_temp temp;
857184610Salfred	struct uss820dci_softc *sc;
858184610Salfred	struct uss820dci_td *td;
859184610Salfred	uint32_t x;
860184610Salfred	uint8_t ep_no;
861184610Salfred
862184610Salfred	DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
863193644Sthompsa	    xfer->address, UE_GET_ADDR(xfer->endpointno),
864194228Sthompsa	    xfer->sumlen, usbd_get_speed(xfer->xroot->udev));
865184610Salfred
866184610Salfred	temp.max_frame_size = xfer->max_frame_size;
867184610Salfred
868184610Salfred	td = xfer->td_start[0];
869184610Salfred	xfer->td_transfer_first = td;
870184610Salfred	xfer->td_transfer_cache = td;
871184610Salfred
872184610Salfred	/* setup temp */
873184610Salfred
874199673Sthompsa	temp.pc = NULL;
875184610Salfred	temp.td = NULL;
876184610Salfred	temp.td_next = xfer->td_start[0];
877190183Sthompsa	temp.offset = 0;
878184610Salfred	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
879192552Sthompsa	temp.did_stall = !xfer->flags_int.control_stall;
880184610Salfred
881187173Sthompsa	sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
882193644Sthompsa	ep_no = (xfer->endpointno & UE_ADDR);
883184610Salfred
884184610Salfred	/* check if we should prepend a setup message */
885184610Salfred
886184610Salfred	if (xfer->flags_int.control_xfr) {
887184610Salfred		if (xfer->flags_int.control_hdr) {
888184610Salfred
889184610Salfred			temp.func = &uss820dci_setup_rx;
890184610Salfred			temp.len = xfer->frlengths[0];
891184610Salfred			temp.pc = xfer->frbuffers + 0;
892184610Salfred			temp.short_pkt = temp.len ? 1 : 0;
893190183Sthompsa			/* check for last frame */
894190183Sthompsa			if (xfer->nframes == 1) {
895190183Sthompsa				/* no STATUS stage yet, SETUP is last */
896190183Sthompsa				if (xfer->flags_int.control_act)
897190183Sthompsa					temp.setup_alt_next = 0;
898190183Sthompsa			}
899184610Salfred
900184610Salfred			uss820dci_setup_standard_chain_sub(&temp);
901184610Salfred		}
902184610Salfred		x = 1;
903184610Salfred	} else {
904184610Salfred		x = 0;
905184610Salfred	}
906184610Salfred
907184610Salfred	if (x != xfer->nframes) {
908193644Sthompsa		if (xfer->endpointno & UE_DIR_IN) {
909184610Salfred			temp.func = &uss820dci_data_tx;
910184610Salfred		} else {
911184610Salfred			temp.func = &uss820dci_data_rx;
912184610Salfred		}
913184610Salfred
914184610Salfred		/* setup "pc" pointer */
915184610Salfred		temp.pc = xfer->frbuffers + x;
916184610Salfred	}
917184610Salfred	while (x != xfer->nframes) {
918184610Salfred
919184610Salfred		/* DATA0 / DATA1 message */
920184610Salfred
921184610Salfred		temp.len = xfer->frlengths[x];
922184610Salfred
923184610Salfred		x++;
924184610Salfred
925184610Salfred		if (x == xfer->nframes) {
926190183Sthompsa			if (xfer->flags_int.control_xfr) {
927190183Sthompsa				if (xfer->flags_int.control_act) {
928190183Sthompsa					temp.setup_alt_next = 0;
929190183Sthompsa				}
930190183Sthompsa			} else {
931190183Sthompsa				temp.setup_alt_next = 0;
932190183Sthompsa			}
933184610Salfred		}
934184610Salfred		if (temp.len == 0) {
935184610Salfred
936184610Salfred			/* make sure that we send an USB packet */
937184610Salfred
938184610Salfred			temp.short_pkt = 0;
939184610Salfred
940184610Salfred		} else {
941184610Salfred
942184610Salfred			/* regular data transfer */
943184610Salfred
944184610Salfred			temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
945184610Salfred		}
946184610Salfred
947184610Salfred		uss820dci_setup_standard_chain_sub(&temp);
948184610Salfred
949184610Salfred		if (xfer->flags_int.isochronous_xfr) {
950184610Salfred			temp.offset += temp.len;
951184610Salfred		} else {
952184610Salfred			/* get next Page Cache pointer */
953184610Salfred			temp.pc = xfer->frbuffers + x;
954184610Salfred		}
955184610Salfred	}
956184610Salfred
957190183Sthompsa	/* check for control transfer */
958190183Sthompsa	if (xfer->flags_int.control_xfr) {
959184610Salfred		uint8_t need_sync;
960184610Salfred
961190183Sthompsa		/* always setup a valid "pc" pointer for status and sync */
962190183Sthompsa		temp.pc = xfer->frbuffers + 0;
963184610Salfred		temp.len = 0;
964184610Salfred		temp.short_pkt = 0;
965190183Sthompsa		temp.setup_alt_next = 0;
966184610Salfred
967190183Sthompsa		/* check if we should append a status stage */
968190183Sthompsa		if (!xfer->flags_int.control_act) {
969190183Sthompsa
970190183Sthompsa			/*
971190183Sthompsa			 * Send a DATA1 message and invert the current
972190183Sthompsa			 * endpoint direction.
973190183Sthompsa			 */
974193644Sthompsa			if (xfer->endpointno & UE_DIR_IN) {
975190183Sthompsa				temp.func = &uss820dci_data_rx;
976190183Sthompsa				need_sync = 0;
977190183Sthompsa			} else {
978190183Sthompsa				temp.func = &uss820dci_data_tx;
979190183Sthompsa				need_sync = 1;
980190183Sthompsa			}
981184610Salfred			temp.len = 0;
982184610Salfred			temp.short_pkt = 0;
983184610Salfred
984184610Salfred			uss820dci_setup_standard_chain_sub(&temp);
985190183Sthompsa			if (need_sync) {
986190183Sthompsa				/* we need a SYNC point after TX */
987190183Sthompsa				temp.func = &uss820dci_data_tx_sync;
988190183Sthompsa				uss820dci_setup_standard_chain_sub(&temp);
989190183Sthompsa			}
990184610Salfred		}
991184610Salfred	}
992184610Salfred	/* must have at least one frame! */
993184610Salfred	td = temp.td;
994184610Salfred	xfer->td_transfer_last = td;
995184610Salfred}
996184610Salfred
997184610Salfredstatic void
998184610Salfreduss820dci_timeout(void *arg)
999184610Salfred{
1000192984Sthompsa	struct usb_xfer *xfer = arg;
1001184610Salfred
1002184610Salfred	DPRINTF("xfer=%p\n", xfer);
1003184610Salfred
1004187173Sthompsa	USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
1005184610Salfred
1006184610Salfred	/* transfer is transferred */
1007184610Salfred	uss820dci_device_done(xfer, USB_ERR_TIMEOUT);
1008184610Salfred}
1009184610Salfred
1010184610Salfredstatic void
1011192984Sthompsauss820dci_intr_set(struct usb_xfer *xfer, uint8_t set)
1012184610Salfred{
1013187173Sthompsa	struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
1014193644Sthompsa	uint8_t ep_no = (xfer->endpointno & UE_ADDR);
1015184610Salfred	uint8_t ep_reg;
1016184610Salfred	uint8_t temp;
1017184610Salfred
1018193644Sthompsa	DPRINTFN(15, "endpoint 0x%02x\n", xfer->endpointno);
1019184610Salfred
1020184610Salfred	if (ep_no > 3) {
1021184610Salfred		ep_reg = USS820_SBIE1;
1022184610Salfred	} else {
1023184610Salfred		ep_reg = USS820_SBIE;
1024184610Salfred	}
1025184610Salfred
1026184610Salfred	ep_no &= 3;
1027184610Salfred	ep_no = 1 << (2 * ep_no);
1028184610Salfred
1029184610Salfred	if (xfer->flags_int.control_xfr) {
1030184610Salfred		if (xfer->flags_int.control_hdr) {
1031184610Salfred			ep_no <<= 1;	/* RX interrupt only */
1032184610Salfred		} else {
1033184610Salfred			ep_no |= (ep_no << 1);	/* RX and TX interrupt */
1034184610Salfred		}
1035184610Salfred	} else {
1036193644Sthompsa		if (!(xfer->endpointno & UE_DIR_IN)) {
1037184610Salfred			ep_no <<= 1;
1038184610Salfred		}
1039184610Salfred	}
1040184610Salfred	temp = USS820_READ_1(sc, ep_reg);
1041184610Salfred	if (set) {
1042184610Salfred		temp |= ep_no;
1043184610Salfred	} else {
1044184610Salfred		temp &= ~ep_no;
1045184610Salfred	}
1046184610Salfred	USS820_WRITE_1(sc, ep_reg, temp);
1047184610Salfred}
1048184610Salfred
1049184610Salfredstatic void
1050192984Sthompsauss820dci_start_standard_chain(struct usb_xfer *xfer)
1051184610Salfred{
1052184610Salfred	DPRINTFN(9, "\n");
1053184610Salfred
1054184610Salfred	/* poll one time */
1055184610Salfred	if (uss820dci_xfer_do_fifo(xfer)) {
1056184610Salfred
1057184610Salfred		/*
1058184610Salfred		 * Only enable the endpoint interrupt when we are
1059184610Salfred		 * actually waiting for data, hence we are dealing
1060184610Salfred		 * with level triggered interrupts !
1061184610Salfred		 */
1062184610Salfred		uss820dci_intr_set(xfer, 1);
1063184610Salfred
1064184610Salfred		/* put transfer on interrupt queue */
1065194228Sthompsa		usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
1066184610Salfred
1067184610Salfred		/* start timeout, if any */
1068184610Salfred		if (xfer->timeout != 0) {
1069194228Sthompsa			usbd_transfer_timeout_ms(xfer,
1070184610Salfred			    &uss820dci_timeout, xfer->timeout);
1071184610Salfred		}
1072184610Salfred	}
1073184610Salfred}
1074184610Salfred
1075184610Salfredstatic void
1076190735Sthompsauss820dci_root_intr(struct uss820dci_softc *sc)
1077184610Salfred{
1078184610Salfred	DPRINTFN(9, "\n");
1079184610Salfred
1080184824Sthompsa	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
1081184610Salfred
1082184610Salfred	/* set port bit */
1083184610Salfred	sc->sc_hub_idata[0] = 0x02;	/* we only have one port */
1084184610Salfred
1085190735Sthompsa	uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata,
1086190735Sthompsa	    sizeof(sc->sc_hub_idata));
1087184610Salfred}
1088184610Salfred
1089193045Sthompsastatic usb_error_t
1090192984Sthompsauss820dci_standard_done_sub(struct usb_xfer *xfer)
1091184610Salfred{
1092184610Salfred	struct uss820dci_td *td;
1093184610Salfred	uint32_t len;
1094184610Salfred	uint8_t error;
1095184610Salfred
1096184610Salfred	DPRINTFN(9, "\n");
1097184610Salfred
1098184610Salfred	td = xfer->td_transfer_cache;
1099184610Salfred
1100184610Salfred	do {
1101184610Salfred		len = td->remainder;
1102184610Salfred
1103184610Salfred		if (xfer->aframes != xfer->nframes) {
1104184610Salfred			/*
1105184610Salfred		         * Verify the length and subtract
1106184610Salfred		         * the remainder from "frlengths[]":
1107184610Salfred		         */
1108184610Salfred			if (len > xfer->frlengths[xfer->aframes]) {
1109184610Salfred				td->error = 1;
1110184610Salfred			} else {
1111184610Salfred				xfer->frlengths[xfer->aframes] -= len;
1112184610Salfred			}
1113184610Salfred		}
1114184610Salfred		/* Check for transfer error */
1115184610Salfred		if (td->error) {
1116184610Salfred			/* the transfer is finished */
1117184610Salfred			error = 1;
1118184610Salfred			td = NULL;
1119184610Salfred			break;
1120184610Salfred		}
1121184610Salfred		/* Check for short transfer */
1122184610Salfred		if (len > 0) {
1123184610Salfred			if (xfer->flags_int.short_frames_ok) {
1124184610Salfred				/* follow alt next */
1125184610Salfred				if (td->alt_next) {
1126184610Salfred					td = td->obj_next;
1127184610Salfred				} else {
1128184610Salfred					td = NULL;
1129184610Salfred				}
1130184610Salfred			} else {
1131184610Salfred				/* the transfer is finished */
1132184610Salfred				td = NULL;
1133184610Salfred			}
1134184610Salfred			error = 0;
1135184610Salfred			break;
1136184610Salfred		}
1137184610Salfred		td = td->obj_next;
1138184610Salfred
1139184610Salfred		/* this USB frame is complete */
1140184610Salfred		error = 0;
1141184610Salfred		break;
1142184610Salfred
1143184610Salfred	} while (0);
1144184610Salfred
1145184610Salfred	/* update transfer cache */
1146184610Salfred
1147184610Salfred	xfer->td_transfer_cache = td;
1148184610Salfred
1149184610Salfred	return (error ?
1150184610Salfred	    USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION);
1151184610Salfred}
1152184610Salfred
1153184610Salfredstatic void
1154192984Sthompsauss820dci_standard_done(struct usb_xfer *xfer)
1155184610Salfred{
1156193045Sthompsa	usb_error_t err = 0;
1157184610Salfred
1158193644Sthompsa	DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n",
1159193644Sthompsa	    xfer, xfer->endpoint);
1160184610Salfred
1161184610Salfred	/* reset scanner */
1162184610Salfred
1163184610Salfred	xfer->td_transfer_cache = xfer->td_transfer_first;
1164184610Salfred
1165184610Salfred	if (xfer->flags_int.control_xfr) {
1166184610Salfred
1167184610Salfred		if (xfer->flags_int.control_hdr) {
1168184610Salfred
1169184610Salfred			err = uss820dci_standard_done_sub(xfer);
1170184610Salfred		}
1171184610Salfred		xfer->aframes = 1;
1172184610Salfred
1173184610Salfred		if (xfer->td_transfer_cache == NULL) {
1174184610Salfred			goto done;
1175184610Salfred		}
1176184610Salfred	}
1177184610Salfred	while (xfer->aframes != xfer->nframes) {
1178184610Salfred
1179184610Salfred		err = uss820dci_standard_done_sub(xfer);
1180184610Salfred		xfer->aframes++;
1181184610Salfred
1182184610Salfred		if (xfer->td_transfer_cache == NULL) {
1183184610Salfred			goto done;
1184184610Salfred		}
1185184610Salfred	}
1186184610Salfred
1187184610Salfred	if (xfer->flags_int.control_xfr &&
1188184610Salfred	    !xfer->flags_int.control_act) {
1189184610Salfred
1190184610Salfred		err = uss820dci_standard_done_sub(xfer);
1191184610Salfred	}
1192184610Salfreddone:
1193184610Salfred	uss820dci_device_done(xfer, err);
1194184610Salfred}
1195184610Salfred
1196184610Salfred/*------------------------------------------------------------------------*
1197184610Salfred *	uss820dci_device_done
1198184610Salfred *
1199184610Salfred * NOTE: this function can be called more than one time on the
1200184610Salfred * same USB transfer!
1201184610Salfred *------------------------------------------------------------------------*/
1202184610Salfredstatic void
1203193045Sthompsauss820dci_device_done(struct usb_xfer *xfer, usb_error_t error)
1204184610Salfred{
1205187173Sthompsa	USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
1206184610Salfred
1207193644Sthompsa	DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n",
1208193644Sthompsa	    xfer, xfer->endpoint, error);
1209184610Salfred
1210192499Sthompsa	if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
1211184610Salfred		uss820dci_intr_set(xfer, 0);
1212184610Salfred	}
1213184610Salfred	/* dequeue transfer and start next transfer */
1214194228Sthompsa	usbd_transfer_done(xfer, error);
1215184610Salfred}
1216184610Salfred
1217184610Salfredstatic void
1218239214Shselaskyuss820dci_xfer_stall(struct usb_xfer *xfer)
1219239214Shselasky{
1220239214Shselasky	uss820dci_device_done(xfer, USB_ERR_STALLED);
1221239214Shselasky}
1222239214Shselasky
1223239214Shselaskystatic void
1224239214Shselaskyuss820dci_set_stall(struct usb_device *udev,
1225195121Sthompsa    struct usb_endpoint *ep, uint8_t *did_stall)
1226184610Salfred{
1227184610Salfred	struct uss820dci_softc *sc;
1228184610Salfred	uint8_t ep_no;
1229184610Salfred	uint8_t ep_type;
1230184610Salfred	uint8_t ep_dir;
1231184610Salfred	uint8_t temp;
1232184610Salfred
1233184824Sthompsa	USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
1234184610Salfred
1235193644Sthompsa	DPRINTFN(5, "endpoint=%p\n", ep);
1236184610Salfred
1237184610Salfred	/* set FORCESTALL */
1238184610Salfred	sc = USS820_DCI_BUS2SC(udev->bus);
1239193644Sthompsa	ep_no = (ep->edesc->bEndpointAddress & UE_ADDR);
1240193644Sthompsa	ep_dir = (ep->edesc->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT));
1241193644Sthompsa	ep_type = (ep->edesc->bmAttributes & UE_XFERTYPE);
1242184610Salfred
1243184610Salfred	if (ep_type == UE_CONTROL) {
1244184610Salfred		/* should not happen */
1245184610Salfred		return;
1246184610Salfred	}
1247184610Salfred	USS820_WRITE_1(sc, USS820_EPINDEX, ep_no);
1248184610Salfred
1249184610Salfred	if (ep_dir == UE_DIR_IN) {
1250184610Salfred		temp = USS820_EPCON_TXSTL;
1251184610Salfred	} else {
1252184610Salfred		temp = USS820_EPCON_RXSTL;
1253184610Salfred	}
1254184610Salfred	uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF, temp);
1255184610Salfred}
1256184610Salfred
1257184610Salfredstatic void
1258184610Salfreduss820dci_clear_stall_sub(struct uss820dci_softc *sc,
1259184610Salfred    uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir)
1260184610Salfred{
1261184610Salfred	uint8_t temp;
1262184610Salfred
1263184610Salfred	if (ep_type == UE_CONTROL) {
1264184610Salfred		/* clearing stall is not needed */
1265184610Salfred		return;
1266184610Salfred	}
1267184610Salfred	/* select endpoint index */
1268184610Salfred	USS820_WRITE_1(sc, USS820_EPINDEX, ep_no);
1269184610Salfred
1270184610Salfred	/* clear stall and disable I/O transfers */
1271184610Salfred	if (ep_dir == UE_DIR_IN) {
1272184610Salfred		temp = 0xFF ^ (USS820_EPCON_TXOE |
1273184610Salfred		    USS820_EPCON_TXSTL);
1274184610Salfred	} else {
1275184610Salfred		temp = 0xFF ^ (USS820_EPCON_RXIE |
1276184610Salfred		    USS820_EPCON_RXSTL);
1277184610Salfred	}
1278184610Salfred	uss820dci_update_shared_1(sc, USS820_EPCON, temp, 0);
1279184610Salfred
1280184610Salfred	if (ep_dir == UE_DIR_IN) {
1281184610Salfred		/* reset data toggle */
1282184610Salfred		USS820_WRITE_1(sc, USS820_TXSTAT,
1283184610Salfred		    USS820_TXSTAT_TXSOVW);
1284184610Salfred
1285184610Salfred		/* reset FIFO */
1286184610Salfred		temp = USS820_READ_1(sc, USS820_TXCON);
1287184610Salfred		temp |= USS820_TXCON_TXCLR;
1288184610Salfred		USS820_WRITE_1(sc, USS820_TXCON, temp);
1289184610Salfred		temp &= ~USS820_TXCON_TXCLR;
1290184610Salfred		USS820_WRITE_1(sc, USS820_TXCON, temp);
1291184610Salfred	} else {
1292184610Salfred
1293184610Salfred		/* reset data toggle */
1294184610Salfred		uss820dci_update_shared_1(sc, USS820_RXSTAT,
1295184610Salfred		    0, USS820_RXSTAT_RXSOVW);
1296184610Salfred
1297184610Salfred		/* reset FIFO */
1298184610Salfred		temp = USS820_READ_1(sc, USS820_RXCON);
1299184610Salfred		temp |= USS820_RXCON_RXCLR;
1300184610Salfred		temp &= ~USS820_RXCON_RXFFRC;
1301184610Salfred		USS820_WRITE_1(sc, USS820_RXCON, temp);
1302184610Salfred		temp &= ~USS820_RXCON_RXCLR;
1303184610Salfred		USS820_WRITE_1(sc, USS820_RXCON, temp);
1304184610Salfred	}
1305184610Salfred}
1306184610Salfred
1307184610Salfredstatic void
1308193644Sthompsauss820dci_clear_stall(struct usb_device *udev, struct usb_endpoint *ep)
1309184610Salfred{
1310184610Salfred	struct uss820dci_softc *sc;
1311192984Sthompsa	struct usb_endpoint_descriptor *ed;
1312184610Salfred
1313184824Sthompsa	USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
1314184610Salfred
1315193644Sthompsa	DPRINTFN(5, "endpoint=%p\n", ep);
1316184610Salfred
1317184610Salfred	/* check mode */
1318192499Sthompsa	if (udev->flags.usb_mode != USB_MODE_DEVICE) {
1319184610Salfred		/* not supported */
1320184610Salfred		return;
1321184610Salfred	}
1322184610Salfred	/* get softc */
1323184610Salfred	sc = USS820_DCI_BUS2SC(udev->bus);
1324184610Salfred
1325184610Salfred	/* get endpoint descriptor */
1326193644Sthompsa	ed = ep->edesc;
1327184610Salfred
1328184610Salfred	/* reset endpoint */
1329184610Salfred	uss820dci_clear_stall_sub(sc,
1330184610Salfred	    (ed->bEndpointAddress & UE_ADDR),
1331184610Salfred	    (ed->bmAttributes & UE_XFERTYPE),
1332184610Salfred	    (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)));
1333184610Salfred}
1334184610Salfred
1335193045Sthompsausb_error_t
1336184610Salfreduss820dci_init(struct uss820dci_softc *sc)
1337184610Salfred{
1338192984Sthompsa	const struct usb_hw_ep_profile *pf;
1339184610Salfred	uint8_t n;
1340184610Salfred	uint8_t temp;
1341184610Salfred
1342184610Salfred	DPRINTF("start\n");
1343184610Salfred
1344184610Salfred	/* set up the bus structure */
1345184610Salfred	sc->sc_bus.usbrev = USB_REV_1_1;
1346184610Salfred	sc->sc_bus.methods = &uss820dci_bus_methods;
1347184610Salfred
1348184824Sthompsa	USB_BUS_LOCK(&sc->sc_bus);
1349184610Salfred
1350184610Salfred	/* we always have VBUS */
1351184610Salfred	sc->sc_flags.status_vbus = 1;
1352184610Salfred
1353184610Salfred	/* reset the chip */
1354184610Salfred	USS820_WRITE_1(sc, USS820_SCR, USS820_SCR_SRESET);
1355184610Salfred	DELAY(100);
1356184610Salfred	USS820_WRITE_1(sc, USS820_SCR, 0);
1357184610Salfred
1358184610Salfred	/* wait for reset to complete */
1359184610Salfred	for (n = 0;; n++) {
1360184610Salfred
1361184610Salfred		temp = USS820_READ_1(sc, USS820_MCSR);
1362184610Salfred
1363184610Salfred		if (temp & USS820_MCSR_INIT) {
1364184610Salfred			break;
1365184610Salfred		}
1366184610Salfred		if (n == 100) {
1367184824Sthompsa			USB_BUS_UNLOCK(&sc->sc_bus);
1368184610Salfred			return (USB_ERR_INVAL);
1369184610Salfred		}
1370184610Salfred		/* wait a little for things to stabilise */
1371184610Salfred		DELAY(100);
1372184610Salfred	}
1373184610Salfred
1374184610Salfred	/* do a pulldown */
1375184610Salfred	uss820dci_pull_down(sc);
1376184610Salfred
1377184610Salfred	/* wait 10ms for pulldown to stabilise */
1378194228Sthompsa	usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
1379184610Salfred
1380184610Salfred	/* check hardware revision */
1381184610Salfred	temp = USS820_READ_1(sc, USS820_REV);
1382184610Salfred
1383184610Salfred	if (temp < 0x13) {
1384184824Sthompsa		USB_BUS_UNLOCK(&sc->sc_bus);
1385184610Salfred		return (USB_ERR_INVAL);
1386184610Salfred	}
1387184610Salfred	/* enable interrupts */
1388184610Salfred	USS820_WRITE_1(sc, USS820_SCR,
1389184610Salfred	    USS820_SCR_T_IRQ |
1390184610Salfred	    USS820_SCR_IE_RESET |
1391186730Salfred	/* USS820_SCR_RWUPE | */
1392184610Salfred	    USS820_SCR_IE_SUSP |
1393184610Salfred	    USS820_SCR_IRQPOL);
1394184610Salfred
1395184610Salfred	/* enable interrupts */
1396184610Salfred	USS820_WRITE_1(sc, USS820_SCRATCH,
1397184610Salfred	    USS820_SCRATCH_IE_RESUME);
1398184610Salfred
1399184610Salfred	/* enable features */
1400184610Salfred	USS820_WRITE_1(sc, USS820_MCSR,
1401184610Salfred	    USS820_MCSR_BDFEAT |
1402184610Salfred	    USS820_MCSR_FEAT);
1403184610Salfred
1404184610Salfred	sc->sc_flags.mcsr_feat = 1;
1405184610Salfred
1406184610Salfred	/* disable interrupts */
1407184610Salfred	USS820_WRITE_1(sc, USS820_SBIE, 0);
1408184610Salfred
1409184610Salfred	/* disable interrupts */
1410184610Salfred	USS820_WRITE_1(sc, USS820_SBIE1, 0);
1411184610Salfred
1412184610Salfred	/* disable all endpoints */
1413184610Salfred	for (n = 0; n != USS820_EP_MAX; n++) {
1414184610Salfred
1415184610Salfred		/* select endpoint */
1416184610Salfred		USS820_WRITE_1(sc, USS820_EPINDEX, n);
1417184610Salfred
1418184610Salfred		/* disable endpoint */
1419184610Salfred		uss820dci_update_shared_1(sc, USS820_EPCON, 0, 0);
1420184610Salfred	}
1421184610Salfred
1422184610Salfred	/*
1423184610Salfred	 * Initialise default values for some registers that cannot be
1424184610Salfred	 * changed during operation!
1425184610Salfred	 */
1426184610Salfred	for (n = 0; n != USS820_EP_MAX; n++) {
1427184610Salfred
1428184610Salfred		uss820dci_get_hw_ep_profile(NULL, &pf, n);
1429184610Salfred
1430184610Salfred		/* the maximum frame sizes should be the same */
1431184610Salfred		if (pf->max_in_frame_size != pf->max_out_frame_size) {
1432184610Salfred			DPRINTF("Max frame size mismatch %u != %u\n",
1433184610Salfred			    pf->max_in_frame_size, pf->max_out_frame_size);
1434184610Salfred		}
1435184610Salfred		if (pf->support_isochronous) {
1436184610Salfred			if (pf->max_in_frame_size <= 64) {
1437184610Salfred				temp = (USS820_TXCON_FFSZ_16_64 |
1438184610Salfred				    USS820_TXCON_TXISO |
1439184610Salfred				    USS820_TXCON_ATM);
1440184610Salfred			} else if (pf->max_in_frame_size <= 256) {
1441184610Salfred				temp = (USS820_TXCON_FFSZ_64_256 |
1442184610Salfred				    USS820_TXCON_TXISO |
1443184610Salfred				    USS820_TXCON_ATM);
1444184610Salfred			} else if (pf->max_in_frame_size <= 512) {
1445184610Salfred				temp = (USS820_TXCON_FFSZ_8_512 |
1446184610Salfred				    USS820_TXCON_TXISO |
1447184610Salfred				    USS820_TXCON_ATM);
1448184610Salfred			} else {	/* 1024 bytes */
1449184610Salfred				temp = (USS820_TXCON_FFSZ_32_1024 |
1450184610Salfred				    USS820_TXCON_TXISO |
1451184610Salfred				    USS820_TXCON_ATM);
1452184610Salfred			}
1453184610Salfred		} else {
1454184610Salfred			if ((pf->max_in_frame_size <= 8) &&
1455184610Salfred			    (sc->sc_flags.mcsr_feat)) {
1456184610Salfred				temp = (USS820_TXCON_FFSZ_8_512 |
1457184610Salfred				    USS820_TXCON_ATM);
1458184610Salfred			} else if (pf->max_in_frame_size <= 16) {
1459184610Salfred				temp = (USS820_TXCON_FFSZ_16_64 |
1460184610Salfred				    USS820_TXCON_ATM);
1461184610Salfred			} else if ((pf->max_in_frame_size <= 32) &&
1462184610Salfred			    (sc->sc_flags.mcsr_feat)) {
1463184610Salfred				temp = (USS820_TXCON_FFSZ_32_1024 |
1464184610Salfred				    USS820_TXCON_ATM);
1465184610Salfred			} else {	/* 64 bytes */
1466184610Salfred				temp = (USS820_TXCON_FFSZ_64_256 |
1467184610Salfred				    USS820_TXCON_ATM);
1468184610Salfred			}
1469184610Salfred		}
1470184610Salfred
1471184610Salfred		/* need to configure the chip early */
1472184610Salfred
1473184610Salfred		USS820_WRITE_1(sc, USS820_EPINDEX, n);
1474184610Salfred		USS820_WRITE_1(sc, USS820_TXCON, temp);
1475184610Salfred		USS820_WRITE_1(sc, USS820_RXCON, temp);
1476184610Salfred
1477184610Salfred		if (pf->support_control) {
1478184610Salfred			temp = USS820_EPCON_CTLEP |
1479184610Salfred			    USS820_EPCON_RXSPM |
1480184610Salfred			    USS820_EPCON_RXIE |
1481184610Salfred			    USS820_EPCON_RXEPEN |
1482184610Salfred			    USS820_EPCON_TXOE |
1483184610Salfred			    USS820_EPCON_TXEPEN;
1484184610Salfred		} else {
1485184610Salfred			temp = USS820_EPCON_RXEPEN | USS820_EPCON_TXEPEN;
1486184610Salfred		}
1487184610Salfred
1488184610Salfred		uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF, temp);
1489184610Salfred	}
1490184610Salfred
1491184824Sthompsa	USB_BUS_UNLOCK(&sc->sc_bus);
1492184610Salfred
1493184610Salfred	/* catch any lost interrupts */
1494184610Salfred
1495184610Salfred	uss820dci_do_poll(&sc->sc_bus);
1496184610Salfred
1497184610Salfred	return (0);			/* success */
1498184610Salfred}
1499184610Salfred
1500184610Salfredvoid
1501184610Salfreduss820dci_uninit(struct uss820dci_softc *sc)
1502184610Salfred{
1503184610Salfred	uint8_t temp;
1504184610Salfred
1505184824Sthompsa	USB_BUS_LOCK(&sc->sc_bus);
1506184610Salfred
1507184610Salfred	/* disable all interrupts */
1508184610Salfred	temp = USS820_READ_1(sc, USS820_SCR);
1509184610Salfred	temp &= ~USS820_SCR_T_IRQ;
1510184610Salfred	USS820_WRITE_1(sc, USS820_SCR, temp);
1511184610Salfred
1512184610Salfred	sc->sc_flags.port_powered = 0;
1513184610Salfred	sc->sc_flags.status_vbus = 0;
1514184610Salfred	sc->sc_flags.status_bus_reset = 0;
1515184610Salfred	sc->sc_flags.status_suspend = 0;
1516184610Salfred	sc->sc_flags.change_suspend = 0;
1517184610Salfred	sc->sc_flags.change_connect = 1;
1518184610Salfred
1519184610Salfred	uss820dci_pull_down(sc);
1520184824Sthompsa	USB_BUS_UNLOCK(&sc->sc_bus);
1521184610Salfred}
1522184610Salfred
1523228483Shselaskystatic void
1524184610Salfreduss820dci_suspend(struct uss820dci_softc *sc)
1525184610Salfred{
1526228483Shselasky	/* TODO */
1527184610Salfred}
1528184610Salfred
1529228483Shselaskystatic void
1530184610Salfreduss820dci_resume(struct uss820dci_softc *sc)
1531184610Salfred{
1532228483Shselasky	/* TODO */
1533184610Salfred}
1534184610Salfred
1535184610Salfredstatic void
1536192984Sthompsauss820dci_do_poll(struct usb_bus *bus)
1537184610Salfred{
1538184610Salfred	struct uss820dci_softc *sc = USS820_DCI_BUS2SC(bus);
1539184610Salfred
1540184824Sthompsa	USB_BUS_LOCK(&sc->sc_bus);
1541184610Salfred	uss820dci_interrupt_poll(sc);
1542184824Sthompsa	USB_BUS_UNLOCK(&sc->sc_bus);
1543184610Salfred}
1544184610Salfred
1545184610Salfred/*------------------------------------------------------------------------*
1546184610Salfred * at91dci bulk support
1547184610Salfred *------------------------------------------------------------------------*/
1548184610Salfredstatic void
1549192984Sthompsauss820dci_device_bulk_open(struct usb_xfer *xfer)
1550184610Salfred{
1551184610Salfred	return;
1552184610Salfred}
1553184610Salfred
1554184610Salfredstatic void
1555192984Sthompsauss820dci_device_bulk_close(struct usb_xfer *xfer)
1556184610Salfred{
1557184610Salfred	uss820dci_device_done(xfer, USB_ERR_CANCELLED);
1558184610Salfred}
1559184610Salfred
1560184610Salfredstatic void
1561192984Sthompsauss820dci_device_bulk_enter(struct usb_xfer *xfer)
1562184610Salfred{
1563184610Salfred	return;
1564184610Salfred}
1565184610Salfred
1566184610Salfredstatic void
1567192984Sthompsauss820dci_device_bulk_start(struct usb_xfer *xfer)
1568184610Salfred{
1569184610Salfred	/* setup TDs */
1570184610Salfred	uss820dci_setup_standard_chain(xfer);
1571184610Salfred	uss820dci_start_standard_chain(xfer);
1572184610Salfred}
1573184610Salfred
1574192984Sthompsastruct usb_pipe_methods uss820dci_device_bulk_methods =
1575184610Salfred{
1576184610Salfred	.open = uss820dci_device_bulk_open,
1577184610Salfred	.close = uss820dci_device_bulk_close,
1578184610Salfred	.enter = uss820dci_device_bulk_enter,
1579184610Salfred	.start = uss820dci_device_bulk_start,
1580184610Salfred};
1581184610Salfred
1582184610Salfred/*------------------------------------------------------------------------*
1583184610Salfred * at91dci control support
1584184610Salfred *------------------------------------------------------------------------*/
1585184610Salfredstatic void
1586192984Sthompsauss820dci_device_ctrl_open(struct usb_xfer *xfer)
1587184610Salfred{
1588184610Salfred	return;
1589184610Salfred}
1590184610Salfred
1591184610Salfredstatic void
1592192984Sthompsauss820dci_device_ctrl_close(struct usb_xfer *xfer)
1593184610Salfred{
1594184610Salfred	uss820dci_device_done(xfer, USB_ERR_CANCELLED);
1595184610Salfred}
1596184610Salfred
1597184610Salfredstatic void
1598192984Sthompsauss820dci_device_ctrl_enter(struct usb_xfer *xfer)
1599184610Salfred{
1600184610Salfred	return;
1601184610Salfred}
1602184610Salfred
1603184610Salfredstatic void
1604192984Sthompsauss820dci_device_ctrl_start(struct usb_xfer *xfer)
1605184610Salfred{
1606184610Salfred	/* setup TDs */
1607184610Salfred	uss820dci_setup_standard_chain(xfer);
1608184610Salfred	uss820dci_start_standard_chain(xfer);
1609184610Salfred}
1610184610Salfred
1611192984Sthompsastruct usb_pipe_methods uss820dci_device_ctrl_methods =
1612184610Salfred{
1613184610Salfred	.open = uss820dci_device_ctrl_open,
1614184610Salfred	.close = uss820dci_device_ctrl_close,
1615184610Salfred	.enter = uss820dci_device_ctrl_enter,
1616184610Salfred	.start = uss820dci_device_ctrl_start,
1617184610Salfred};
1618184610Salfred
1619184610Salfred/*------------------------------------------------------------------------*
1620184610Salfred * at91dci interrupt support
1621184610Salfred *------------------------------------------------------------------------*/
1622184610Salfredstatic void
1623192984Sthompsauss820dci_device_intr_open(struct usb_xfer *xfer)
1624184610Salfred{
1625184610Salfred	return;
1626184610Salfred}
1627184610Salfred
1628184610Salfredstatic void
1629192984Sthompsauss820dci_device_intr_close(struct usb_xfer *xfer)
1630184610Salfred{
1631184610Salfred	uss820dci_device_done(xfer, USB_ERR_CANCELLED);
1632184610Salfred}
1633184610Salfred
1634184610Salfredstatic void
1635192984Sthompsauss820dci_device_intr_enter(struct usb_xfer *xfer)
1636184610Salfred{
1637184610Salfred	return;
1638184610Salfred}
1639184610Salfred
1640184610Salfredstatic void
1641192984Sthompsauss820dci_device_intr_start(struct usb_xfer *xfer)
1642184610Salfred{
1643184610Salfred	/* setup TDs */
1644184610Salfred	uss820dci_setup_standard_chain(xfer);
1645184610Salfred	uss820dci_start_standard_chain(xfer);
1646184610Salfred}
1647184610Salfred
1648192984Sthompsastruct usb_pipe_methods uss820dci_device_intr_methods =
1649184610Salfred{
1650184610Salfred	.open = uss820dci_device_intr_open,
1651184610Salfred	.close = uss820dci_device_intr_close,
1652184610Salfred	.enter = uss820dci_device_intr_enter,
1653184610Salfred	.start = uss820dci_device_intr_start,
1654184610Salfred};
1655184610Salfred
1656184610Salfred/*------------------------------------------------------------------------*
1657184610Salfred * at91dci full speed isochronous support
1658184610Salfred *------------------------------------------------------------------------*/
1659184610Salfredstatic void
1660192984Sthompsauss820dci_device_isoc_fs_open(struct usb_xfer *xfer)
1661184610Salfred{
1662184610Salfred	return;
1663184610Salfred}
1664184610Salfred
1665184610Salfredstatic void
1666192984Sthompsauss820dci_device_isoc_fs_close(struct usb_xfer *xfer)
1667184610Salfred{
1668184610Salfred	uss820dci_device_done(xfer, USB_ERR_CANCELLED);
1669184610Salfred}
1670184610Salfred
1671184610Salfredstatic void
1672192984Sthompsauss820dci_device_isoc_fs_enter(struct usb_xfer *xfer)
1673184610Salfred{
1674187173Sthompsa	struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
1675184610Salfred	uint32_t temp;
1676184610Salfred	uint32_t nframes;
1677184610Salfred
1678184610Salfred	DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
1679193644Sthompsa	    xfer, xfer->endpoint->isoc_next, xfer->nframes);
1680184610Salfred
1681184610Salfred	/* get the current frame index - we don't need the high bits */
1682184610Salfred
1683184610Salfred	nframes = USS820_READ_1(sc, USS820_SOFL);
1684184610Salfred
1685184610Salfred	/*
1686184610Salfred	 * check if the frame index is within the window where the
1687184610Salfred	 * frames will be inserted
1688184610Salfred	 */
1689193644Sthompsa	temp = (nframes - xfer->endpoint->isoc_next) & USS820_SOFL_MASK;
1690184610Salfred
1691193644Sthompsa	if ((xfer->endpoint->is_synced == 0) ||
1692184610Salfred	    (temp < xfer->nframes)) {
1693184610Salfred		/*
1694184610Salfred		 * If there is data underflow or the pipe queue is
1695184610Salfred		 * empty we schedule the transfer a few frames ahead
1696184610Salfred		 * of the current frame position. Else two isochronous
1697184610Salfred		 * transfers might overlap.
1698184610Salfred		 */
1699193644Sthompsa		xfer->endpoint->isoc_next = (nframes + 3) & USS820_SOFL_MASK;
1700193644Sthompsa		xfer->endpoint->is_synced = 1;
1701193644Sthompsa		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
1702184610Salfred	}
1703184610Salfred	/*
1704184610Salfred	 * compute how many milliseconds the insertion is ahead of the
1705184610Salfred	 * current frame position:
1706184610Salfred	 */
1707193644Sthompsa	temp = (xfer->endpoint->isoc_next - nframes) & USS820_SOFL_MASK;
1708184610Salfred
1709184610Salfred	/*
1710184610Salfred	 * pre-compute when the isochronous transfer will be finished:
1711184610Salfred	 */
1712184610Salfred	xfer->isoc_time_complete =
1713194228Sthompsa	    usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
1714184610Salfred	    xfer->nframes;
1715184610Salfred
1716184610Salfred	/* compute frame number for next insertion */
1717193644Sthompsa	xfer->endpoint->isoc_next += xfer->nframes;
1718184610Salfred
1719184610Salfred	/* setup TDs */
1720184610Salfred	uss820dci_setup_standard_chain(xfer);
1721184610Salfred}
1722184610Salfred
1723184610Salfredstatic void
1724192984Sthompsauss820dci_device_isoc_fs_start(struct usb_xfer *xfer)
1725184610Salfred{
1726184610Salfred	/* start TD chain */
1727184610Salfred	uss820dci_start_standard_chain(xfer);
1728184610Salfred}
1729184610Salfred
1730192984Sthompsastruct usb_pipe_methods uss820dci_device_isoc_fs_methods =
1731184610Salfred{
1732184610Salfred	.open = uss820dci_device_isoc_fs_open,
1733184610Salfred	.close = uss820dci_device_isoc_fs_close,
1734184610Salfred	.enter = uss820dci_device_isoc_fs_enter,
1735184610Salfred	.start = uss820dci_device_isoc_fs_start,
1736184610Salfred};
1737184610Salfred
1738184610Salfred/*------------------------------------------------------------------------*
1739184610Salfred * at91dci root control support
1740184610Salfred *------------------------------------------------------------------------*
1741190735Sthompsa * Simulate a hardware HUB by handling all the necessary requests.
1742184610Salfred *------------------------------------------------------------------------*/
1743184610Salfred
1744192984Sthompsastatic const struct usb_device_descriptor uss820dci_devd = {
1745192984Sthompsa	.bLength = sizeof(struct usb_device_descriptor),
1746184610Salfred	.bDescriptorType = UDESC_DEVICE,
1747184610Salfred	.bcdUSB = {0x00, 0x02},
1748184610Salfred	.bDeviceClass = UDCLASS_HUB,
1749184610Salfred	.bDeviceSubClass = UDSUBCLASS_HUB,
1750213802Shselasky	.bDeviceProtocol = UDPROTO_FSHUB,
1751184610Salfred	.bMaxPacketSize = 64,
1752184610Salfred	.bcdDevice = {0x00, 0x01},
1753184610Salfred	.iManufacturer = 1,
1754184610Salfred	.iProduct = 2,
1755184610Salfred	.bNumConfigurations = 1,
1756184610Salfred};
1757184610Salfred
1758192984Sthompsastatic const struct usb_device_qualifier uss820dci_odevd = {
1759192984Sthompsa	.bLength = sizeof(struct usb_device_qualifier),
1760184610Salfred	.bDescriptorType = UDESC_DEVICE_QUALIFIER,
1761184610Salfred	.bcdUSB = {0x00, 0x02},
1762184610Salfred	.bDeviceClass = UDCLASS_HUB,
1763184610Salfred	.bDeviceSubClass = UDSUBCLASS_HUB,
1764184610Salfred	.bDeviceProtocol = UDPROTO_FSHUB,
1765184610Salfred	.bMaxPacketSize0 = 0,
1766184610Salfred	.bNumConfigurations = 0,
1767184610Salfred};
1768184610Salfred
1769184610Salfredstatic const struct uss820dci_config_desc uss820dci_confd = {
1770184610Salfred	.confd = {
1771192984Sthompsa		.bLength = sizeof(struct usb_config_descriptor),
1772184610Salfred		.bDescriptorType = UDESC_CONFIG,
1773184610Salfred		.wTotalLength[0] = sizeof(uss820dci_confd),
1774184610Salfred		.bNumInterface = 1,
1775184610Salfred		.bConfigurationValue = 1,
1776184610Salfred		.iConfiguration = 0,
1777184610Salfred		.bmAttributes = UC_SELF_POWERED,
1778184610Salfred		.bMaxPower = 0,
1779184610Salfred	},
1780184610Salfred	.ifcd = {
1781192984Sthompsa		.bLength = sizeof(struct usb_interface_descriptor),
1782184610Salfred		.bDescriptorType = UDESC_INTERFACE,
1783184610Salfred		.bNumEndpoints = 1,
1784184610Salfred		.bInterfaceClass = UICLASS_HUB,
1785184610Salfred		.bInterfaceSubClass = UISUBCLASS_HUB,
1786213802Shselasky		.bInterfaceProtocol = 0,
1787184610Salfred	},
1788184610Salfred
1789184610Salfred	.endpd = {
1790192984Sthompsa		.bLength = sizeof(struct usb_endpoint_descriptor),
1791184610Salfred		.bDescriptorType = UDESC_ENDPOINT,
1792184610Salfred		.bEndpointAddress = (UE_DIR_IN | USS820_DCI_INTR_ENDPT),
1793184610Salfred		.bmAttributes = UE_INTERRUPT,
1794184610Salfred		.wMaxPacketSize[0] = 8,
1795184610Salfred		.bInterval = 255,
1796184610Salfred	},
1797184610Salfred};
1798184610Salfred
1799233774Shselasky#define	HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
1800233774Shselasky
1801192984Sthompsastatic const struct usb_hub_descriptor_min uss820dci_hubd = {
1802184610Salfred	.bDescLength = sizeof(uss820dci_hubd),
1803184610Salfred	.bDescriptorType = UDESC_HUB,
1804184610Salfred	.bNbrPorts = 1,
1805233774Shselasky	HSETW(.wHubCharacteristics, (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL)),
1806184610Salfred	.bPwrOn2PwrGood = 50,
1807184610Salfred	.bHubContrCurrent = 0,
1808184610Salfred	.DeviceRemovable = {0},		/* port is removable */
1809184610Salfred};
1810184610Salfred
1811184610Salfred#define	STRING_VENDOR \
1812246125Shselasky  "A\0G\0E\0R\0E"
1813184610Salfred
1814184610Salfred#define	STRING_PRODUCT \
1815246125Shselasky  "D\0C\0I\0 \0R\0o\0o\0t\0 \0H\0U\0B"
1816184610Salfred
1817184610SalfredUSB_MAKE_STRING_DESC(STRING_VENDOR, uss820dci_vendor);
1818184610SalfredUSB_MAKE_STRING_DESC(STRING_PRODUCT, uss820dci_product);
1819184610Salfred
1820193045Sthompsastatic usb_error_t
1821192984Sthompsauss820dci_roothub_exec(struct usb_device *udev,
1822192984Sthompsa    struct usb_device_request *req, const void **pptr, uint16_t *plength)
1823184610Salfred{
1824191402Sthompsa	struct uss820dci_softc *sc = USS820_DCI_BUS2SC(udev->bus);
1825191402Sthompsa	const void *ptr;
1826191402Sthompsa	uint16_t len;
1827184610Salfred	uint16_t value;
1828184610Salfred	uint16_t index;
1829193045Sthompsa	usb_error_t err;
1830184610Salfred
1831184824Sthompsa	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
1832184610Salfred
1833184610Salfred	/* buffer reset */
1834191402Sthompsa	ptr = (const void *)&sc->sc_hub_temp;
1835191402Sthompsa	len = 0;
1836191402Sthompsa	err = 0;
1837184610Salfred
1838191402Sthompsa	value = UGETW(req->wValue);
1839191402Sthompsa	index = UGETW(req->wIndex);
1840184610Salfred
1841184610Salfred	/* demultiplex the control request */
1842184610Salfred
1843191402Sthompsa	switch (req->bmRequestType) {
1844184610Salfred	case UT_READ_DEVICE:
1845191402Sthompsa		switch (req->bRequest) {
1846184610Salfred		case UR_GET_DESCRIPTOR:
1847184610Salfred			goto tr_handle_get_descriptor;
1848184610Salfred		case UR_GET_CONFIG:
1849184610Salfred			goto tr_handle_get_config;
1850184610Salfred		case UR_GET_STATUS:
1851184610Salfred			goto tr_handle_get_status;
1852184610Salfred		default:
1853184610Salfred			goto tr_stalled;
1854184610Salfred		}
1855184610Salfred		break;
1856184610Salfred
1857184610Salfred	case UT_WRITE_DEVICE:
1858191402Sthompsa		switch (req->bRequest) {
1859184610Salfred		case UR_SET_ADDRESS:
1860184610Salfred			goto tr_handle_set_address;
1861184610Salfred		case UR_SET_CONFIG:
1862184610Salfred			goto tr_handle_set_config;
1863184610Salfred		case UR_CLEAR_FEATURE:
1864184610Salfred			goto tr_valid;	/* nop */
1865184610Salfred		case UR_SET_DESCRIPTOR:
1866184610Salfred			goto tr_valid;	/* nop */
1867184610Salfred		case UR_SET_FEATURE:
1868184610Salfred		default:
1869184610Salfred			goto tr_stalled;
1870184610Salfred		}
1871184610Salfred		break;
1872184610Salfred
1873184610Salfred	case UT_WRITE_ENDPOINT:
1874191402Sthompsa		switch (req->bRequest) {
1875184610Salfred		case UR_CLEAR_FEATURE:
1876191402Sthompsa			switch (UGETW(req->wValue)) {
1877184610Salfred			case UF_ENDPOINT_HALT:
1878184610Salfred				goto tr_handle_clear_halt;
1879184610Salfred			case UF_DEVICE_REMOTE_WAKEUP:
1880184610Salfred				goto tr_handle_clear_wakeup;
1881184610Salfred			default:
1882184610Salfred				goto tr_stalled;
1883184610Salfred			}
1884184610Salfred			break;
1885184610Salfred		case UR_SET_FEATURE:
1886191402Sthompsa			switch (UGETW(req->wValue)) {
1887184610Salfred			case UF_ENDPOINT_HALT:
1888184610Salfred				goto tr_handle_set_halt;
1889184610Salfred			case UF_DEVICE_REMOTE_WAKEUP:
1890184610Salfred				goto tr_handle_set_wakeup;
1891184610Salfred			default:
1892184610Salfred				goto tr_stalled;
1893184610Salfred			}
1894184610Salfred			break;
1895184610Salfred		case UR_SYNCH_FRAME:
1896184610Salfred			goto tr_valid;	/* nop */
1897184610Salfred		default:
1898184610Salfred			goto tr_stalled;
1899184610Salfred		}
1900184610Salfred		break;
1901184610Salfred
1902184610Salfred	case UT_READ_ENDPOINT:
1903191402Sthompsa		switch (req->bRequest) {
1904184610Salfred		case UR_GET_STATUS:
1905184610Salfred			goto tr_handle_get_ep_status;
1906184610Salfred		default:
1907184610Salfred			goto tr_stalled;
1908184610Salfred		}
1909184610Salfred		break;
1910184610Salfred
1911184610Salfred	case UT_WRITE_INTERFACE:
1912191402Sthompsa		switch (req->bRequest) {
1913184610Salfred		case UR_SET_INTERFACE:
1914184610Salfred			goto tr_handle_set_interface;
1915184610Salfred		case UR_CLEAR_FEATURE:
1916184610Salfred			goto tr_valid;	/* nop */
1917184610Salfred		case UR_SET_FEATURE:
1918184610Salfred		default:
1919184610Salfred			goto tr_stalled;
1920184610Salfred		}
1921184610Salfred		break;
1922184610Salfred
1923184610Salfred	case UT_READ_INTERFACE:
1924191402Sthompsa		switch (req->bRequest) {
1925184610Salfred		case UR_GET_INTERFACE:
1926184610Salfred			goto tr_handle_get_interface;
1927184610Salfred		case UR_GET_STATUS:
1928184610Salfred			goto tr_handle_get_iface_status;
1929184610Salfred		default:
1930184610Salfred			goto tr_stalled;
1931184610Salfred		}
1932184610Salfred		break;
1933184610Salfred
1934184610Salfred	case UT_WRITE_CLASS_INTERFACE:
1935184610Salfred	case UT_WRITE_VENDOR_INTERFACE:
1936184610Salfred		/* XXX forward */
1937184610Salfred		break;
1938184610Salfred
1939184610Salfred	case UT_READ_CLASS_INTERFACE:
1940184610Salfred	case UT_READ_VENDOR_INTERFACE:
1941184610Salfred		/* XXX forward */
1942184610Salfred		break;
1943184610Salfred
1944184610Salfred	case UT_WRITE_CLASS_DEVICE:
1945191402Sthompsa		switch (req->bRequest) {
1946184610Salfred		case UR_CLEAR_FEATURE:
1947184610Salfred			goto tr_valid;
1948184610Salfred		case UR_SET_DESCRIPTOR:
1949184610Salfred		case UR_SET_FEATURE:
1950184610Salfred			break;
1951184610Salfred		default:
1952184610Salfred			goto tr_stalled;
1953184610Salfred		}
1954184610Salfred		break;
1955184610Salfred
1956184610Salfred	case UT_WRITE_CLASS_OTHER:
1957191402Sthompsa		switch (req->bRequest) {
1958184610Salfred		case UR_CLEAR_FEATURE:
1959184610Salfred			goto tr_handle_clear_port_feature;
1960184610Salfred		case UR_SET_FEATURE:
1961184610Salfred			goto tr_handle_set_port_feature;
1962184610Salfred		case UR_CLEAR_TT_BUFFER:
1963184610Salfred		case UR_RESET_TT:
1964184610Salfred		case UR_STOP_TT:
1965184610Salfred			goto tr_valid;
1966184610Salfred
1967184610Salfred		default:
1968184610Salfred			goto tr_stalled;
1969184610Salfred		}
1970184610Salfred		break;
1971184610Salfred
1972184610Salfred	case UT_READ_CLASS_OTHER:
1973191402Sthompsa		switch (req->bRequest) {
1974184610Salfred		case UR_GET_TT_STATE:
1975184610Salfred			goto tr_handle_get_tt_state;
1976184610Salfred		case UR_GET_STATUS:
1977184610Salfred			goto tr_handle_get_port_status;
1978184610Salfred		default:
1979184610Salfred			goto tr_stalled;
1980184610Salfred		}
1981184610Salfred		break;
1982184610Salfred
1983184610Salfred	case UT_READ_CLASS_DEVICE:
1984191402Sthompsa		switch (req->bRequest) {
1985184610Salfred		case UR_GET_DESCRIPTOR:
1986184610Salfred			goto tr_handle_get_class_descriptor;
1987184610Salfred		case UR_GET_STATUS:
1988184610Salfred			goto tr_handle_get_class_status;
1989184610Salfred
1990184610Salfred		default:
1991184610Salfred			goto tr_stalled;
1992184610Salfred		}
1993184610Salfred		break;
1994184610Salfred	default:
1995184610Salfred		goto tr_stalled;
1996184610Salfred	}
1997184610Salfred	goto tr_valid;
1998184610Salfred
1999184610Salfredtr_handle_get_descriptor:
2000184610Salfred	switch (value >> 8) {
2001184610Salfred	case UDESC_DEVICE:
2002184610Salfred		if (value & 0xff) {
2003184610Salfred			goto tr_stalled;
2004184610Salfred		}
2005191402Sthompsa		len = sizeof(uss820dci_devd);
2006191402Sthompsa		ptr = (const void *)&uss820dci_devd;
2007184610Salfred		goto tr_valid;
2008184610Salfred	case UDESC_CONFIG:
2009184610Salfred		if (value & 0xff) {
2010184610Salfred			goto tr_stalled;
2011184610Salfred		}
2012191402Sthompsa		len = sizeof(uss820dci_confd);
2013191402Sthompsa		ptr = (const void *)&uss820dci_confd;
2014184610Salfred		goto tr_valid;
2015184610Salfred	case UDESC_STRING:
2016184610Salfred		switch (value & 0xff) {
2017184610Salfred		case 0:		/* Language table */
2018246123Shselasky			len = sizeof(usb_string_lang_en);
2019246123Shselasky			ptr = (const void *)&usb_string_lang_en;
2020184610Salfred			goto tr_valid;
2021184610Salfred
2022184610Salfred		case 1:		/* Vendor */
2023191402Sthompsa			len = sizeof(uss820dci_vendor);
2024191402Sthompsa			ptr = (const void *)&uss820dci_vendor;
2025184610Salfred			goto tr_valid;
2026184610Salfred
2027184610Salfred		case 2:		/* Product */
2028191402Sthompsa			len = sizeof(uss820dci_product);
2029191402Sthompsa			ptr = (const void *)&uss820dci_product;
2030184610Salfred			goto tr_valid;
2031184610Salfred		default:
2032184610Salfred			break;
2033184610Salfred		}
2034184610Salfred		break;
2035184610Salfred	default:
2036184610Salfred		goto tr_stalled;
2037184610Salfred	}
2038184610Salfred	goto tr_stalled;
2039184610Salfred
2040184610Salfredtr_handle_get_config:
2041191402Sthompsa	len = 1;
2042184610Salfred	sc->sc_hub_temp.wValue[0] = sc->sc_conf;
2043184610Salfred	goto tr_valid;
2044184610Salfred
2045184610Salfredtr_handle_get_status:
2046191402Sthompsa	len = 2;
2047184610Salfred	USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED);
2048184610Salfred	goto tr_valid;
2049184610Salfred
2050184610Salfredtr_handle_set_address:
2051184610Salfred	if (value & 0xFF00) {
2052184610Salfred		goto tr_stalled;
2053184610Salfred	}
2054184610Salfred	sc->sc_rt_addr = value;
2055184610Salfred	goto tr_valid;
2056184610Salfred
2057184610Salfredtr_handle_set_config:
2058184610Salfred	if (value >= 2) {
2059184610Salfred		goto tr_stalled;
2060184610Salfred	}
2061184610Salfred	sc->sc_conf = value;
2062184610Salfred	goto tr_valid;
2063184610Salfred
2064184610Salfredtr_handle_get_interface:
2065191402Sthompsa	len = 1;
2066184610Salfred	sc->sc_hub_temp.wValue[0] = 0;
2067184610Salfred	goto tr_valid;
2068184610Salfred
2069184610Salfredtr_handle_get_tt_state:
2070184610Salfredtr_handle_get_class_status:
2071184610Salfredtr_handle_get_iface_status:
2072184610Salfredtr_handle_get_ep_status:
2073191402Sthompsa	len = 2;
2074184610Salfred	USETW(sc->sc_hub_temp.wValue, 0);
2075184610Salfred	goto tr_valid;
2076184610Salfred
2077184610Salfredtr_handle_set_halt:
2078184610Salfredtr_handle_set_interface:
2079184610Salfredtr_handle_set_wakeup:
2080184610Salfredtr_handle_clear_wakeup:
2081184610Salfredtr_handle_clear_halt:
2082184610Salfred	goto tr_valid;
2083184610Salfred
2084184610Salfredtr_handle_clear_port_feature:
2085184610Salfred	if (index != 1) {
2086184610Salfred		goto tr_stalled;
2087184610Salfred	}
2088184610Salfred	DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index);
2089184610Salfred
2090184610Salfred	switch (value) {
2091184610Salfred	case UHF_PORT_SUSPEND:
2092184610Salfred		uss820dci_wakeup_peer(sc);
2093184610Salfred		break;
2094184610Salfred
2095184610Salfred	case UHF_PORT_ENABLE:
2096184610Salfred		sc->sc_flags.port_enabled = 0;
2097184610Salfred		break;
2098184610Salfred
2099184610Salfred	case UHF_PORT_TEST:
2100184610Salfred	case UHF_PORT_INDICATOR:
2101184610Salfred	case UHF_C_PORT_ENABLE:
2102184610Salfred	case UHF_C_PORT_OVER_CURRENT:
2103184610Salfred	case UHF_C_PORT_RESET:
2104184610Salfred		/* nops */
2105184610Salfred		break;
2106184610Salfred	case UHF_PORT_POWER:
2107184610Salfred		sc->sc_flags.port_powered = 0;
2108184610Salfred		uss820dci_pull_down(sc);
2109184610Salfred		break;
2110184610Salfred	case UHF_C_PORT_CONNECTION:
2111184610Salfred		sc->sc_flags.change_connect = 0;
2112184610Salfred		break;
2113184610Salfred	case UHF_C_PORT_SUSPEND:
2114184610Salfred		sc->sc_flags.change_suspend = 0;
2115184610Salfred		break;
2116184610Salfred	default:
2117191402Sthompsa		err = USB_ERR_IOERROR;
2118184610Salfred		goto done;
2119184610Salfred	}
2120184610Salfred	goto tr_valid;
2121184610Salfred
2122184610Salfredtr_handle_set_port_feature:
2123184610Salfred	if (index != 1) {
2124184610Salfred		goto tr_stalled;
2125184610Salfred	}
2126184610Salfred	DPRINTFN(9, "UR_SET_PORT_FEATURE\n");
2127184610Salfred
2128184610Salfred	switch (value) {
2129184610Salfred	case UHF_PORT_ENABLE:
2130184610Salfred		sc->sc_flags.port_enabled = 1;
2131184610Salfred		break;
2132184610Salfred	case UHF_PORT_SUSPEND:
2133184610Salfred	case UHF_PORT_RESET:
2134184610Salfred	case UHF_PORT_TEST:
2135184610Salfred	case UHF_PORT_INDICATOR:
2136184610Salfred		/* nops */
2137184610Salfred		break;
2138184610Salfred	case UHF_PORT_POWER:
2139184610Salfred		sc->sc_flags.port_powered = 1;
2140184610Salfred		break;
2141184610Salfred	default:
2142191402Sthompsa		err = USB_ERR_IOERROR;
2143184610Salfred		goto done;
2144184610Salfred	}
2145184610Salfred	goto tr_valid;
2146184610Salfred
2147184610Salfredtr_handle_get_port_status:
2148184610Salfred
2149184610Salfred	DPRINTFN(9, "UR_GET_PORT_STATUS\n");
2150184610Salfred
2151184610Salfred	if (index != 1) {
2152184610Salfred		goto tr_stalled;
2153184610Salfred	}
2154184610Salfred	if (sc->sc_flags.status_vbus) {
2155184610Salfred		uss820dci_pull_up(sc);
2156184610Salfred	} else {
2157184610Salfred		uss820dci_pull_down(sc);
2158184610Salfred	}
2159184610Salfred
2160184610Salfred	/* Select FULL-speed and Device Side Mode */
2161184610Salfred
2162184610Salfred	value = UPS_PORT_MODE_DEVICE;
2163184610Salfred
2164184610Salfred	if (sc->sc_flags.port_powered) {
2165184610Salfred		value |= UPS_PORT_POWER;
2166184610Salfred	}
2167184610Salfred	if (sc->sc_flags.port_enabled) {
2168184610Salfred		value |= UPS_PORT_ENABLED;
2169184610Salfred	}
2170184610Salfred	if (sc->sc_flags.status_vbus &&
2171184610Salfred	    sc->sc_flags.status_bus_reset) {
2172184610Salfred		value |= UPS_CURRENT_CONNECT_STATUS;
2173184610Salfred	}
2174184610Salfred	if (sc->sc_flags.status_suspend) {
2175184610Salfred		value |= UPS_SUSPEND;
2176184610Salfred	}
2177184610Salfred	USETW(sc->sc_hub_temp.ps.wPortStatus, value);
2178184610Salfred
2179184610Salfred	value = 0;
2180184610Salfred
2181184610Salfred	if (sc->sc_flags.change_connect) {
2182184610Salfred		value |= UPS_C_CONNECT_STATUS;
2183184610Salfred	}
2184184610Salfred	if (sc->sc_flags.change_suspend) {
2185184610Salfred		value |= UPS_C_SUSPEND;
2186184610Salfred	}
2187184610Salfred	USETW(sc->sc_hub_temp.ps.wPortChange, value);
2188191402Sthompsa	len = sizeof(sc->sc_hub_temp.ps);
2189184610Salfred	goto tr_valid;
2190184610Salfred
2191184610Salfredtr_handle_get_class_descriptor:
2192184610Salfred	if (value & 0xFF) {
2193184610Salfred		goto tr_stalled;
2194184610Salfred	}
2195191402Sthompsa	ptr = (const void *)&uss820dci_hubd;
2196191402Sthompsa	len = sizeof(uss820dci_hubd);
2197184610Salfred	goto tr_valid;
2198184610Salfred
2199184610Salfredtr_stalled:
2200191402Sthompsa	err = USB_ERR_STALLED;
2201184610Salfredtr_valid:
2202184610Salfreddone:
2203191402Sthompsa	*plength = len;
2204191402Sthompsa	*pptr = ptr;
2205191402Sthompsa	return (err);
2206184610Salfred}
2207184610Salfred
2208184610Salfredstatic void
2209192984Sthompsauss820dci_xfer_setup(struct usb_setup_params *parm)
2210184610Salfred{
2211192984Sthompsa	const struct usb_hw_ep_profile *pf;
2212184610Salfred	struct uss820dci_softc *sc;
2213192984Sthompsa	struct usb_xfer *xfer;
2214184610Salfred	void *last_obj;
2215184610Salfred	uint32_t ntd;
2216184610Salfred	uint32_t n;
2217184610Salfred	uint8_t ep_no;
2218184610Salfred
2219184610Salfred	sc = USS820_DCI_BUS2SC(parm->udev->bus);
2220184610Salfred	xfer = parm->curr_xfer;
2221184610Salfred
2222184610Salfred	/*
2223184610Salfred	 * NOTE: This driver does not use any of the parameters that
2224184610Salfred	 * are computed from the following values. Just set some
2225184610Salfred	 * reasonable dummies:
2226184610Salfred	 */
2227184610Salfred	parm->hc_max_packet_size = 0x500;
2228184610Salfred	parm->hc_max_packet_count = 1;
2229184610Salfred	parm->hc_max_frame_size = 0x500;
2230184610Salfred
2231194228Sthompsa	usbd_transfer_setup_sub(parm);
2232184610Salfred
2233184610Salfred	/*
2234184610Salfred	 * compute maximum number of TDs
2235184610Salfred	 */
2236184610Salfred	if (parm->methods == &uss820dci_device_ctrl_methods) {
2237184610Salfred
2238184610Salfred		ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC */ ;
2239184610Salfred
2240184610Salfred	} else if (parm->methods == &uss820dci_device_bulk_methods) {
2241184610Salfred
2242184610Salfred		ntd = xfer->nframes + 1 /* SYNC */ ;
2243184610Salfred
2244184610Salfred	} else if (parm->methods == &uss820dci_device_intr_methods) {
2245184610Salfred
2246184610Salfred		ntd = xfer->nframes + 1 /* SYNC */ ;
2247184610Salfred
2248184610Salfred	} else if (parm->methods == &uss820dci_device_isoc_fs_methods) {
2249184610Salfred
2250184610Salfred		ntd = xfer->nframes + 1 /* SYNC */ ;
2251184610Salfred
2252184610Salfred	} else {
2253184610Salfred
2254184610Salfred		ntd = 0;
2255184610Salfred	}
2256184610Salfred
2257184610Salfred	/*
2258194228Sthompsa	 * check if "usbd_transfer_setup_sub" set an error
2259184610Salfred	 */
2260184610Salfred	if (parm->err) {
2261184610Salfred		return;
2262184610Salfred	}
2263184610Salfred	/*
2264184610Salfred	 * allocate transfer descriptors
2265184610Salfred	 */
2266184610Salfred	last_obj = NULL;
2267184610Salfred
2268184610Salfred	/*
2269184610Salfred	 * get profile stuff
2270184610Salfred	 */
2271184610Salfred	if (ntd) {
2272184610Salfred
2273193644Sthompsa		ep_no = xfer->endpointno & UE_ADDR;
2274184610Salfred		uss820dci_get_hw_ep_profile(parm->udev, &pf, ep_no);
2275184610Salfred
2276184610Salfred		if (pf == NULL) {
2277184610Salfred			/* should not happen */
2278184610Salfred			parm->err = USB_ERR_INVAL;
2279184610Salfred			return;
2280184610Salfred		}
2281184610Salfred	} else {
2282184610Salfred		ep_no = 0;
2283184610Salfred		pf = NULL;
2284184610Salfred	}
2285184610Salfred
2286184610Salfred	/* align data */
2287184610Salfred	parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
2288184610Salfred
2289184610Salfred	for (n = 0; n != ntd; n++) {
2290184610Salfred
2291184610Salfred		struct uss820dci_td *td;
2292184610Salfred
2293184610Salfred		if (parm->buf) {
2294184610Salfred
2295184610Salfred			td = USB_ADD_BYTES(parm->buf, parm->size[0]);
2296184610Salfred
2297184610Salfred			/* init TD */
2298184610Salfred			td->io_tag = sc->sc_io_tag;
2299184610Salfred			td->io_hdl = sc->sc_io_hdl;
2300184610Salfred			td->max_packet_size = xfer->max_packet_size;
2301184610Salfred			td->ep_index = ep_no;
2302184610Salfred			if (pf->support_multi_buffer &&
2303184610Salfred			    (parm->methods != &uss820dci_device_ctrl_methods)) {
2304184610Salfred				td->support_multi_buffer = 1;
2305184610Salfred			}
2306184610Salfred			td->obj_next = last_obj;
2307184610Salfred
2308184610Salfred			last_obj = td;
2309184610Salfred		}
2310184610Salfred		parm->size[0] += sizeof(*td);
2311184610Salfred	}
2312184610Salfred
2313184610Salfred	xfer->td_start[0] = last_obj;
2314184610Salfred}
2315184610Salfred
2316184610Salfredstatic void
2317192984Sthompsauss820dci_xfer_unsetup(struct usb_xfer *xfer)
2318184610Salfred{
2319184610Salfred	return;
2320184610Salfred}
2321184610Salfred
2322184610Salfredstatic void
2323193644Sthompsauss820dci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
2324193644Sthompsa    struct usb_endpoint *ep)
2325184610Salfred{
2326184610Salfred	struct uss820dci_softc *sc = USS820_DCI_BUS2SC(udev->bus);
2327184610Salfred
2328193644Sthompsa	DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
2329193644Sthompsa	    ep, udev->address,
2330192499Sthompsa	    edesc->bEndpointAddress, udev->flags.usb_mode,
2331184610Salfred	    sc->sc_rt_addr);
2332184610Salfred
2333190735Sthompsa	if (udev->device_index != sc->sc_rt_addr) {
2334184610Salfred
2335184610Salfred		if (udev->speed != USB_SPEED_FULL) {
2336184610Salfred			/* not supported */
2337184610Salfred			return;
2338184610Salfred		}
2339184610Salfred		switch (edesc->bmAttributes & UE_XFERTYPE) {
2340184610Salfred		case UE_CONTROL:
2341193644Sthompsa			ep->methods = &uss820dci_device_ctrl_methods;
2342184610Salfred			break;
2343184610Salfred		case UE_INTERRUPT:
2344193644Sthompsa			ep->methods = &uss820dci_device_intr_methods;
2345184610Salfred			break;
2346184610Salfred		case UE_ISOCHRONOUS:
2347193644Sthompsa			ep->methods = &uss820dci_device_isoc_fs_methods;
2348184610Salfred			break;
2349184610Salfred		case UE_BULK:
2350193644Sthompsa			ep->methods = &uss820dci_device_bulk_methods;
2351184610Salfred			break;
2352184610Salfred		default:
2353184610Salfred			/* do nothing */
2354184610Salfred			break;
2355184610Salfred		}
2356184610Salfred	}
2357184610Salfred}
2358184610Salfred
2359228483Shselaskystatic void
2360228483Shselaskyuss820dci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state)
2361228483Shselasky{
2362228483Shselasky	struct uss820dci_softc *sc = USS820_DCI_BUS2SC(bus);
2363228483Shselasky
2364228483Shselasky	switch (state) {
2365228483Shselasky	case USB_HW_POWER_SUSPEND:
2366228483Shselasky		uss820dci_suspend(sc);
2367228483Shselasky		break;
2368228483Shselasky	case USB_HW_POWER_SHUTDOWN:
2369228483Shselasky		uss820dci_uninit(sc);
2370228483Shselasky		break;
2371228483Shselasky	case USB_HW_POWER_RESUME:
2372228483Shselasky		uss820dci_resume(sc);
2373228483Shselasky		break;
2374228483Shselasky	default:
2375228483Shselasky		break;
2376228483Shselasky	}
2377228483Shselasky}
2378228483Shselasky
2379192984Sthompsastruct usb_bus_methods uss820dci_bus_methods =
2380184610Salfred{
2381193644Sthompsa	.endpoint_init = &uss820dci_ep_init,
2382184610Salfred	.xfer_setup = &uss820dci_xfer_setup,
2383184610Salfred	.xfer_unsetup = &uss820dci_xfer_unsetup,
2384184610Salfred	.get_hw_ep_profile = &uss820dci_get_hw_ep_profile,
2385239214Shselasky	.xfer_stall = &uss820dci_xfer_stall,
2386184610Salfred	.set_stall = &uss820dci_set_stall,
2387184610Salfred	.clear_stall = &uss820dci_clear_stall,
2388190735Sthompsa	.roothub_exec = &uss820dci_roothub_exec,
2389195960Salfred	.xfer_poll = &uss820dci_do_poll,
2390228483Shselasky	.set_hw_power_sleep = uss820dci_set_hw_power_sleep,
2391184610Salfred};
2392