summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/controller/uhci.h
blob: 0878e798e74913459dbe6201b5209c1dc9f235cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/* $FreeBSD$ */
/*-
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Lennart Augustsson (lennart@augustsson.net) at
 * Carlstedt Research & Technology.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _UHCI_H_
#define	_UHCI_H_

#define	UHCI_MAX_DEVICES MIN(USB_MAX_DEVICES, 128)

#define	UHCI_FRAMELIST_COUNT	1024	/* units */
#define	UHCI_FRAMELIST_ALIGN	4096	/* bytes */

/* Structures alignment (bytes) */
#define	UHCI_TD_ALIGN		16
#define	UHCI_QH_ALIGN		16

#if	((USB_PAGE_SIZE < UHCI_TD_ALIGN) || (UHCI_TD_ALIGN == 0) ||	\
	(USB_PAGE_SIZE < UHCI_QH_ALIGN) || (UHCI_QH_ALIGN == 0))
#error	"Invalid USB page size!"
#endif

typedef uint32_t uhci_physaddr_t;

#define	UHCI_PTR_T		0x00000001
#define	UHCI_PTR_TD		0x00000000
#define	UHCI_PTR_QH		0x00000002
#define	UHCI_PTR_VF		0x00000004

/*
 * The Queue Heads (QH) and Transfer Descriptors (TD) are accessed by
 * both the CPU and the USB-controller which run concurrently. Great
 * care must be taken. When the data-structures are linked into the
 * USB controller's frame list, the USB-controller "owns" the
 * td_status and qh_elink fields, which will not be written by the
 * CPU.
 *
 */

struct uhci_td {
/*
 * Data used by the UHCI controller.
 * volatile is used in order to mantain struct members ordering.
 */
	volatile uint32_t td_next;
	volatile uint32_t td_status;
#define	UHCI_TD_GET_ACTLEN(s)	(((s) + 1) & 0x3ff)
#define	UHCI_TD_ZERO_ACTLEN(t)	((t) | 0x3ff)
#define	UHCI_TD_BITSTUFF	0x00020000
#define	UHCI_TD_CRCTO		0x00040000
#define	UHCI_TD_NAK		0x00080000
#define	UHCI_TD_BABBLE		0x00100000
#define	UHCI_TD_DBUFFER		0x00200000
#define	UHCI_TD_STALLED		0x00400000
#define	UHCI_TD_ACTIVE		0x00800000
#define	UHCI_TD_IOC		0x01000000
#define	UHCI_TD_IOS		0x02000000
#define	UHCI_TD_LS		0x04000000
#define	UHCI_TD_GET_ERRCNT(s)	(((s) >> 27) & 3)
#define	UHCI_TD_SET_ERRCNT(n)	((n) << 27)
#define	UHCI_TD_SPD		0x20000000
	volatile uint32_t td_token;
#define	UHCI_TD_PID		0x000000ff
#define	UHCI_TD_PID_IN		0x00000069
#define	UHCI_TD_PID_OUT		0x000000e1
#define	UHCI_TD_PID_SETUP	0x0000002d
#define	UHCI_TD_GET_PID(s)	((s) & 0xff)
#define	UHCI_TD_SET_DEVADDR(a)	((a) << 8)
#define	UHCI_TD_GET_DEVADDR(s)	(((s) >> 8) & 0x7f)
#define	UHCI_TD_SET_ENDPT(e)	(((e) & 0xf) << 15)
#define	UHCI_TD_GET_ENDPT(s)	(((s) >> 15) & 0xf)
#define	UHCI_TD_SET_DT(t)	((t) << 19)
#define	UHCI_TD_GET_DT(s)	(((s) >> 19) & 1)
#define	UHCI_TD_SET_MAXLEN(l)	(((l)-1) << 21)
#define	UHCI_TD_GET_MAXLEN(s)	((((s) >> 21) + 1) & 0x7ff)
#define	UHCI_TD_MAXLEN_MASK	0xffe00000
	volatile uint32_t td_buffer;
/*
 * Extra information needed:
 */
	struct uhci_td *next;
	struct uhci_td *prev;
	struct uhci_td *obj_next;
	struct usb_page_cache *page_cache;
	struct usb_page_cache *fix_pc;
	uint32_t td_self;
	uint16_t len;
} __aligned(UHCI_TD_ALIGN);

typedef struct uhci_td uhci_td_t;

#define	UHCI_TD_ERROR	(UHCI_TD_BITSTUFF | UHCI_TD_CRCTO | 		\
			UHCI_TD_BABBLE | UHCI_TD_DBUFFER | UHCI_TD_STALLED)

#define	UHCI_TD_SETUP(len, endp, dev)	(UHCI_TD_SET_MAXLEN(len) |	\
					UHCI_TD_SET_ENDPT(endp) |	\
					UHCI_TD_SET_DEVADDR(dev) |	\
					UHCI_TD_PID_SETUP)

#define	UHCI_TD_OUT(len, endp, dev, dt)	(UHCI_TD_SET_MAXLEN(len) |	\
					UHCI_TD_SET_ENDPT(endp) |	\
					UHCI_TD_SET_DEVADDR(dev) |	\
					UHCI_TD_PID_OUT | UHCI_TD_SET_DT(dt))

#define	UHCI_TD_IN(len, endp, dev, dt)	(UHCI_TD_SET_MAXLEN(len) |	\
					UHCI_TD_SET_ENDPT(endp) |	\
					UHCI_TD_SET_DEVADDR(dev) |	\
					UHCI_TD_PID_IN | UHCI_TD_SET_DT(dt))

struct uhci_qh {
/*
 * Data used by the UHCI controller.
 */
	volatile uint32_t qh_h_next;
	volatile uint32_t qh_e_next;
/*
 * Extra information needed:
 */
	struct uhci_qh *h_next;
	struct uhci_qh *h_prev;
	struct uhci_qh *obj_next;
	struct uhci_td *e_next;
	struct usb_page_cache *page_cache;
	uint32_t qh_self;
	uint16_t intr_pos;
} __aligned(UHCI_QH_ALIGN);

typedef struct uhci_qh uhci_qh_t;

/* Maximum number of isochronous TD's and QH's interrupt */
#define	UHCI_VFRAMELIST_COUNT	128
#define	UHCI_IFRAMELIST_COUNT	(2 * UHCI_VFRAMELIST_COUNT)

#if	(((UHCI_VFRAMELIST_COUNT & (UHCI_VFRAMELIST_COUNT-1)) != 0) ||	\
	(UHCI_VFRAMELIST_COUNT > UHCI_FRAMELIST_COUNT))
#error	"UHCI_VFRAMELIST_COUNT is not power of two"
#error	"or UHCI_VFRAMELIST_COUNT > UHCI_FRAMELIST_COUNT"
#endif

#if (UHCI_VFRAMELIST_COUNT < USB_MAX_FS_ISOC_FRAMES_PER_XFER)
#error "maximum number of full-speed isochronous frames is higher than supported!"
#endif

struct uhci_config_desc {
	struct usb_config_descriptor confd;
	struct usb_interface_descriptor ifcd;
	struct usb_endpoint_descriptor endpd;
} __packed;

union uhci_hub_desc {
	struct usb_status stat;
	struct usb_port_status ps;
	uint8_t	temp[128];
};

struct uhci_hw_softc {
	struct usb_page_cache pframes_pc;
	struct usb_page_cache isoc_start_pc[UHCI_VFRAMELIST_COUNT];
	struct usb_page_cache intr_start_pc[UHCI_IFRAMELIST_COUNT];
	struct usb_page_cache ls_ctl_start_pc;
	struct usb_page_cache fs_ctl_start_pc;
	struct usb_page_cache bulk_start_pc;
	struct usb_page_cache last_qh_pc;
	struct usb_page_cache last_td_pc;

	struct usb_page pframes_pg;
	struct usb_page isoc_start_pg[UHCI_VFRAMELIST_COUNT];
	struct usb_page intr_start_pg[UHCI_IFRAMELIST_COUNT];
	struct usb_page ls_ctl_start_pg;
	struct usb_page fs_ctl_start_pg;
	struct usb_page bulk_start_pg;
	struct usb_page last_qh_pg;
	struct usb_page last_td_pg;
};

typedef struct uhci_softc {
	struct uhci_hw_softc sc_hw;
	struct usb_bus sc_bus;		/* base device */
	union uhci_hub_desc sc_hub_desc;
	struct usb_callout sc_root_intr;

	struct usb_device *sc_devices[UHCI_MAX_DEVICES];
	/* pointer to last TD for isochronous */
	struct uhci_td *sc_isoc_p_last[UHCI_VFRAMELIST_COUNT];
	/* pointer to last QH for interrupt */
	struct uhci_qh *sc_intr_p_last[UHCI_IFRAMELIST_COUNT];
	/* pointer to last QH for low speed control */
	struct uhci_qh *sc_ls_ctl_p_last;
	/* pointer to last QH for full speed control */
	struct uhci_qh *sc_fs_ctl_p_last;
	/* pointer to last QH for bulk */
	struct uhci_qh *sc_bulk_p_last;
	struct uhci_qh *sc_reclaim_qh_p;
	struct uhci_qh *sc_last_qh_p;
	struct uhci_td *sc_last_td_p;
	struct resource *sc_io_res;
	struct resource *sc_irq_res;
	void   *sc_intr_hdl;
	device_t sc_dev;
	bus_size_t sc_io_size;
	bus_space_tag_t sc_io_tag;
	bus_space_handle_t sc_io_hdl;

	uint32_t sc_loops;		/* number of QHs that wants looping */

	uint16_t sc_intr_stat[UHCI_IFRAMELIST_COUNT];
	uint16_t sc_saved_frnum;

	uint8_t	sc_addr;		/* device address */
	uint8_t	sc_conf;		/* device configuration */
	uint8_t	sc_isreset;		/* bits set if a root hub is reset */
	uint8_t	sc_isresumed;		/* bits set if a port was resumed */
	uint8_t	sc_saved_sof;
	uint8_t	sc_hub_idata[1];

	char	sc_vendor[16];		/* vendor string for root hub */
} uhci_softc_t;

usb_bus_mem_cb_t uhci_iterate_hw_softc;

usb_error_t uhci_init(uhci_softc_t *sc);
void	uhci_suspend(uhci_softc_t *sc);
void	uhci_resume(uhci_softc_t *sc);
void	uhci_reset(uhci_softc_t *sc);
void	uhci_interrupt(uhci_softc_t *sc);

#endif					/* _UHCI_H_ */
OpenPOWER on IntegriCloud