| @@ -1,1055 +1,1055 @@ | | | @@ -1,1055 +1,1055 @@ |
1 | /* $NetBSD: ehci.c,v 1.267 2019/06/13 17:20:25 maxv Exp $ */ | | 1 | /* $NetBSD: ehci.c,v 1.268 2019/12/31 18:11:18 skrll Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 2004-2012 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2004-2012 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Lennart Augustsson (lennart@augustsson.net), Charles M. Hannum, | | 8 | * by Lennart Augustsson (lennart@augustsson.net), Charles M. Hannum, |
9 | * Jeremy Morse (jeremy.morse@gmail.com), Jared D. McNeill | | 9 | * Jeremy Morse (jeremy.morse@gmail.com), Jared D. McNeill |
10 | * (jmcneill@invisible.ca) and Matthew R. Green (mrg@eterna.com.au). | | 10 | * (jmcneill@invisible.ca) and Matthew R. Green (mrg@eterna.com.au). |
11 | * | | 11 | * |
12 | * Redistribution and use in source and binary forms, with or without | | 12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions | | 13 | * modification, are permitted provided that the following conditions |
14 | * are met: | | 14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright | | 15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. | | 16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright | | 17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the | | 18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. | | 19 | * documentation and/or other materials provided with the distribution. |
20 | * | | 20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | | 21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 | * POSSIBILITY OF SUCH DAMAGE. | | 31 | * POSSIBILITY OF SUCH DAMAGE. |
32 | */ | | 32 | */ |
33 | | | 33 | |
34 | /* | | 34 | /* |
35 | * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. | | 35 | * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. |
36 | * | | 36 | * |
37 | * The EHCI 1.0 spec can be found at | | 37 | * The EHCI 1.0 spec can be found at |
38 | * http://www.intel.com/technology/usb/spec.htm | | 38 | * http://www.intel.com/technology/usb/spec.htm |
39 | * and the USB 2.0 spec at | | 39 | * and the USB 2.0 spec at |
40 | * http://www.usb.org/developers/docs/ | | 40 | * http://www.usb.org/developers/docs/ |
41 | * | | 41 | * |
42 | */ | | 42 | */ |
43 | | | 43 | |
44 | /* | | 44 | /* |
45 | * TODO: | | 45 | * TODO: |
46 | * 1) hold off explorations by companion controllers until ehci has started. | | 46 | * 1) hold off explorations by companion controllers until ehci has started. |
47 | * | | 47 | * |
48 | * 2) The hub driver needs to handle and schedule the transaction translator, | | 48 | * 2) The hub driver needs to handle and schedule the transaction translator, |
49 | * to assign place in frame where different devices get to go. See chapter | | 49 | * to assign place in frame where different devices get to go. See chapter |
50 | * on hubs in USB 2.0 for details. | | 50 | * on hubs in USB 2.0 for details. |
51 | * | | 51 | * |
52 | * 3) Command failures are not recovered correctly. | | 52 | * 3) Command failures are not recovered correctly. |
53 | */ | | 53 | */ |
54 | | | 54 | |
55 | #include <sys/cdefs.h> | | 55 | #include <sys/cdefs.h> |
56 | __KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.267 2019/06/13 17:20:25 maxv Exp $"); | | 56 | __KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.268 2019/12/31 18:11:18 skrll Exp $"); |
57 | | | 57 | |
58 | #include "ohci.h" | | 58 | #include "ohci.h" |
59 | #include "uhci.h" | | 59 | #include "uhci.h" |
60 | | | 60 | |
61 | #ifdef _KERNEL_OPT | | 61 | #ifdef _KERNEL_OPT |
62 | #include "opt_usb.h" | | 62 | #include "opt_usb.h" |
63 | #endif | | 63 | #endif |
64 | | | 64 | |
65 | #include <sys/param.h> | | 65 | #include <sys/param.h> |
66 | | | 66 | |
67 | #include <sys/bus.h> | | 67 | #include <sys/bus.h> |
68 | #include <sys/cpu.h> | | 68 | #include <sys/cpu.h> |
69 | #include <sys/device.h> | | 69 | #include <sys/device.h> |
70 | #include <sys/kernel.h> | | 70 | #include <sys/kernel.h> |
71 | #include <sys/kmem.h> | | 71 | #include <sys/kmem.h> |
72 | #include <sys/mutex.h> | | 72 | #include <sys/mutex.h> |
73 | #include <sys/proc.h> | | 73 | #include <sys/proc.h> |
74 | #include <sys/queue.h> | | 74 | #include <sys/queue.h> |
75 | #include <sys/select.h> | | 75 | #include <sys/select.h> |
76 | #include <sys/sysctl.h> | | 76 | #include <sys/sysctl.h> |
77 | #include <sys/systm.h> | | 77 | #include <sys/systm.h> |
78 | #include <sys/reboot.h> | | 78 | #include <sys/reboot.h> |
79 | | | 79 | |
80 | #include <machine/endian.h> | | 80 | #include <machine/endian.h> |
81 | | | 81 | |
82 | #include <dev/usb/usb.h> | | 82 | #include <dev/usb/usb.h> |
83 | #include <dev/usb/usbdi.h> | | 83 | #include <dev/usb/usbdi.h> |
84 | #include <dev/usb/usbdivar.h> | | 84 | #include <dev/usb/usbdivar.h> |
85 | #include <dev/usb/usbhist.h> | | 85 | #include <dev/usb/usbhist.h> |
86 | #include <dev/usb/usb_mem.h> | | 86 | #include <dev/usb/usb_mem.h> |
87 | #include <dev/usb/usb_quirks.h> | | 87 | #include <dev/usb/usb_quirks.h> |
88 | | | 88 | |
89 | #include <dev/usb/ehcireg.h> | | 89 | #include <dev/usb/ehcireg.h> |
90 | #include <dev/usb/ehcivar.h> | | 90 | #include <dev/usb/ehcivar.h> |
91 | #include <dev/usb/usbroothub.h> | | 91 | #include <dev/usb/usbroothub.h> |
92 | | | 92 | |
93 | #ifdef USB_DEBUG | | 93 | #ifdef USB_DEBUG |
94 | #ifndef EHCI_DEBUG | | 94 | #ifndef EHCI_DEBUG |
95 | #define ehcidebug 0 | | 95 | #define ehcidebug 0 |
96 | #else | | 96 | #else |
97 | static int ehcidebug = 0; | | 97 | static int ehcidebug = 0; |
98 | | | 98 | |
99 | SYSCTL_SETUP(sysctl_hw_ehci_setup, "sysctl hw.ehci setup") | | 99 | SYSCTL_SETUP(sysctl_hw_ehci_setup, "sysctl hw.ehci setup") |
100 | { | | 100 | { |
101 | int err; | | 101 | int err; |
102 | const struct sysctlnode *rnode; | | 102 | const struct sysctlnode *rnode; |
103 | const struct sysctlnode *cnode; | | 103 | const struct sysctlnode *cnode; |
104 | | | 104 | |
105 | err = sysctl_createv(clog, 0, NULL, &rnode, | | 105 | err = sysctl_createv(clog, 0, NULL, &rnode, |
106 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "ehci", | | 106 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "ehci", |
107 | SYSCTL_DESCR("ehci global controls"), | | 107 | SYSCTL_DESCR("ehci global controls"), |
108 | NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); | | 108 | NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); |
109 | | | 109 | |
110 | if (err) | | 110 | if (err) |
111 | goto fail; | | 111 | goto fail; |
112 | | | 112 | |
113 | /* control debugging printfs */ | | 113 | /* control debugging printfs */ |
114 | err = sysctl_createv(clog, 0, &rnode, &cnode, | | 114 | err = sysctl_createv(clog, 0, &rnode, &cnode, |
115 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, | | 115 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, |
116 | "debug", SYSCTL_DESCR("Enable debugging output"), | | 116 | "debug", SYSCTL_DESCR("Enable debugging output"), |
117 | NULL, 0, &ehcidebug, sizeof(ehcidebug), CTL_CREATE, CTL_EOL); | | 117 | NULL, 0, &ehcidebug, sizeof(ehcidebug), CTL_CREATE, CTL_EOL); |
118 | if (err) | | 118 | if (err) |
119 | goto fail; | | 119 | goto fail; |
120 | | | 120 | |
121 | return; | | 121 | return; |
122 | fail: | | 122 | fail: |
123 | aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err); | | 123 | aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err); |
124 | } | | 124 | } |
125 | | | 125 | |
126 | #endif /* EHCI_DEBUG */ | | 126 | #endif /* EHCI_DEBUG */ |
127 | #endif /* USB_DEBUG */ | | 127 | #endif /* USB_DEBUG */ |
128 | | | 128 | |
129 | #define DPRINTF(FMT,A,B,C,D) USBHIST_LOG(ehcidebug,FMT,A,B,C,D) | | 129 | #define DPRINTF(FMT,A,B,C,D) USBHIST_LOG(ehcidebug,FMT,A,B,C,D) |
130 | #define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(ehcidebug,N,FMT,A,B,C,D) | | 130 | #define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(ehcidebug,N,FMT,A,B,C,D) |
131 | #define EHCIHIST_FUNC() USBHIST_FUNC() | | 131 | #define EHCIHIST_FUNC() USBHIST_FUNC() |
132 | #define EHCIHIST_CALLED() USBHIST_CALLED(ehcidebug) | | 132 | #define EHCIHIST_CALLED() USBHIST_CALLED(ehcidebug) |
133 | | | 133 | |
134 | struct ehci_pipe { | | 134 | struct ehci_pipe { |
135 | struct usbd_pipe pipe; | | 135 | struct usbd_pipe pipe; |
136 | int nexttoggle; | | 136 | int nexttoggle; |
137 | | | 137 | |
138 | ehci_soft_qh_t *sqh; | | 138 | ehci_soft_qh_t *sqh; |
139 | union { | | 139 | union { |
140 | /* Control pipe */ | | 140 | /* Control pipe */ |
141 | struct { | | 141 | struct { |
142 | usb_dma_t reqdma; | | 142 | usb_dma_t reqdma; |
143 | } ctrl; | | 143 | } ctrl; |
144 | /* Interrupt pipe */ | | 144 | /* Interrupt pipe */ |
145 | struct { | | 145 | struct { |
146 | u_int length; | | 146 | u_int length; |
147 | } intr; | | 147 | } intr; |
148 | /* Iso pipe */ | | 148 | /* Iso pipe */ |
149 | struct { | | 149 | struct { |
150 | u_int next_frame; | | 150 | u_int next_frame; |
151 | u_int cur_xfers; | | 151 | u_int cur_xfers; |
152 | } isoc; | | 152 | } isoc; |
153 | }; | | 153 | }; |
154 | }; | | 154 | }; |
155 | | | 155 | |
156 | typedef TAILQ_HEAD(ex_completeq, ehci_xfer) ex_completeq_t; | | 156 | typedef TAILQ_HEAD(ex_completeq, ehci_xfer) ex_completeq_t; |
157 | | | 157 | |
158 | Static usbd_status ehci_open(struct usbd_pipe *); | | 158 | Static usbd_status ehci_open(struct usbd_pipe *); |
159 | Static void ehci_poll(struct usbd_bus *); | | 159 | Static void ehci_poll(struct usbd_bus *); |
160 | Static void ehci_softintr(void *); | | 160 | Static void ehci_softintr(void *); |
161 | Static int ehci_intr1(ehci_softc_t *); | | 161 | Static int ehci_intr1(ehci_softc_t *); |
162 | Static void ehci_check_qh_intr(ehci_softc_t *, struct ehci_xfer *, | | 162 | Static void ehci_check_qh_intr(ehci_softc_t *, struct ehci_xfer *, |
163 | ex_completeq_t *); | | 163 | ex_completeq_t *); |
164 | Static void ehci_check_itd_intr(ehci_softc_t *, struct ehci_xfer *, | | 164 | Static void ehci_check_itd_intr(ehci_softc_t *, struct ehci_xfer *, |
165 | ex_completeq_t *); | | 165 | ex_completeq_t *); |
166 | Static void ehci_check_sitd_intr(ehci_softc_t *, struct ehci_xfer *, | | 166 | Static void ehci_check_sitd_intr(ehci_softc_t *, struct ehci_xfer *, |
167 | ex_completeq_t *); | | 167 | ex_completeq_t *); |
168 | Static void ehci_idone(struct ehci_xfer *, ex_completeq_t *); | | 168 | Static void ehci_idone(struct ehci_xfer *, ex_completeq_t *); |
169 | Static void ehci_timeout(void *); | | 169 | Static void ehci_timeout(void *); |
170 | Static void ehci_timeout_task(void *); | | 170 | Static void ehci_timeout_task(void *); |
171 | Static void ehci_intrlist_timeout(void *); | | 171 | Static void ehci_intrlist_timeout(void *); |
172 | Static void ehci_doorbell(void *); | | 172 | Static void ehci_doorbell(void *); |
173 | Static void ehci_pcd(void *); | | 173 | Static void ehci_pcd(void *); |
174 | | | 174 | |
175 | Static struct usbd_xfer * | | 175 | Static struct usbd_xfer * |
176 | ehci_allocx(struct usbd_bus *, unsigned int); | | 176 | ehci_allocx(struct usbd_bus *, unsigned int); |
177 | Static void ehci_freex(struct usbd_bus *, struct usbd_xfer *); | | 177 | Static void ehci_freex(struct usbd_bus *, struct usbd_xfer *); |
178 | | | 178 | |
179 | Static void ehci_get_lock(struct usbd_bus *, kmutex_t **); | | 179 | Static void ehci_get_lock(struct usbd_bus *, kmutex_t **); |
180 | Static int ehci_roothub_ctrl(struct usbd_bus *, | | 180 | Static int ehci_roothub_ctrl(struct usbd_bus *, |
181 | usb_device_request_t *, void *, int); | | 181 | usb_device_request_t *, void *, int); |
182 | | | 182 | |
183 | Static usbd_status ehci_root_intr_transfer(struct usbd_xfer *); | | 183 | Static usbd_status ehci_root_intr_transfer(struct usbd_xfer *); |
184 | Static usbd_status ehci_root_intr_start(struct usbd_xfer *); | | 184 | Static usbd_status ehci_root_intr_start(struct usbd_xfer *); |
185 | Static void ehci_root_intr_abort(struct usbd_xfer *); | | 185 | Static void ehci_root_intr_abort(struct usbd_xfer *); |
186 | Static void ehci_root_intr_close(struct usbd_pipe *); | | 186 | Static void ehci_root_intr_close(struct usbd_pipe *); |
187 | Static void ehci_root_intr_done(struct usbd_xfer *); | | 187 | Static void ehci_root_intr_done(struct usbd_xfer *); |
188 | | | 188 | |
189 | Static int ehci_device_ctrl_init(struct usbd_xfer *); | | 189 | Static int ehci_device_ctrl_init(struct usbd_xfer *); |
190 | Static void ehci_device_ctrl_fini(struct usbd_xfer *); | | 190 | Static void ehci_device_ctrl_fini(struct usbd_xfer *); |
191 | Static usbd_status ehci_device_ctrl_transfer(struct usbd_xfer *); | | 191 | Static usbd_status ehci_device_ctrl_transfer(struct usbd_xfer *); |
192 | Static usbd_status ehci_device_ctrl_start(struct usbd_xfer *); | | 192 | Static usbd_status ehci_device_ctrl_start(struct usbd_xfer *); |
193 | Static void ehci_device_ctrl_abort(struct usbd_xfer *); | | 193 | Static void ehci_device_ctrl_abort(struct usbd_xfer *); |
194 | Static void ehci_device_ctrl_close(struct usbd_pipe *); | | 194 | Static void ehci_device_ctrl_close(struct usbd_pipe *); |
195 | Static void ehci_device_ctrl_done(struct usbd_xfer *); | | 195 | Static void ehci_device_ctrl_done(struct usbd_xfer *); |
196 | | | 196 | |
197 | Static int ehci_device_bulk_init(struct usbd_xfer *); | | 197 | Static int ehci_device_bulk_init(struct usbd_xfer *); |
198 | Static void ehci_device_bulk_fini(struct usbd_xfer *); | | 198 | Static void ehci_device_bulk_fini(struct usbd_xfer *); |
199 | Static usbd_status ehci_device_bulk_transfer(struct usbd_xfer *); | | 199 | Static usbd_status ehci_device_bulk_transfer(struct usbd_xfer *); |
200 | Static usbd_status ehci_device_bulk_start(struct usbd_xfer *); | | 200 | Static usbd_status ehci_device_bulk_start(struct usbd_xfer *); |
201 | Static void ehci_device_bulk_abort(struct usbd_xfer *); | | 201 | Static void ehci_device_bulk_abort(struct usbd_xfer *); |
202 | Static void ehci_device_bulk_close(struct usbd_pipe *); | | 202 | Static void ehci_device_bulk_close(struct usbd_pipe *); |
203 | Static void ehci_device_bulk_done(struct usbd_xfer *); | | 203 | Static void ehci_device_bulk_done(struct usbd_xfer *); |
204 | | | 204 | |
205 | Static int ehci_device_intr_init(struct usbd_xfer *); | | 205 | Static int ehci_device_intr_init(struct usbd_xfer *); |
206 | Static void ehci_device_intr_fini(struct usbd_xfer *); | | 206 | Static void ehci_device_intr_fini(struct usbd_xfer *); |
207 | Static usbd_status ehci_device_intr_transfer(struct usbd_xfer *); | | 207 | Static usbd_status ehci_device_intr_transfer(struct usbd_xfer *); |
208 | Static usbd_status ehci_device_intr_start(struct usbd_xfer *); | | 208 | Static usbd_status ehci_device_intr_start(struct usbd_xfer *); |
209 | Static void ehci_device_intr_abort(struct usbd_xfer *); | | 209 | Static void ehci_device_intr_abort(struct usbd_xfer *); |
210 | Static void ehci_device_intr_close(struct usbd_pipe *); | | 210 | Static void ehci_device_intr_close(struct usbd_pipe *); |
211 | Static void ehci_device_intr_done(struct usbd_xfer *); | | 211 | Static void ehci_device_intr_done(struct usbd_xfer *); |
212 | | | 212 | |
213 | Static int ehci_device_isoc_init(struct usbd_xfer *); | | 213 | Static int ehci_device_isoc_init(struct usbd_xfer *); |
214 | Static void ehci_device_isoc_fini(struct usbd_xfer *); | | 214 | Static void ehci_device_isoc_fini(struct usbd_xfer *); |
215 | Static usbd_status ehci_device_isoc_transfer(struct usbd_xfer *); | | 215 | Static usbd_status ehci_device_isoc_transfer(struct usbd_xfer *); |
216 | Static void ehci_device_isoc_abort(struct usbd_xfer *); | | 216 | Static void ehci_device_isoc_abort(struct usbd_xfer *); |
217 | Static void ehci_device_isoc_close(struct usbd_pipe *); | | 217 | Static void ehci_device_isoc_close(struct usbd_pipe *); |
218 | Static void ehci_device_isoc_done(struct usbd_xfer *); | | 218 | Static void ehci_device_isoc_done(struct usbd_xfer *); |
219 | | | 219 | |
220 | Static int ehci_device_fs_isoc_init(struct usbd_xfer *); | | 220 | Static int ehci_device_fs_isoc_init(struct usbd_xfer *); |
221 | Static void ehci_device_fs_isoc_fini(struct usbd_xfer *); | | 221 | Static void ehci_device_fs_isoc_fini(struct usbd_xfer *); |
222 | Static usbd_status ehci_device_fs_isoc_transfer(struct usbd_xfer *); | | 222 | Static usbd_status ehci_device_fs_isoc_transfer(struct usbd_xfer *); |
223 | Static void ehci_device_fs_isoc_abort(struct usbd_xfer *); | | 223 | Static void ehci_device_fs_isoc_abort(struct usbd_xfer *); |
224 | Static void ehci_device_fs_isoc_close(struct usbd_pipe *); | | 224 | Static void ehci_device_fs_isoc_close(struct usbd_pipe *); |
225 | Static void ehci_device_fs_isoc_done(struct usbd_xfer *); | | 225 | Static void ehci_device_fs_isoc_done(struct usbd_xfer *); |
226 | | | 226 | |
227 | Static void ehci_device_clear_toggle(struct usbd_pipe *); | | 227 | Static void ehci_device_clear_toggle(struct usbd_pipe *); |
228 | Static void ehci_noop(struct usbd_pipe *); | | 228 | Static void ehci_noop(struct usbd_pipe *); |
229 | | | 229 | |
230 | Static void ehci_disown(ehci_softc_t *, int, int); | | 230 | Static void ehci_disown(ehci_softc_t *, int, int); |
231 | | | 231 | |
232 | Static ehci_soft_qh_t * ehci_alloc_sqh(ehci_softc_t *); | | 232 | Static ehci_soft_qh_t * ehci_alloc_sqh(ehci_softc_t *); |
233 | Static void ehci_free_sqh(ehci_softc_t *, ehci_soft_qh_t *); | | 233 | Static void ehci_free_sqh(ehci_softc_t *, ehci_soft_qh_t *); |
234 | | | 234 | |
235 | Static ehci_soft_qtd_t *ehci_alloc_sqtd(ehci_softc_t *); | | 235 | Static ehci_soft_qtd_t *ehci_alloc_sqtd(ehci_softc_t *); |
236 | Static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *); | | 236 | Static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *); |
237 | Static int ehci_alloc_sqtd_chain(ehci_softc_t *, | | 237 | Static int ehci_alloc_sqtd_chain(ehci_softc_t *, |
238 | struct usbd_xfer *, int, int, ehci_soft_qtd_t **); | | 238 | struct usbd_xfer *, int, int, ehci_soft_qtd_t **); |
239 | Static void ehci_free_sqtds(ehci_softc_t *, struct ehci_xfer *); | | 239 | Static void ehci_free_sqtds(ehci_softc_t *, struct ehci_xfer *); |
240 | | | 240 | |
241 | Static void ehci_reset_sqtd_chain(ehci_softc_t *, struct usbd_xfer *, | | 241 | Static void ehci_reset_sqtd_chain(ehci_softc_t *, struct usbd_xfer *, |
242 | int, int, int *, ehci_soft_qtd_t **); | | 242 | int, int, int *, ehci_soft_qtd_t **); |
243 | Static void ehci_append_sqtd(ehci_soft_qtd_t *, ehci_soft_qtd_t *); | | 243 | Static void ehci_append_sqtd(ehci_soft_qtd_t *, ehci_soft_qtd_t *); |
244 | | | 244 | |
245 | Static ehci_soft_itd_t *ehci_alloc_itd(ehci_softc_t *); | | 245 | Static ehci_soft_itd_t *ehci_alloc_itd(ehci_softc_t *); |
246 | Static ehci_soft_sitd_t * | | 246 | Static ehci_soft_sitd_t * |
247 | ehci_alloc_sitd(ehci_softc_t *); | | 247 | ehci_alloc_sitd(ehci_softc_t *); |
248 | | | 248 | |
249 | Static void ehci_remove_itd_chain(ehci_softc_t *, ehci_soft_itd_t *); | | 249 | Static void ehci_remove_itd_chain(ehci_softc_t *, ehci_soft_itd_t *); |
250 | Static void ehci_remove_sitd_chain(ehci_softc_t *, ehci_soft_sitd_t *); | | 250 | Static void ehci_remove_sitd_chain(ehci_softc_t *, ehci_soft_sitd_t *); |
251 | Static void ehci_free_itd_chain(ehci_softc_t *, ehci_soft_itd_t *); | | 251 | Static void ehci_free_itd_chain(ehci_softc_t *, ehci_soft_itd_t *); |
252 | Static void ehci_free_sitd_chain(ehci_softc_t *, ehci_soft_sitd_t *); | | 252 | Static void ehci_free_sitd_chain(ehci_softc_t *, ehci_soft_sitd_t *); |
253 | | | 253 | |
254 | static inline void | | 254 | static inline void |
255 | ehci_free_itd_locked(ehci_softc_t *sc, ehci_soft_itd_t *itd) | | 255 | ehci_free_itd_locked(ehci_softc_t *sc, ehci_soft_itd_t *itd) |
256 | { | | 256 | { |
257 | | | 257 | |
258 | LIST_INSERT_HEAD(&sc->sc_freeitds, itd, free_list); | | 258 | LIST_INSERT_HEAD(&sc->sc_freeitds, itd, free_list); |
259 | } | | 259 | } |
260 | | | 260 | |
261 | static inline void | | 261 | static inline void |
262 | ehci_free_sitd_locked(ehci_softc_t *sc, ehci_soft_sitd_t *sitd) | | 262 | ehci_free_sitd_locked(ehci_softc_t *sc, ehci_soft_sitd_t *sitd) |
263 | { | | 263 | { |
264 | | | 264 | |
265 | LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, free_list); | | 265 | LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, free_list); |
266 | } | | 266 | } |
267 | | | 267 | |
268 | Static void ehci_abort_isoc_xfer(struct usbd_xfer *, usbd_status); | | 268 | Static void ehci_abort_isoc_xfer(struct usbd_xfer *, usbd_status); |
269 | | | 269 | |
270 | Static usbd_status ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *, | | 270 | Static usbd_status ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *, |
271 | int); | | 271 | int); |
272 | | | 272 | |
273 | Static void ehci_add_qh(ehci_softc_t *, ehci_soft_qh_t *, | | 273 | Static void ehci_add_qh(ehci_softc_t *, ehci_soft_qh_t *, |
274 | ehci_soft_qh_t *); | | 274 | ehci_soft_qh_t *); |
275 | Static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *, | | 275 | Static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *, |
276 | ehci_soft_qh_t *); | | 276 | ehci_soft_qh_t *); |
277 | Static void ehci_set_qh_qtd(ehci_soft_qh_t *, ehci_soft_qtd_t *); | | 277 | Static void ehci_set_qh_qtd(ehci_soft_qh_t *, ehci_soft_qtd_t *); |
278 | Static void ehci_sync_hc(ehci_softc_t *); | | 278 | Static void ehci_sync_hc(ehci_softc_t *); |
279 | | | 279 | |
280 | Static void ehci_close_pipe(struct usbd_pipe *, ehci_soft_qh_t *); | | 280 | Static void ehci_close_pipe(struct usbd_pipe *, ehci_soft_qh_t *); |
281 | Static void ehci_abort_xfer(struct usbd_xfer *, usbd_status); | | 281 | Static void ehci_abort_xfer(struct usbd_xfer *, usbd_status); |
282 | | | 282 | |
283 | #ifdef EHCI_DEBUG | | 283 | #ifdef EHCI_DEBUG |
284 | Static ehci_softc_t *theehci; | | 284 | Static ehci_softc_t *theehci; |
285 | void ehci_dump(void); | | 285 | void ehci_dump(void); |
286 | #endif | | 286 | #endif |
287 | | | 287 | |
288 | #ifdef EHCI_DEBUG | | 288 | #ifdef EHCI_DEBUG |
289 | Static void ehci_dump_regs(ehci_softc_t *); | | 289 | Static void ehci_dump_regs(ehci_softc_t *); |
290 | Static void ehci_dump_sqtds(ehci_soft_qtd_t *); | | 290 | Static void ehci_dump_sqtds(ehci_soft_qtd_t *); |
291 | Static void ehci_dump_sqtd(ehci_soft_qtd_t *); | | 291 | Static void ehci_dump_sqtd(ehci_soft_qtd_t *); |
292 | Static void ehci_dump_qtd(ehci_qtd_t *); | | 292 | Static void ehci_dump_qtd(ehci_qtd_t *); |
293 | Static void ehci_dump_sqh(ehci_soft_qh_t *); | | 293 | Static void ehci_dump_sqh(ehci_soft_qh_t *); |
294 | Static void ehci_dump_sitd(struct ehci_soft_itd *); | | 294 | Static void ehci_dump_sitd(struct ehci_soft_itd *); |
295 | Static void ehci_dump_itds(ehci_soft_itd_t *); | | 295 | Static void ehci_dump_itds(ehci_soft_itd_t *); |
296 | Static void ehci_dump_itd(struct ehci_soft_itd *); | | 296 | Static void ehci_dump_itd(struct ehci_soft_itd *); |
297 | Static void ehci_dump_exfer(struct ehci_xfer *); | | 297 | Static void ehci_dump_exfer(struct ehci_xfer *); |
298 | #endif | | 298 | #endif |
299 | | | 299 | |
300 | #define EHCI_NULL htole32(EHCI_LINK_TERMINATE) | | 300 | #define EHCI_NULL htole32(EHCI_LINK_TERMINATE) |
301 | | | 301 | |
302 | static inline void | | 302 | static inline void |
303 | ehci_add_intr_list(ehci_softc_t *sc, struct ehci_xfer *ex) | | 303 | ehci_add_intr_list(ehci_softc_t *sc, struct ehci_xfer *ex) |
304 | { | | 304 | { |
305 | | | 305 | |
306 | TAILQ_INSERT_TAIL(&sc->sc_intrhead, ex, ex_next); | | 306 | TAILQ_INSERT_TAIL(&sc->sc_intrhead, ex, ex_next); |
307 | } | | 307 | } |
308 | | | 308 | |
309 | static inline void | | 309 | static inline void |
310 | ehci_del_intr_list(ehci_softc_t *sc, struct ehci_xfer *ex) | | 310 | ehci_del_intr_list(ehci_softc_t *sc, struct ehci_xfer *ex) |
311 | { | | 311 | { |
312 | | | 312 | |
313 | TAILQ_REMOVE(&sc->sc_intrhead, ex, ex_next); | | 313 | TAILQ_REMOVE(&sc->sc_intrhead, ex, ex_next); |
314 | } | | 314 | } |
315 | | | 315 | |
316 | Static const struct usbd_bus_methods ehci_bus_methods = { | | 316 | Static const struct usbd_bus_methods ehci_bus_methods = { |
317 | .ubm_open = ehci_open, | | 317 | .ubm_open = ehci_open, |
318 | .ubm_softint = ehci_softintr, | | 318 | .ubm_softint = ehci_softintr, |
319 | .ubm_dopoll = ehci_poll, | | 319 | .ubm_dopoll = ehci_poll, |
320 | .ubm_allocx = ehci_allocx, | | 320 | .ubm_allocx = ehci_allocx, |
321 | .ubm_freex = ehci_freex, | | 321 | .ubm_freex = ehci_freex, |
322 | .ubm_getlock = ehci_get_lock, | | 322 | .ubm_getlock = ehci_get_lock, |
323 | .ubm_rhctrl = ehci_roothub_ctrl, | | 323 | .ubm_rhctrl = ehci_roothub_ctrl, |
324 | }; | | 324 | }; |
325 | | | 325 | |
326 | Static const struct usbd_pipe_methods ehci_root_intr_methods = { | | 326 | Static const struct usbd_pipe_methods ehci_root_intr_methods = { |
327 | .upm_transfer = ehci_root_intr_transfer, | | 327 | .upm_transfer = ehci_root_intr_transfer, |
328 | .upm_start = ehci_root_intr_start, | | 328 | .upm_start = ehci_root_intr_start, |
329 | .upm_abort = ehci_root_intr_abort, | | 329 | .upm_abort = ehci_root_intr_abort, |
330 | .upm_close = ehci_root_intr_close, | | 330 | .upm_close = ehci_root_intr_close, |
331 | .upm_cleartoggle = ehci_noop, | | 331 | .upm_cleartoggle = ehci_noop, |
332 | .upm_done = ehci_root_intr_done, | | 332 | .upm_done = ehci_root_intr_done, |
333 | }; | | 333 | }; |
334 | | | 334 | |
335 | Static const struct usbd_pipe_methods ehci_device_ctrl_methods = { | | 335 | Static const struct usbd_pipe_methods ehci_device_ctrl_methods = { |
336 | .upm_init = ehci_device_ctrl_init, | | 336 | .upm_init = ehci_device_ctrl_init, |
337 | .upm_fini = ehci_device_ctrl_fini, | | 337 | .upm_fini = ehci_device_ctrl_fini, |
338 | .upm_transfer = ehci_device_ctrl_transfer, | | 338 | .upm_transfer = ehci_device_ctrl_transfer, |
339 | .upm_start = ehci_device_ctrl_start, | | 339 | .upm_start = ehci_device_ctrl_start, |
340 | .upm_abort = ehci_device_ctrl_abort, | | 340 | .upm_abort = ehci_device_ctrl_abort, |
341 | .upm_close = ehci_device_ctrl_close, | | 341 | .upm_close = ehci_device_ctrl_close, |
342 | .upm_cleartoggle = ehci_noop, | | 342 | .upm_cleartoggle = ehci_noop, |
343 | .upm_done = ehci_device_ctrl_done, | | 343 | .upm_done = ehci_device_ctrl_done, |
344 | }; | | 344 | }; |
345 | | | 345 | |
346 | Static const struct usbd_pipe_methods ehci_device_intr_methods = { | | 346 | Static const struct usbd_pipe_methods ehci_device_intr_methods = { |
347 | .upm_init = ehci_device_intr_init, | | 347 | .upm_init = ehci_device_intr_init, |
348 | .upm_fini = ehci_device_intr_fini, | | 348 | .upm_fini = ehci_device_intr_fini, |
349 | .upm_transfer = ehci_device_intr_transfer, | | 349 | .upm_transfer = ehci_device_intr_transfer, |
350 | .upm_start = ehci_device_intr_start, | | 350 | .upm_start = ehci_device_intr_start, |
351 | .upm_abort = ehci_device_intr_abort, | | 351 | .upm_abort = ehci_device_intr_abort, |
352 | .upm_close = ehci_device_intr_close, | | 352 | .upm_close = ehci_device_intr_close, |
353 | .upm_cleartoggle = ehci_device_clear_toggle, | | 353 | .upm_cleartoggle = ehci_device_clear_toggle, |
354 | .upm_done = ehci_device_intr_done, | | 354 | .upm_done = ehci_device_intr_done, |
355 | }; | | 355 | }; |
356 | | | 356 | |
357 | Static const struct usbd_pipe_methods ehci_device_bulk_methods = { | | 357 | Static const struct usbd_pipe_methods ehci_device_bulk_methods = { |
358 | .upm_init = ehci_device_bulk_init, | | 358 | .upm_init = ehci_device_bulk_init, |
359 | .upm_fini = ehci_device_bulk_fini, | | 359 | .upm_fini = ehci_device_bulk_fini, |
360 | .upm_transfer = ehci_device_bulk_transfer, | | 360 | .upm_transfer = ehci_device_bulk_transfer, |
361 | .upm_start = ehci_device_bulk_start, | | 361 | .upm_start = ehci_device_bulk_start, |
362 | .upm_abort = ehci_device_bulk_abort, | | 362 | .upm_abort = ehci_device_bulk_abort, |
363 | .upm_close = ehci_device_bulk_close, | | 363 | .upm_close = ehci_device_bulk_close, |
364 | .upm_cleartoggle = ehci_device_clear_toggle, | | 364 | .upm_cleartoggle = ehci_device_clear_toggle, |
365 | .upm_done = ehci_device_bulk_done, | | 365 | .upm_done = ehci_device_bulk_done, |
366 | }; | | 366 | }; |
367 | | | 367 | |
368 | Static const struct usbd_pipe_methods ehci_device_isoc_methods = { | | 368 | Static const struct usbd_pipe_methods ehci_device_isoc_methods = { |
369 | .upm_init = ehci_device_isoc_init, | | 369 | .upm_init = ehci_device_isoc_init, |
370 | .upm_fini = ehci_device_isoc_fini, | | 370 | .upm_fini = ehci_device_isoc_fini, |
371 | .upm_transfer = ehci_device_isoc_transfer, | | 371 | .upm_transfer = ehci_device_isoc_transfer, |
372 | .upm_abort = ehci_device_isoc_abort, | | 372 | .upm_abort = ehci_device_isoc_abort, |
373 | .upm_close = ehci_device_isoc_close, | | 373 | .upm_close = ehci_device_isoc_close, |
374 | .upm_cleartoggle = ehci_noop, | | 374 | .upm_cleartoggle = ehci_noop, |
375 | .upm_done = ehci_device_isoc_done, | | 375 | .upm_done = ehci_device_isoc_done, |
376 | }; | | 376 | }; |
377 | | | 377 | |
378 | Static const struct usbd_pipe_methods ehci_device_fs_isoc_methods = { | | 378 | Static const struct usbd_pipe_methods ehci_device_fs_isoc_methods = { |
379 | .upm_init = ehci_device_fs_isoc_init, | | 379 | .upm_init = ehci_device_fs_isoc_init, |
380 | .upm_fini = ehci_device_fs_isoc_fini, | | 380 | .upm_fini = ehci_device_fs_isoc_fini, |
381 | .upm_transfer = ehci_device_fs_isoc_transfer, | | 381 | .upm_transfer = ehci_device_fs_isoc_transfer, |
382 | .upm_abort = ehci_device_fs_isoc_abort, | | 382 | .upm_abort = ehci_device_fs_isoc_abort, |
383 | .upm_close = ehci_device_fs_isoc_close, | | 383 | .upm_close = ehci_device_fs_isoc_close, |
384 | .upm_cleartoggle = ehci_noop, | | 384 | .upm_cleartoggle = ehci_noop, |
385 | .upm_done = ehci_device_fs_isoc_done, | | 385 | .upm_done = ehci_device_fs_isoc_done, |
386 | }; | | 386 | }; |
387 | | | 387 | |
388 | static const uint8_t revbits[EHCI_MAX_POLLRATE] = { | | 388 | static const uint8_t revbits[EHCI_MAX_POLLRATE] = { |
389 | 0x00,0x40,0x20,0x60,0x10,0x50,0x30,0x70,0x08,0x48,0x28,0x68,0x18,0x58,0x38,0x78, | | 389 | 0x00,0x40,0x20,0x60,0x10,0x50,0x30,0x70,0x08,0x48,0x28,0x68,0x18,0x58,0x38,0x78, |
390 | 0x04,0x44,0x24,0x64,0x14,0x54,0x34,0x74,0x0c,0x4c,0x2c,0x6c,0x1c,0x5c,0x3c,0x7c, | | 390 | 0x04,0x44,0x24,0x64,0x14,0x54,0x34,0x74,0x0c,0x4c,0x2c,0x6c,0x1c,0x5c,0x3c,0x7c, |
391 | 0x02,0x42,0x22,0x62,0x12,0x52,0x32,0x72,0x0a,0x4a,0x2a,0x6a,0x1a,0x5a,0x3a,0x7a, | | 391 | 0x02,0x42,0x22,0x62,0x12,0x52,0x32,0x72,0x0a,0x4a,0x2a,0x6a,0x1a,0x5a,0x3a,0x7a, |
392 | 0x06,0x46,0x26,0x66,0x16,0x56,0x36,0x76,0x0e,0x4e,0x2e,0x6e,0x1e,0x5e,0x3e,0x7e, | | 392 | 0x06,0x46,0x26,0x66,0x16,0x56,0x36,0x76,0x0e,0x4e,0x2e,0x6e,0x1e,0x5e,0x3e,0x7e, |
393 | 0x01,0x41,0x21,0x61,0x11,0x51,0x31,0x71,0x09,0x49,0x29,0x69,0x19,0x59,0x39,0x79, | | 393 | 0x01,0x41,0x21,0x61,0x11,0x51,0x31,0x71,0x09,0x49,0x29,0x69,0x19,0x59,0x39,0x79, |
394 | 0x05,0x45,0x25,0x65,0x15,0x55,0x35,0x75,0x0d,0x4d,0x2d,0x6d,0x1d,0x5d,0x3d,0x7d, | | 394 | 0x05,0x45,0x25,0x65,0x15,0x55,0x35,0x75,0x0d,0x4d,0x2d,0x6d,0x1d,0x5d,0x3d,0x7d, |
395 | 0x03,0x43,0x23,0x63,0x13,0x53,0x33,0x73,0x0b,0x4b,0x2b,0x6b,0x1b,0x5b,0x3b,0x7b, | | 395 | 0x03,0x43,0x23,0x63,0x13,0x53,0x33,0x73,0x0b,0x4b,0x2b,0x6b,0x1b,0x5b,0x3b,0x7b, |
396 | 0x07,0x47,0x27,0x67,0x17,0x57,0x37,0x77,0x0f,0x4f,0x2f,0x6f,0x1f,0x5f,0x3f,0x7f, | | 396 | 0x07,0x47,0x27,0x67,0x17,0x57,0x37,0x77,0x0f,0x4f,0x2f,0x6f,0x1f,0x5f,0x3f,0x7f, |
397 | }; | | 397 | }; |
398 | | | 398 | |
399 | int | | 399 | int |
400 | ehci_init(ehci_softc_t *sc) | | 400 | ehci_init(ehci_softc_t *sc) |
401 | { | | 401 | { |
402 | uint32_t vers, sparams, cparams, hcr; | | 402 | uint32_t vers, sparams, cparams, hcr; |
403 | u_int i; | | 403 | u_int i; |
404 | usbd_status err; | | 404 | usbd_status err; |
405 | ehci_soft_qh_t *sqh; | | 405 | ehci_soft_qh_t *sqh; |
406 | u_int ncomp; | | 406 | u_int ncomp; |
407 | | | 407 | |
408 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 408 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
409 | #ifdef EHCI_DEBUG | | 409 | #ifdef EHCI_DEBUG |
410 | theehci = sc; | | 410 | theehci = sc; |
411 | #endif | | 411 | #endif |
412 | | | 412 | |
413 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); | | 413 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); |
414 | mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB); | | 414 | mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB); |
415 | cv_init(&sc->sc_doorbell, "ehcidb"); | | 415 | cv_init(&sc->sc_doorbell, "ehcidb"); |
416 | | | 416 | |
417 | sc->sc_xferpool = pool_cache_init(sizeof(struct ehci_xfer), 0, 0, 0, | | 417 | sc->sc_xferpool = pool_cache_init(sizeof(struct ehci_xfer), 0, 0, 0, |
418 | "ehcixfer", NULL, IPL_USB, NULL, NULL, NULL); | | 418 | "ehcixfer", NULL, IPL_USB, NULL, NULL, NULL); |
419 | | | 419 | |
420 | sc->sc_doorbell_si = softint_establish(SOFTINT_USB | SOFTINT_MPSAFE, | | 420 | sc->sc_doorbell_si = softint_establish(SOFTINT_USB | SOFTINT_MPSAFE, |
421 | ehci_doorbell, sc); | | 421 | ehci_doorbell, sc); |
422 | KASSERT(sc->sc_doorbell_si != NULL); | | 422 | KASSERT(sc->sc_doorbell_si != NULL); |
423 | sc->sc_pcd_si = softint_establish(SOFTINT_USB | SOFTINT_MPSAFE, | | 423 | sc->sc_pcd_si = softint_establish(SOFTINT_USB | SOFTINT_MPSAFE, |
424 | ehci_pcd, sc); | | 424 | ehci_pcd, sc); |
425 | KASSERT(sc->sc_pcd_si != NULL); | | 425 | KASSERT(sc->sc_pcd_si != NULL); |
426 | | | 426 | |
427 | sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH); | | 427 | sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH); |
428 | | | 428 | |
429 | vers = EREAD2(sc, EHCI_HCIVERSION); | | 429 | vers = EREAD2(sc, EHCI_HCIVERSION); |
430 | aprint_verbose("%s: EHCI version %x.%x\n", device_xname(sc->sc_dev), | | 430 | aprint_verbose("%s: EHCI version %x.%x\n", device_xname(sc->sc_dev), |
431 | vers >> 8, vers & 0xff); | | 431 | vers >> 8, vers & 0xff); |
432 | | | 432 | |
433 | sparams = EREAD4(sc, EHCI_HCSPARAMS); | | 433 | sparams = EREAD4(sc, EHCI_HCSPARAMS); |
434 | DPRINTF("sparams=%#jx", sparams, 0, 0, 0); | | 434 | DPRINTF("sparams=%#jx", sparams, 0, 0, 0); |
435 | sc->sc_npcomp = EHCI_HCS_N_PCC(sparams); | | 435 | sc->sc_npcomp = EHCI_HCS_N_PCC(sparams); |
436 | ncomp = EHCI_HCS_N_CC(sparams); | | 436 | ncomp = EHCI_HCS_N_CC(sparams); |
437 | if (ncomp != sc->sc_ncomp) { | | 437 | if (ncomp != sc->sc_ncomp) { |
438 | aprint_verbose("%s: wrong number of companions (%d != %d)\n", | | 438 | aprint_verbose("%s: wrong number of companions (%d != %d)\n", |
439 | device_xname(sc->sc_dev), ncomp, sc->sc_ncomp); | | 439 | device_xname(sc->sc_dev), ncomp, sc->sc_ncomp); |
440 | #if NOHCI == 0 || NUHCI == 0 | | 440 | #if NOHCI == 0 || NUHCI == 0 |
441 | aprint_error("%s: ohci or uhci probably not configured\n", | | 441 | aprint_error("%s: ohci or uhci probably not configured\n", |
442 | device_xname(sc->sc_dev)); | | 442 | device_xname(sc->sc_dev)); |
443 | #endif | | 443 | #endif |
444 | if (ncomp < sc->sc_ncomp) | | 444 | if (ncomp < sc->sc_ncomp) |
445 | sc->sc_ncomp = ncomp; | | 445 | sc->sc_ncomp = ncomp; |
446 | } | | 446 | } |
447 | if (sc->sc_ncomp > 0) { | | 447 | if (sc->sc_ncomp > 0) { |
448 | KASSERT(!(sc->sc_flags & EHCIF_ETTF)); | | 448 | KASSERT(!(sc->sc_flags & EHCIF_ETTF)); |
449 | aprint_normal_dev(sc->sc_dev, | | 449 | aprint_normal_dev(sc->sc_dev, |
450 | "%d companion controller%s, %d port%s%s", | | 450 | "%d companion controller%s, %d port%s%s", |
451 | sc->sc_ncomp, | | 451 | sc->sc_ncomp, |
452 | sc->sc_ncomp!=1 ? "s" : "", | | 452 | sc->sc_ncomp!=1 ? "s" : "", |
453 | EHCI_HCS_N_PCC(sparams), | | 453 | EHCI_HCS_N_PCC(sparams), |
454 | EHCI_HCS_N_PCC(sparams)!=1 ? "s" : "", | | 454 | EHCI_HCS_N_PCC(sparams)!=1 ? "s" : "", |
455 | sc->sc_ncomp!=1 ? " each" : ""); | | 455 | sc->sc_ncomp!=1 ? " each" : ""); |
456 | if (sc->sc_comps[0]) { | | 456 | if (sc->sc_comps[0]) { |
457 | aprint_normal(":"); | | 457 | aprint_normal(":"); |
458 | for (i = 0; i < sc->sc_ncomp; i++) | | 458 | for (i = 0; i < sc->sc_ncomp; i++) |
459 | aprint_normal(" %s", | | 459 | aprint_normal(" %s", |
460 | device_xname(sc->sc_comps[i])); | | 460 | device_xname(sc->sc_comps[i])); |
461 | } | | 461 | } |
462 | aprint_normal("\n"); | | 462 | aprint_normal("\n"); |
463 | | | 463 | |
464 | mutex_init(&sc->sc_complock, MUTEX_DEFAULT, IPL_USB); | | 464 | mutex_init(&sc->sc_complock, MUTEX_DEFAULT, IPL_USB); |
465 | callout_init(&sc->sc_compcallout, CALLOUT_MPSAFE); | | 465 | callout_init(&sc->sc_compcallout, CALLOUT_MPSAFE); |
466 | cv_init(&sc->sc_compcv, "ehciccv"); | | 466 | cv_init(&sc->sc_compcv, "ehciccv"); |
467 | sc->sc_comp_state = CO_EARLY; | | 467 | sc->sc_comp_state = CO_EARLY; |
468 | } | | 468 | } |
469 | sc->sc_noport = EHCI_HCS_N_PORTS(sparams); | | 469 | sc->sc_noport = EHCI_HCS_N_PORTS(sparams); |
470 | sc->sc_hasppc = EHCI_HCS_PPC(sparams); | | 470 | sc->sc_hasppc = EHCI_HCS_PPC(sparams); |
471 | | | 471 | |
472 | cparams = EREAD4(sc, EHCI_HCCPARAMS); | | 472 | cparams = EREAD4(sc, EHCI_HCCPARAMS); |
473 | DPRINTF("cparams=%#jx", cparams, 0, 0, 0); | | 473 | DPRINTF("cparams=%#jx", cparams, 0, 0, 0); |
474 | | | 474 | |
475 | if (EHCI_HCC_64BIT(cparams)) { | | 475 | if (EHCI_HCC_64BIT(cparams)) { |
476 | /* MUST clear segment register if 64 bit capable. */ | | 476 | /* MUST clear segment register if 64 bit capable. */ |
477 | EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); | | 477 | EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); |
478 | } | | 478 | } |
479 | | | 479 | |
480 | if (cparams & EHCI_HCC_IST_FULLFRAME) { | | 480 | if (cparams & EHCI_HCC_IST_FULLFRAME) { |
481 | sc->sc_istthreshold = 0; | | 481 | sc->sc_istthreshold = 0; |
482 | } else { | | 482 | } else { |
483 | sc->sc_istthreshold = EHCI_HCC_GET_IST_THRESHOLD(cparams); | | 483 | sc->sc_istthreshold = EHCI_HCC_GET_IST_THRESHOLD(cparams); |
484 | } | | 484 | } |
485 | | | 485 | |
486 | sc->sc_bus.ub_revision = USBREV_2_0; | | 486 | sc->sc_bus.ub_revision = USBREV_2_0; |
487 | sc->sc_bus.ub_usedma = true; | | 487 | sc->sc_bus.ub_usedma = true; |
488 | sc->sc_bus.ub_dmaflags = USBMALLOC_MULTISEG; | | 488 | sc->sc_bus.ub_dmaflags = USBMALLOC_MULTISEG; |
489 | | | 489 | |
490 | /* Reset the controller */ | | 490 | /* Reset the controller */ |
491 | DPRINTF("resetting", 0, 0, 0, 0); | | 491 | DPRINTF("resetting", 0, 0, 0, 0); |
492 | EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ | | 492 | EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ |
493 | usb_delay_ms(&sc->sc_bus, 1); | | 493 | usb_delay_ms(&sc->sc_bus, 1); |
494 | EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); | | 494 | EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); |
495 | for (i = 0; i < 100; i++) { | | 495 | for (i = 0; i < 100; i++) { |
496 | usb_delay_ms(&sc->sc_bus, 1); | | 496 | usb_delay_ms(&sc->sc_bus, 1); |
497 | hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET; | | 497 | hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET; |
498 | if (!hcr) | | 498 | if (!hcr) |
499 | break; | | 499 | break; |
500 | } | | 500 | } |
501 | if (hcr) { | | 501 | if (hcr) { |
502 | aprint_error("%s: reset timeout\n", device_xname(sc->sc_dev)); | | 502 | aprint_error("%s: reset timeout\n", device_xname(sc->sc_dev)); |
503 | return EIO; | | 503 | return EIO; |
504 | } | | 504 | } |
505 | if (sc->sc_vendor_init) | | 505 | if (sc->sc_vendor_init) |
506 | sc->sc_vendor_init(sc); | | 506 | sc->sc_vendor_init(sc); |
507 | | | 507 | |
508 | /* XXX need proper intr scheduling */ | | 508 | /* XXX need proper intr scheduling */ |
509 | sc->sc_rand = 96; | | 509 | sc->sc_rand = 96; |
510 | | | 510 | |
511 | /* frame list size at default, read back what we got and use that */ | | 511 | /* frame list size at default, read back what we got and use that */ |
512 | switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) { | | 512 | switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) { |
513 | case 0: sc->sc_flsize = 1024; break; | | 513 | case 0: sc->sc_flsize = 1024; break; |
514 | case 1: sc->sc_flsize = 512; break; | | 514 | case 1: sc->sc_flsize = 512; break; |
515 | case 2: sc->sc_flsize = 256; break; | | 515 | case 2: sc->sc_flsize = 256; break; |
516 | case 3: return EIO; | | 516 | case 3: return EIO; |
517 | } | | 517 | } |
518 | err = usb_allocmem(&sc->sc_bus, sc->sc_flsize * sizeof(ehci_link_t), | | 518 | err = usb_allocmem(&sc->sc_bus, sc->sc_flsize * sizeof(ehci_link_t), |
519 | EHCI_FLALIGN_ALIGN, &sc->sc_fldma); | | 519 | EHCI_FLALIGN_ALIGN, &sc->sc_fldma); |
520 | if (err) | | 520 | if (err) |
521 | return err; | | 521 | return err; |
522 | DPRINTF("flsize=%jd", sc->sc_flsize, 0, 0, 0); | | 522 | DPRINTF("flsize=%jd", sc->sc_flsize, 0, 0, 0); |
523 | sc->sc_flist = KERNADDR(&sc->sc_fldma, 0); | | 523 | sc->sc_flist = KERNADDR(&sc->sc_fldma, 0); |
524 | | | 524 | |
525 | for (i = 0; i < sc->sc_flsize; i++) { | | 525 | for (i = 0; i < sc->sc_flsize; i++) { |
526 | sc->sc_flist[i] = EHCI_NULL; | | 526 | sc->sc_flist[i] = EHCI_NULL; |
527 | } | | 527 | } |
528 | | | 528 | |
529 | EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0)); | | 529 | EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0)); |
530 | | | 530 | |
531 | sc->sc_softitds = kmem_zalloc(sc->sc_flsize * sizeof(ehci_soft_itd_t *), | | 531 | sc->sc_softitds = kmem_zalloc(sc->sc_flsize * sizeof(ehci_soft_itd_t *), |
532 | KM_SLEEP); | | 532 | KM_SLEEP); |
533 | LIST_INIT(&sc->sc_freeitds); | | 533 | LIST_INIT(&sc->sc_freeitds); |
534 | LIST_INIT(&sc->sc_freesitds); | | 534 | LIST_INIT(&sc->sc_freesitds); |
535 | TAILQ_INIT(&sc->sc_intrhead); | | 535 | TAILQ_INIT(&sc->sc_intrhead); |
536 | | | 536 | |
537 | /* Set up the bus struct. */ | | 537 | /* Set up the bus struct. */ |
538 | sc->sc_bus.ub_methods = &ehci_bus_methods; | | 538 | sc->sc_bus.ub_methods = &ehci_bus_methods; |
539 | sc->sc_bus.ub_pipesize = sizeof(struct ehci_pipe); | | 539 | sc->sc_bus.ub_pipesize = sizeof(struct ehci_pipe); |
540 | | | 540 | |
541 | sc->sc_eintrs = EHCI_NORMAL_INTRS; | | 541 | sc->sc_eintrs = EHCI_NORMAL_INTRS; |
542 | | | 542 | |
543 | /* | | 543 | /* |
544 | * Allocate the interrupt dummy QHs. These are arranged to give poll | | 544 | * Allocate the interrupt dummy QHs. These are arranged to give poll |
545 | * intervals that are powers of 2 times 1ms. | | 545 | * intervals that are powers of 2 times 1ms. |
546 | */ | | 546 | */ |
547 | for (i = 0; i < EHCI_INTRQHS; i++) { | | 547 | for (i = 0; i < EHCI_INTRQHS; i++) { |
548 | sqh = ehci_alloc_sqh(sc); | | 548 | sqh = ehci_alloc_sqh(sc); |
549 | if (sqh == NULL) { | | 549 | if (sqh == NULL) { |
550 | err = ENOMEM; | | 550 | err = ENOMEM; |
551 | goto bad1; | | 551 | goto bad1; |
552 | } | | 552 | } |
553 | sc->sc_islots[i].sqh = sqh; | | 553 | sc->sc_islots[i].sqh = sqh; |
554 | } | | 554 | } |
555 | for (i = 0; i < EHCI_INTRQHS; i++) { | | 555 | for (i = 0; i < EHCI_INTRQHS; i++) { |
556 | sqh = sc->sc_islots[i].sqh; | | 556 | sqh = sc->sc_islots[i].sqh; |
557 | if (i == 0) { | | 557 | if (i == 0) { |
558 | /* The last (1ms) QH terminates. */ | | 558 | /* The last (1ms) QH terminates. */ |
559 | sqh->qh.qh_link = EHCI_NULL; | | 559 | sqh->qh.qh_link = EHCI_NULL; |
560 | sqh->next = NULL; | | 560 | sqh->next = NULL; |
561 | } else { | | 561 | } else { |
562 | /* Otherwise the next QH has half the poll interval */ | | 562 | /* Otherwise the next QH has half the poll interval */ |
563 | sqh->next = sc->sc_islots[(i + 1) / 2 - 1].sqh; | | 563 | sqh->next = sc->sc_islots[(i + 1) / 2 - 1].sqh; |
564 | sqh->qh.qh_link = htole32(sqh->next->physaddr | | | 564 | sqh->qh.qh_link = htole32(sqh->next->physaddr | |
565 | EHCI_LINK_QH); | | 565 | EHCI_LINK_QH); |
566 | } | | 566 | } |
567 | sqh->qh.qh_endp = htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); | | 567 | sqh->qh.qh_endp = htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); |
568 | sqh->qh.qh_endphub = htole32(EHCI_QH_SET_MULT(1)); | | 568 | sqh->qh.qh_endphub = htole32(EHCI_QH_SET_MULT(1)); |
569 | sqh->qh.qh_curqtd = EHCI_NULL; | | 569 | sqh->qh.qh_curqtd = EHCI_NULL; |
570 | sqh->qh.qh_qtd.qtd_next = EHCI_NULL; | | 570 | sqh->qh.qh_qtd.qtd_next = EHCI_NULL; |
571 | sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; | | 571 | sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; |
572 | sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); | | 572 | sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); |
573 | sqh->sqtd = NULL; | | 573 | sqh->sqtd = NULL; |
574 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), | | 574 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), |
575 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 575 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
576 | } | | 576 | } |
577 | /* Point the frame list at the last level (128ms). */ | | 577 | /* Point the frame list at the last level (128ms). */ |
578 | for (i = 0; i < sc->sc_flsize; i++) { | | 578 | for (i = 0; i < sc->sc_flsize; i++) { |
579 | int j; | | 579 | int j; |
580 | | | 580 | |
581 | j = (i & ~(EHCI_MAX_POLLRATE-1)) | | | 581 | j = (i & ~(EHCI_MAX_POLLRATE-1)) | |
582 | revbits[i & (EHCI_MAX_POLLRATE-1)]; | | 582 | revbits[i & (EHCI_MAX_POLLRATE-1)]; |
583 | sc->sc_flist[j] = htole32(EHCI_LINK_QH | | | 583 | sc->sc_flist[j] = htole32(EHCI_LINK_QH | |
584 | sc->sc_islots[EHCI_IQHIDX(EHCI_IPOLLRATES - 1, | | 584 | sc->sc_islots[EHCI_IQHIDX(EHCI_IPOLLRATES - 1, |
585 | i)].sqh->physaddr); | | 585 | i)].sqh->physaddr); |
586 | } | | 586 | } |
587 | usb_syncmem(&sc->sc_fldma, 0, sc->sc_flsize * sizeof(ehci_link_t), | | 587 | usb_syncmem(&sc->sc_fldma, 0, sc->sc_flsize * sizeof(ehci_link_t), |
588 | BUS_DMASYNC_PREWRITE); | | 588 | BUS_DMASYNC_PREWRITE); |
589 | | | 589 | |
590 | /* Allocate dummy QH that starts the async list. */ | | 590 | /* Allocate dummy QH that starts the async list. */ |
591 | sqh = ehci_alloc_sqh(sc); | | 591 | sqh = ehci_alloc_sqh(sc); |
592 | if (sqh == NULL) { | | 592 | if (sqh == NULL) { |
593 | err = ENOMEM; | | 593 | err = ENOMEM; |
594 | goto bad1; | | 594 | goto bad1; |
595 | } | | 595 | } |
596 | /* Fill the QH */ | | 596 | /* Fill the QH */ |
597 | sqh->qh.qh_endp = | | 597 | sqh->qh.qh_endp = |
598 | htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); | | 598 | htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); |
599 | sqh->qh.qh_link = | | 599 | sqh->qh.qh_link = |
600 | htole32(sqh->physaddr | EHCI_LINK_QH); | | 600 | htole32(sqh->physaddr | EHCI_LINK_QH); |
601 | sqh->qh.qh_curqtd = EHCI_NULL; | | 601 | sqh->qh.qh_curqtd = EHCI_NULL; |
602 | sqh->next = NULL; | | 602 | sqh->next = NULL; |
603 | /* Fill the overlay qTD */ | | 603 | /* Fill the overlay qTD */ |
604 | sqh->qh.qh_qtd.qtd_next = EHCI_NULL; | | 604 | sqh->qh.qh_qtd.qtd_next = EHCI_NULL; |
605 | sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; | | 605 | sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; |
606 | sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); | | 606 | sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); |
607 | sqh->sqtd = NULL; | | 607 | sqh->sqtd = NULL; |
608 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), | | 608 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), |
609 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 609 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
610 | #ifdef EHCI_DEBUG | | 610 | #ifdef EHCI_DEBUG |
611 | DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); | | 611 | DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); |
612 | ehci_dump_sqh(sqh); | | 612 | ehci_dump_sqh(sqh); |
613 | DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); | | 613 | DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); |
614 | #endif | | 614 | #endif |
615 | | | 615 | |
616 | /* Point to async list */ | | 616 | /* Point to async list */ |
617 | sc->sc_async_head = sqh; | | 617 | sc->sc_async_head = sqh; |
618 | EOWRITE4(sc, EHCI_ASYNCLISTADDR, sqh->physaddr | EHCI_LINK_QH); | | 618 | EOWRITE4(sc, EHCI_ASYNCLISTADDR, sqh->physaddr | EHCI_LINK_QH); |
619 | | | 619 | |
620 | callout_init(&sc->sc_tmo_intrlist, CALLOUT_MPSAFE); | | 620 | callout_init(&sc->sc_tmo_intrlist, CALLOUT_MPSAFE); |
621 | | | 621 | |
622 | /* Turn on controller */ | | 622 | /* Turn on controller */ |
623 | EOWRITE4(sc, EHCI_USBCMD, | | 623 | EOWRITE4(sc, EHCI_USBCMD, |
624 | EHCI_CMD_ITC_2 | /* 2 microframes interrupt delay */ | | 624 | EHCI_CMD_ITC_2 | /* 2 microframes interrupt delay */ |
625 | (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | | | 625 | (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | |
626 | EHCI_CMD_ASE | | | 626 | EHCI_CMD_ASE | |
627 | EHCI_CMD_PSE | | | 627 | EHCI_CMD_PSE | |
628 | EHCI_CMD_RS); | | 628 | EHCI_CMD_RS); |
629 | | | 629 | |
630 | /* Take over port ownership */ | | 630 | /* Take over port ownership */ |
631 | EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF); | | 631 | EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF); |
632 | | | 632 | |
633 | for (i = 0; i < 100; i++) { | | 633 | for (i = 0; i < 100; i++) { |
634 | usb_delay_ms(&sc->sc_bus, 1); | | 634 | usb_delay_ms(&sc->sc_bus, 1); |
635 | hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; | | 635 | hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; |
636 | if (!hcr) | | 636 | if (!hcr) |
637 | break; | | 637 | break; |
638 | } | | 638 | } |
639 | if (hcr) { | | 639 | if (hcr) { |
640 | aprint_error("%s: run timeout\n", device_xname(sc->sc_dev)); | | 640 | aprint_error("%s: run timeout\n", device_xname(sc->sc_dev)); |
641 | return EIO; | | 641 | return EIO; |
642 | } | | 642 | } |
643 | | | 643 | |
644 | /* Enable interrupts */ | | 644 | /* Enable interrupts */ |
645 | DPRINTF("enabling interrupts", 0, 0, 0, 0); | | 645 | DPRINTF("enabling interrupts", 0, 0, 0, 0); |
646 | EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); | | 646 | EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); |
647 | | | 647 | |
648 | return 0; | | 648 | return 0; |
649 | | | 649 | |
650 | #if 0 | | 650 | #if 0 |
651 | bad2: | | 651 | bad2: |
652 | ehci_free_sqh(sc, sc->sc_async_head); | | 652 | ehci_free_sqh(sc, sc->sc_async_head); |
653 | #endif | | 653 | #endif |
654 | bad1: | | 654 | bad1: |
655 | usb_freemem(&sc->sc_bus, &sc->sc_fldma); | | 655 | usb_freemem(&sc->sc_bus, &sc->sc_fldma); |
656 | return err; | | 656 | return err; |
657 | } | | 657 | } |
658 | | | 658 | |
659 | int | | 659 | int |
660 | ehci_intr(void *v) | | 660 | ehci_intr(void *v) |
661 | { | | 661 | { |
662 | ehci_softc_t *sc = v; | | 662 | ehci_softc_t *sc = v; |
663 | int ret = 0; | | 663 | int ret = 0; |
664 | | | 664 | |
665 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 665 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
666 | | | 666 | |
667 | if (sc == NULL) | | 667 | if (sc == NULL) |
668 | return 0; | | 668 | return 0; |
669 | | | 669 | |
670 | mutex_spin_enter(&sc->sc_intr_lock); | | 670 | mutex_spin_enter(&sc->sc_intr_lock); |
671 | | | 671 | |
672 | if (sc->sc_dying || !device_has_power(sc->sc_dev)) | | 672 | if (sc->sc_dying || !device_has_power(sc->sc_dev)) |
673 | goto done; | | 673 | goto done; |
674 | | | 674 | |
675 | /* If we get an interrupt while polling, then just ignore it. */ | | 675 | /* If we get an interrupt while polling, then just ignore it. */ |
676 | if (sc->sc_bus.ub_usepolling) { | | 676 | if (sc->sc_bus.ub_usepolling) { |
677 | uint32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); | | 677 | uint32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
678 | | | 678 | |
679 | if (intrs) | | 679 | if (intrs) |
680 | EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ | | 680 | EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ |
681 | DPRINTFN(16, "ignored interrupt while polling", 0, 0, 0, 0); | | 681 | DPRINTFN(16, "ignored interrupt while polling", 0, 0, 0, 0); |
682 | goto done; | | 682 | goto done; |
683 | } | | 683 | } |
684 | | | 684 | |
685 | ret = ehci_intr1(sc); | | 685 | ret = ehci_intr1(sc); |
686 | | | 686 | |
687 | done: | | 687 | done: |
688 | mutex_spin_exit(&sc->sc_intr_lock); | | 688 | mutex_spin_exit(&sc->sc_intr_lock); |
689 | return ret; | | 689 | return ret; |
690 | } | | 690 | } |
691 | | | 691 | |
692 | Static int | | 692 | Static int |
693 | ehci_intr1(ehci_softc_t *sc) | | 693 | ehci_intr1(ehci_softc_t *sc) |
694 | { | | 694 | { |
695 | uint32_t intrs, eintrs; | | 695 | uint32_t intrs, eintrs; |
696 | | | 696 | |
697 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 697 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
698 | | | 698 | |
699 | /* In case the interrupt occurs before initialization has completed. */ | | 699 | /* In case the interrupt occurs before initialization has completed. */ |
700 | if (sc == NULL) { | | 700 | if (sc == NULL) { |
701 | #ifdef DIAGNOSTIC | | 701 | #ifdef DIAGNOSTIC |
702 | printf("ehci_intr1: sc == NULL\n"); | | 702 | printf("ehci_intr1: sc == NULL\n"); |
703 | #endif | | 703 | #endif |
704 | return 0; | | 704 | return 0; |
705 | } | | 705 | } |
706 | | | 706 | |
707 | KASSERT(mutex_owned(&sc->sc_intr_lock)); | | 707 | KASSERT(mutex_owned(&sc->sc_intr_lock)); |
708 | | | 708 | |
709 | intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); | | 709 | intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
710 | if (!intrs) | | 710 | if (!intrs) |
711 | return 0; | | 711 | return 0; |
712 | | | 712 | |
713 | eintrs = intrs & sc->sc_eintrs; | | 713 | eintrs = intrs & sc->sc_eintrs; |
714 | DPRINTF("sc=%#jx intrs=%#jx(%#jx) eintrs=%#jx", (uintptr_t)sc, intrs, | | 714 | DPRINTF("sc=%#jx intrs=%#jx(%#jx) eintrs=%#jx", (uintptr_t)sc, intrs, |
715 | EOREAD4(sc, EHCI_USBSTS), eintrs); | | 715 | EOREAD4(sc, EHCI_USBSTS), eintrs); |
716 | if (!eintrs) | | 716 | if (!eintrs) |
717 | return 0; | | 717 | return 0; |
718 | | | 718 | |
719 | EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ | | 719 | EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ |
720 | if (eintrs & EHCI_STS_IAA) { | | 720 | if (eintrs & EHCI_STS_IAA) { |
721 | DPRINTF("door bell", 0, 0, 0, 0); | | 721 | DPRINTF("door bell", 0, 0, 0, 0); |
722 | kpreempt_disable(); | | 722 | kpreempt_disable(); |
723 | KASSERT(sc->sc_doorbell_si != NULL); | | 723 | KASSERT(sc->sc_doorbell_si != NULL); |
724 | softint_schedule(sc->sc_doorbell_si); | | 724 | softint_schedule(sc->sc_doorbell_si); |
725 | kpreempt_enable(); | | 725 | kpreempt_enable(); |
726 | eintrs &= ~EHCI_STS_IAA; | | 726 | eintrs &= ~EHCI_STS_IAA; |
727 | } | | 727 | } |
728 | if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) { | | 728 | if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) { |
729 | DPRINTF("INT=%jd ERRINT=%jd", | | 729 | DPRINTF("INT=%jd ERRINT=%jd", |
730 | eintrs & EHCI_STS_INT ? 1 : 0, | | 730 | eintrs & EHCI_STS_INT ? 1 : 0, |
731 | eintrs & EHCI_STS_ERRINT ? 1 : 0, 0, 0); | | 731 | eintrs & EHCI_STS_ERRINT ? 1 : 0, 0, 0); |
732 | usb_schedsoftintr(&sc->sc_bus); | | 732 | usb_schedsoftintr(&sc->sc_bus); |
733 | eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT); | | 733 | eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT); |
734 | } | | 734 | } |
735 | if (eintrs & EHCI_STS_HSE) { | | 735 | if (eintrs & EHCI_STS_HSE) { |
736 | printf("%s: unrecoverable error, controller halted\n", | | 736 | printf("%s: unrecoverable error, controller halted\n", |
737 | device_xname(sc->sc_dev)); | | 737 | device_xname(sc->sc_dev)); |
738 | /* XXX what else */ | | 738 | /* XXX what else */ |
739 | } | | 739 | } |
740 | if (eintrs & EHCI_STS_PCD) { | | 740 | if (eintrs & EHCI_STS_PCD) { |
741 | kpreempt_disable(); | | 741 | kpreempt_disable(); |
742 | KASSERT(sc->sc_pcd_si != NULL); | | 742 | KASSERT(sc->sc_pcd_si != NULL); |
743 | softint_schedule(sc->sc_pcd_si); | | 743 | softint_schedule(sc->sc_pcd_si); |
744 | kpreempt_enable(); | | 744 | kpreempt_enable(); |
745 | eintrs &= ~EHCI_STS_PCD; | | 745 | eintrs &= ~EHCI_STS_PCD; |
746 | } | | 746 | } |
747 | | | 747 | |
748 | if (eintrs != 0) { | | 748 | if (eintrs != 0) { |
749 | /* Block unprocessed interrupts. */ | | 749 | /* Block unprocessed interrupts. */ |
750 | sc->sc_eintrs &= ~eintrs; | | 750 | sc->sc_eintrs &= ~eintrs; |
751 | EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); | | 751 | EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); |
752 | printf("%s: blocking intrs 0x%x\n", | | 752 | printf("%s: blocking intrs 0x%x\n", |
753 | device_xname(sc->sc_dev), eintrs); | | 753 | device_xname(sc->sc_dev), eintrs); |
754 | } | | 754 | } |
755 | | | 755 | |
756 | return 1; | | 756 | return 1; |
757 | } | | 757 | } |
758 | | | 758 | |
759 | Static void | | 759 | Static void |
760 | ehci_doorbell(void *addr) | | 760 | ehci_doorbell(void *addr) |
761 | { | | 761 | { |
762 | ehci_softc_t *sc = addr; | | 762 | ehci_softc_t *sc = addr; |
763 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 763 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
764 | | | 764 | |
765 | mutex_enter(&sc->sc_lock); | | 765 | mutex_enter(&sc->sc_lock); |
766 | cv_broadcast(&sc->sc_doorbell); | | 766 | cv_broadcast(&sc->sc_doorbell); |
767 | mutex_exit(&sc->sc_lock); | | 767 | mutex_exit(&sc->sc_lock); |
768 | } | | 768 | } |
769 | | | 769 | |
770 | Static void | | 770 | Static void |
771 | ehci_pcd(void *addr) | | 771 | ehci_pcd(void *addr) |
772 | { | | 772 | { |
773 | ehci_softc_t *sc = addr; | | 773 | ehci_softc_t *sc = addr; |
774 | struct usbd_xfer *xfer; | | 774 | struct usbd_xfer *xfer; |
775 | u_char *p; | | 775 | u_char *p; |
776 | int i, m; | | 776 | int i, m; |
777 | | | 777 | |
778 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 778 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
779 | | | 779 | |
780 | mutex_enter(&sc->sc_lock); | | 780 | mutex_enter(&sc->sc_lock); |
781 | xfer = sc->sc_intrxfer; | | 781 | xfer = sc->sc_intrxfer; |
782 | | | 782 | |
783 | if (xfer == NULL) { | | 783 | if (xfer == NULL) { |
784 | /* Just ignore the change. */ | | 784 | /* Just ignore the change. */ |
785 | goto done; | | 785 | goto done; |
786 | } | | 786 | } |
787 | | | 787 | |
788 | p = xfer->ux_buf; | | 788 | p = xfer->ux_buf; |
789 | m = uimin(sc->sc_noport, xfer->ux_length * 8 - 1); | | 789 | m = uimin(sc->sc_noport, xfer->ux_length * 8 - 1); |
790 | memset(p, 0, xfer->ux_length); | | 790 | memset(p, 0, xfer->ux_length); |
791 | for (i = 1; i <= m; i++) { | | 791 | for (i = 1; i <= m; i++) { |
792 | /* Pick out CHANGE bits from the status reg. */ | | 792 | /* Pick out CHANGE bits from the status reg. */ |
793 | if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) | | 793 | if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) |
794 | p[i/8] |= 1 << (i%8); | | 794 | p[i/8] |= 1 << (i%8); |
795 | if (i % 8 == 7) | | 795 | if (i % 8 == 7) |
796 | DPRINTF("change(%jd)=0x%02jx", i / 8, p[i/8], 0, 0); | | 796 | DPRINTF("change(%jd)=0x%02jx", i / 8, p[i/8], 0, 0); |
797 | } | | 797 | } |
798 | xfer->ux_actlen = xfer->ux_length; | | 798 | xfer->ux_actlen = xfer->ux_length; |
799 | xfer->ux_status = USBD_NORMAL_COMPLETION; | | 799 | xfer->ux_status = USBD_NORMAL_COMPLETION; |
800 | | | 800 | |
801 | usb_transfer_complete(xfer); | | 801 | usb_transfer_complete(xfer); |
802 | | | 802 | |
803 | done: | | 803 | done: |
804 | mutex_exit(&sc->sc_lock); | | 804 | mutex_exit(&sc->sc_lock); |
805 | } | | 805 | } |
806 | | | 806 | |
807 | Static void | | 807 | Static void |
808 | ehci_softintr(void *v) | | 808 | ehci_softintr(void *v) |
809 | { | | 809 | { |
810 | struct usbd_bus *bus = v; | | 810 | struct usbd_bus *bus = v; |
811 | ehci_softc_t *sc = EHCI_BUS2SC(bus); | | 811 | ehci_softc_t *sc = EHCI_BUS2SC(bus); |
812 | struct ehci_xfer *ex, *nextex; | | 812 | struct ehci_xfer *ex, *nextex; |
813 | | | 813 | |
814 | KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); | | 814 | KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); |
815 | | | 815 | |
816 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 816 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
817 | | | 817 | |
818 | ex_completeq_t cq; | | 818 | ex_completeq_t cq; |
819 | TAILQ_INIT(&cq); | | 819 | TAILQ_INIT(&cq); |
820 | | | 820 | |
821 | /* | | 821 | /* |
822 | * The only explanation I can think of for why EHCI is as brain dead | | 822 | * The only explanation I can think of for why EHCI is as brain dead |
823 | * as UHCI interrupt-wise is that Intel was involved in both. | | 823 | * as UHCI interrupt-wise is that Intel was involved in both. |
824 | * An interrupt just tells us that something is done, we have no | | 824 | * An interrupt just tells us that something is done, we have no |
825 | * clue what, so we need to scan through all active transfers. :-( | | 825 | * clue what, so we need to scan through all active transfers. :-( |
826 | */ | | 826 | */ |
827 | | | 827 | |
828 | /* | | 828 | /* |
829 | * ehci_idone will remove transfer from sc->sc_intrhead if it's | | 829 | * ehci_idone will remove transfer from sc->sc_intrhead if it's |
830 | * complete and add to our cq list | | 830 | * complete and add to our cq list |
831 | * | | 831 | * |
832 | */ | | 832 | */ |
833 | TAILQ_FOREACH_SAFE(ex, &sc->sc_intrhead, ex_next, nextex) { | | 833 | TAILQ_FOREACH_SAFE(ex, &sc->sc_intrhead, ex_next, nextex) { |
834 | switch (ex->ex_type) { | | 834 | switch (ex->ex_type) { |
835 | case EX_CTRL: | | 835 | case EX_CTRL: |
836 | case EX_BULK: | | 836 | case EX_BULK: |
837 | case EX_INTR: | | 837 | case EX_INTR: |
838 | ehci_check_qh_intr(sc, ex, &cq); | | 838 | ehci_check_qh_intr(sc, ex, &cq); |
839 | break; | | 839 | break; |
840 | case EX_ISOC: | | 840 | case EX_ISOC: |
841 | ehci_check_itd_intr(sc, ex, &cq); | | 841 | ehci_check_itd_intr(sc, ex, &cq); |
842 | break; | | 842 | break; |
843 | case EX_FS_ISOC: | | 843 | case EX_FS_ISOC: |
844 | ehci_check_sitd_intr(sc, ex, &cq); | | 844 | ehci_check_sitd_intr(sc, ex, &cq); |
845 | break; | | 845 | break; |
846 | default: | | 846 | default: |
847 | KASSERT(false); | | 847 | KASSERT(false); |
848 | } | | 848 | } |
849 | | | 849 | |
850 | } | | 850 | } |
851 | | | 851 | |
852 | /* | | 852 | /* |
853 | * We abuse ex_next for the interrupt and complete lists and | | 853 | * We abuse ex_next for the interrupt and complete lists and |
854 | * interrupt transfers will get re-added here so use | | 854 | * interrupt transfers will get re-added here so use |
855 | * the _SAFE version of TAILQ_FOREACH. | | 855 | * the _SAFE version of TAILQ_FOREACH. |
856 | */ | | 856 | */ |
857 | TAILQ_FOREACH_SAFE(ex, &cq, ex_next, nextex) { | | 857 | TAILQ_FOREACH_SAFE(ex, &cq, ex_next, nextex) { |
858 | usb_transfer_complete(&ex->ex_xfer); | | 858 | usb_transfer_complete(&ex->ex_xfer); |
859 | } | | 859 | } |
860 | | | 860 | |
861 | /* Schedule a callout to catch any dropped transactions. */ | | 861 | /* Schedule a callout to catch any dropped transactions. */ |
862 | if ((sc->sc_flags & EHCIF_DROPPED_INTR_WORKAROUND) && | | 862 | if ((sc->sc_flags & EHCIF_DROPPED_INTR_WORKAROUND) && |
863 | !TAILQ_EMPTY(&sc->sc_intrhead)) | | 863 | !TAILQ_EMPTY(&sc->sc_intrhead)) |
864 | callout_reset(&sc->sc_tmo_intrlist, | | 864 | callout_reset(&sc->sc_tmo_intrlist, |
865 | hz, ehci_intrlist_timeout, sc); | | 865 | hz, ehci_intrlist_timeout, sc); |
866 | } | | 866 | } |
867 | | | 867 | |
868 | Static void | | 868 | Static void |
869 | ehci_check_qh_intr(ehci_softc_t *sc, struct ehci_xfer *ex, ex_completeq_t *cq) | | 869 | ehci_check_qh_intr(ehci_softc_t *sc, struct ehci_xfer *ex, ex_completeq_t *cq) |
870 | { | | 870 | { |
871 | ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; | | 871 | ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; |
872 | uint32_t status; | | 872 | uint32_t status; |
873 | | | 873 | |
874 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 874 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
875 | | | 875 | |
876 | KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); | | 876 | KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); |
877 | | | 877 | |
878 | if (ex->ex_type == EX_CTRL) { | | 878 | if (ex->ex_type == EX_CTRL) { |
879 | fsqtd = ex->ex_setup; | | 879 | fsqtd = ex->ex_setup; |
880 | lsqtd = ex->ex_status; | | 880 | lsqtd = ex->ex_status; |
881 | } else { | | 881 | } else { |
882 | fsqtd = ex->ex_sqtdstart; | | 882 | fsqtd = ex->ex_sqtdstart; |
883 | lsqtd = ex->ex_sqtdend; | | 883 | lsqtd = ex->ex_sqtdend; |
884 | } | | 884 | } |
885 | KASSERTMSG(fsqtd != NULL && lsqtd != NULL, | | 885 | KASSERTMSG(fsqtd != NULL && lsqtd != NULL, |
886 | "xfer %p xt %d fsqtd %p lsqtd %p", ex, ex->ex_type, fsqtd, lsqtd); | | 886 | "xfer %p xt %d fsqtd %p lsqtd %p", ex, ex->ex_type, fsqtd, lsqtd); |
887 | | | 887 | |
888 | /* | | 888 | /* |
889 | * If the last TD is still active we need to check whether there | | 889 | * If the last TD is still active we need to check whether there |
890 | * is an error somewhere in the middle, or whether there was a | | 890 | * is an error somewhere in the middle, or whether there was a |
891 | * short packet (SPD and not ACTIVE). | | 891 | * short packet (SPD and not ACTIVE). |
892 | */ | | 892 | */ |
893 | usb_syncmem(&lsqtd->dma, | | 893 | usb_syncmem(&lsqtd->dma, |
894 | lsqtd->offs + offsetof(ehci_qtd_t, qtd_status), | | 894 | lsqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
895 | sizeof(lsqtd->qtd.qtd_status), | | 895 | sizeof(lsqtd->qtd.qtd_status), |
896 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 896 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
897 | status = le32toh(lsqtd->qtd.qtd_status); | | 897 | status = le32toh(lsqtd->qtd.qtd_status); |
898 | usb_syncmem(&lsqtd->dma, | | 898 | usb_syncmem(&lsqtd->dma, |
899 | lsqtd->offs + offsetof(ehci_qtd_t, qtd_status), | | 899 | lsqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
900 | sizeof(lsqtd->qtd.qtd_status), BUS_DMASYNC_PREREAD); | | 900 | sizeof(lsqtd->qtd.qtd_status), BUS_DMASYNC_PREREAD); |
901 | if (status & EHCI_QTD_ACTIVE) { | | 901 | if (status & EHCI_QTD_ACTIVE) { |
902 | DPRINTFN(10, "active ex=%#jx", (uintptr_t)ex, 0, 0, 0); | | 902 | DPRINTFN(10, "active ex=%#jx", (uintptr_t)ex, 0, 0, 0); |
903 | | | 903 | |
904 | /* last qTD has already been checked */ | | 904 | /* last qTD has already been checked */ |
905 | for (sqtd = fsqtd; sqtd != lsqtd; sqtd = sqtd->nextqtd) { | | 905 | for (sqtd = fsqtd; sqtd != lsqtd; sqtd = sqtd->nextqtd) { |
906 | usb_syncmem(&sqtd->dma, | | 906 | usb_syncmem(&sqtd->dma, |
907 | sqtd->offs + offsetof(ehci_qtd_t, qtd_status), | | 907 | sqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
908 | sizeof(sqtd->qtd.qtd_status), | | 908 | sizeof(sqtd->qtd.qtd_status), |
909 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 909 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
910 | status = le32toh(sqtd->qtd.qtd_status); | | 910 | status = le32toh(sqtd->qtd.qtd_status); |
911 | usb_syncmem(&sqtd->dma, | | 911 | usb_syncmem(&sqtd->dma, |
912 | sqtd->offs + offsetof(ehci_qtd_t, qtd_status), | | 912 | sqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
913 | sizeof(sqtd->qtd.qtd_status), BUS_DMASYNC_PREREAD); | | 913 | sizeof(sqtd->qtd.qtd_status), BUS_DMASYNC_PREREAD); |
914 | /* If there's an active QTD the xfer isn't done. */ | | 914 | /* If there's an active QTD the xfer isn't done. */ |
915 | if (status & EHCI_QTD_ACTIVE) | | 915 | if (status & EHCI_QTD_ACTIVE) |
916 | break; | | 916 | break; |
917 | /* Any kind of error makes the xfer done. */ | | 917 | /* Any kind of error makes the xfer done. */ |
918 | if (status & EHCI_QTD_HALTED) | | 918 | if (status & EHCI_QTD_HALTED) |
919 | goto done; | | 919 | goto done; |
920 | /* Handle short packets */ | | 920 | /* Handle short packets */ |
921 | if (EHCI_QTD_GET_BYTES(status) != 0) { | | 921 | if (EHCI_QTD_GET_BYTES(status) != 0) { |
922 | /* | | 922 | /* |
923 | * If we get here for a control transfer then | | 923 | * If we get here for a control transfer then |
924 | * we need to let the hardware complete the | | 924 | * we need to let the hardware complete the |
925 | * status phase. That is, we're not done | | 925 | * status phase. That is, we're not done |
926 | * quite yet. | | 926 | * quite yet. |
927 | * | | 927 | * |
928 | * Otherwise, we're done. | | 928 | * Otherwise, we're done. |
929 | */ | | 929 | */ |
930 | if (ex->ex_type == EX_CTRL) { | | 930 | if (ex->ex_type == EX_CTRL) { |
931 | break; | | 931 | break; |
932 | } | | 932 | } |
933 | goto done; | | 933 | goto done; |
934 | } | | 934 | } |
935 | } | | 935 | } |
936 | DPRINTFN(10, "ex=%#jx std=%#jx still active", | | 936 | DPRINTFN(10, "ex=%#jx std=%#jx still active", |
937 | (uintptr_t)ex, (uintptr_t)ex->ex_sqtdstart, 0, 0); | | 937 | (uintptr_t)ex, (uintptr_t)ex->ex_sqtdstart, 0, 0); |
938 | #ifdef EHCI_DEBUG | | 938 | #ifdef EHCI_DEBUG |
939 | DPRINTFN(5, "--- still active start ---", 0, 0, 0, 0); | | 939 | DPRINTFN(5, "--- still active start ---", 0, 0, 0, 0); |
940 | ehci_dump_sqtds(ex->ex_sqtdstart); | | 940 | ehci_dump_sqtds(ex->ex_sqtdstart); |
941 | DPRINTFN(5, "--- still active end ---", 0, 0, 0, 0); | | 941 | DPRINTFN(5, "--- still active end ---", 0, 0, 0, 0); |
942 | #endif | | 942 | #endif |
943 | return; | | 943 | return; |
944 | } | | 944 | } |
945 | done: | | 945 | done: |
946 | DPRINTFN(10, "ex=%#jx done", (uintptr_t)ex, 0, 0, 0); | | 946 | DPRINTFN(10, "ex=%#jx done", (uintptr_t)ex, 0, 0, 0); |
947 | ehci_idone(ex, cq); | | 947 | ehci_idone(ex, cq); |
948 | } | | 948 | } |
949 | | | 949 | |
950 | Static void | | 950 | Static void |
951 | ehci_check_itd_intr(ehci_softc_t *sc, struct ehci_xfer *ex, ex_completeq_t *cq) | | 951 | ehci_check_itd_intr(ehci_softc_t *sc, struct ehci_xfer *ex, ex_completeq_t *cq) |
952 | { | | 952 | { |
953 | ehci_soft_itd_t *itd; | | 953 | ehci_soft_itd_t *itd; |
954 | int i; | | 954 | int i; |
955 | | | 955 | |
956 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 956 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
957 | | | 957 | |
958 | KASSERT(mutex_owned(&sc->sc_lock)); | | 958 | KASSERT(mutex_owned(&sc->sc_lock)); |
959 | | | 959 | |
960 | if (&ex->ex_xfer != SIMPLEQ_FIRST(&ex->ex_xfer.ux_pipe->up_queue)) | | 960 | if (&ex->ex_xfer != SIMPLEQ_FIRST(&ex->ex_xfer.ux_pipe->up_queue)) |
961 | return; | | 961 | return; |
962 | | | 962 | |
963 | KASSERTMSG(ex->ex_itdstart != NULL && ex->ex_itdend != NULL, | | 963 | KASSERTMSG(ex->ex_itdstart != NULL && ex->ex_itdend != NULL, |
964 | "xfer %p fitd %p litd %p", ex, ex->ex_itdstart, ex->ex_itdend); | | 964 | "xfer %p fitd %p litd %p", ex, ex->ex_itdstart, ex->ex_itdend); |
965 | | | 965 | |
966 | itd = ex->ex_itdend; | | 966 | itd = ex->ex_itdend; |
967 | | | 967 | |
968 | /* | | 968 | /* |
969 | * check no active transfers in last itd, meaning we're finished | | 969 | * check no active transfers in last itd, meaning we're finished |
970 | */ | | 970 | */ |
971 | | | 971 | |
972 | usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl), | | 972 | usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl), |
973 | sizeof(itd->itd.itd_ctl), | | 973 | sizeof(itd->itd.itd_ctl), |
974 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 974 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
975 | | | 975 | |
976 | for (i = 0; i < EHCI_ITD_NUFRAMES; i++) { | | 976 | for (i = 0; i < EHCI_ITD_NUFRAMES; i++) { |
977 | if (le32toh(itd->itd.itd_ctl[i]) & EHCI_ITD_ACTIVE) | | 977 | if (le32toh(itd->itd.itd_ctl[i]) & EHCI_ITD_ACTIVE) |
978 | break; | | 978 | break; |
979 | } | | 979 | } |
980 | | | 980 | |
981 | if (i == EHCI_ITD_NUFRAMES) { | | 981 | if (i == EHCI_ITD_NUFRAMES) { |
982 | goto done; /* All 8 descriptors inactive, it's done */ | | 982 | goto done; /* All 8 descriptors inactive, it's done */ |
983 | } | | 983 | } |
984 | | | 984 | |
985 | usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl), | | 985 | usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl), |
986 | sizeof(itd->itd.itd_ctl), BUS_DMASYNC_PREREAD); | | 986 | sizeof(itd->itd.itd_ctl), BUS_DMASYNC_PREREAD); |
987 | | | 987 | |
988 | DPRINTFN(10, "ex %#jx itd %#jx still active", | | 988 | DPRINTFN(10, "ex %#jx itd %#jx still active", |
989 | (uintptr_t)ex, (uintptr_t)ex->ex_itdstart, 0, 0); | | 989 | (uintptr_t)ex, (uintptr_t)ex->ex_itdstart, 0, 0); |
990 | return; | | 990 | return; |
991 | done: | | 991 | done: |
992 | DPRINTF("ex %#jx done", (uintptr_t)ex, 0, 0, 0); | | 992 | DPRINTF("ex %#jx done", (uintptr_t)ex, 0, 0, 0); |
993 | ehci_idone(ex, cq); | | 993 | ehci_idone(ex, cq); |
994 | } | | 994 | } |
995 | | | 995 | |
996 | void | | 996 | void |
997 | ehci_check_sitd_intr(ehci_softc_t *sc, struct ehci_xfer *ex, ex_completeq_t *cq) | | 997 | ehci_check_sitd_intr(ehci_softc_t *sc, struct ehci_xfer *ex, ex_completeq_t *cq) |
998 | { | | 998 | { |
999 | ehci_soft_sitd_t *sitd; | | 999 | ehci_soft_sitd_t *sitd; |
1000 | | | 1000 | |
1001 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1001 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1002 | | | 1002 | |
1003 | KASSERT(mutex_owned(&sc->sc_lock)); | | 1003 | KASSERT(mutex_owned(&sc->sc_lock)); |
1004 | | | 1004 | |
1005 | if (&ex->ex_xfer != SIMPLEQ_FIRST(&ex->ex_xfer.ux_pipe->up_queue)) | | 1005 | if (&ex->ex_xfer != SIMPLEQ_FIRST(&ex->ex_xfer.ux_pipe->up_queue)) |
1006 | return; | | 1006 | return; |
1007 | | | 1007 | |
1008 | KASSERTMSG(ex->ex_sitdstart != NULL && ex->ex_sitdend != NULL, | | 1008 | KASSERTMSG(ex->ex_sitdstart != NULL && ex->ex_sitdend != NULL, |
1009 | "xfer %p fsitd %p lsitd %p", ex, ex->ex_sitdstart, ex->ex_sitdend); | | 1009 | "xfer %p fsitd %p lsitd %p", ex, ex->ex_sitdstart, ex->ex_sitdend); |
1010 | | | 1010 | |
1011 | sitd = ex->ex_sitdend; | | 1011 | sitd = ex->ex_sitdend; |
1012 | | | 1012 | |
1013 | /* | | 1013 | /* |
1014 | * check no active transfers in last sitd, meaning we're finished | | 1014 | * check no active transfers in last sitd, meaning we're finished |
1015 | */ | | 1015 | */ |
1016 | | | 1016 | |
1017 | usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_trans), | | 1017 | usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_trans), |
1018 | sizeof(sitd->sitd.sitd_trans), | | 1018 | sizeof(sitd->sitd.sitd_trans), |
1019 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 1019 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
1020 | | | 1020 | |
1021 | bool active = ((le32toh(sitd->sitd.sitd_trans) & EHCI_SITD_ACTIVE) != 0); | | 1021 | bool active = ((le32toh(sitd->sitd.sitd_trans) & EHCI_SITD_ACTIVE) != 0); |
1022 | | | 1022 | |
1023 | usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_trans), | | 1023 | usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_trans), |
1024 | sizeof(sitd->sitd.sitd_trans), BUS_DMASYNC_PREREAD); | | 1024 | sizeof(sitd->sitd.sitd_trans), BUS_DMASYNC_PREREAD); |
1025 | | | 1025 | |
1026 | if (active) | | 1026 | if (active) |
1027 | return; | | 1027 | return; |
1028 | | | 1028 | |
1029 | DPRINTFN(10, "ex=%#jx done", (uintptr_t)ex, 0, 0, 0); | | 1029 | DPRINTFN(10, "ex=%#jx done", (uintptr_t)ex, 0, 0, 0); |
1030 | ehci_idone(ex, cq); | | 1030 | ehci_idone(ex, cq); |
1031 | } | | 1031 | } |
1032 | | | 1032 | |
1033 | Static void | | 1033 | Static void |
1034 | ehci_idone(struct ehci_xfer *ex, ex_completeq_t *cq) | | 1034 | ehci_idone(struct ehci_xfer *ex, ex_completeq_t *cq) |
1035 | { | | 1035 | { |
1036 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1036 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1037 | struct usbd_xfer *xfer = &ex->ex_xfer; | | 1037 | struct usbd_xfer *xfer = &ex->ex_xfer; |
1038 | struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); | | 1038 | struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
1039 | struct ehci_softc *sc = EHCI_XFER2SC(xfer); | | 1039 | struct ehci_softc *sc = EHCI_XFER2SC(xfer); |
1040 | ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; | | 1040 | ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; |
1041 | uint32_t status = 0, nstatus = 0; | | 1041 | uint32_t status = 0, nstatus = 0; |
1042 | int actlen = 0; | | 1042 | int actlen = 0; |
1043 | | | 1043 | |
1044 | KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); | | 1044 | KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); |
1045 | | | 1045 | |
1046 | DPRINTF("ex=%#jx", (uintptr_t)ex, 0, 0, 0); | | 1046 | DPRINTF("ex=%#jx", (uintptr_t)ex, 0, 0, 0); |
1047 | | | 1047 | |
1048 | /* | | 1048 | /* |
1049 | * If software has completed it, either by cancellation | | 1049 | * If software has completed it, either by cancellation |
1050 | * or timeout, drop it on the floor. | | 1050 | * or timeout, drop it on the floor. |
1051 | */ | | 1051 | */ |
1052 | if (xfer->ux_status != USBD_IN_PROGRESS) { | | 1052 | if (xfer->ux_status != USBD_IN_PROGRESS) { |
1053 | KASSERT(xfer->ux_status == USBD_CANCELLED || | | 1053 | KASSERT(xfer->ux_status == USBD_CANCELLED || |
1054 | xfer->ux_status == USBD_TIMEOUT); | | 1054 | xfer->ux_status == USBD_TIMEOUT); |
1055 | DPRINTF("aborted xfer=%#jx", (uintptr_t)xfer, 0, 0, 0); | | 1055 | DPRINTF("aborted xfer=%#jx", (uintptr_t)xfer, 0, 0, 0); |
| @@ -1613,1999 +1613,1999 @@ ehci_dump(void) | | | @@ -1613,1999 +1613,1999 @@ ehci_dump(void) |
1613 | EOREAD4(sc, EHCI_USBINTR)); | | 1613 | EOREAD4(sc, EHCI_USBINTR)); |
1614 | printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", | | 1614 | printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", |
1615 | EOREAD4(sc, EHCI_FRINDEX), | | 1615 | EOREAD4(sc, EHCI_FRINDEX), |
1616 | EOREAD4(sc, EHCI_CTRLDSSEGMENT), | | 1616 | EOREAD4(sc, EHCI_CTRLDSSEGMENT), |
1617 | EOREAD4(sc, EHCI_PERIODICLISTBASE), | | 1617 | EOREAD4(sc, EHCI_PERIODICLISTBASE), |
1618 | EOREAD4(sc, EHCI_ASYNCLISTADDR)); | | 1618 | EOREAD4(sc, EHCI_ASYNCLISTADDR)); |
1619 | for (i = 1; i <= sc->sc_noport; i++) | | 1619 | for (i = 1; i <= sc->sc_noport; i++) |
1620 | printf("port %d status=0x%08x\n", i, | | 1620 | printf("port %d status=0x%08x\n", i, |
1621 | EOREAD4(sc, EHCI_PORTSC(i))); | | 1621 | EOREAD4(sc, EHCI_PORTSC(i))); |
1622 | } | | 1622 | } |
1623 | | | 1623 | |
1624 | Static void | | 1624 | Static void |
1625 | ehci_dump_regs(ehci_softc_t *sc) | | 1625 | ehci_dump_regs(ehci_softc_t *sc) |
1626 | { | | 1626 | { |
1627 | int i; | | 1627 | int i; |
1628 | | | 1628 | |
1629 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1629 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1630 | | | 1630 | |
1631 | DPRINTF("cmd = 0x%08jx sts = 0x%08jx ien = 0x%08jx", | | 1631 | DPRINTF("cmd = 0x%08jx sts = 0x%08jx ien = 0x%08jx", |
1632 | EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), | | 1632 | EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), |
1633 | EOREAD4(sc, EHCI_USBINTR), 0); | | 1633 | EOREAD4(sc, EHCI_USBINTR), 0); |
1634 | DPRINTF("frindex = 0x%08jx ctrdsegm = 0x%08jx periodic = 0x%08jx " | | 1634 | DPRINTF("frindex = 0x%08jx ctrdsegm = 0x%08jx periodic = 0x%08jx " |
1635 | "async = 0x%08jx", | | 1635 | "async = 0x%08jx", |
1636 | EOREAD4(sc, EHCI_FRINDEX), EOREAD4(sc, EHCI_CTRLDSSEGMENT), | | 1636 | EOREAD4(sc, EHCI_FRINDEX), EOREAD4(sc, EHCI_CTRLDSSEGMENT), |
1637 | EOREAD4(sc, EHCI_PERIODICLISTBASE), | | 1637 | EOREAD4(sc, EHCI_PERIODICLISTBASE), |
1638 | EOREAD4(sc, EHCI_ASYNCLISTADDR)); | | 1638 | EOREAD4(sc, EHCI_ASYNCLISTADDR)); |
1639 | for (i = 1; i <= sc->sc_noport; i += 2) { | | 1639 | for (i = 1; i <= sc->sc_noport; i += 2) { |
1640 | if (i == sc->sc_noport) { | | 1640 | if (i == sc->sc_noport) { |
1641 | DPRINTF("port %jd status = 0x%08jx", i, | | 1641 | DPRINTF("port %jd status = 0x%08jx", i, |
1642 | EOREAD4(sc, EHCI_PORTSC(i)), 0, 0); | | 1642 | EOREAD4(sc, EHCI_PORTSC(i)), 0, 0); |
1643 | } else { | | 1643 | } else { |
1644 | DPRINTF("port %jd status = 0x%08jx port %jd " | | 1644 | DPRINTF("port %jd status = 0x%08jx port %jd " |
1645 | "status = 0x%08jx", | | 1645 | "status = 0x%08jx", |
1646 | i, EOREAD4(sc, EHCI_PORTSC(i)), | | 1646 | i, EOREAD4(sc, EHCI_PORTSC(i)), |
1647 | i+1, EOREAD4(sc, EHCI_PORTSC(i+1))); | | 1647 | i+1, EOREAD4(sc, EHCI_PORTSC(i+1))); |
1648 | } | | 1648 | } |
1649 | } | | 1649 | } |
1650 | } | | 1650 | } |
1651 | | | 1651 | |
1652 | #define ehci_dump_link(link, type) do { \ | | 1652 | #define ehci_dump_link(link, type) do { \ |
1653 | DPRINTF(" link 0x%08jx (T = %jd):", \ | | 1653 | DPRINTF(" link 0x%08jx (T = %jd):", \ |
1654 | link, \ | | 1654 | link, \ |
1655 | link & EHCI_LINK_TERMINATE ? 1 : 0, 0, 0); \ | | 1655 | link & EHCI_LINK_TERMINATE ? 1 : 0, 0, 0); \ |
1656 | if (type) { \ | | 1656 | if (type) { \ |
1657 | DPRINTF( \ | | 1657 | DPRINTF( \ |
1658 | " ITD = %jd QH = %jd SITD = %jd FSTN = %jd",\ | | 1658 | " ITD = %jd QH = %jd SITD = %jd FSTN = %jd",\ |
1659 | EHCI_LINK_TYPE(link) == EHCI_LINK_ITD ? 1 : 0, \ | | 1659 | EHCI_LINK_TYPE(link) == EHCI_LINK_ITD ? 1 : 0, \ |
1660 | EHCI_LINK_TYPE(link) == EHCI_LINK_QH ? 1 : 0, \ | | 1660 | EHCI_LINK_TYPE(link) == EHCI_LINK_QH ? 1 : 0, \ |
1661 | EHCI_LINK_TYPE(link) == EHCI_LINK_SITD ? 1 : 0, \ | | 1661 | EHCI_LINK_TYPE(link) == EHCI_LINK_SITD ? 1 : 0, \ |
1662 | EHCI_LINK_TYPE(link) == EHCI_LINK_FSTN ? 1 : 0); \ | | 1662 | EHCI_LINK_TYPE(link) == EHCI_LINK_FSTN ? 1 : 0); \ |
1663 | } \ | | 1663 | } \ |
1664 | } while(0) | | 1664 | } while(0) |
1665 | | | 1665 | |
1666 | Static void | | 1666 | Static void |
1667 | ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) | | 1667 | ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) |
1668 | { | | 1668 | { |
1669 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1669 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1670 | int i; | | 1670 | int i; |
1671 | uint32_t stop = 0; | | 1671 | uint32_t stop = 0; |
1672 | | | 1672 | |
1673 | for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) { | | 1673 | for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) { |
1674 | ehci_dump_sqtd(sqtd); | | 1674 | ehci_dump_sqtd(sqtd); |
1675 | usb_syncmem(&sqtd->dma, | | 1675 | usb_syncmem(&sqtd->dma, |
1676 | sqtd->offs + offsetof(ehci_qtd_t, qtd_next), | | 1676 | sqtd->offs + offsetof(ehci_qtd_t, qtd_next), |
1677 | sizeof(sqtd->qtd), | | 1677 | sizeof(sqtd->qtd), |
1678 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 1678 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
1679 | stop = sqtd->qtd.qtd_next & htole32(EHCI_LINK_TERMINATE); | | 1679 | stop = sqtd->qtd.qtd_next & htole32(EHCI_LINK_TERMINATE); |
1680 | usb_syncmem(&sqtd->dma, | | 1680 | usb_syncmem(&sqtd->dma, |
1681 | sqtd->offs + offsetof(ehci_qtd_t, qtd_next), | | 1681 | sqtd->offs + offsetof(ehci_qtd_t, qtd_next), |
1682 | sizeof(sqtd->qtd), BUS_DMASYNC_PREREAD); | | 1682 | sizeof(sqtd->qtd), BUS_DMASYNC_PREREAD); |
1683 | } | | 1683 | } |
1684 | if (!stop) | | 1684 | if (!stop) |
1685 | DPRINTF("dump aborted, too many TDs", 0, 0, 0, 0); | | 1685 | DPRINTF("dump aborted, too many TDs", 0, 0, 0, 0); |
1686 | } | | 1686 | } |
1687 | | | 1687 | |
1688 | Static void | | 1688 | Static void |
1689 | ehci_dump_sqtd(ehci_soft_qtd_t *sqtd) | | 1689 | ehci_dump_sqtd(ehci_soft_qtd_t *sqtd) |
1690 | { | | 1690 | { |
1691 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1691 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1692 | | | 1692 | |
1693 | usb_syncmem(&sqtd->dma, sqtd->offs, | | 1693 | usb_syncmem(&sqtd->dma, sqtd->offs, |
1694 | sizeof(sqtd->qtd), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 1694 | sizeof(sqtd->qtd), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
1695 | | | 1695 | |
1696 | DPRINTFN(10, "QTD(%#jx) at 0x%08jx:", (uintptr_t)sqtd, sqtd->physaddr, | | 1696 | DPRINTFN(10, "QTD(%#jx) at 0x%08jx:", (uintptr_t)sqtd, sqtd->physaddr, |
1697 | 0, 0); | | 1697 | 0, 0); |
1698 | ehci_dump_qtd(&sqtd->qtd); | | 1698 | ehci_dump_qtd(&sqtd->qtd); |
1699 | | | 1699 | |
1700 | usb_syncmem(&sqtd->dma, sqtd->offs, | | 1700 | usb_syncmem(&sqtd->dma, sqtd->offs, |
1701 | sizeof(sqtd->qtd), BUS_DMASYNC_PREREAD); | | 1701 | sizeof(sqtd->qtd), BUS_DMASYNC_PREREAD); |
1702 | } | | 1702 | } |
1703 | | | 1703 | |
1704 | Static void | | 1704 | Static void |
1705 | ehci_dump_qtd(ehci_qtd_t *qtd) | | 1705 | ehci_dump_qtd(ehci_qtd_t *qtd) |
1706 | { | | 1706 | { |
1707 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1707 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1708 | uint32_t s = le32toh(qtd->qtd_status); | | 1708 | uint32_t s = le32toh(qtd->qtd_status); |
1709 | | | 1709 | |
1710 | DPRINTFN(10, | | 1710 | DPRINTFN(10, |
1711 | " next = 0x%08jx altnext = 0x%08jx status = 0x%08jx", | | 1711 | " next = 0x%08jx altnext = 0x%08jx status = 0x%08jx", |
1712 | qtd->qtd_next, qtd->qtd_altnext, s, 0); | | 1712 | qtd->qtd_next, qtd->qtd_altnext, s, 0); |
1713 | DPRINTFN(10, | | 1713 | DPRINTFN(10, |
1714 | " toggle = %jd ioc = %jd bytes = %#jx c_page = %#jx", | | 1714 | " toggle = %jd ioc = %jd bytes = %#jx c_page = %#jx", |
1715 | EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_IOC(s), | | 1715 | EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_IOC(s), |
1716 | EHCI_QTD_GET_BYTES(s), EHCI_QTD_GET_C_PAGE(s)); | | 1716 | EHCI_QTD_GET_BYTES(s), EHCI_QTD_GET_C_PAGE(s)); |
1717 | DPRINTFN(10, | | 1717 | DPRINTFN(10, |
1718 | " cerr = %jd pid = %jd stat = %jx", | | 1718 | " cerr = %jd pid = %jd stat = %jx", |
1719 | EHCI_QTD_GET_CERR(s), EHCI_QTD_GET_PID(s), EHCI_QTD_GET_STATUS(s), | | 1719 | EHCI_QTD_GET_CERR(s), EHCI_QTD_GET_PID(s), EHCI_QTD_GET_STATUS(s), |
1720 | 0); | | 1720 | 0); |
1721 | DPRINTFN(10, | | 1721 | DPRINTFN(10, |
1722 | "active =%jd halted=%jd buferr=%jd babble=%jd", | | 1722 | "active =%jd halted=%jd buferr=%jd babble=%jd", |
1723 | s & EHCI_QTD_ACTIVE ? 1 : 0, | | 1723 | s & EHCI_QTD_ACTIVE ? 1 : 0, |
1724 | s & EHCI_QTD_HALTED ? 1 : 0, | | 1724 | s & EHCI_QTD_HALTED ? 1 : 0, |
1725 | s & EHCI_QTD_BUFERR ? 1 : 0, | | 1725 | s & EHCI_QTD_BUFERR ? 1 : 0, |
1726 | s & EHCI_QTD_BABBLE ? 1 : 0); | | 1726 | s & EHCI_QTD_BABBLE ? 1 : 0); |
1727 | DPRINTFN(10, | | 1727 | DPRINTFN(10, |
1728 | "xacterr=%jd missed=%jd split =%jd ping =%jd", | | 1728 | "xacterr=%jd missed=%jd split =%jd ping =%jd", |
1729 | s & EHCI_QTD_XACTERR ? 1 : 0, | | 1729 | s & EHCI_QTD_XACTERR ? 1 : 0, |
1730 | s & EHCI_QTD_MISSEDMICRO ? 1 : 0, | | 1730 | s & EHCI_QTD_MISSEDMICRO ? 1 : 0, |
1731 | s & EHCI_QTD_SPLITXSTATE ? 1 : 0, | | 1731 | s & EHCI_QTD_SPLITXSTATE ? 1 : 0, |
1732 | s & EHCI_QTD_PINGSTATE ? 1 : 0); | | 1732 | s & EHCI_QTD_PINGSTATE ? 1 : 0); |
1733 | DPRINTFN(10, | | 1733 | DPRINTFN(10, |
1734 | "buffer[0] = %#jx buffer[1] = %#jx " | | 1734 | "buffer[0] = %#jx buffer[1] = %#jx " |
1735 | "buffer[2] = %#jx buffer[3] = %#jx", | | 1735 | "buffer[2] = %#jx buffer[3] = %#jx", |
1736 | le32toh(qtd->qtd_buffer[0]), le32toh(qtd->qtd_buffer[1]), | | 1736 | le32toh(qtd->qtd_buffer[0]), le32toh(qtd->qtd_buffer[1]), |
1737 | le32toh(qtd->qtd_buffer[2]), le32toh(qtd->qtd_buffer[3])); | | 1737 | le32toh(qtd->qtd_buffer[2]), le32toh(qtd->qtd_buffer[3])); |
1738 | DPRINTFN(10, | | 1738 | DPRINTFN(10, |
1739 | "buffer[4] = %#jx", le32toh(qtd->qtd_buffer[4]), 0, 0, 0); | | 1739 | "buffer[4] = %#jx", le32toh(qtd->qtd_buffer[4]), 0, 0, 0); |
1740 | } | | 1740 | } |
1741 | | | 1741 | |
1742 | Static void | | 1742 | Static void |
1743 | ehci_dump_sqh(ehci_soft_qh_t *sqh) | | 1743 | ehci_dump_sqh(ehci_soft_qh_t *sqh) |
1744 | { | | 1744 | { |
1745 | ehci_qh_t *qh = &sqh->qh; | | 1745 | ehci_qh_t *qh = &sqh->qh; |
1746 | ehci_link_t link; | | 1746 | ehci_link_t link; |
1747 | uint32_t endp, endphub; | | 1747 | uint32_t endp, endphub; |
1748 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1748 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1749 | | | 1749 | |
1750 | usb_syncmem(&sqh->dma, sqh->offs, | | 1750 | usb_syncmem(&sqh->dma, sqh->offs, |
1751 | sizeof(sqh->qh), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 1751 | sizeof(sqh->qh), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
1752 | | | 1752 | |
1753 | DPRINTFN(10, "QH(%#jx) at %#jx:", (uintptr_t)sqh, sqh->physaddr, 0, 0); | | 1753 | DPRINTFN(10, "QH(%#jx) at %#jx:", (uintptr_t)sqh, sqh->physaddr, 0, 0); |
1754 | link = le32toh(qh->qh_link); | | 1754 | link = le32toh(qh->qh_link); |
1755 | ehci_dump_link(link, true); | | 1755 | ehci_dump_link(link, true); |
1756 | | | 1756 | |
1757 | endp = le32toh(qh->qh_endp); | | 1757 | endp = le32toh(qh->qh_endp); |
1758 | DPRINTFN(10, " endp = %#jx", endp, 0, 0, 0); | | 1758 | DPRINTFN(10, " endp = %#jx", endp, 0, 0, 0); |
1759 | DPRINTFN(10, " addr = 0x%02jx inact = %jd endpt = %jd " | | 1759 | DPRINTFN(10, " addr = 0x%02jx inact = %jd endpt = %jd " |
1760 | "eps = %jd", | | 1760 | "eps = %jd", |
1761 | EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), | | 1761 | EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), |
1762 | EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp)); | | 1762 | EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp)); |
1763 | DPRINTFN(10, " dtc = %jd hrecl = %jd", | | 1763 | DPRINTFN(10, " dtc = %jd hrecl = %jd", |
1764 | EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp), 0, 0); | | 1764 | EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp), 0, 0); |
1765 | DPRINTFN(10, " ctl = %jd nrl = %jd mpl = %#jx(%jd)", | | 1765 | DPRINTFN(10, " ctl = %jd nrl = %jd mpl = %#jx(%jd)", |
1766 | EHCI_QH_GET_CTL(endp),EHCI_QH_GET_NRL(endp), | | 1766 | EHCI_QH_GET_CTL(endp),EHCI_QH_GET_NRL(endp), |
1767 | EHCI_QH_GET_MPL(endp), EHCI_QH_GET_MPL(endp)); | | 1767 | EHCI_QH_GET_MPL(endp), EHCI_QH_GET_MPL(endp)); |
1768 | | | 1768 | |
1769 | endphub = le32toh(qh->qh_endphub); | | 1769 | endphub = le32toh(qh->qh_endphub); |
1770 | DPRINTFN(10, " endphub = %#jx", endphub, 0, 0, 0); | | 1770 | DPRINTFN(10, " endphub = %#jx", endphub, 0, 0, 0); |
1771 | DPRINTFN(10, " smask = 0x%02jx cmask = 0x%02jx one %jx", | | 1771 | DPRINTFN(10, " smask = 0x%02jx cmask = 0x%02jx one %jx", |
1772 | EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), 1, 0); | | 1772 | EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), 1, 0); |
1773 | DPRINTFN(10, " huba = 0x%02jx port = %jd mult = %jd", | | 1773 | DPRINTFN(10, " huba = 0x%02jx port = %jd mult = %jd", |
1774 | EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), | | 1774 | EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), |
1775 | EHCI_QH_GET_MULT(endphub), 0); | | 1775 | EHCI_QH_GET_MULT(endphub), 0); |
1776 | | | 1776 | |
1777 | link = le32toh(qh->qh_curqtd); | | 1777 | link = le32toh(qh->qh_curqtd); |
1778 | ehci_dump_link(link, false); | | 1778 | ehci_dump_link(link, false); |
1779 | DPRINTFN(10, "Overlay qTD:", 0, 0, 0, 0); | | 1779 | DPRINTFN(10, "Overlay qTD:", 0, 0, 0, 0); |
1780 | ehci_dump_qtd(&qh->qh_qtd); | | 1780 | ehci_dump_qtd(&qh->qh_qtd); |
1781 | | | 1781 | |
1782 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), | | 1782 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), |
1783 | BUS_DMASYNC_PREREAD); | | 1783 | BUS_DMASYNC_PREREAD); |
1784 | } | | 1784 | } |
1785 | | | 1785 | |
1786 | Static void | | 1786 | Static void |
1787 | ehci_dump_itds(ehci_soft_itd_t *itd) | | 1787 | ehci_dump_itds(ehci_soft_itd_t *itd) |
1788 | { | | 1788 | { |
1789 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1789 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1790 | int i; | | 1790 | int i; |
1791 | uint32_t stop = 0; | | 1791 | uint32_t stop = 0; |
1792 | | | 1792 | |
1793 | for (i = 0; itd && i < 20 && !stop; itd = itd->xfer_next, i++) { | | 1793 | for (i = 0; itd && i < 20 && !stop; itd = itd->xfer_next, i++) { |
1794 | ehci_dump_itd(itd); | | 1794 | ehci_dump_itd(itd); |
1795 | usb_syncmem(&itd->dma, | | 1795 | usb_syncmem(&itd->dma, |
1796 | itd->offs + offsetof(ehci_itd_t, itd_next), | | 1796 | itd->offs + offsetof(ehci_itd_t, itd_next), |
1797 | sizeof(itd->itd), | | 1797 | sizeof(itd->itd), |
1798 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 1798 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
1799 | stop = itd->itd.itd_next & htole32(EHCI_LINK_TERMINATE); | | 1799 | stop = itd->itd.itd_next & htole32(EHCI_LINK_TERMINATE); |
1800 | usb_syncmem(&itd->dma, | | 1800 | usb_syncmem(&itd->dma, |
1801 | itd->offs + offsetof(ehci_itd_t, itd_next), | | 1801 | itd->offs + offsetof(ehci_itd_t, itd_next), |
1802 | sizeof(itd->itd), BUS_DMASYNC_PREREAD); | | 1802 | sizeof(itd->itd), BUS_DMASYNC_PREREAD); |
1803 | } | | 1803 | } |
1804 | if (!stop) | | 1804 | if (!stop) |
1805 | DPRINTF("dump aborted, too many TDs", 0, 0, 0, 0); | | 1805 | DPRINTF("dump aborted, too many TDs", 0, 0, 0, 0); |
1806 | } | | 1806 | } |
1807 | | | 1807 | |
1808 | Static void | | 1808 | Static void |
1809 | ehci_dump_itd(struct ehci_soft_itd *itd) | | 1809 | ehci_dump_itd(struct ehci_soft_itd *itd) |
1810 | { | | 1810 | { |
1811 | ehci_isoc_trans_t t; | | 1811 | ehci_isoc_trans_t t; |
1812 | ehci_isoc_bufr_ptr_t b, b2, b3; | | 1812 | ehci_isoc_bufr_ptr_t b, b2, b3; |
1813 | int i; | | 1813 | int i; |
1814 | | | 1814 | |
1815 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1815 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1816 | | | 1816 | |
1817 | DPRINTF("ITD: next phys = %#jx", itd->itd.itd_next, 0, 0, 0); | | 1817 | DPRINTF("ITD: next phys = %#jx", itd->itd.itd_next, 0, 0, 0); |
1818 | | | 1818 | |
1819 | for (i = 0; i < EHCI_ITD_NUFRAMES; i++) { | | 1819 | for (i = 0; i < EHCI_ITD_NUFRAMES; i++) { |
1820 | t = le32toh(itd->itd.itd_ctl[i]); | | 1820 | t = le32toh(itd->itd.itd_ctl[i]); |
1821 | DPRINTF("ITDctl %jd: stat = %jx len = %jx", | | 1821 | DPRINTF("ITDctl %jd: stat = %jx len = %jx", |
1822 | i, EHCI_ITD_GET_STATUS(t), EHCI_ITD_GET_LEN(t), 0); | | 1822 | i, EHCI_ITD_GET_STATUS(t), EHCI_ITD_GET_LEN(t), 0); |
1823 | DPRINTF(" ioc = %jx pg = %jx offs = %jx", | | 1823 | DPRINTF(" ioc = %jx pg = %jx offs = %jx", |
1824 | EHCI_ITD_GET_IOC(t), EHCI_ITD_GET_PG(t), | | 1824 | EHCI_ITD_GET_IOC(t), EHCI_ITD_GET_PG(t), |
1825 | EHCI_ITD_GET_OFFS(t), 0); | | 1825 | EHCI_ITD_GET_OFFS(t), 0); |
1826 | } | | 1826 | } |
1827 | DPRINTF("ITDbufr: ", 0, 0, 0, 0); | | 1827 | DPRINTF("ITDbufr: ", 0, 0, 0, 0); |
1828 | for (i = 0; i < EHCI_ITD_NBUFFERS; i++) | | 1828 | for (i = 0; i < EHCI_ITD_NBUFFERS; i++) |
1829 | DPRINTF(" %jx", | | 1829 | DPRINTF(" %jx", |
1830 | EHCI_ITD_GET_BPTR(le32toh(itd->itd.itd_bufr[i])), 0, 0, 0); | | 1830 | EHCI_ITD_GET_BPTR(le32toh(itd->itd.itd_bufr[i])), 0, 0, 0); |
1831 | | | 1831 | |
1832 | b = le32toh(itd->itd.itd_bufr[0]); | | 1832 | b = le32toh(itd->itd.itd_bufr[0]); |
1833 | b2 = le32toh(itd->itd.itd_bufr[1]); | | 1833 | b2 = le32toh(itd->itd.itd_bufr[1]); |
1834 | b3 = le32toh(itd->itd.itd_bufr[2]); | | 1834 | b3 = le32toh(itd->itd.itd_bufr[2]); |
1835 | DPRINTF(" ep = %jx daddr = %jx dir = %jd", | | 1835 | DPRINTF(" ep = %jx daddr = %jx dir = %jd", |
1836 | EHCI_ITD_GET_EP(b), EHCI_ITD_GET_DADDR(b), EHCI_ITD_GET_DIR(b2), 0); | | 1836 | EHCI_ITD_GET_EP(b), EHCI_ITD_GET_DADDR(b), EHCI_ITD_GET_DIR(b2), 0); |
1837 | DPRINTF(" maxpkt = %jx multi = %jx", | | 1837 | DPRINTF(" maxpkt = %jx multi = %jx", |
1838 | EHCI_ITD_GET_MAXPKT(b2), EHCI_ITD_GET_MULTI(b3), 0, 0); | | 1838 | EHCI_ITD_GET_MAXPKT(b2), EHCI_ITD_GET_MULTI(b3), 0, 0); |
1839 | } | | 1839 | } |
1840 | | | 1840 | |
1841 | Static void | | 1841 | Static void |
1842 | ehci_dump_sitd(struct ehci_soft_itd *itd) | | 1842 | ehci_dump_sitd(struct ehci_soft_itd *itd) |
1843 | { | | 1843 | { |
1844 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1844 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1845 | | | 1845 | |
1846 | DPRINTF("SITD %#jx next = %p prev = %#jx", | | 1846 | DPRINTF("SITD %#jx next = %p prev = %#jx", |
1847 | (uintptr_t)itd, (uintptr_t)itd->frame_list.next, | | 1847 | (uintptr_t)itd, (uintptr_t)itd->frame_list.next, |
1848 | (uintptr_t)itd->frame_list.prev, 0); | | 1848 | (uintptr_t)itd->frame_list.prev, 0); |
1849 | DPRINTF(" xfernext=%#jx physaddr=%jX slot=%jd", | | 1849 | DPRINTF(" xfernext=%#jx physaddr=%jX slot=%jd", |
1850 | (uintptr_t)itd->xfer_next, itd->physaddr, itd->slot, 0); | | 1850 | (uintptr_t)itd->xfer_next, itd->physaddr, itd->slot, 0); |
1851 | } | | 1851 | } |
1852 | | | 1852 | |
1853 | Static void | | 1853 | Static void |
1854 | ehci_dump_exfer(struct ehci_xfer *ex) | | 1854 | ehci_dump_exfer(struct ehci_xfer *ex) |
1855 | { | | 1855 | { |
1856 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1856 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1857 | | | 1857 | |
1858 | DPRINTF("ex = %#jx type %jd isdone %jd", (uintptr_t)ex, ex->ex_type, | | 1858 | DPRINTF("ex = %#jx type %jd isdone %jd", (uintptr_t)ex, ex->ex_type, |
1859 | ex->ex_isdone, 0); | | 1859 | ex->ex_isdone, 0); |
1860 | | | 1860 | |
1861 | switch (ex->ex_type) { | | 1861 | switch (ex->ex_type) { |
1862 | case EX_CTRL: | | 1862 | case EX_CTRL: |
1863 | DPRINTF(" setup = %#jx data = %#jx status = %#jx", | | 1863 | DPRINTF(" setup = %#jx data = %#jx status = %#jx", |
1864 | (uintptr_t)ex->ex_setup, (uintptr_t)ex->ex_data, | | 1864 | (uintptr_t)ex->ex_setup, (uintptr_t)ex->ex_data, |
1865 | (uintptr_t)ex->ex_status, 0); | | 1865 | (uintptr_t)ex->ex_status, 0); |
1866 | break; | | 1866 | break; |
1867 | case EX_BULK: | | 1867 | case EX_BULK: |
1868 | case EX_INTR: | | 1868 | case EX_INTR: |
1869 | DPRINTF(" qtdstart = %#jx qtdend = %#jx", | | 1869 | DPRINTF(" qtdstart = %#jx qtdend = %#jx", |
1870 | (uintptr_t)ex->ex_sqtdstart, (uintptr_t)ex->ex_sqtdend, | | 1870 | (uintptr_t)ex->ex_sqtdstart, (uintptr_t)ex->ex_sqtdend, |
1871 | 0, 0); | | 1871 | 0, 0); |
1872 | break; | | 1872 | break; |
1873 | case EX_ISOC: | | 1873 | case EX_ISOC: |
1874 | DPRINTF(" itdstart = %#jx itdend = %#jx", | | 1874 | DPRINTF(" itdstart = %#jx itdend = %#jx", |
1875 | (uintptr_t)ex->ex_itdstart, (uintptr_t)ex->ex_itdend, 0, 0); | | 1875 | (uintptr_t)ex->ex_itdstart, (uintptr_t)ex->ex_itdend, 0, 0); |
1876 | break; | | 1876 | break; |
1877 | case EX_FS_ISOC: | | 1877 | case EX_FS_ISOC: |
1878 | DPRINTF(" sitdstart = %#jx sitdend = %#jx", | | 1878 | DPRINTF(" sitdstart = %#jx sitdend = %#jx", |
1879 | (uintptr_t)ex->ex_sitdstart, (uintptr_t)ex->ex_sitdend, | | 1879 | (uintptr_t)ex->ex_sitdstart, (uintptr_t)ex->ex_sitdend, |
1880 | 0, 0); | | 1880 | 0, 0); |
1881 | break; | | 1881 | break; |
1882 | default: | | 1882 | default: |
1883 | DPRINTF(" unknown type", 0, 0, 0, 0); | | 1883 | DPRINTF(" unknown type", 0, 0, 0, 0); |
1884 | } | | 1884 | } |
1885 | } | | 1885 | } |
1886 | #endif | | 1886 | #endif |
1887 | | | 1887 | |
1888 | Static usbd_status | | 1888 | Static usbd_status |
1889 | ehci_open(struct usbd_pipe *pipe) | | 1889 | ehci_open(struct usbd_pipe *pipe) |
1890 | { | | 1890 | { |
1891 | struct usbd_device *dev = pipe->up_dev; | | 1891 | struct usbd_device *dev = pipe->up_dev; |
1892 | ehci_softc_t *sc = EHCI_PIPE2SC(pipe); | | 1892 | ehci_softc_t *sc = EHCI_PIPE2SC(pipe); |
1893 | usb_endpoint_descriptor_t *ed = pipe->up_endpoint->ue_edesc; | | 1893 | usb_endpoint_descriptor_t *ed = pipe->up_endpoint->ue_edesc; |
1894 | uint8_t rhaddr = dev->ud_bus->ub_rhaddr; | | 1894 | uint8_t rhaddr = dev->ud_bus->ub_rhaddr; |
1895 | uint8_t addr = dev->ud_addr; | | 1895 | uint8_t addr = dev->ud_addr; |
1896 | uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes); | | 1896 | uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes); |
1897 | struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); | | 1897 | struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); |
1898 | ehci_soft_qh_t *sqh; | | 1898 | ehci_soft_qh_t *sqh; |
1899 | usbd_status err; | | 1899 | usbd_status err; |
1900 | int ival, speed, naks; | | 1900 | int ival, speed, naks; |
1901 | int hshubaddr, hshubport; | | 1901 | int hshubaddr, hshubport; |
1902 | | | 1902 | |
1903 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 1903 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
1904 | | | 1904 | |
1905 | DPRINTF("pipe=%#jx, addr=%jd, endpt=%jd (%jd)", (uintptr_t)pipe, addr, | | 1905 | DPRINTF("pipe=%#jx, addr=%jd, endpt=%jd (%jd)", (uintptr_t)pipe, addr, |
1906 | ed->bEndpointAddress, rhaddr); | | 1906 | ed->bEndpointAddress, rhaddr); |
1907 | | | 1907 | |
1908 | if (dev->ud_myhsport) { | | 1908 | if (dev->ud_myhsport) { |
1909 | /* | | 1909 | /* |
1910 | * When directly attached FS/LS device while doing embedded | | 1910 | * When directly attached FS/LS device while doing embedded |
1911 | * transaction translations and we are the hub, set the hub | | 1911 | * transaction translations and we are the hub, set the hub |
1912 | * address to 0 (us). | | 1912 | * address to 0 (us). |
1913 | */ | | 1913 | */ |
1914 | if (!(sc->sc_flags & EHCIF_ETTF) | | 1914 | if (!(sc->sc_flags & EHCIF_ETTF) |
1915 | || (dev->ud_myhsport->up_parent->ud_addr != rhaddr)) { | | 1915 | || (dev->ud_myhsport->up_parent->ud_addr != rhaddr)) { |
1916 | hshubaddr = dev->ud_myhsport->up_parent->ud_addr; | | 1916 | hshubaddr = dev->ud_myhsport->up_parent->ud_addr; |
1917 | } else { | | 1917 | } else { |
1918 | hshubaddr = 0; | | 1918 | hshubaddr = 0; |
1919 | } | | 1919 | } |
1920 | hshubport = dev->ud_myhsport->up_portno; | | 1920 | hshubport = dev->ud_myhsport->up_portno; |
1921 | } else { | | 1921 | } else { |
1922 | hshubaddr = 0; | | 1922 | hshubaddr = 0; |
1923 | hshubport = 0; | | 1923 | hshubport = 0; |
1924 | } | | 1924 | } |
1925 | | | 1925 | |
1926 | if (sc->sc_dying) | | 1926 | if (sc->sc_dying) |
1927 | return USBD_IOERROR; | | 1927 | return USBD_IOERROR; |
1928 | | | 1928 | |
1929 | /* toggle state needed for bulk endpoints */ | | 1929 | /* toggle state needed for bulk endpoints */ |
1930 | epipe->nexttoggle = pipe->up_endpoint->ue_toggle; | | 1930 | epipe->nexttoggle = pipe->up_endpoint->ue_toggle; |
1931 | | | 1931 | |
1932 | if (addr == rhaddr) { | | 1932 | if (addr == rhaddr) { |
1933 | switch (ed->bEndpointAddress) { | | 1933 | switch (ed->bEndpointAddress) { |
1934 | case USB_CONTROL_ENDPOINT: | | 1934 | case USB_CONTROL_ENDPOINT: |
1935 | pipe->up_methods = &roothub_ctrl_methods; | | 1935 | pipe->up_methods = &roothub_ctrl_methods; |
1936 | break; | | 1936 | break; |
1937 | case UE_DIR_IN | USBROOTHUB_INTR_ENDPT: | | 1937 | case UE_DIR_IN | USBROOTHUB_INTR_ENDPT: |
1938 | pipe->up_methods = &ehci_root_intr_methods; | | 1938 | pipe->up_methods = &ehci_root_intr_methods; |
1939 | break; | | 1939 | break; |
1940 | default: | | 1940 | default: |
1941 | DPRINTF("bad bEndpointAddress 0x%02jx", | | 1941 | DPRINTF("bad bEndpointAddress 0x%02jx", |
1942 | ed->bEndpointAddress, 0, 0, 0); | | 1942 | ed->bEndpointAddress, 0, 0, 0); |
1943 | return USBD_INVAL; | | 1943 | return USBD_INVAL; |
1944 | } | | 1944 | } |
1945 | return USBD_NORMAL_COMPLETION; | | 1945 | return USBD_NORMAL_COMPLETION; |
1946 | } | | 1946 | } |
1947 | | | 1947 | |
1948 | /* XXX All this stuff is only valid for async. */ | | 1948 | /* XXX All this stuff is only valid for async. */ |
1949 | switch (dev->ud_speed) { | | 1949 | switch (dev->ud_speed) { |
1950 | case USB_SPEED_LOW: speed = EHCI_QH_SPEED_LOW; break; | | 1950 | case USB_SPEED_LOW: speed = EHCI_QH_SPEED_LOW; break; |
1951 | case USB_SPEED_FULL: speed = EHCI_QH_SPEED_FULL; break; | | 1951 | case USB_SPEED_FULL: speed = EHCI_QH_SPEED_FULL; break; |
1952 | case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break; | | 1952 | case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break; |
1953 | default: panic("ehci_open: bad device speed %d", dev->ud_speed); | | 1953 | default: panic("ehci_open: bad device speed %d", dev->ud_speed); |
1954 | } | | 1954 | } |
1955 | if (speed == EHCI_QH_SPEED_LOW && xfertype == UE_ISOCHRONOUS) { | | 1955 | if (speed == EHCI_QH_SPEED_LOW && xfertype == UE_ISOCHRONOUS) { |
1956 | DPRINTF("hshubaddr=%jd hshubport=%jd", hshubaddr, hshubport, 0, | | 1956 | DPRINTF("hshubaddr=%jd hshubport=%jd", hshubaddr, hshubport, 0, |
1957 | 0); | | 1957 | 0); |
1958 | return USBD_INVAL; | | 1958 | return USBD_INVAL; |
1959 | } | | 1959 | } |
1960 | | | 1960 | |
1961 | /* | | 1961 | /* |
1962 | * For interrupt transfer, nak throttling must be disabled, but for | | 1962 | * For interrupt transfer, nak throttling must be disabled, but for |
1963 | * the other transfer type, nak throttling should be enabled from the | | 1963 | * the other transfer type, nak throttling should be enabled from the |
1964 | * viewpoint that avoids the memory thrashing. | | 1964 | * viewpoint that avoids the memory thrashing. |
1965 | */ | | 1965 | */ |
1966 | naks = (xfertype == UE_INTERRUPT) ? 0 | | 1966 | naks = (xfertype == UE_INTERRUPT) ? 0 |
1967 | : ((speed == EHCI_QH_SPEED_HIGH) ? 4 : 0); | | 1967 | : ((speed == EHCI_QH_SPEED_HIGH) ? 4 : 0); |
1968 | | | 1968 | |
1969 | /* Allocate sqh for everything, save isoc xfers */ | | 1969 | /* Allocate sqh for everything, save isoc xfers */ |
1970 | if (xfertype != UE_ISOCHRONOUS) { | | 1970 | if (xfertype != UE_ISOCHRONOUS) { |
1971 | sqh = ehci_alloc_sqh(sc); | | 1971 | sqh = ehci_alloc_sqh(sc); |
1972 | if (sqh == NULL) | | 1972 | if (sqh == NULL) |
1973 | return USBD_NOMEM; | | 1973 | return USBD_NOMEM; |
1974 | /* qh_link filled when the QH is added */ | | 1974 | /* qh_link filled when the QH is added */ |
1975 | sqh->qh.qh_endp = htole32( | | 1975 | sqh->qh.qh_endp = htole32( |
1976 | EHCI_QH_SET_ADDR(addr) | | | 1976 | EHCI_QH_SET_ADDR(addr) | |
1977 | EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) | | | 1977 | EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) | |
1978 | EHCI_QH_SET_EPS(speed) | | | 1978 | EHCI_QH_SET_EPS(speed) | |
1979 | EHCI_QH_DTC | | | 1979 | EHCI_QH_DTC | |
1980 | EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) | | | 1980 | EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) | |
1981 | (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ? | | 1981 | (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ? |
1982 | EHCI_QH_CTL : 0) | | | 1982 | EHCI_QH_CTL : 0) | |
1983 | EHCI_QH_SET_NRL(naks) | | 1983 | EHCI_QH_SET_NRL(naks) |
1984 | ); | | 1984 | ); |
1985 | sqh->qh.qh_endphub = htole32( | | 1985 | sqh->qh.qh_endphub = htole32( |
1986 | EHCI_QH_SET_MULT(1) | | | 1986 | EHCI_QH_SET_MULT(1) | |
1987 | EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x02 : 0) | | 1987 | EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x02 : 0) |
1988 | ); | | 1988 | ); |
1989 | if (speed != EHCI_QH_SPEED_HIGH) | | 1989 | if (speed != EHCI_QH_SPEED_HIGH) |
1990 | sqh->qh.qh_endphub |= htole32( | | 1990 | sqh->qh.qh_endphub |= htole32( |
1991 | EHCI_QH_SET_PORT(hshubport) | | | 1991 | EHCI_QH_SET_PORT(hshubport) | |
1992 | EHCI_QH_SET_HUBA(hshubaddr) | | | 1992 | EHCI_QH_SET_HUBA(hshubaddr) | |
1993 | (xfertype == UE_INTERRUPT ? | | 1993 | (xfertype == UE_INTERRUPT ? |
1994 | EHCI_QH_SET_CMASK(0x08) : 0) | | 1994 | EHCI_QH_SET_CMASK(0x08) : 0) |
1995 | ); | | 1995 | ); |
1996 | sqh->qh.qh_curqtd = EHCI_NULL; | | 1996 | sqh->qh.qh_curqtd = EHCI_NULL; |
1997 | /* Fill the overlay qTD */ | | 1997 | /* Fill the overlay qTD */ |
1998 | sqh->qh.qh_qtd.qtd_next = EHCI_NULL; | | 1998 | sqh->qh.qh_qtd.qtd_next = EHCI_NULL; |
1999 | sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; | | 1999 | sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; |
2000 | sqh->qh.qh_qtd.qtd_status = htole32(0); | | 2000 | sqh->qh.qh_qtd.qtd_status = htole32(0); |
2001 | | | 2001 | |
2002 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), | | 2002 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), |
2003 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 2003 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
2004 | epipe->sqh = sqh; | | 2004 | epipe->sqh = sqh; |
2005 | } else { | | 2005 | } else { |
2006 | sqh = NULL; | | 2006 | sqh = NULL; |
2007 | } /*xfertype == UE_ISOC*/ | | 2007 | } /*xfertype == UE_ISOC*/ |
2008 | | | 2008 | |
2009 | switch (xfertype) { | | 2009 | switch (xfertype) { |
2010 | case UE_CONTROL: | | 2010 | case UE_CONTROL: |
2011 | err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t), | | 2011 | err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t), |
2012 | 0, &epipe->ctrl.reqdma); | | 2012 | 0, &epipe->ctrl.reqdma); |
2013 | #ifdef EHCI_DEBUG | | 2013 | #ifdef EHCI_DEBUG |
2014 | if (err) | | 2014 | if (err) |
2015 | printf("ehci_open: usb_allocmem()=%d\n", err); | | 2015 | printf("ehci_open: usb_allocmem()=%d\n", err); |
2016 | #endif | | 2016 | #endif |
2017 | if (err) | | 2017 | if (err) |
2018 | goto bad; | | 2018 | goto bad; |
2019 | pipe->up_methods = &ehci_device_ctrl_methods; | | 2019 | pipe->up_methods = &ehci_device_ctrl_methods; |
2020 | mutex_enter(&sc->sc_lock); | | 2020 | mutex_enter(&sc->sc_lock); |
2021 | ehci_add_qh(sc, sqh, sc->sc_async_head); | | 2021 | ehci_add_qh(sc, sqh, sc->sc_async_head); |
2022 | mutex_exit(&sc->sc_lock); | | 2022 | mutex_exit(&sc->sc_lock); |
2023 | break; | | 2023 | break; |
2024 | case UE_BULK: | | 2024 | case UE_BULK: |
2025 | pipe->up_methods = &ehci_device_bulk_methods; | | 2025 | pipe->up_methods = &ehci_device_bulk_methods; |
2026 | mutex_enter(&sc->sc_lock); | | 2026 | mutex_enter(&sc->sc_lock); |
2027 | ehci_add_qh(sc, sqh, sc->sc_async_head); | | 2027 | ehci_add_qh(sc, sqh, sc->sc_async_head); |
2028 | mutex_exit(&sc->sc_lock); | | 2028 | mutex_exit(&sc->sc_lock); |
2029 | break; | | 2029 | break; |
2030 | case UE_INTERRUPT: | | 2030 | case UE_INTERRUPT: |
2031 | pipe->up_methods = &ehci_device_intr_methods; | | 2031 | pipe->up_methods = &ehci_device_intr_methods; |
2032 | ival = pipe->up_interval; | | 2032 | ival = pipe->up_interval; |
2033 | if (ival == USBD_DEFAULT_INTERVAL) { | | 2033 | if (ival == USBD_DEFAULT_INTERVAL) { |
2034 | if (speed == EHCI_QH_SPEED_HIGH) { | | 2034 | if (speed == EHCI_QH_SPEED_HIGH) { |
2035 | if (ed->bInterval > 16) { | | 2035 | if (ed->bInterval > 16) { |
2036 | /* | | 2036 | /* |
2037 | * illegal with high-speed, but there | | 2037 | * illegal with high-speed, but there |
2038 | * were documentation bugs in the spec, | | 2038 | * were documentation bugs in the spec, |
2039 | * so be generous | | 2039 | * so be generous |
2040 | */ | | 2040 | */ |
2041 | ival = 256; | | 2041 | ival = 256; |
2042 | } else | | 2042 | } else |
2043 | ival = (1 << (ed->bInterval - 1)) / 8; | | 2043 | ival = (1 << (ed->bInterval - 1)) / 8; |
2044 | } else | | 2044 | } else |
2045 | ival = ed->bInterval; | | 2045 | ival = ed->bInterval; |
2046 | } | | 2046 | } |
2047 | err = ehci_device_setintr(sc, sqh, ival); | | 2047 | err = ehci_device_setintr(sc, sqh, ival); |
2048 | if (err) | | 2048 | if (err) |
2049 | goto bad; | | 2049 | goto bad; |
2050 | break; | | 2050 | break; |
2051 | case UE_ISOCHRONOUS: | | 2051 | case UE_ISOCHRONOUS: |
2052 | pipe->up_serialise = false; | | 2052 | pipe->up_serialise = false; |
2053 | if (speed == EHCI_QH_SPEED_HIGH) | | 2053 | if (speed == EHCI_QH_SPEED_HIGH) |
2054 | pipe->up_methods = &ehci_device_isoc_methods; | | 2054 | pipe->up_methods = &ehci_device_isoc_methods; |
2055 | else | | 2055 | else |
2056 | pipe->up_methods = &ehci_device_fs_isoc_methods; | | 2056 | pipe->up_methods = &ehci_device_fs_isoc_methods; |
2057 | if (ed->bInterval == 0 || ed->bInterval > 16) { | | 2057 | if (ed->bInterval == 0 || ed->bInterval > 16) { |
2058 | printf("ehci: opening pipe with invalid bInterval\n"); | | 2058 | printf("ehci: opening pipe with invalid bInterval\n"); |
2059 | err = USBD_INVAL; | | 2059 | err = USBD_INVAL; |
2060 | goto bad; | | 2060 | goto bad; |
2061 | } | | 2061 | } |
2062 | if (UGETW(ed->wMaxPacketSize) == 0) { | | 2062 | if (UGETW(ed->wMaxPacketSize) == 0) { |
2063 | printf("ehci: zero length endpoint open request\n"); | | 2063 | printf("ehci: zero length endpoint open request\n"); |
2064 | err = USBD_INVAL; | | 2064 | err = USBD_INVAL; |
2065 | goto bad; | | 2065 | goto bad; |
2066 | } | | 2066 | } |
2067 | epipe->isoc.next_frame = 0; | | 2067 | epipe->isoc.next_frame = 0; |
2068 | epipe->isoc.cur_xfers = 0; | | 2068 | epipe->isoc.cur_xfers = 0; |
2069 | break; | | 2069 | break; |
2070 | default: | | 2070 | default: |
2071 | DPRINTF("bad xfer type %jd", xfertype, 0, 0, 0); | | 2071 | DPRINTF("bad xfer type %jd", xfertype, 0, 0, 0); |
2072 | err = USBD_INVAL; | | 2072 | err = USBD_INVAL; |
2073 | goto bad; | | 2073 | goto bad; |
2074 | } | | 2074 | } |
2075 | return USBD_NORMAL_COMPLETION; | | 2075 | return USBD_NORMAL_COMPLETION; |
2076 | | | 2076 | |
2077 | bad: | | 2077 | bad: |
2078 | if (sqh != NULL) { | | 2078 | if (sqh != NULL) { |
2079 | mutex_enter(&sc->sc_lock); | | 2079 | mutex_enter(&sc->sc_lock); |
2080 | ehci_free_sqh(sc, sqh); | | 2080 | ehci_free_sqh(sc, sqh); |
2081 | mutex_exit(&sc->sc_lock); | | 2081 | mutex_exit(&sc->sc_lock); |
2082 | } | | 2082 | } |
2083 | return err; | | 2083 | return err; |
2084 | } | | 2084 | } |
2085 | | | 2085 | |
2086 | /* | | 2086 | /* |
2087 | * Add an ED to the schedule. Called with USB lock held. | | 2087 | * Add an ED to the schedule. Called with USB lock held. |
2088 | */ | | 2088 | */ |
2089 | Static void | | 2089 | Static void |
2090 | ehci_add_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head) | | 2090 | ehci_add_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head) |
2091 | { | | 2091 | { |
2092 | | | 2092 | |
2093 | KASSERT(mutex_owned(&sc->sc_lock)); | | 2093 | KASSERT(mutex_owned(&sc->sc_lock)); |
2094 | | | 2094 | |
2095 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 2095 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
2096 | | | 2096 | |
2097 | usb_syncmem(&head->dma, head->offs + offsetof(ehci_qh_t, qh_link), | | 2097 | usb_syncmem(&head->dma, head->offs + offsetof(ehci_qh_t, qh_link), |
2098 | sizeof(head->qh.qh_link), BUS_DMASYNC_POSTWRITE); | | 2098 | sizeof(head->qh.qh_link), BUS_DMASYNC_POSTWRITE); |
2099 | | | 2099 | |
2100 | sqh->next = head->next; | | 2100 | sqh->next = head->next; |
2101 | sqh->qh.qh_link = head->qh.qh_link; | | 2101 | sqh->qh.qh_link = head->qh.qh_link; |
2102 | | | 2102 | |
2103 | usb_syncmem(&sqh->dma, sqh->offs + offsetof(ehci_qh_t, qh_link), | | 2103 | usb_syncmem(&sqh->dma, sqh->offs + offsetof(ehci_qh_t, qh_link), |
2104 | sizeof(sqh->qh.qh_link), BUS_DMASYNC_PREWRITE); | | 2104 | sizeof(sqh->qh.qh_link), BUS_DMASYNC_PREWRITE); |
2105 | | | 2105 | |
2106 | head->next = sqh; | | 2106 | head->next = sqh; |
2107 | head->qh.qh_link = htole32(sqh->physaddr | EHCI_LINK_QH); | | 2107 | head->qh.qh_link = htole32(sqh->physaddr | EHCI_LINK_QH); |
2108 | | | 2108 | |
2109 | usb_syncmem(&head->dma, head->offs + offsetof(ehci_qh_t, qh_link), | | 2109 | usb_syncmem(&head->dma, head->offs + offsetof(ehci_qh_t, qh_link), |
2110 | sizeof(head->qh.qh_link), BUS_DMASYNC_PREWRITE); | | 2110 | sizeof(head->qh.qh_link), BUS_DMASYNC_PREWRITE); |
2111 | | | 2111 | |
2112 | #ifdef EHCI_DEBUG | | 2112 | #ifdef EHCI_DEBUG |
2113 | DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); | | 2113 | DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); |
2114 | ehci_dump_sqh(sqh); | | 2114 | ehci_dump_sqh(sqh); |
2115 | DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); | | 2115 | DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); |
2116 | #endif | | 2116 | #endif |
2117 | } | | 2117 | } |
2118 | | | 2118 | |
2119 | /* | | 2119 | /* |
2120 | * Remove an ED from the schedule. Called with USB lock held. | | 2120 | * Remove an ED from the schedule. Called with USB lock held. |
2121 | */ | | 2121 | */ |
2122 | Static void | | 2122 | Static void |
2123 | ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head) | | 2123 | ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head) |
2124 | { | | 2124 | { |
2125 | ehci_soft_qh_t *p; | | 2125 | ehci_soft_qh_t *p; |
2126 | | | 2126 | |
2127 | KASSERT(mutex_owned(&sc->sc_lock)); | | 2127 | KASSERT(mutex_owned(&sc->sc_lock)); |
2128 | | | 2128 | |
2129 | /* XXX */ | | 2129 | /* XXX */ |
2130 | for (p = head; p != NULL && p->next != sqh; p = p->next) | | 2130 | for (p = head; p != NULL && p->next != sqh; p = p->next) |
2131 | ; | | 2131 | ; |
2132 | if (p == NULL) | | 2132 | if (p == NULL) |
2133 | panic("ehci_rem_qh: ED not found"); | | 2133 | panic("ehci_rem_qh: ED not found"); |
2134 | usb_syncmem(&sqh->dma, sqh->offs + offsetof(ehci_qh_t, qh_link), | | 2134 | usb_syncmem(&sqh->dma, sqh->offs + offsetof(ehci_qh_t, qh_link), |
2135 | sizeof(sqh->qh.qh_link), BUS_DMASYNC_POSTWRITE); | | 2135 | sizeof(sqh->qh.qh_link), BUS_DMASYNC_POSTWRITE); |
2136 | p->next = sqh->next; | | 2136 | p->next = sqh->next; |
2137 | p->qh.qh_link = sqh->qh.qh_link; | | 2137 | p->qh.qh_link = sqh->qh.qh_link; |
2138 | usb_syncmem(&p->dma, p->offs + offsetof(ehci_qh_t, qh_link), | | 2138 | usb_syncmem(&p->dma, p->offs + offsetof(ehci_qh_t, qh_link), |
2139 | sizeof(p->qh.qh_link), BUS_DMASYNC_PREWRITE); | | 2139 | sizeof(p->qh.qh_link), BUS_DMASYNC_PREWRITE); |
2140 | | | 2140 | |
2141 | ehci_sync_hc(sc); | | 2141 | ehci_sync_hc(sc); |
2142 | } | | 2142 | } |
2143 | | | 2143 | |
2144 | Static void | | 2144 | Static void |
2145 | ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) | | 2145 | ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) |
2146 | { | | 2146 | { |
2147 | int i; | | 2147 | int i; |
2148 | uint32_t status; | | 2148 | uint32_t status; |
2149 | | | 2149 | |
2150 | /* Save toggle bit and ping status. */ | | 2150 | /* Save toggle bit and ping status. */ |
2151 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), | | 2151 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), |
2152 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 2152 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
2153 | status = sqh->qh.qh_qtd.qtd_status & | | 2153 | status = sqh->qh.qh_qtd.qtd_status & |
2154 | htole32(EHCI_QTD_TOGGLE_MASK | | | 2154 | htole32(EHCI_QTD_TOGGLE_MASK | |
2155 | EHCI_QTD_SET_STATUS(EHCI_QTD_PINGSTATE)); | | 2155 | EHCI_QTD_SET_STATUS(EHCI_QTD_PINGSTATE)); |
2156 | /* Set HALTED to make hw leave it alone. */ | | 2156 | /* Set HALTED to make hw leave it alone. */ |
2157 | sqh->qh.qh_qtd.qtd_status = | | 2157 | sqh->qh.qh_qtd.qtd_status = |
2158 | htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED)); | | 2158 | htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED)); |
2159 | usb_syncmem(&sqh->dma, | | 2159 | usb_syncmem(&sqh->dma, |
2160 | sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), | | 2160 | sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), |
2161 | sizeof(sqh->qh.qh_qtd.qtd_status), | | 2161 | sizeof(sqh->qh.qh_qtd.qtd_status), |
2162 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 2162 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
2163 | sqh->qh.qh_curqtd = 0; | | 2163 | sqh->qh.qh_curqtd = 0; |
2164 | sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr); | | 2164 | sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr); |
2165 | sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; | | 2165 | sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; |
2166 | for (i = 0; i < EHCI_QTD_NBUFFERS; i++) | | 2166 | for (i = 0; i < EHCI_QTD_NBUFFERS; i++) |
2167 | sqh->qh.qh_qtd.qtd_buffer[i] = 0; | | 2167 | sqh->qh.qh_qtd.qtd_buffer[i] = 0; |
2168 | sqh->sqtd = sqtd; | | 2168 | sqh->sqtd = sqtd; |
2169 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), | | 2169 | usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), |
2170 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 2170 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
2171 | /* Set !HALTED && !ACTIVE to start execution, preserve some fields */ | | 2171 | /* Set !HALTED && !ACTIVE to start execution, preserve some fields */ |
2172 | sqh->qh.qh_qtd.qtd_status = status; | | 2172 | sqh->qh.qh_qtd.qtd_status = status; |
2173 | usb_syncmem(&sqh->dma, | | 2173 | usb_syncmem(&sqh->dma, |
2174 | sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), | | 2174 | sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), |
2175 | sizeof(sqh->qh.qh_qtd.qtd_status), | | 2175 | sizeof(sqh->qh.qh_qtd.qtd_status), |
2176 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 2176 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
2177 | } | | 2177 | } |
2178 | | | 2178 | |
2179 | /* | | 2179 | /* |
2180 | * Ensure that the HC has released all references to the QH. We do this | | 2180 | * Ensure that the HC has released all references to the QH. We do this |
2181 | * by asking for a Async Advance Doorbell interrupt and then we wait for | | 2181 | * by asking for a Async Advance Doorbell interrupt and then we wait for |
2182 | * the interrupt. | | 2182 | * the interrupt. |
2183 | * To make this easier we first obtain exclusive use of the doorbell. | | 2183 | * To make this easier we first obtain exclusive use of the doorbell. |
2184 | */ | | 2184 | */ |
2185 | Static void | | 2185 | Static void |
2186 | ehci_sync_hc(ehci_softc_t *sc) | | 2186 | ehci_sync_hc(ehci_softc_t *sc) |
2187 | { | | 2187 | { |
2188 | int error __diagused; | | 2188 | int error __diagused; |
2189 | | | 2189 | |
2190 | KASSERT(mutex_owned(&sc->sc_lock)); | | 2190 | KASSERT(mutex_owned(&sc->sc_lock)); |
2191 | | | 2191 | |
2192 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 2192 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
2193 | | | 2193 | |
2194 | if (sc->sc_dying) { | | 2194 | if (sc->sc_dying) { |
2195 | DPRINTF("dying", 0, 0, 0, 0); | | 2195 | DPRINTF("dying", 0, 0, 0, 0); |
2196 | return; | | 2196 | return; |
2197 | } | | 2197 | } |
2198 | | | 2198 | |
2199 | /* ask for doorbell */ | | 2199 | /* ask for doorbell */ |
2200 | EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); | | 2200 | EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); |
2201 | DPRINTF("cmd = 0x%08jx sts = 0x%08jx", | | 2201 | DPRINTF("cmd = 0x%08jx sts = 0x%08jx", |
2202 | EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0); | | 2202 | EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0); |
2203 | | | 2203 | |
2204 | error = cv_timedwait(&sc->sc_doorbell, &sc->sc_lock, hz); /* bell wait */ | | 2204 | error = cv_timedwait(&sc->sc_doorbell, &sc->sc_lock, hz); /* bell wait */ |
2205 | | | 2205 | |
2206 | DPRINTF("cmd = 0x%08jx sts = 0x%08jx ... done", | | 2206 | DPRINTF("cmd = 0x%08jx sts = 0x%08jx ... done", |
2207 | EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0); | | 2207 | EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0); |
2208 | #ifdef DIAGNOSTIC | | 2208 | #ifdef DIAGNOSTIC |
2209 | if (error == EWOULDBLOCK) { | | 2209 | if (error == EWOULDBLOCK) { |
2210 | printf("ehci_sync_hc: timed out\n"); | | 2210 | printf("ehci_sync_hc: timed out\n"); |
2211 | } else if (error) { | | 2211 | } else if (error) { |
2212 | printf("ehci_sync_hc: cv_timedwait: error %d\n", error); | | 2212 | printf("ehci_sync_hc: cv_timedwait: error %d\n", error); |
2213 | } | | 2213 | } |
2214 | #endif | | 2214 | #endif |
2215 | } | | 2215 | } |
2216 | | | 2216 | |
2217 | Static void | | 2217 | Static void |
2218 | ehci_remove_itd_chain(ehci_softc_t *sc, struct ehci_soft_itd *itd) | | 2218 | ehci_remove_itd_chain(ehci_softc_t *sc, struct ehci_soft_itd *itd) |
2219 | { | | 2219 | { |
2220 | | | 2220 | |
2221 | KASSERT(mutex_owned(&sc->sc_lock)); | | 2221 | KASSERT(mutex_owned(&sc->sc_lock)); |
2222 | | | 2222 | |
2223 | for (; itd != NULL; itd = itd->xfer_next) { | | 2223 | for (; itd != NULL; itd = itd->xfer_next) { |
2224 | struct ehci_soft_itd *prev = itd->frame_list.prev; | | 2224 | struct ehci_soft_itd *prev = itd->frame_list.prev; |
2225 | | | 2225 | |
2226 | /* Unlink itd from hardware chain, or frame array */ | | 2226 | /* Unlink itd from hardware chain, or frame array */ |
2227 | if (prev == NULL) { /* We're at the table head */ | | 2227 | if (prev == NULL) { /* We're at the table head */ |
2228 | sc->sc_softitds[itd->slot] = itd->frame_list.next; | | 2228 | sc->sc_softitds[itd->slot] = itd->frame_list.next; |
2229 | sc->sc_flist[itd->slot] = itd->itd.itd_next; | | 2229 | sc->sc_flist[itd->slot] = itd->itd.itd_next; |
2230 | usb_syncmem(&sc->sc_fldma, | | 2230 | usb_syncmem(&sc->sc_fldma, |
2231 | sizeof(ehci_link_t) * itd->slot, | | 2231 | sizeof(ehci_link_t) * itd->slot, |
2232 | sizeof(ehci_link_t), | | 2232 | sizeof(ehci_link_t), |
2233 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 2233 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
2234 | | | 2234 | |
2235 | if (itd->frame_list.next != NULL) | | 2235 | if (itd->frame_list.next != NULL) |
2236 | itd->frame_list.next->frame_list.prev = NULL; | | 2236 | itd->frame_list.next->frame_list.prev = NULL; |
2237 | } else { | | 2237 | } else { |
2238 | /* XXX this part is untested... */ | | 2238 | /* XXX this part is untested... */ |
2239 | prev->itd.itd_next = itd->itd.itd_next; | | 2239 | prev->itd.itd_next = itd->itd.itd_next; |
2240 | usb_syncmem(&itd->dma, | | 2240 | usb_syncmem(&itd->dma, |
2241 | itd->offs + offsetof(ehci_itd_t, itd_next), | | 2241 | itd->offs + offsetof(ehci_itd_t, itd_next), |
2242 | sizeof(itd->itd.itd_next), BUS_DMASYNC_PREWRITE); | | 2242 | sizeof(itd->itd.itd_next), BUS_DMASYNC_PREWRITE); |
2243 | | | 2243 | |
2244 | prev->frame_list.next = itd->frame_list.next; | | 2244 | prev->frame_list.next = itd->frame_list.next; |
2245 | if (itd->frame_list.next != NULL) | | 2245 | if (itd->frame_list.next != NULL) |
2246 | itd->frame_list.next->frame_list.prev = prev; | | 2246 | itd->frame_list.next->frame_list.prev = prev; |
2247 | } | | 2247 | } |
2248 | } | | 2248 | } |
2249 | } | | 2249 | } |
2250 | | | 2250 | |
2251 | Static void | | 2251 | Static void |
2252 | ehci_free_itd_chain(ehci_softc_t *sc, struct ehci_soft_itd *itd) | | 2252 | ehci_free_itd_chain(ehci_softc_t *sc, struct ehci_soft_itd *itd) |
2253 | { | | 2253 | { |
2254 | struct ehci_soft_itd *next; | | 2254 | struct ehci_soft_itd *next; |
2255 | | | 2255 | |
2256 | mutex_enter(&sc->sc_lock); | | 2256 | mutex_enter(&sc->sc_lock); |
2257 | next = NULL; | | 2257 | next = NULL; |
2258 | for (; itd != NULL; itd = next) { | | 2258 | for (; itd != NULL; itd = next) { |
2259 | next = itd->xfer_next; | | 2259 | next = itd->xfer_next; |
2260 | ehci_free_itd_locked(sc, itd); | | 2260 | ehci_free_itd_locked(sc, itd); |
2261 | } | | 2261 | } |
2262 | mutex_exit(&sc->sc_lock); | | 2262 | mutex_exit(&sc->sc_lock); |
2263 | } | | 2263 | } |
2264 | | | 2264 | |
2265 | Static void | | 2265 | Static void |
2266 | ehci_remove_sitd_chain(ehci_softc_t *sc, struct ehci_soft_sitd *sitd) | | 2266 | ehci_remove_sitd_chain(ehci_softc_t *sc, struct ehci_soft_sitd *sitd) |
2267 | { | | 2267 | { |
2268 | | | 2268 | |
2269 | KASSERT(mutex_owned(&sc->sc_lock)); | | 2269 | KASSERT(mutex_owned(&sc->sc_lock)); |
2270 | | | 2270 | |
2271 | for (; sitd != NULL; sitd = sitd->xfer_next) { | | 2271 | for (; sitd != NULL; sitd = sitd->xfer_next) { |
2272 | struct ehci_soft_sitd *prev = sitd->frame_list.prev; | | 2272 | struct ehci_soft_sitd *prev = sitd->frame_list.prev; |
2273 | | | 2273 | |
2274 | /* Unlink sitd from hardware chain, or frame array */ | | 2274 | /* Unlink sitd from hardware chain, or frame array */ |
2275 | if (prev == NULL) { /* We're at the table head */ | | 2275 | if (prev == NULL) { /* We're at the table head */ |
2276 | sc->sc_softsitds[sitd->slot] = sitd->frame_list.next; | | 2276 | sc->sc_softsitds[sitd->slot] = sitd->frame_list.next; |
2277 | sc->sc_flist[sitd->slot] = sitd->sitd.sitd_next; | | 2277 | sc->sc_flist[sitd->slot] = sitd->sitd.sitd_next; |
2278 | usb_syncmem(&sc->sc_fldma, | | 2278 | usb_syncmem(&sc->sc_fldma, |
2279 | sizeof(ehci_link_t) * sitd->slot, | | 2279 | sizeof(ehci_link_t) * sitd->slot, |
2280 | sizeof(ehci_link_t), | | 2280 | sizeof(ehci_link_t), |
2281 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 2281 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
2282 | | | 2282 | |
2283 | if (sitd->frame_list.next != NULL) | | 2283 | if (sitd->frame_list.next != NULL) |
2284 | sitd->frame_list.next->frame_list.prev = NULL; | | 2284 | sitd->frame_list.next->frame_list.prev = NULL; |
2285 | } else { | | 2285 | } else { |
2286 | /* XXX this part is untested... */ | | 2286 | /* XXX this part is untested... */ |
2287 | prev->sitd.sitd_next = sitd->sitd.sitd_next; | | 2287 | prev->sitd.sitd_next = sitd->sitd.sitd_next; |
2288 | usb_syncmem(&sitd->dma, | | 2288 | usb_syncmem(&sitd->dma, |
2289 | sitd->offs + offsetof(ehci_sitd_t, sitd_next), | | 2289 | sitd->offs + offsetof(ehci_sitd_t, sitd_next), |
2290 | sizeof(sitd->sitd.sitd_next), BUS_DMASYNC_PREWRITE); | | 2290 | sizeof(sitd->sitd.sitd_next), BUS_DMASYNC_PREWRITE); |
2291 | | | 2291 | |
2292 | prev->frame_list.next = sitd->frame_list.next; | | 2292 | prev->frame_list.next = sitd->frame_list.next; |
2293 | if (sitd->frame_list.next != NULL) | | 2293 | if (sitd->frame_list.next != NULL) |
2294 | sitd->frame_list.next->frame_list.prev = prev; | | 2294 | sitd->frame_list.next->frame_list.prev = prev; |
2295 | } | | 2295 | } |
2296 | } | | 2296 | } |
2297 | } | | 2297 | } |
2298 | | | 2298 | |
2299 | Static void | | 2299 | Static void |
2300 | ehci_free_sitd_chain(ehci_softc_t *sc, struct ehci_soft_sitd *sitd) | | 2300 | ehci_free_sitd_chain(ehci_softc_t *sc, struct ehci_soft_sitd *sitd) |
2301 | { | | 2301 | { |
2302 | | | 2302 | |
2303 | mutex_enter(&sc->sc_lock); | | 2303 | mutex_enter(&sc->sc_lock); |
2304 | struct ehci_soft_sitd *next = NULL; | | 2304 | struct ehci_soft_sitd *next = NULL; |
2305 | for (; sitd != NULL; sitd = next) { | | 2305 | for (; sitd != NULL; sitd = next) { |
2306 | next = sitd->xfer_next; | | 2306 | next = sitd->xfer_next; |
2307 | ehci_free_sitd_locked(sc, sitd); | | 2307 | ehci_free_sitd_locked(sc, sitd); |
2308 | } | | 2308 | } |
2309 | mutex_exit(&sc->sc_lock); | | 2309 | mutex_exit(&sc->sc_lock); |
2310 | } | | 2310 | } |
2311 | | | 2311 | |
2312 | /***********/ | | 2312 | /***********/ |
2313 | | | 2313 | |
2314 | Static int | | 2314 | Static int |
2315 | ehci_roothub_ctrl(struct usbd_bus *bus, usb_device_request_t *req, | | 2315 | ehci_roothub_ctrl(struct usbd_bus *bus, usb_device_request_t *req, |
2316 | void *buf, int buflen) | | 2316 | void *buf, int buflen) |
2317 | { | | 2317 | { |
2318 | ehci_softc_t *sc = EHCI_BUS2SC(bus); | | 2318 | ehci_softc_t *sc = EHCI_BUS2SC(bus); |
2319 | usb_hub_descriptor_t hubd; | | 2319 | usb_hub_descriptor_t hubd; |
2320 | usb_port_status_t ps; | | 2320 | usb_port_status_t ps; |
2321 | uint16_t len, value, index; | | 2321 | uint16_t len, value, index; |
2322 | int l, totlen = 0; | | 2322 | int l, totlen = 0; |
2323 | int port, i; | | 2323 | int port, i; |
2324 | uint32_t v; | | 2324 | uint32_t v; |
2325 | | | 2325 | |
2326 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 2326 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
2327 | | | 2327 | |
2328 | if (sc->sc_dying) | | 2328 | if (sc->sc_dying) |
2329 | return -1; | | 2329 | return -1; |
2330 | | | 2330 | |
2331 | DPRINTF("type=0x%02jx request=%02jx", req->bmRequestType, req->bRequest, | | 2331 | DPRINTF("type=0x%02jx request=%02jx", req->bmRequestType, req->bRequest, |
2332 | 0, 0); | | 2332 | 0, 0); |
2333 | | | 2333 | |
2334 | len = UGETW(req->wLength); | | 2334 | len = UGETW(req->wLength); |
2335 | value = UGETW(req->wValue); | | 2335 | value = UGETW(req->wValue); |
2336 | index = UGETW(req->wIndex); | | 2336 | index = UGETW(req->wIndex); |
2337 | | | 2337 | |
2338 | #define C(x,y) ((x) | ((y) << 8)) | | 2338 | #define C(x,y) ((x) | ((y) << 8)) |
2339 | switch (C(req->bRequest, req->bmRequestType)) { | | 2339 | switch (C(req->bRequest, req->bmRequestType)) { |
2340 | case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): | | 2340 | case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): |
2341 | if (len == 0) | | 2341 | if (len == 0) |
2342 | break; | | 2342 | break; |
2343 | switch (value) { | | 2343 | switch (value) { |
2344 | #define sd ((usb_string_descriptor_t *)buf) | | 2344 | #define sd ((usb_string_descriptor_t *)buf) |
2345 | case C(2, UDESC_STRING): | | 2345 | case C(2, UDESC_STRING): |
2346 | /* Product */ | | 2346 | /* Product */ |
2347 | totlen = usb_makestrdesc(sd, len, "EHCI root hub"); | | 2347 | totlen = usb_makestrdesc(sd, len, "EHCI root hub"); |
2348 | break; | | 2348 | break; |
2349 | #undef sd | | 2349 | #undef sd |
2350 | default: | | 2350 | default: |
2351 | /* default from usbroothub */ | | 2351 | /* default from usbroothub */ |
2352 | return buflen; | | 2352 | return buflen; |
2353 | } | | 2353 | } |
2354 | break; | | 2354 | break; |
2355 | | | 2355 | |
2356 | /* Hub requests */ | | 2356 | /* Hub requests */ |
2357 | case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): | | 2357 | case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): |
2358 | break; | | 2358 | break; |
2359 | case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): | | 2359 | case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): |
2360 | DPRINTF("UR_CLEAR_PORT_FEATURE port=%jd feature=%jd", index, | | 2360 | DPRINTF("UR_CLEAR_PORT_FEATURE port=%jd feature=%jd", index, |
2361 | value, 0, 0); | | 2361 | value, 0, 0); |
2362 | if (index < 1 || index > sc->sc_noport) { | | 2362 | if (index < 1 || index > sc->sc_noport) { |
2363 | return -1; | | 2363 | return -1; |
2364 | } | | 2364 | } |
2365 | port = EHCI_PORTSC(index); | | 2365 | port = EHCI_PORTSC(index); |
2366 | v = EOREAD4(sc, port); | | 2366 | v = EOREAD4(sc, port); |
2367 | DPRINTF("portsc=0x%08jx", v, 0, 0, 0); | | 2367 | DPRINTF("portsc=0x%08jx", v, 0, 0, 0); |
2368 | v &= ~EHCI_PS_CLEAR; | | 2368 | v &= ~EHCI_PS_CLEAR; |
2369 | switch (value) { | | 2369 | switch (value) { |
2370 | case UHF_PORT_ENABLE: | | 2370 | case UHF_PORT_ENABLE: |
2371 | EOWRITE4(sc, port, v &~ EHCI_PS_PE); | | 2371 | EOWRITE4(sc, port, v &~ EHCI_PS_PE); |
2372 | break; | | 2372 | break; |
2373 | case UHF_PORT_SUSPEND: | | 2373 | case UHF_PORT_SUSPEND: |
2374 | if (!(v & EHCI_PS_SUSP)) /* not suspended */ | | 2374 | if (!(v & EHCI_PS_SUSP)) /* not suspended */ |
2375 | break; | | 2375 | break; |
2376 | v &= ~EHCI_PS_SUSP; | | 2376 | v &= ~EHCI_PS_SUSP; |
2377 | EOWRITE4(sc, port, v | EHCI_PS_FPR); | | 2377 | EOWRITE4(sc, port, v | EHCI_PS_FPR); |
2378 | /* see USB2 spec ch. 7.1.7.7 */ | | 2378 | /* see USB2 spec ch. 7.1.7.7 */ |
2379 | usb_delay_ms(&sc->sc_bus, 20); | | 2379 | usb_delay_ms(&sc->sc_bus, 20); |
2380 | EOWRITE4(sc, port, v); | | 2380 | EOWRITE4(sc, port, v); |
2381 | usb_delay_ms(&sc->sc_bus, 2); | | 2381 | usb_delay_ms(&sc->sc_bus, 2); |
2382 | #ifdef DEBUG | | 2382 | #ifdef DEBUG |
2383 | v = EOREAD4(sc, port); | | 2383 | v = EOREAD4(sc, port); |
2384 | if (v & (EHCI_PS_FPR | EHCI_PS_SUSP)) | | 2384 | if (v & (EHCI_PS_FPR | EHCI_PS_SUSP)) |
2385 | printf("ehci: resume failed: %x\n", v); | | 2385 | printf("ehci: resume failed: %x\n", v); |
2386 | #endif | | 2386 | #endif |
2387 | break; | | 2387 | break; |
2388 | case UHF_PORT_POWER: | | 2388 | case UHF_PORT_POWER: |
2389 | if (sc->sc_hasppc) | | 2389 | if (sc->sc_hasppc) |
2390 | EOWRITE4(sc, port, v &~ EHCI_PS_PP); | | 2390 | EOWRITE4(sc, port, v &~ EHCI_PS_PP); |
2391 | break; | | 2391 | break; |
2392 | case UHF_PORT_TEST: | | 2392 | case UHF_PORT_TEST: |
2393 | DPRINTF("clear port test %jd", index, 0, 0, 0); | | 2393 | DPRINTF("clear port test %jd", index, 0, 0, 0); |
2394 | break; | | 2394 | break; |
2395 | case UHF_PORT_INDICATOR: | | 2395 | case UHF_PORT_INDICATOR: |
2396 | DPRINTF("clear port ind %jd", index, 0, 0, 0); | | 2396 | DPRINTF("clear port ind %jd", index, 0, 0, 0); |
2397 | EOWRITE4(sc, port, v &~ EHCI_PS_PIC); | | 2397 | EOWRITE4(sc, port, v &~ EHCI_PS_PIC); |
2398 | break; | | 2398 | break; |
2399 | case UHF_C_PORT_CONNECTION: | | 2399 | case UHF_C_PORT_CONNECTION: |
2400 | EOWRITE4(sc, port, v | EHCI_PS_CSC); | | 2400 | EOWRITE4(sc, port, v | EHCI_PS_CSC); |
2401 | break; | | 2401 | break; |
2402 | case UHF_C_PORT_ENABLE: | | 2402 | case UHF_C_PORT_ENABLE: |
2403 | EOWRITE4(sc, port, v | EHCI_PS_PEC); | | 2403 | EOWRITE4(sc, port, v | EHCI_PS_PEC); |
2404 | break; | | 2404 | break; |
2405 | case UHF_C_PORT_SUSPEND: | | 2405 | case UHF_C_PORT_SUSPEND: |
2406 | /* how? */ | | 2406 | /* how? */ |
2407 | break; | | 2407 | break; |
2408 | case UHF_C_PORT_OVER_CURRENT: | | 2408 | case UHF_C_PORT_OVER_CURRENT: |
2409 | EOWRITE4(sc, port, v | EHCI_PS_OCC); | | 2409 | EOWRITE4(sc, port, v | EHCI_PS_OCC); |
2410 | break; | | 2410 | break; |
2411 | case UHF_C_PORT_RESET: | | 2411 | case UHF_C_PORT_RESET: |
2412 | sc->sc_isreset[index] = 0; | | 2412 | sc->sc_isreset[index] = 0; |
2413 | break; | | 2413 | break; |
2414 | default: | | 2414 | default: |
2415 | return -1; | | 2415 | return -1; |
2416 | } | | 2416 | } |
2417 | #if 0 | | 2417 | #if 0 |
2418 | switch(value) { | | 2418 | switch(value) { |
2419 | case UHF_C_PORT_CONNECTION: | | 2419 | case UHF_C_PORT_CONNECTION: |
2420 | case UHF_C_PORT_ENABLE: | | 2420 | case UHF_C_PORT_ENABLE: |
2421 | case UHF_C_PORT_SUSPEND: | | 2421 | case UHF_C_PORT_SUSPEND: |
2422 | case UHF_C_PORT_OVER_CURRENT: | | 2422 | case UHF_C_PORT_OVER_CURRENT: |
2423 | case UHF_C_PORT_RESET: | | 2423 | case UHF_C_PORT_RESET: |
2424 | default: | | 2424 | default: |
2425 | break; | | 2425 | break; |
2426 | } | | 2426 | } |
2427 | #endif | | 2427 | #endif |
2428 | break; | | 2428 | break; |
2429 | case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): | | 2429 | case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): |
2430 | if (len == 0) | | 2430 | if (len == 0) |
2431 | break; | | 2431 | break; |
2432 | if ((value & 0xff) != 0) { | | 2432 | if ((value & 0xff) != 0) { |
2433 | return -1; | | 2433 | return -1; |
2434 | } | | 2434 | } |
2435 | totlen = uimin(buflen, sizeof(hubd)); | | 2435 | totlen = uimin(buflen, sizeof(hubd)); |
2436 | memcpy(&hubd, buf, totlen); | | 2436 | memcpy(&hubd, buf, totlen); |
2437 | hubd.bNbrPorts = sc->sc_noport; | | 2437 | hubd.bNbrPorts = sc->sc_noport; |
2438 | v = EOREAD4(sc, EHCI_HCSPARAMS); | | 2438 | v = EOREAD4(sc, EHCI_HCSPARAMS); |
2439 | USETW(hubd.wHubCharacteristics, | | 2439 | USETW(hubd.wHubCharacteristics, |
2440 | EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH | | | 2440 | EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH | |
2441 | EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS)) | | 2441 | EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS)) |
2442 | ? UHD_PORT_IND : 0); | | 2442 | ? UHD_PORT_IND : 0); |
2443 | hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ | | 2443 | hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ |
2444 | for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) | | 2444 | for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) |
2445 | hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */ | | 2445 | hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */ |
2446 | hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i; | | 2446 | hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i; |
2447 | totlen = uimin(totlen, hubd.bDescLength); | | 2447 | totlen = uimin(totlen, hubd.bDescLength); |
2448 | memcpy(buf, &hubd, totlen); | | 2448 | memcpy(buf, &hubd, totlen); |
2449 | break; | | 2449 | break; |
2450 | case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): | | 2450 | case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): |
2451 | if (len != 4) { | | 2451 | if (len != 4) { |
2452 | return -1; | | 2452 | return -1; |
2453 | } | | 2453 | } |
2454 | memset(buf, 0, len); /* ? XXX */ | | 2454 | memset(buf, 0, len); /* ? XXX */ |
2455 | totlen = len; | | 2455 | totlen = len; |
2456 | break; | | 2456 | break; |
2457 | case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): | | 2457 | case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): |
2458 | DPRINTF("get port status i=%jd", index, 0, 0, 0); | | 2458 | DPRINTF("get port status i=%jd", index, 0, 0, 0); |
2459 | if (index < 1 || index > sc->sc_noport) { | | 2459 | if (index < 1 || index > sc->sc_noport) { |
2460 | return -1; | | 2460 | return -1; |
2461 | } | | 2461 | } |
2462 | if (len != 4) { | | 2462 | if (len != 4) { |
2463 | return -1; | | 2463 | return -1; |
2464 | } | | 2464 | } |
2465 | v = EOREAD4(sc, EHCI_PORTSC(index)); | | 2465 | v = EOREAD4(sc, EHCI_PORTSC(index)); |
2466 | DPRINTF("port status=0x%04jx", v, 0, 0, 0); | | 2466 | DPRINTF("port status=0x%04jx", v, 0, 0, 0); |
2467 | | | 2467 | |
2468 | i = UPS_HIGH_SPEED; | | 2468 | i = UPS_HIGH_SPEED; |
2469 | if (sc->sc_flags & EHCIF_ETTF) { | | 2469 | if (sc->sc_flags & EHCIF_ETTF) { |
2470 | /* | | 2470 | /* |
2471 | * If we are doing embedded transaction translation, | | 2471 | * If we are doing embedded transaction translation, |
2472 | * then directly attached LS/FS devices are reset by | | 2472 | * then directly attached LS/FS devices are reset by |
2473 | * the EHCI controller itself. PSPD is encoded | | 2473 | * the EHCI controller itself. PSPD is encoded |
2474 | * the same way as in USBSTATUS. | | 2474 | * the same way as in USBSTATUS. |
2475 | */ | | 2475 | */ |
2476 | i = __SHIFTOUT(v, EHCI_PS_PSPD) * UPS_LOW_SPEED; | | 2476 | i = __SHIFTOUT(v, EHCI_PS_PSPD) * UPS_LOW_SPEED; |
2477 | } | | 2477 | } |
2478 | if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; | | 2478 | if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; |
2479 | if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED; | | 2479 | if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED; |
2480 | if (v & EHCI_PS_SUSP) i |= UPS_SUSPEND; | | 2480 | if (v & EHCI_PS_SUSP) i |= UPS_SUSPEND; |
2481 | if (v & EHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; | | 2481 | if (v & EHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; |
2482 | if (v & EHCI_PS_PR) i |= UPS_RESET; | | 2482 | if (v & EHCI_PS_PR) i |= UPS_RESET; |
2483 | if (v & EHCI_PS_PP) i |= UPS_PORT_POWER; | | 2483 | if (v & EHCI_PS_PP) i |= UPS_PORT_POWER; |
2484 | if (sc->sc_vendor_port_status) | | 2484 | if (sc->sc_vendor_port_status) |
2485 | i = sc->sc_vendor_port_status(sc, v, i); | | 2485 | i = sc->sc_vendor_port_status(sc, v, i); |
2486 | USETW(ps.wPortStatus, i); | | 2486 | USETW(ps.wPortStatus, i); |
2487 | i = 0; | | 2487 | i = 0; |
2488 | if (v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; | | 2488 | if (v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; |
2489 | if (v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; | | 2489 | if (v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; |
2490 | if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; | | 2490 | if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; |
2491 | if (sc->sc_isreset[index]) i |= UPS_C_PORT_RESET; | | 2491 | if (sc->sc_isreset[index]) i |= UPS_C_PORT_RESET; |
2492 | USETW(ps.wPortChange, i); | | 2492 | USETW(ps.wPortChange, i); |
2493 | totlen = uimin(len, sizeof(ps)); | | 2493 | totlen = uimin(len, sizeof(ps)); |
2494 | memcpy(buf, &ps, totlen); | | 2494 | memcpy(buf, &ps, totlen); |
2495 | break; | | 2495 | break; |
2496 | case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): | | 2496 | case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): |
2497 | return -1; | | 2497 | return -1; |
2498 | case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): | | 2498 | case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): |
2499 | break; | | 2499 | break; |
2500 | case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): | | 2500 | case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): |
2501 | if (index < 1 || index > sc->sc_noport) { | | 2501 | if (index < 1 || index > sc->sc_noport) { |
2502 | return -1; | | 2502 | return -1; |
2503 | } | | 2503 | } |
2504 | port = EHCI_PORTSC(index); | | 2504 | port = EHCI_PORTSC(index); |
2505 | v = EOREAD4(sc, port); | | 2505 | v = EOREAD4(sc, port); |
2506 | DPRINTF("portsc=0x%08jx", v, 0, 0, 0); | | 2506 | DPRINTF("portsc=0x%08jx", v, 0, 0, 0); |
2507 | v &= ~EHCI_PS_CLEAR; | | 2507 | v &= ~EHCI_PS_CLEAR; |
2508 | switch(value) { | | 2508 | switch(value) { |
2509 | case UHF_PORT_ENABLE: | | 2509 | case UHF_PORT_ENABLE: |
2510 | EOWRITE4(sc, port, v | EHCI_PS_PE); | | 2510 | EOWRITE4(sc, port, v | EHCI_PS_PE); |
2511 | break; | | 2511 | break; |
2512 | case UHF_PORT_SUSPEND: | | 2512 | case UHF_PORT_SUSPEND: |
2513 | EOWRITE4(sc, port, v | EHCI_PS_SUSP); | | 2513 | EOWRITE4(sc, port, v | EHCI_PS_SUSP); |
2514 | break; | | 2514 | break; |
2515 | case UHF_PORT_RESET: | | 2515 | case UHF_PORT_RESET: |
2516 | DPRINTF("reset port %jd", index, 0, 0, 0); | | 2516 | DPRINTF("reset port %jd", index, 0, 0, 0); |
2517 | if (EHCI_PS_IS_LOWSPEED(v) | | 2517 | if (EHCI_PS_IS_LOWSPEED(v) |
2518 | && sc->sc_ncomp > 0 | | 2518 | && sc->sc_ncomp > 0 |
2519 | && !(sc->sc_flags & EHCIF_ETTF)) { | | 2519 | && !(sc->sc_flags & EHCIF_ETTF)) { |
2520 | /* | | 2520 | /* |
2521 | * Low speed device on non-ETTF controller or | | 2521 | * Low speed device on non-ETTF controller or |
2522 | * unaccompanied controller, give up ownership. | | 2522 | * unaccompanied controller, give up ownership. |
2523 | */ | | 2523 | */ |
2524 | ehci_disown(sc, index, 1); | | 2524 | ehci_disown(sc, index, 1); |
2525 | break; | | 2525 | break; |
2526 | } | | 2526 | } |
2527 | /* Start reset sequence. */ | | 2527 | /* Start reset sequence. */ |
2528 | v &= ~ (EHCI_PS_PE | EHCI_PS_PR); | | 2528 | v &= ~ (EHCI_PS_PE | EHCI_PS_PR); |
2529 | EOWRITE4(sc, port, v | EHCI_PS_PR); | | 2529 | EOWRITE4(sc, port, v | EHCI_PS_PR); |
2530 | /* Wait for reset to complete. */ | | 2530 | /* Wait for reset to complete. */ |
2531 | usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY); | | 2531 | usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY); |
2532 | if (sc->sc_dying) { | | 2532 | if (sc->sc_dying) { |
2533 | return -1; | | 2533 | return -1; |
2534 | } | | 2534 | } |
2535 | /* | | 2535 | /* |
2536 | * An embedded transaction translator will automatically | | 2536 | * An embedded transaction translator will automatically |
2537 | * terminate the reset sequence so there's no need to | | 2537 | * terminate the reset sequence so there's no need to |
2538 | * it. | | 2538 | * it. |
2539 | */ | | 2539 | */ |
2540 | v = EOREAD4(sc, port); | | 2540 | v = EOREAD4(sc, port); |
2541 | if (v & EHCI_PS_PR) { | | 2541 | if (v & EHCI_PS_PR) { |
2542 | /* Terminate reset sequence. */ | | 2542 | /* Terminate reset sequence. */ |
2543 | EOWRITE4(sc, port, v & ~EHCI_PS_PR); | | 2543 | EOWRITE4(sc, port, v & ~EHCI_PS_PR); |
2544 | /* Wait for HC to complete reset. */ | | 2544 | /* Wait for HC to complete reset. */ |
2545 | usb_delay_ms(&sc->sc_bus, | | 2545 | usb_delay_ms(&sc->sc_bus, |
2546 | EHCI_PORT_RESET_COMPLETE); | | 2546 | EHCI_PORT_RESET_COMPLETE); |
2547 | if (sc->sc_dying) { | | 2547 | if (sc->sc_dying) { |
2548 | return -1; | | 2548 | return -1; |
2549 | } | | 2549 | } |
2550 | } | | 2550 | } |
2551 | | | 2551 | |
2552 | v = EOREAD4(sc, port); | | 2552 | v = EOREAD4(sc, port); |
2553 | DPRINTF("ehci after reset, status=0x%08jx", v, 0, 0, 0); | | 2553 | DPRINTF("ehci after reset, status=0x%08jx", v, 0, 0, 0); |
2554 | if (v & EHCI_PS_PR) { | | 2554 | if (v & EHCI_PS_PR) { |
2555 | printf("%s: port reset timeout\n", | | 2555 | printf("%s: port reset timeout\n", |
2556 | device_xname(sc->sc_dev)); | | 2556 | device_xname(sc->sc_dev)); |
2557 | return USBD_TIMEOUT; | | 2557 | return USBD_TIMEOUT; |
2558 | } | | 2558 | } |
2559 | if (!(v & EHCI_PS_PE)) { | | 2559 | if (!(v & EHCI_PS_PE)) { |
2560 | /* Not a high speed device, give up ownership.*/ | | 2560 | /* Not a high speed device, give up ownership.*/ |
2561 | ehci_disown(sc, index, 0); | | 2561 | ehci_disown(sc, index, 0); |
2562 | break; | | 2562 | break; |
2563 | } | | 2563 | } |
2564 | sc->sc_isreset[index] = 1; | | 2564 | sc->sc_isreset[index] = 1; |
2565 | DPRINTF("ehci port %jd reset, status = 0x%08jx", index, | | 2565 | DPRINTF("ehci port %jd reset, status = 0x%08jx", index, |
2566 | v, 0, 0); | | 2566 | v, 0, 0); |
2567 | break; | | 2567 | break; |
2568 | case UHF_PORT_POWER: | | 2568 | case UHF_PORT_POWER: |
2569 | DPRINTF("set port power %jd (has PPC = %jd)", index, | | 2569 | DPRINTF("set port power %jd (has PPC = %jd)", index, |
2570 | sc->sc_hasppc, 0, 0); | | 2570 | sc->sc_hasppc, 0, 0); |
2571 | if (sc->sc_hasppc) | | 2571 | if (sc->sc_hasppc) |
2572 | EOWRITE4(sc, port, v | EHCI_PS_PP); | | 2572 | EOWRITE4(sc, port, v | EHCI_PS_PP); |
2573 | break; | | 2573 | break; |
2574 | case UHF_PORT_TEST: | | 2574 | case UHF_PORT_TEST: |
2575 | DPRINTF("set port test %jd", index, 0, 0, 0); | | 2575 | DPRINTF("set port test %jd", index, 0, 0, 0); |
2576 | break; | | 2576 | break; |
2577 | case UHF_PORT_INDICATOR: | | 2577 | case UHF_PORT_INDICATOR: |
2578 | DPRINTF("set port ind %jd", index, 0, 0, 0); | | 2578 | DPRINTF("set port ind %jd", index, 0, 0, 0); |
2579 | EOWRITE4(sc, port, v | EHCI_PS_PIC); | | 2579 | EOWRITE4(sc, port, v | EHCI_PS_PIC); |
2580 | break; | | 2580 | break; |
2581 | default: | | 2581 | default: |
2582 | return -1; | | 2582 | return -1; |
2583 | } | | 2583 | } |
2584 | break; | | 2584 | break; |
2585 | case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): | | 2585 | case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): |
2586 | case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): | | 2586 | case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): |
2587 | case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER): | | 2587 | case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER): |
2588 | case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): | | 2588 | case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): |
2589 | break; | | 2589 | break; |
2590 | default: | | 2590 | default: |
2591 | /* default from usbroothub */ | | 2591 | /* default from usbroothub */ |
2592 | DPRINTF("returning %jd (usbroothub default)", buflen, 0, 0, 0); | | 2592 | DPRINTF("returning %jd (usbroothub default)", buflen, 0, 0, 0); |
2593 | | | 2593 | |
2594 | return buflen; | | 2594 | return buflen; |
2595 | } | | 2595 | } |
2596 | | | 2596 | |
2597 | DPRINTF("returning %jd", totlen, 0, 0, 0); | | 2597 | DPRINTF("returning %jd", totlen, 0, 0, 0); |
2598 | | | 2598 | |
2599 | return totlen; | | 2599 | return totlen; |
2600 | } | | 2600 | } |
2601 | | | 2601 | |
2602 | /* | | 2602 | /* |
2603 | * Handle ehci hand-off in early boot vs RB_ASKNAME/RB_SINGLE. | | 2603 | * Handle ehci hand-off in early boot vs RB_ASKNAME/RB_SINGLE. |
2604 | * | | 2604 | * |
2605 | * This pile of garbage below works around the following problem without | | 2605 | * This pile of garbage below works around the following problem without |
2606 | * holding boots with no hand-over devices present, while penalising | | 2606 | * holding boots with no hand-over devices present, while penalising |
2607 | * boots where the first ehci probe hands off devices with a 5 second | | 2607 | * boots where the first ehci probe hands off devices with a 5 second |
2608 | * delay, if RB_ASKNAME/RB_SINGLE is set. This is typically not a problem | | 2608 | * delay, if RB_ASKNAME/RB_SINGLE is set. This is typically not a problem |
2609 | * for RB_SINGLE, but the same basic issue exists. | | 2609 | * for RB_SINGLE, but the same basic issue exists. |
2610 | * | | 2610 | * |
2611 | * The way ehci hand-off works, the companion controller does not get the | | 2611 | * The way ehci hand-off works, the companion controller does not get the |
2612 | * device until after its' initial bus explore, so the reference dropped | | 2612 | * device until after its initial bus explore, so the reference dropped |
2613 | * after the first explore is not enough. 5 seconds should be enough, | | 2613 | * after the first explore is not enough. 5 seconds should be enough, |
2614 | * and EHCI_DISOWN_DELAY_SECONDS can be set to another value. | | 2614 | * and EHCI_DISOWN_DELAY_SECONDS can be set to another value. |
2615 | * | | 2615 | * |
2616 | * There are 3 states. CO_EARLY is set during attach. CO_SCHED is set | | 2616 | * There are 3 states. CO_EARLY is set during attach. CO_SCHED is set |
2617 | * if the callback is scheduled. CO_DONE is set when the callout has | | 2617 | * if the callback is scheduled. CO_DONE is set when the callout has |
2618 | * called config_pending_decr(). | | 2618 | * called config_pending_decr(). |
2619 | * | | 2619 | * |
2620 | * There's a mutex, a cv and a callout here, and we delay detach if the | | 2620 | * There's a mutex, a cv and a callout here, and we delay detach if the |
2621 | * callout has been set. | | 2621 | * callout has been set. |
2622 | */ | | 2622 | */ |
2623 | #ifndef EHCI_DISOWN_DELAY_SECONDS | | 2623 | #ifndef EHCI_DISOWN_DELAY_SECONDS |
2624 | #define EHCI_DISOWN_DELAY_SECONDS 5 | | 2624 | #define EHCI_DISOWN_DELAY_SECONDS 5 |
2625 | #endif | | 2625 | #endif |
2626 | static int ehci_disown_delay_seconds = EHCI_DISOWN_DELAY_SECONDS; | | 2626 | static int ehci_disown_delay_seconds = EHCI_DISOWN_DELAY_SECONDS; |
2627 | | | 2627 | |
2628 | static void | | 2628 | static void |
2629 | ehci_disown_callback(void *arg) | | 2629 | ehci_disown_callback(void *arg) |
2630 | { | | 2630 | { |
2631 | ehci_softc_t *sc = arg; | | 2631 | ehci_softc_t *sc = arg; |
2632 | | | 2632 | |
2633 | config_pending_decr(sc->sc_dev); | | 2633 | config_pending_decr(sc->sc_dev); |
2634 | | | 2634 | |
2635 | mutex_enter(&sc->sc_complock); | | 2635 | mutex_enter(&sc->sc_complock); |
2636 | KASSERT(sc->sc_comp_state == CO_SCHED); | | 2636 | KASSERT(sc->sc_comp_state == CO_SCHED); |
2637 | sc->sc_comp_state = CO_DONE; | | 2637 | sc->sc_comp_state = CO_DONE; |
2638 | cv_signal(&sc->sc_compcv); | | 2638 | cv_signal(&sc->sc_compcv); |
2639 | mutex_exit(&sc->sc_complock); | | 2639 | mutex_exit(&sc->sc_complock); |
2640 | } | | 2640 | } |
2641 | | | 2641 | |
2642 | static void | | 2642 | static void |
2643 | ehci_disown_sched_callback(ehci_softc_t *sc) | | 2643 | ehci_disown_sched_callback(ehci_softc_t *sc) |
2644 | { | | 2644 | { |
2645 | extern bool root_is_mounted; | | 2645 | extern bool root_is_mounted; |
2646 | | | 2646 | |
2647 | mutex_enter(&sc->sc_complock); | | 2647 | mutex_enter(&sc->sc_complock); |
2648 | | | 2648 | |
2649 | if (root_is_mounted || | | 2649 | if (root_is_mounted || |
2650 | (boothowto & (RB_ASKNAME|RB_SINGLE)) == 0 || | | 2650 | (boothowto & (RB_ASKNAME|RB_SINGLE)) == 0 || |
2651 | sc->sc_comp_state != CO_EARLY) { | | 2651 | sc->sc_comp_state != CO_EARLY) { |
2652 | mutex_exit(&sc->sc_complock); | | 2652 | mutex_exit(&sc->sc_complock); |
2653 | return; | | 2653 | return; |
2654 | } | | 2654 | } |
2655 | | | 2655 | |
2656 | callout_reset(&sc->sc_compcallout, ehci_disown_delay_seconds * hz, | | 2656 | callout_reset(&sc->sc_compcallout, ehci_disown_delay_seconds * hz, |
2657 | ehci_disown_callback, &sc->sc_dev); | | 2657 | ehci_disown_callback, &sc->sc_dev); |
2658 | sc->sc_comp_state = CO_SCHED; | | 2658 | sc->sc_comp_state = CO_SCHED; |
2659 | | | 2659 | |
2660 | mutex_exit(&sc->sc_complock); | | 2660 | mutex_exit(&sc->sc_complock); |
2661 | | | 2661 | |
2662 | config_pending_incr(sc->sc_dev); | | 2662 | config_pending_incr(sc->sc_dev); |
2663 | aprint_normal("delaying %s by %u seconds due to USB owner change.", | | 2663 | aprint_normal("delaying %s by %u seconds due to USB owner change.", |
2664 | (boothowto & RB_ASKNAME) == 0 ? "ask root" : "single user", | | 2664 | (boothowto & RB_ASKNAME) == 0 ? "ask root" : "single user", |
2665 | ehci_disown_delay_seconds); | | 2665 | ehci_disown_delay_seconds); |
2666 | } | | 2666 | } |
2667 | | | 2667 | |
2668 | Static void | | 2668 | Static void |
2669 | ehci_disown(ehci_softc_t *sc, int index, int lowspeed) | | 2669 | ehci_disown(ehci_softc_t *sc, int index, int lowspeed) |
2670 | { | | 2670 | { |
2671 | int port; | | 2671 | int port; |
2672 | uint32_t v; | | 2672 | uint32_t v; |
2673 | | | 2673 | |
2674 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 2674 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
2675 | | | 2675 | |
2676 | DPRINTF("index=%jd lowspeed=%jd", index, lowspeed, 0, 0); | | 2676 | DPRINTF("index=%jd lowspeed=%jd", index, lowspeed, 0, 0); |
2677 | if (sc->sc_npcomp != 0) { | | 2677 | if (sc->sc_npcomp != 0) { |
2678 | int i = (index-1) / sc->sc_npcomp; | | 2678 | int i = (index-1) / sc->sc_npcomp; |
2679 | if (i < sc->sc_ncomp) { | | 2679 | if (i < sc->sc_ncomp) { |
2680 | ehci_disown_sched_callback(sc); | | 2680 | ehci_disown_sched_callback(sc); |
2681 | #ifdef DIAGNOSTIC | | 2681 | #ifdef DIAGNOSTIC |
2682 | printf("%s: handing over %s speed device on " | | 2682 | printf("%s: handing over %s speed device on " |
2683 | "port %d to %s\n", | | 2683 | "port %d to %s\n", |
2684 | device_xname(sc->sc_dev), | | 2684 | device_xname(sc->sc_dev), |
2685 | lowspeed ? "low" : "full", | | 2685 | lowspeed ? "low" : "full", |
2686 | index, sc->sc_comps[i] ? | | 2686 | index, sc->sc_comps[i] ? |
2687 | device_xname(sc->sc_comps[i]) : | | 2687 | device_xname(sc->sc_comps[i]) : |
2688 | "companion controller"); | | 2688 | "companion controller"); |
2689 | } else { | | 2689 | } else { |
2690 | printf("%s: strange port\n", | | 2690 | printf("%s: strange port\n", |
2691 | device_xname(sc->sc_dev)); | | 2691 | device_xname(sc->sc_dev)); |
2692 | #endif | | 2692 | #endif |
2693 | } | | 2693 | } |
2694 | } else { | | 2694 | } else { |
2695 | #ifdef DIAGNOSTIC | | 2695 | #ifdef DIAGNOSTIC |
2696 | printf("%s: npcomp == 0\n", device_xname(sc->sc_dev)); | | 2696 | printf("%s: npcomp == 0\n", device_xname(sc->sc_dev)); |
2697 | #endif | | 2697 | #endif |
2698 | } | | 2698 | } |
2699 | port = EHCI_PORTSC(index); | | 2699 | port = EHCI_PORTSC(index); |
2700 | v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; | | 2700 | v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; |
2701 | EOWRITE4(sc, port, v | EHCI_PS_PO); | | 2701 | EOWRITE4(sc, port, v | EHCI_PS_PO); |
2702 | } | | 2702 | } |
2703 | | | 2703 | |
2704 | Static usbd_status | | 2704 | Static usbd_status |
2705 | ehci_root_intr_transfer(struct usbd_xfer *xfer) | | 2705 | ehci_root_intr_transfer(struct usbd_xfer *xfer) |
2706 | { | | 2706 | { |
2707 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); | | 2707 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
2708 | usbd_status err; | | 2708 | usbd_status err; |
2709 | | | 2709 | |
2710 | /* Insert last in queue. */ | | 2710 | /* Insert last in queue. */ |
2711 | mutex_enter(&sc->sc_lock); | | 2711 | mutex_enter(&sc->sc_lock); |
2712 | err = usb_insert_transfer(xfer); | | 2712 | err = usb_insert_transfer(xfer); |
2713 | mutex_exit(&sc->sc_lock); | | 2713 | mutex_exit(&sc->sc_lock); |
2714 | if (err) | | 2714 | if (err) |
2715 | return err; | | 2715 | return err; |
2716 | | | 2716 | |
2717 | /* Pipe isn't running, start first */ | | 2717 | /* Pipe isn't running, start first */ |
2718 | return ehci_root_intr_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue)); | | 2718 | return ehci_root_intr_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue)); |
2719 | } | | 2719 | } |
2720 | | | 2720 | |
2721 | Static usbd_status | | 2721 | Static usbd_status |
2722 | ehci_root_intr_start(struct usbd_xfer *xfer) | | 2722 | ehci_root_intr_start(struct usbd_xfer *xfer) |
2723 | { | | 2723 | { |
2724 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); | | 2724 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
2725 | const bool polling = sc->sc_bus.ub_usepolling; | | 2725 | const bool polling = sc->sc_bus.ub_usepolling; |
2726 | | | 2726 | |
2727 | if (sc->sc_dying) | | 2727 | if (sc->sc_dying) |
2728 | return USBD_IOERROR; | | 2728 | return USBD_IOERROR; |
2729 | | | 2729 | |
2730 | if (!polling) | | 2730 | if (!polling) |
2731 | mutex_enter(&sc->sc_lock); | | 2731 | mutex_enter(&sc->sc_lock); |
2732 | sc->sc_intrxfer = xfer; | | 2732 | sc->sc_intrxfer = xfer; |
2733 | if (!polling) | | 2733 | if (!polling) |
2734 | mutex_exit(&sc->sc_lock); | | 2734 | mutex_exit(&sc->sc_lock); |
2735 | | | 2735 | |
2736 | return USBD_IN_PROGRESS; | | 2736 | return USBD_IN_PROGRESS; |
2737 | } | | 2737 | } |
2738 | | | 2738 | |
2739 | /* Abort a root interrupt request. */ | | 2739 | /* Abort a root interrupt request. */ |
2740 | Static void | | 2740 | Static void |
2741 | ehci_root_intr_abort(struct usbd_xfer *xfer) | | 2741 | ehci_root_intr_abort(struct usbd_xfer *xfer) |
2742 | { | | 2742 | { |
2743 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); | | 2743 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
2744 | | | 2744 | |
2745 | KASSERT(mutex_owned(&sc->sc_lock)); | | 2745 | KASSERT(mutex_owned(&sc->sc_lock)); |
2746 | KASSERT(xfer->ux_pipe->up_intrxfer == xfer); | | 2746 | KASSERT(xfer->ux_pipe->up_intrxfer == xfer); |
2747 | | | 2747 | |
2748 | sc->sc_intrxfer = NULL; | | 2748 | sc->sc_intrxfer = NULL; |
2749 | | | 2749 | |
2750 | xfer->ux_status = USBD_CANCELLED; | | 2750 | xfer->ux_status = USBD_CANCELLED; |
2751 | usb_transfer_complete(xfer); | | 2751 | usb_transfer_complete(xfer); |
2752 | } | | 2752 | } |
2753 | | | 2753 | |
2754 | /* Close the root pipe. */ | | 2754 | /* Close the root pipe. */ |
2755 | Static void | | 2755 | Static void |
2756 | ehci_root_intr_close(struct usbd_pipe *pipe) | | 2756 | ehci_root_intr_close(struct usbd_pipe *pipe) |
2757 | { | | 2757 | { |
2758 | ehci_softc_t *sc = EHCI_PIPE2SC(pipe); | | 2758 | ehci_softc_t *sc = EHCI_PIPE2SC(pipe); |
2759 | | | 2759 | |
2760 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 2760 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
2761 | | | 2761 | |
2762 | KASSERT(mutex_owned(&sc->sc_lock)); | | 2762 | KASSERT(mutex_owned(&sc->sc_lock)); |
2763 | | | 2763 | |
2764 | sc->sc_intrxfer = NULL; | | 2764 | sc->sc_intrxfer = NULL; |
2765 | } | | 2765 | } |
2766 | | | 2766 | |
2767 | Static void | | 2767 | Static void |
2768 | ehci_root_intr_done(struct usbd_xfer *xfer) | | 2768 | ehci_root_intr_done(struct usbd_xfer *xfer) |
2769 | { | | 2769 | { |
2770 | } | | 2770 | } |
2771 | | | 2771 | |
2772 | /************************/ | | 2772 | /************************/ |
2773 | | | 2773 | |
2774 | Static ehci_soft_qh_t * | | 2774 | Static ehci_soft_qh_t * |
2775 | ehci_alloc_sqh(ehci_softc_t *sc) | | 2775 | ehci_alloc_sqh(ehci_softc_t *sc) |
2776 | { | | 2776 | { |
2777 | ehci_soft_qh_t *sqh; | | 2777 | ehci_soft_qh_t *sqh; |
2778 | usbd_status err; | | 2778 | usbd_status err; |
2779 | int i, offs; | | 2779 | int i, offs; |
2780 | usb_dma_t dma; | | 2780 | usb_dma_t dma; |
2781 | | | 2781 | |
2782 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 2782 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
2783 | | | 2783 | |
2784 | mutex_enter(&sc->sc_lock); | | 2784 | mutex_enter(&sc->sc_lock); |
2785 | if (sc->sc_freeqhs == NULL) { | | 2785 | if (sc->sc_freeqhs == NULL) { |
2786 | DPRINTF("allocating chunk", 0, 0, 0, 0); | | 2786 | DPRINTF("allocating chunk", 0, 0, 0, 0); |
2787 | mutex_exit(&sc->sc_lock); | | 2787 | mutex_exit(&sc->sc_lock); |
2788 | | | 2788 | |
2789 | err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK, | | 2789 | err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK, |
2790 | EHCI_PAGE_SIZE, &dma); | | 2790 | EHCI_PAGE_SIZE, &dma); |
2791 | #ifdef EHCI_DEBUG | | 2791 | #ifdef EHCI_DEBUG |
2792 | if (err) | | 2792 | if (err) |
2793 | printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err); | | 2793 | printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err); |
2794 | #endif | | 2794 | #endif |
2795 | if (err) | | 2795 | if (err) |
2796 | return NULL; | | 2796 | return NULL; |
2797 | | | 2797 | |
2798 | mutex_enter(&sc->sc_lock); | | 2798 | mutex_enter(&sc->sc_lock); |
2799 | for (i = 0; i < EHCI_SQH_CHUNK; i++) { | | 2799 | for (i = 0; i < EHCI_SQH_CHUNK; i++) { |
2800 | offs = i * EHCI_SQH_SIZE; | | 2800 | offs = i * EHCI_SQH_SIZE; |
2801 | sqh = KERNADDR(&dma, offs); | | 2801 | sqh = KERNADDR(&dma, offs); |
2802 | sqh->physaddr = DMAADDR(&dma, offs); | | 2802 | sqh->physaddr = DMAADDR(&dma, offs); |
2803 | sqh->dma = dma; | | 2803 | sqh->dma = dma; |
2804 | sqh->offs = offs; | | 2804 | sqh->offs = offs; |
2805 | sqh->next = sc->sc_freeqhs; | | 2805 | sqh->next = sc->sc_freeqhs; |
2806 | sc->sc_freeqhs = sqh; | | 2806 | sc->sc_freeqhs = sqh; |
2807 | } | | 2807 | } |
2808 | } | | 2808 | } |
2809 | sqh = sc->sc_freeqhs; | | 2809 | sqh = sc->sc_freeqhs; |
2810 | sc->sc_freeqhs = sqh->next; | | 2810 | sc->sc_freeqhs = sqh->next; |
2811 | mutex_exit(&sc->sc_lock); | | 2811 | mutex_exit(&sc->sc_lock); |
2812 | | | 2812 | |
2813 | memset(&sqh->qh, 0, sizeof(ehci_qh_t)); | | 2813 | memset(&sqh->qh, 0, sizeof(ehci_qh_t)); |
2814 | sqh->next = NULL; | | 2814 | sqh->next = NULL; |
2815 | return sqh; | | 2815 | return sqh; |
2816 | } | | 2816 | } |
2817 | | | 2817 | |
2818 | Static void | | 2818 | Static void |
2819 | ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh) | | 2819 | ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh) |
2820 | { | | 2820 | { |
2821 | KASSERT(mutex_owned(&sc->sc_lock)); | | 2821 | KASSERT(mutex_owned(&sc->sc_lock)); |
2822 | | | 2822 | |
2823 | sqh->next = sc->sc_freeqhs; | | 2823 | sqh->next = sc->sc_freeqhs; |
2824 | sc->sc_freeqhs = sqh; | | 2824 | sc->sc_freeqhs = sqh; |
2825 | } | | 2825 | } |
2826 | | | 2826 | |
2827 | Static ehci_soft_qtd_t * | | 2827 | Static ehci_soft_qtd_t * |
2828 | ehci_alloc_sqtd(ehci_softc_t *sc) | | 2828 | ehci_alloc_sqtd(ehci_softc_t *sc) |
2829 | { | | 2829 | { |
2830 | ehci_soft_qtd_t *sqtd = NULL; | | 2830 | ehci_soft_qtd_t *sqtd = NULL; |
2831 | usbd_status err; | | 2831 | usbd_status err; |
2832 | int i, offs; | | 2832 | int i, offs; |
2833 | usb_dma_t dma; | | 2833 | usb_dma_t dma; |
2834 | | | 2834 | |
2835 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 2835 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
2836 | | | 2836 | |
2837 | mutex_enter(&sc->sc_lock); | | 2837 | mutex_enter(&sc->sc_lock); |
2838 | if (sc->sc_freeqtds == NULL) { | | 2838 | if (sc->sc_freeqtds == NULL) { |
2839 | DPRINTF("allocating chunk", 0, 0, 0, 0); | | 2839 | DPRINTF("allocating chunk", 0, 0, 0, 0); |
2840 | mutex_exit(&sc->sc_lock); | | 2840 | mutex_exit(&sc->sc_lock); |
2841 | | | 2841 | |
2842 | err = usb_allocmem(&sc->sc_bus, EHCI_SQTD_SIZE*EHCI_SQTD_CHUNK, | | 2842 | err = usb_allocmem(&sc->sc_bus, EHCI_SQTD_SIZE*EHCI_SQTD_CHUNK, |
2843 | EHCI_PAGE_SIZE, &dma); | | 2843 | EHCI_PAGE_SIZE, &dma); |
2844 | #ifdef EHCI_DEBUG | | 2844 | #ifdef EHCI_DEBUG |
2845 | if (err) | | 2845 | if (err) |
2846 | printf("ehci_alloc_sqtd: usb_allocmem()=%d\n", err); | | 2846 | printf("ehci_alloc_sqtd: usb_allocmem()=%d\n", err); |
2847 | #endif | | 2847 | #endif |
2848 | if (err) | | 2848 | if (err) |
2849 | goto done; | | 2849 | goto done; |
2850 | | | 2850 | |
2851 | mutex_enter(&sc->sc_lock); | | 2851 | mutex_enter(&sc->sc_lock); |
2852 | for (i = 0; i < EHCI_SQTD_CHUNK; i++) { | | 2852 | for (i = 0; i < EHCI_SQTD_CHUNK; i++) { |
2853 | offs = i * EHCI_SQTD_SIZE; | | 2853 | offs = i * EHCI_SQTD_SIZE; |
2854 | sqtd = KERNADDR(&dma, offs); | | 2854 | sqtd = KERNADDR(&dma, offs); |
2855 | sqtd->physaddr = DMAADDR(&dma, offs); | | 2855 | sqtd->physaddr = DMAADDR(&dma, offs); |
2856 | sqtd->dma = dma; | | 2856 | sqtd->dma = dma; |
2857 | sqtd->offs = offs; | | 2857 | sqtd->offs = offs; |
2858 | | | 2858 | |
2859 | sqtd->nextqtd = sc->sc_freeqtds; | | 2859 | sqtd->nextqtd = sc->sc_freeqtds; |
2860 | sc->sc_freeqtds = sqtd; | | 2860 | sc->sc_freeqtds = sqtd; |
2861 | } | | 2861 | } |
2862 | } | | 2862 | } |
2863 | | | 2863 | |
2864 | sqtd = sc->sc_freeqtds; | | 2864 | sqtd = sc->sc_freeqtds; |
2865 | sc->sc_freeqtds = sqtd->nextqtd; | | 2865 | sc->sc_freeqtds = sqtd->nextqtd; |
2866 | mutex_exit(&sc->sc_lock); | | 2866 | mutex_exit(&sc->sc_lock); |
2867 | | | 2867 | |
2868 | memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t)); | | 2868 | memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t)); |
2869 | sqtd->nextqtd = NULL; | | 2869 | sqtd->nextqtd = NULL; |
2870 | sqtd->xfer = NULL; | | 2870 | sqtd->xfer = NULL; |
2871 | | | 2871 | |
2872 | done: | | 2872 | done: |
2873 | return sqtd; | | 2873 | return sqtd; |
2874 | } | | 2874 | } |
2875 | | | 2875 | |
2876 | Static void | | 2876 | Static void |
2877 | ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd) | | 2877 | ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd) |
2878 | { | | 2878 | { |
2879 | | | 2879 | |
2880 | mutex_enter(&sc->sc_lock); | | 2880 | mutex_enter(&sc->sc_lock); |
2881 | sqtd->nextqtd = sc->sc_freeqtds; | | 2881 | sqtd->nextqtd = sc->sc_freeqtds; |
2882 | sc->sc_freeqtds = sqtd; | | 2882 | sc->sc_freeqtds = sqtd; |
2883 | mutex_exit(&sc->sc_lock); | | 2883 | mutex_exit(&sc->sc_lock); |
2884 | } | | 2884 | } |
2885 | | | 2885 | |
2886 | Static int | | 2886 | Static int |
2887 | ehci_alloc_sqtd_chain(ehci_softc_t *sc, struct usbd_xfer *xfer, | | 2887 | ehci_alloc_sqtd_chain(ehci_softc_t *sc, struct usbd_xfer *xfer, |
2888 | int alen, int rd, ehci_soft_qtd_t **sp) | | 2888 | int alen, int rd, ehci_soft_qtd_t **sp) |
2889 | { | | 2889 | { |
2890 | struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); | | 2890 | struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
2891 | uint16_t flags = xfer->ux_flags; | | 2891 | uint16_t flags = xfer->ux_flags; |
2892 | | | 2892 | |
2893 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 2893 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
2894 | | | 2894 | |
2895 | ASSERT_SLEEPABLE(); | | 2895 | ASSERT_SLEEPABLE(); |
2896 | KASSERT(sp); | | 2896 | KASSERT(sp); |
2897 | KASSERT(alen != 0 || (!rd && (flags & USBD_FORCE_SHORT_XFER))); | | 2897 | KASSERT(alen != 0 || (!rd && (flags & USBD_FORCE_SHORT_XFER))); |
2898 | | | 2898 | |
2899 | size_t nsqtd = (!rd && (flags & USBD_FORCE_SHORT_XFER)) ? 1 : 0; | | 2899 | size_t nsqtd = (!rd && (flags & USBD_FORCE_SHORT_XFER)) ? 1 : 0; |
2900 | nsqtd += ((alen + EHCI_PAGE_SIZE - 1) / EHCI_PAGE_SIZE); | | 2900 | nsqtd += ((alen + EHCI_PAGE_SIZE - 1) / EHCI_PAGE_SIZE); |
2901 | exfer->ex_sqtds = kmem_zalloc(sizeof(ehci_soft_qtd_t *) * nsqtd, | | 2901 | exfer->ex_sqtds = kmem_zalloc(sizeof(ehci_soft_qtd_t *) * nsqtd, |
2902 | KM_SLEEP); | | 2902 | KM_SLEEP); |
2903 | exfer->ex_nsqtd = nsqtd; | | 2903 | exfer->ex_nsqtd = nsqtd; |
2904 | | | 2904 | |
2905 | DPRINTF("xfer %#jx len %jd nsqtd %jd flags %jx", (uintptr_t)xfer, | | 2905 | DPRINTF("xfer %#jx len %jd nsqtd %jd flags %jx", (uintptr_t)xfer, |
2906 | alen, nsqtd, flags); | | 2906 | alen, nsqtd, flags); |
2907 | | | 2907 | |
2908 | for (size_t j = 0; j < exfer->ex_nsqtd;) { | | 2908 | for (size_t j = 0; j < exfer->ex_nsqtd;) { |
2909 | ehci_soft_qtd_t *cur = ehci_alloc_sqtd(sc); | | 2909 | ehci_soft_qtd_t *cur = ehci_alloc_sqtd(sc); |
2910 | if (cur == NULL) | | 2910 | if (cur == NULL) |
2911 | goto nomem; | | 2911 | goto nomem; |
2912 | exfer->ex_sqtds[j++] = cur; | | 2912 | exfer->ex_sqtds[j++] = cur; |
2913 | | | 2913 | |
2914 | cur->xfer = xfer; | | 2914 | cur->xfer = xfer; |
2915 | cur->len = 0; | | 2915 | cur->len = 0; |
2916 | | | 2916 | |
2917 | } | | 2917 | } |
2918 | | | 2918 | |
2919 | *sp = exfer->ex_sqtds[0]; | | 2919 | *sp = exfer->ex_sqtds[0]; |
2920 | DPRINTF("return sqtd=%#jx", (uintptr_t)*sp, 0, 0, 0); | | 2920 | DPRINTF("return sqtd=%#jx", (uintptr_t)*sp, 0, 0, 0); |
2921 | | | 2921 | |
2922 | return 0; | | 2922 | return 0; |
2923 | | | 2923 | |
2924 | nomem: | | 2924 | nomem: |
2925 | ehci_free_sqtds(sc, exfer); | | 2925 | ehci_free_sqtds(sc, exfer); |
2926 | kmem_free(exfer->ex_sqtds, sizeof(ehci_soft_qtd_t *) * nsqtd); | | 2926 | kmem_free(exfer->ex_sqtds, sizeof(ehci_soft_qtd_t *) * nsqtd); |
2927 | DPRINTF("no memory", 0, 0, 0, 0); | | 2927 | DPRINTF("no memory", 0, 0, 0, 0); |
2928 | return ENOMEM; | | 2928 | return ENOMEM; |
2929 | } | | 2929 | } |
2930 | | | 2930 | |
2931 | Static void | | 2931 | Static void |
2932 | ehci_free_sqtds(ehci_softc_t *sc, struct ehci_xfer *exfer) | | 2932 | ehci_free_sqtds(ehci_softc_t *sc, struct ehci_xfer *exfer) |
2933 | { | | 2933 | { |
2934 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 2934 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
2935 | DPRINTF("exfer=%#jx", (uintptr_t)exfer, 0, 0, 0); | | 2935 | DPRINTF("exfer=%#jx", (uintptr_t)exfer, 0, 0, 0); |
2936 | | | 2936 | |
2937 | mutex_enter(&sc->sc_lock); | | 2937 | mutex_enter(&sc->sc_lock); |
2938 | for (size_t i = 0; i < exfer->ex_nsqtd; i++) { | | 2938 | for (size_t i = 0; i < exfer->ex_nsqtd; i++) { |
2939 | ehci_soft_qtd_t *sqtd = exfer->ex_sqtds[i]; | | 2939 | ehci_soft_qtd_t *sqtd = exfer->ex_sqtds[i]; |
2940 | | | 2940 | |
2941 | if (sqtd == NULL) | | 2941 | if (sqtd == NULL) |
2942 | break; | | 2942 | break; |
2943 | | | 2943 | |
2944 | sqtd->nextqtd = sc->sc_freeqtds; | | 2944 | sqtd->nextqtd = sc->sc_freeqtds; |
2945 | sc->sc_freeqtds = sqtd; | | 2945 | sc->sc_freeqtds = sqtd; |
2946 | } | | 2946 | } |
2947 | mutex_exit(&sc->sc_lock); | | 2947 | mutex_exit(&sc->sc_lock); |
2948 | } | | 2948 | } |
2949 | | | 2949 | |
2950 | Static void | | 2950 | Static void |
2951 | ehci_append_sqtd(ehci_soft_qtd_t *sqtd, ehci_soft_qtd_t *prev) | | 2951 | ehci_append_sqtd(ehci_soft_qtd_t *sqtd, ehci_soft_qtd_t *prev) |
2952 | { | | 2952 | { |
2953 | if (prev) { | | 2953 | if (prev) { |
2954 | prev->nextqtd = sqtd; | | 2954 | prev->nextqtd = sqtd; |
2955 | prev->qtd.qtd_next = htole32(sqtd->physaddr); | | 2955 | prev->qtd.qtd_next = htole32(sqtd->physaddr); |
2956 | prev->qtd.qtd_altnext = prev->qtd.qtd_next; | | 2956 | prev->qtd.qtd_altnext = prev->qtd.qtd_next; |
2957 | usb_syncmem(&prev->dma, prev->offs, sizeof(prev->qtd), | | 2957 | usb_syncmem(&prev->dma, prev->offs, sizeof(prev->qtd), |
2958 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 2958 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
2959 | } | | 2959 | } |
2960 | } | | 2960 | } |
2961 | | | 2961 | |
2962 | Static void | | 2962 | Static void |
2963 | ehci_reset_sqtd_chain(ehci_softc_t *sc, struct usbd_xfer *xfer, | | 2963 | ehci_reset_sqtd_chain(ehci_softc_t *sc, struct usbd_xfer *xfer, |
2964 | int length, int isread, int *toggle, ehci_soft_qtd_t **lsqtd) | | 2964 | int length, int isread, int *toggle, ehci_soft_qtd_t **lsqtd) |
2965 | { | | 2965 | { |
2966 | struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); | | 2966 | struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
2967 | usb_dma_t *dma = &xfer->ux_dmabuf; | | 2967 | usb_dma_t *dma = &xfer->ux_dmabuf; |
2968 | uint16_t flags = xfer->ux_flags; | | 2968 | uint16_t flags = xfer->ux_flags; |
2969 | ehci_soft_qtd_t *sqtd, *prev; | | 2969 | ehci_soft_qtd_t *sqtd, *prev; |
2970 | int tog = *toggle; | | 2970 | int tog = *toggle; |
2971 | int mps = UGETW(xfer->ux_pipe->up_endpoint->ue_edesc->wMaxPacketSize); | | 2971 | int mps = UGETW(xfer->ux_pipe->up_endpoint->ue_edesc->wMaxPacketSize); |
2972 | int len = length; | | 2972 | int len = length; |
2973 | | | 2973 | |
2974 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 2974 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
2975 | DPRINTF("xfer=%#jx len %jd isread %jd toggle %jd", (uintptr_t)xfer, | | 2975 | DPRINTF("xfer=%#jx len %jd isread %jd toggle %jd", (uintptr_t)xfer, |
2976 | len, isread, tog); | | 2976 | len, isread, tog); |
2977 | DPRINTF(" VA %#jx", (uintptr_t)KERNADDR(&xfer->ux_dmabuf, 0), | | 2977 | DPRINTF(" VA %#jx", (uintptr_t)KERNADDR(&xfer->ux_dmabuf, 0), |
2978 | 0, 0, 0); | | 2978 | 0, 0, 0); |
2979 | | | 2979 | |
2980 | KASSERT(length != 0 || (!isread && (flags & USBD_FORCE_SHORT_XFER))); | | 2980 | KASSERT(length != 0 || (!isread && (flags & USBD_FORCE_SHORT_XFER))); |
2981 | | | 2981 | |
2982 | const uint32_t qtdstatus = EHCI_QTD_ACTIVE | | | 2982 | const uint32_t qtdstatus = EHCI_QTD_ACTIVE | |
2983 | EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | | | 2983 | EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | |
2984 | EHCI_QTD_SET_CERR(3) | | 2984 | EHCI_QTD_SET_CERR(3) |
2985 | ; | | 2985 | ; |
2986 | | | 2986 | |
2987 | sqtd = prev = NULL; | | 2987 | sqtd = prev = NULL; |
2988 | size_t curoffs = 0; | | 2988 | size_t curoffs = 0; |
2989 | size_t j = 0; | | 2989 | size_t j = 0; |
2990 | for (; len != 0 && j < exfer->ex_nsqtd; prev = sqtd) { | | 2990 | for (; len != 0 && j < exfer->ex_nsqtd; prev = sqtd) { |
2991 | sqtd = exfer->ex_sqtds[j++]; | | 2991 | sqtd = exfer->ex_sqtds[j++]; |
2992 | DPRINTF("sqtd[%jd]=%#jx prev %#jx", j, (uintptr_t)sqtd, | | 2992 | DPRINTF("sqtd[%jd]=%#jx prev %#jx", j, (uintptr_t)sqtd, |
2993 | (uintptr_t)prev, 0); | | 2993 | (uintptr_t)prev, 0); |
2994 | | | 2994 | |
2995 | /* | | 2995 | /* |
2996 | * The EHCI hardware can handle at most 5 pages and they do | | 2996 | * The EHCI hardware can handle at most 5 pages and they do |
2997 | * not have to be contiguous | | 2997 | * not have to be contiguous |
2998 | */ | | 2998 | */ |
2999 | vaddr_t va = (vaddr_t)KERNADDR(dma, curoffs); | | 2999 | vaddr_t va = (vaddr_t)KERNADDR(dma, curoffs); |
3000 | vaddr_t va_offs = EHCI_PAGE_OFFSET(va); | | 3000 | vaddr_t va_offs = EHCI_PAGE_OFFSET(va); |
3001 | size_t curlen = len; | | 3001 | size_t curlen = len; |
3002 | if (curlen >= EHCI_QTD_MAXTRANSFER - va_offs) { | | 3002 | if (curlen >= EHCI_QTD_MAXTRANSFER - va_offs) { |
3003 | /* must use multiple TDs, fill as much as possible. */ | | 3003 | /* must use multiple TDs, fill as much as possible. */ |
3004 | curlen = EHCI_QTD_MAXTRANSFER - va_offs; | | 3004 | curlen = EHCI_QTD_MAXTRANSFER - va_offs; |
3005 | | | 3005 | |
3006 | /* the length must be a multiple of the max size */ | | 3006 | /* the length must be a multiple of the max size */ |
3007 | curlen -= curlen % mps; | | 3007 | curlen -= curlen % mps; |
3008 | } | | 3008 | } |
3009 | KASSERT(curlen != 0); | | 3009 | KASSERT(curlen != 0); |
3010 | DPRINTF(" len=%jd curlen=%jd curoffs=%ju", len, curlen, | | 3010 | DPRINTF(" len=%jd curlen=%jd curoffs=%ju", len, curlen, |
3011 | curoffs, 0); | | 3011 | curoffs, 0); |
3012 | | | 3012 | |
3013 | /* Fill the qTD */ | | 3013 | /* Fill the qTD */ |
3014 | sqtd->qtd.qtd_next = sqtd->qtd.qtd_altnext = EHCI_NULL; | | 3014 | sqtd->qtd.qtd_next = sqtd->qtd.qtd_altnext = EHCI_NULL; |
3015 | sqtd->qtd.qtd_status = htole32( | | 3015 | sqtd->qtd.qtd_status = htole32( |
3016 | qtdstatus | | | 3016 | qtdstatus | |
3017 | EHCI_QTD_SET_BYTES(curlen) | | | 3017 | EHCI_QTD_SET_BYTES(curlen) | |
3018 | EHCI_QTD_SET_TOGGLE(tog)); | | 3018 | EHCI_QTD_SET_TOGGLE(tog)); |
3019 | | | 3019 | |
3020 | /* Find number of pages we'll be using, insert dma addresses */ | | 3020 | /* Find number of pages we'll be using, insert dma addresses */ |
3021 | size_t pages = EHCI_NPAGES(curlen); | | 3021 | size_t pages = EHCI_NPAGES(curlen); |
3022 | KASSERT(pages <= EHCI_QTD_NBUFFERS); | | 3022 | KASSERT(pages <= EHCI_QTD_NBUFFERS); |
3023 | size_t pageoffs = EHCI_PAGE(curoffs); | | 3023 | size_t pageoffs = EHCI_PAGE(curoffs); |
3024 | for (size_t i = 0; i < pages; i++) { | | 3024 | for (size_t i = 0; i < pages; i++) { |
3025 | paddr_t a = DMAADDR(dma, | | 3025 | paddr_t a = DMAADDR(dma, |
3026 | pageoffs + i * EHCI_PAGE_SIZE); | | 3026 | pageoffs + i * EHCI_PAGE_SIZE); |
3027 | sqtd->qtd.qtd_buffer[i] = htole32(EHCI_PAGE(a)); | | 3027 | sqtd->qtd.qtd_buffer[i] = htole32(EHCI_PAGE(a)); |
3028 | /* Cast up to avoid compiler warnings */ | | 3028 | /* Cast up to avoid compiler warnings */ |
3029 | sqtd->qtd.qtd_buffer_hi[i] = htole32((uint64_t)a >> 32); | | 3029 | sqtd->qtd.qtd_buffer_hi[i] = htole32((uint64_t)a >> 32); |
3030 | DPRINTF(" buffer[%jd/%jd] 0x%08jx 0x%08jx", | | 3030 | DPRINTF(" buffer[%jd/%jd] 0x%08jx 0x%08jx", |
3031 | i, pages, | | 3031 | i, pages, |
3032 | le32toh(sqtd->qtd.qtd_buffer_hi[i]), | | 3032 | le32toh(sqtd->qtd.qtd_buffer_hi[i]), |
3033 | le32toh(sqtd->qtd.qtd_buffer[i])); | | 3033 | le32toh(sqtd->qtd.qtd_buffer[i])); |
3034 | } | | 3034 | } |
3035 | /* First buffer pointer requires a page offset to start at */ | | 3035 | /* First buffer pointer requires a page offset to start at */ |
3036 | sqtd->qtd.qtd_buffer[0] |= htole32(va_offs); | | 3036 | sqtd->qtd.qtd_buffer[0] |= htole32(va_offs); |
3037 | | | 3037 | |
3038 | usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), | | 3038 | usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), |
3039 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 3039 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
3040 | | | 3040 | |
3041 | sqtd->len = curlen; | | 3041 | sqtd->len = curlen; |
3042 | | | 3042 | |
3043 | DPRINTF(" va %#jx pa %#jx len %jd", (uintptr_t)va, | | 3043 | DPRINTF(" va %#jx pa %#jx len %jd", (uintptr_t)va, |
3044 | (uintptr_t)DMAADDR(&xfer->ux_dmabuf, curoffs), curlen, 0); | | 3044 | (uintptr_t)DMAADDR(&xfer->ux_dmabuf, curoffs), curlen, 0); |
3045 | | | 3045 | |
3046 | ehci_append_sqtd(sqtd, prev); | | 3046 | ehci_append_sqtd(sqtd, prev); |
3047 | | | 3047 | |
3048 | if (((curlen + mps - 1) / mps) & 1) { | | 3048 | if (((curlen + mps - 1) / mps) & 1) { |
3049 | tog ^= 1; | | 3049 | tog ^= 1; |
3050 | } | | 3050 | } |
3051 | | | 3051 | |
3052 | curoffs += curlen; | | 3052 | curoffs += curlen; |
3053 | len -= curlen; | | 3053 | len -= curlen; |
3054 | } | | 3054 | } |
3055 | KASSERTMSG(len == 0, "xfer %p olen %d len %d mps %d ex_nsqtd %zu j %zu", | | 3055 | KASSERTMSG(len == 0, "xfer %p olen %d len %d mps %d ex_nsqtd %zu j %zu", |
3056 | xfer, length, len, mps, exfer->ex_nsqtd, j); | | 3056 | xfer, length, len, mps, exfer->ex_nsqtd, j); |
3057 | | | 3057 | |
3058 | if (!isread && | | 3058 | if (!isread && |
3059 | (flags & USBD_FORCE_SHORT_XFER) && | | 3059 | (flags & USBD_FORCE_SHORT_XFER) && |
3060 | length % mps == 0) { | | 3060 | length % mps == 0) { |
3061 | /* Force a 0 length transfer at the end. */ | | 3061 | /* Force a 0 length transfer at the end. */ |
3062 | | | 3062 | |
3063 | KASSERTMSG(j < exfer->ex_nsqtd, "j=%zu nsqtd=%zu", j, | | 3063 | KASSERTMSG(j < exfer->ex_nsqtd, "j=%zu nsqtd=%zu", j, |
3064 | exfer->ex_nsqtd); | | 3064 | exfer->ex_nsqtd); |
3065 | prev = sqtd; | | 3065 | prev = sqtd; |
3066 | sqtd = exfer->ex_sqtds[j++]; | | 3066 | sqtd = exfer->ex_sqtds[j++]; |
3067 | memset(&sqtd->qtd, 0, sizeof(sqtd->qtd)); | | 3067 | memset(&sqtd->qtd, 0, sizeof(sqtd->qtd)); |
3068 | sqtd->qtd.qtd_next = sqtd->qtd.qtd_altnext = EHCI_NULL; | | 3068 | sqtd->qtd.qtd_next = sqtd->qtd.qtd_altnext = EHCI_NULL; |
3069 | sqtd->qtd.qtd_status = htole32( | | 3069 | sqtd->qtd.qtd_status = htole32( |
3070 | qtdstatus | | | 3070 | qtdstatus | |
3071 | EHCI_QTD_SET_BYTES(0) | | | 3071 | EHCI_QTD_SET_BYTES(0) | |
3072 | EHCI_QTD_SET_TOGGLE(tog)); | | 3072 | EHCI_QTD_SET_TOGGLE(tog)); |
3073 | | | 3073 | |
3074 | usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), | | 3074 | usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), |
3075 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 3075 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
3076 | | | 3076 | |
3077 | ehci_append_sqtd(sqtd, prev); | | 3077 | ehci_append_sqtd(sqtd, prev); |
3078 | tog ^= 1; | | 3078 | tog ^= 1; |
3079 | } | | 3079 | } |
3080 | | | 3080 | |
3081 | *lsqtd = sqtd; | | 3081 | *lsqtd = sqtd; |
3082 | *toggle = tog; | | 3082 | *toggle = tog; |
3083 | } | | 3083 | } |
3084 | | | 3084 | |
3085 | Static ehci_soft_itd_t * | | 3085 | Static ehci_soft_itd_t * |
3086 | ehci_alloc_itd(ehci_softc_t *sc) | | 3086 | ehci_alloc_itd(ehci_softc_t *sc) |
3087 | { | | 3087 | { |
3088 | struct ehci_soft_itd *itd, *freeitd; | | 3088 | struct ehci_soft_itd *itd, *freeitd; |
3089 | usbd_status err; | | 3089 | usbd_status err; |
3090 | usb_dma_t dma; | | 3090 | usb_dma_t dma; |
3091 | | | 3091 | |
3092 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 3092 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
3093 | | | 3093 | |
3094 | mutex_enter(&sc->sc_lock); | | 3094 | mutex_enter(&sc->sc_lock); |
3095 | | | 3095 | |
3096 | freeitd = LIST_FIRST(&sc->sc_freeitds); | | 3096 | freeitd = LIST_FIRST(&sc->sc_freeitds); |
3097 | if (freeitd == NULL) { | | 3097 | if (freeitd == NULL) { |
3098 | DPRINTF("allocating chunk", 0, 0, 0, 0); | | 3098 | DPRINTF("allocating chunk", 0, 0, 0, 0); |
3099 | mutex_exit(&sc->sc_lock); | | 3099 | mutex_exit(&sc->sc_lock); |
3100 | err = usb_allocmem(&sc->sc_bus, EHCI_ITD_SIZE * EHCI_ITD_CHUNK, | | 3100 | err = usb_allocmem(&sc->sc_bus, EHCI_ITD_SIZE * EHCI_ITD_CHUNK, |
3101 | EHCI_PAGE_SIZE, &dma); | | 3101 | EHCI_PAGE_SIZE, &dma); |
3102 | | | 3102 | |
3103 | if (err) { | | 3103 | if (err) { |
3104 | DPRINTF("alloc returned %jd", err, 0, 0, 0); | | 3104 | DPRINTF("alloc returned %jd", err, 0, 0, 0); |
3105 | return NULL; | | 3105 | return NULL; |
3106 | } | | 3106 | } |
3107 | mutex_enter(&sc->sc_lock); | | 3107 | mutex_enter(&sc->sc_lock); |
3108 | | | 3108 | |
3109 | for (int i = 0; i < EHCI_ITD_CHUNK; i++) { | | 3109 | for (int i = 0; i < EHCI_ITD_CHUNK; i++) { |
3110 | int offs = i * EHCI_ITD_SIZE; | | 3110 | int offs = i * EHCI_ITD_SIZE; |
3111 | itd = KERNADDR(&dma, offs); | | 3111 | itd = KERNADDR(&dma, offs); |
3112 | itd->physaddr = DMAADDR(&dma, offs); | | 3112 | itd->physaddr = DMAADDR(&dma, offs); |
3113 | itd->dma = dma; | | 3113 | itd->dma = dma; |
3114 | itd->offs = offs; | | 3114 | itd->offs = offs; |
3115 | LIST_INSERT_HEAD(&sc->sc_freeitds, itd, free_list); | | 3115 | LIST_INSERT_HEAD(&sc->sc_freeitds, itd, free_list); |
3116 | } | | 3116 | } |
3117 | freeitd = LIST_FIRST(&sc->sc_freeitds); | | 3117 | freeitd = LIST_FIRST(&sc->sc_freeitds); |
3118 | } | | 3118 | } |
3119 | | | 3119 | |
3120 | itd = freeitd; | | 3120 | itd = freeitd; |
3121 | LIST_REMOVE(itd, free_list); | | 3121 | LIST_REMOVE(itd, free_list); |
3122 | mutex_exit(&sc->sc_lock); | | 3122 | mutex_exit(&sc->sc_lock); |
3123 | memset(&itd->itd, 0, sizeof(ehci_itd_t)); | | 3123 | memset(&itd->itd, 0, sizeof(ehci_itd_t)); |
3124 | | | 3124 | |
3125 | itd->frame_list.next = NULL; | | 3125 | itd->frame_list.next = NULL; |
3126 | itd->frame_list.prev = NULL; | | 3126 | itd->frame_list.prev = NULL; |
3127 | itd->xfer_next = NULL; | | 3127 | itd->xfer_next = NULL; |
3128 | itd->slot = 0; | | 3128 | itd->slot = 0; |
3129 | | | 3129 | |
3130 | return itd; | | 3130 | return itd; |
3131 | } | | 3131 | } |
3132 | | | 3132 | |
3133 | Static ehci_soft_sitd_t * | | 3133 | Static ehci_soft_sitd_t * |
3134 | ehci_alloc_sitd(ehci_softc_t *sc) | | 3134 | ehci_alloc_sitd(ehci_softc_t *sc) |
3135 | { | | 3135 | { |
3136 | struct ehci_soft_sitd *sitd, *freesitd; | | 3136 | struct ehci_soft_sitd *sitd, *freesitd; |
3137 | usbd_status err; | | 3137 | usbd_status err; |
3138 | int i, offs; | | 3138 | int i, offs; |
3139 | usb_dma_t dma; | | 3139 | usb_dma_t dma; |
3140 | | | 3140 | |
3141 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 3141 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
3142 | | | 3142 | |
3143 | mutex_enter(&sc->sc_lock); | | 3143 | mutex_enter(&sc->sc_lock); |
3144 | freesitd = LIST_FIRST(&sc->sc_freesitds); | | 3144 | freesitd = LIST_FIRST(&sc->sc_freesitds); |
3145 | if (freesitd == NULL) { | | 3145 | if (freesitd == NULL) { |
3146 | DPRINTF("allocating chunk", 0, 0, 0, 0); | | 3146 | DPRINTF("allocating chunk", 0, 0, 0, 0); |
3147 | mutex_exit(&sc->sc_lock); | | 3147 | mutex_exit(&sc->sc_lock); |
3148 | err = usb_allocmem(&sc->sc_bus, EHCI_SITD_SIZE * EHCI_SITD_CHUNK, | | 3148 | err = usb_allocmem(&sc->sc_bus, EHCI_SITD_SIZE * EHCI_SITD_CHUNK, |
3149 | EHCI_PAGE_SIZE, &dma); | | 3149 | EHCI_PAGE_SIZE, &dma); |
3150 | | | 3150 | |
3151 | if (err) { | | 3151 | if (err) { |
3152 | DPRINTF("alloc returned %jd", err, 0, 0, | | 3152 | DPRINTF("alloc returned %jd", err, 0, 0, |
3153 | 0); | | 3153 | 0); |
3154 | return NULL; | | 3154 | return NULL; |
3155 | } | | 3155 | } |
3156 | | | 3156 | |
3157 | mutex_enter(&sc->sc_lock); | | 3157 | mutex_enter(&sc->sc_lock); |
3158 | for (i = 0; i < EHCI_SITD_CHUNK; i++) { | | 3158 | for (i = 0; i < EHCI_SITD_CHUNK; i++) { |
3159 | offs = i * EHCI_SITD_SIZE; | | 3159 | offs = i * EHCI_SITD_SIZE; |
3160 | sitd = KERNADDR(&dma, offs); | | 3160 | sitd = KERNADDR(&dma, offs); |
3161 | sitd->physaddr = DMAADDR(&dma, offs); | | 3161 | sitd->physaddr = DMAADDR(&dma, offs); |
3162 | sitd->dma = dma; | | 3162 | sitd->dma = dma; |
3163 | sitd->offs = offs; | | 3163 | sitd->offs = offs; |
3164 | LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, free_list); | | 3164 | LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, free_list); |
3165 | } | | 3165 | } |
3166 | freesitd = LIST_FIRST(&sc->sc_freesitds); | | 3166 | freesitd = LIST_FIRST(&sc->sc_freesitds); |
3167 | } | | 3167 | } |
3168 | | | 3168 | |
3169 | sitd = freesitd; | | 3169 | sitd = freesitd; |
3170 | LIST_REMOVE(sitd, free_list); | | 3170 | LIST_REMOVE(sitd, free_list); |
3171 | mutex_exit(&sc->sc_lock); | | 3171 | mutex_exit(&sc->sc_lock); |
3172 | | | 3172 | |
3173 | memset(&sitd->sitd, 0, sizeof(ehci_sitd_t)); | | 3173 | memset(&sitd->sitd, 0, sizeof(ehci_sitd_t)); |
3174 | | | 3174 | |
3175 | sitd->frame_list.next = NULL; | | 3175 | sitd->frame_list.next = NULL; |
3176 | sitd->frame_list.prev = NULL; | | 3176 | sitd->frame_list.prev = NULL; |
3177 | sitd->xfer_next = NULL; | | 3177 | sitd->xfer_next = NULL; |
3178 | sitd->slot = 0; | | 3178 | sitd->slot = 0; |
3179 | | | 3179 | |
3180 | return sitd; | | 3180 | return sitd; |
3181 | } | | 3181 | } |
3182 | | | 3182 | |
3183 | /****************/ | | 3183 | /****************/ |
3184 | | | 3184 | |
3185 | /* | | 3185 | /* |
3186 | * Close a reqular pipe. | | 3186 | * Close a reqular pipe. |
3187 | * Assumes that there are no pending transactions. | | 3187 | * Assumes that there are no pending transactions. |
3188 | */ | | 3188 | */ |
3189 | Static void | | 3189 | Static void |
3190 | ehci_close_pipe(struct usbd_pipe *pipe, ehci_soft_qh_t *head) | | 3190 | ehci_close_pipe(struct usbd_pipe *pipe, ehci_soft_qh_t *head) |
3191 | { | | 3191 | { |
3192 | struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); | | 3192 | struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); |
3193 | ehci_softc_t *sc = EHCI_PIPE2SC(pipe); | | 3193 | ehci_softc_t *sc = EHCI_PIPE2SC(pipe); |
3194 | ehci_soft_qh_t *sqh = epipe->sqh; | | 3194 | ehci_soft_qh_t *sqh = epipe->sqh; |
3195 | | | 3195 | |
3196 | KASSERT(mutex_owned(&sc->sc_lock)); | | 3196 | KASSERT(mutex_owned(&sc->sc_lock)); |
3197 | | | 3197 | |
3198 | ehci_rem_qh(sc, sqh, head); | | 3198 | ehci_rem_qh(sc, sqh, head); |
3199 | ehci_free_sqh(sc, epipe->sqh); | | 3199 | ehci_free_sqh(sc, epipe->sqh); |
3200 | } | | 3200 | } |
3201 | | | 3201 | |
3202 | /* | | 3202 | /* |
3203 | * Cancel or timeout a device request. We have two cases to deal with | | 3203 | * Cancel or timeout a device request. We have two cases to deal with |
3204 | * | | 3204 | * |
3205 | * 1) A driver wants to stop scheduled or inflight transfers | | 3205 | * 1) A driver wants to stop scheduled or inflight transfers |
3206 | * 2) A transfer has timed out | | 3206 | * 2) A transfer has timed out |
3207 | * | | 3207 | * |
3208 | * have (partially) happened since the hardware runs concurrently. | | 3208 | * have (partially) happened since the hardware runs concurrently. |
3209 | * | | 3209 | * |
3210 | * Transfer state is protected by the bus lock and we set the transfer status | | 3210 | * Transfer state is protected by the bus lock and we set the transfer status |
3211 | * as soon as either of the above happens (with bus lock held). | | 3211 | * as soon as either of the above happens (with bus lock held). |
3212 | * | | 3212 | * |
3213 | * Then we arrange for the hardware to tells us that it is not still | | 3213 | * Then we arrange for the hardware to tells us that it is not still |
3214 | * processing the TDs by setting the QH halted bit and wait for the ehci | | 3214 | * processing the TDs by setting the QH halted bit and wait for the ehci |
3215 | * door bell | | 3215 | * door bell |
3216 | */ | | 3216 | */ |
3217 | Static void | | 3217 | Static void |
3218 | ehci_abort_xfer(struct usbd_xfer *xfer, usbd_status status) | | 3218 | ehci_abort_xfer(struct usbd_xfer *xfer, usbd_status status) |
3219 | { | | 3219 | { |
3220 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 3220 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
3221 | struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); | | 3221 | struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
3222 | struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); | | 3222 | struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
3223 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); | | 3223 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
3224 | ehci_soft_qh_t *sqh = epipe->sqh; | | 3224 | ehci_soft_qh_t *sqh = epipe->sqh; |
3225 | ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; | | 3225 | ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; |
3226 | ehci_physaddr_t cur; | | 3226 | ehci_physaddr_t cur; |
3227 | uint32_t qhstatus; | | 3227 | uint32_t qhstatus; |
3228 | int hit; | | 3228 | int hit; |
3229 | | | 3229 | |
3230 | KASSERTMSG((status == USBD_CANCELLED || status == USBD_TIMEOUT), | | 3230 | KASSERTMSG((status == USBD_CANCELLED || status == USBD_TIMEOUT), |
3231 | "invalid status for abort: %d", (int)status); | | 3231 | "invalid status for abort: %d", (int)status); |
3232 | | | 3232 | |
3233 | DPRINTF("xfer=%#jx pipe=%#jx", (uintptr_t)xfer, (uintptr_t)epipe, 0, 0); | | 3233 | DPRINTF("xfer=%#jx pipe=%#jx", (uintptr_t)xfer, (uintptr_t)epipe, 0, 0); |
3234 | | | 3234 | |
3235 | KASSERT(mutex_owned(&sc->sc_lock)); | | 3235 | KASSERT(mutex_owned(&sc->sc_lock)); |
3236 | ASSERT_SLEEPABLE(); | | 3236 | ASSERT_SLEEPABLE(); |
3237 | | | 3237 | |
3238 | if (status == USBD_CANCELLED) { | | 3238 | if (status == USBD_CANCELLED) { |
3239 | /* | | 3239 | /* |
3240 | * We are synchronously aborting. Try to stop the | | 3240 | * We are synchronously aborting. Try to stop the |
3241 | * callout and task, but if we can't, wait for them to | | 3241 | * callout and task, but if we can't, wait for them to |
3242 | * complete. | | 3242 | * complete. |
3243 | */ | | 3243 | */ |
3244 | callout_halt(&xfer->ux_callout, &sc->sc_lock); | | 3244 | callout_halt(&xfer->ux_callout, &sc->sc_lock); |
3245 | usb_rem_task_wait(xfer->ux_pipe->up_dev, &xfer->ux_aborttask, | | 3245 | usb_rem_task_wait(xfer->ux_pipe->up_dev, &xfer->ux_aborttask, |
3246 | USB_TASKQ_HC, &sc->sc_lock); | | 3246 | USB_TASKQ_HC, &sc->sc_lock); |
3247 | } else { | | 3247 | } else { |
3248 | /* Otherwise, we are timing out. */ | | 3248 | /* Otherwise, we are timing out. */ |
3249 | KASSERT(status == USBD_TIMEOUT); | | 3249 | KASSERT(status == USBD_TIMEOUT); |
3250 | } | | 3250 | } |
3251 | | | 3251 | |
3252 | /* | | 3252 | /* |
3253 | * The xfer cannot have been cancelled already. It is the | | 3253 | * The xfer cannot have been cancelled already. It is the |
3254 | * responsibility of the caller of usbd_abort_pipe not to try | | 3254 | * responsibility of the caller of usbd_abort_pipe not to try |
3255 | * to abort a pipe multiple times, whether concurrently or | | 3255 | * to abort a pipe multiple times, whether concurrently or |
3256 | * sequentially. | | 3256 | * sequentially. |
3257 | */ | | 3257 | */ |
3258 | KASSERT(xfer->ux_status != USBD_CANCELLED); | | 3258 | KASSERT(xfer->ux_status != USBD_CANCELLED); |
3259 | | | 3259 | |
3260 | /* Only the timeout, which runs only once, can time it out. */ | | 3260 | /* Only the timeout, which runs only once, can time it out. */ |
3261 | KASSERT(xfer->ux_status != USBD_TIMEOUT); | | 3261 | KASSERT(xfer->ux_status != USBD_TIMEOUT); |
3262 | | | 3262 | |
3263 | /* If anyone else beat us, we're done. */ | | 3263 | /* If anyone else beat us, we're done. */ |
3264 | if (xfer->ux_status != USBD_IN_PROGRESS) | | 3264 | if (xfer->ux_status != USBD_IN_PROGRESS) |
3265 | return; | | 3265 | return; |
3266 | | | 3266 | |
3267 | /* We beat everyone else. Claim the status. */ | | 3267 | /* We beat everyone else. Claim the status. */ |
3268 | xfer->ux_status = status; | | 3268 | xfer->ux_status = status; |
3269 | | | 3269 | |
3270 | /* | | 3270 | /* |
3271 | * If we're dying, skip the hardware action and just notify the | | 3271 | * If we're dying, skip the hardware action and just notify the |
3272 | * software that we're done. | | 3272 | * software that we're done. |
3273 | */ | | 3273 | */ |
3274 | if (sc->sc_dying) { | | 3274 | if (sc->sc_dying) { |
3275 | goto dying; | | 3275 | goto dying; |
3276 | } | | 3276 | } |
3277 | | | 3277 | |
3278 | /* | | 3278 | /* |
3279 | * HC Step 1: Make interrupt routine and hardware ignore xfer. | | 3279 | * HC Step 1: Make interrupt routine and hardware ignore xfer. |
3280 | */ | | 3280 | */ |
3281 | ehci_del_intr_list(sc, exfer); | | 3281 | ehci_del_intr_list(sc, exfer); |
3282 | | | 3282 | |
3283 | usb_syncmem(&sqh->dma, | | 3283 | usb_syncmem(&sqh->dma, |
3284 | sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), | | 3284 | sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), |
3285 | sizeof(sqh->qh.qh_qtd.qtd_status), | | 3285 | sizeof(sqh->qh.qh_qtd.qtd_status), |
3286 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 3286 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
3287 | qhstatus = sqh->qh.qh_qtd.qtd_status; | | 3287 | qhstatus = sqh->qh.qh_qtd.qtd_status; |
3288 | sqh->qh.qh_qtd.qtd_status = qhstatus | htole32(EHCI_QTD_HALTED); | | 3288 | sqh->qh.qh_qtd.qtd_status = qhstatus | htole32(EHCI_QTD_HALTED); |
3289 | usb_syncmem(&sqh->dma, | | 3289 | usb_syncmem(&sqh->dma, |
3290 | sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), | | 3290 | sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), |
3291 | sizeof(sqh->qh.qh_qtd.qtd_status), | | 3291 | sizeof(sqh->qh.qh_qtd.qtd_status), |
3292 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 3292 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
3293 | | | 3293 | |
3294 | if (exfer->ex_type == EX_CTRL) { | | 3294 | if (exfer->ex_type == EX_CTRL) { |
3295 | fsqtd = exfer->ex_setup; | | 3295 | fsqtd = exfer->ex_setup; |
3296 | lsqtd = exfer->ex_status; | | 3296 | lsqtd = exfer->ex_status; |
3297 | } else { | | 3297 | } else { |
3298 | fsqtd = exfer->ex_sqtdstart; | | 3298 | fsqtd = exfer->ex_sqtdstart; |
3299 | lsqtd = exfer->ex_sqtdend; | | 3299 | lsqtd = exfer->ex_sqtdend; |
3300 | } | | 3300 | } |
3301 | for (sqtd = fsqtd; ; sqtd = sqtd->nextqtd) { | | 3301 | for (sqtd = fsqtd; ; sqtd = sqtd->nextqtd) { |
3302 | usb_syncmem(&sqtd->dma, | | 3302 | usb_syncmem(&sqtd->dma, |
3303 | sqtd->offs + offsetof(ehci_qtd_t, qtd_status), | | 3303 | sqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
3304 | sizeof(sqtd->qtd.qtd_status), | | 3304 | sizeof(sqtd->qtd.qtd_status), |
3305 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 3305 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
3306 | sqtd->qtd.qtd_status |= htole32(EHCI_QTD_HALTED); | | 3306 | sqtd->qtd.qtd_status |= htole32(EHCI_QTD_HALTED); |
3307 | usb_syncmem(&sqtd->dma, | | 3307 | usb_syncmem(&sqtd->dma, |
3308 | sqtd->offs + offsetof(ehci_qtd_t, qtd_status), | | 3308 | sqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
3309 | sizeof(sqtd->qtd.qtd_status), | | 3309 | sizeof(sqtd->qtd.qtd_status), |
3310 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 3310 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
3311 | if (sqtd == lsqtd) | | 3311 | if (sqtd == lsqtd) |
3312 | break; | | 3312 | break; |
3313 | } | | 3313 | } |
3314 | | | 3314 | |
3315 | /* | | 3315 | /* |
3316 | * HC Step 2: Wait until we know hardware has finished any possible | | 3316 | * HC Step 2: Wait until we know hardware has finished any possible |
3317 | * use of the xfer. | | 3317 | * use of the xfer. |
3318 | */ | | 3318 | */ |
3319 | ehci_sync_hc(sc); | | 3319 | ehci_sync_hc(sc); |
3320 | | | 3320 | |
3321 | /* | | 3321 | /* |
3322 | * HC Step 3: Remove any vestiges of the xfer from the hardware. | | 3322 | * HC Step 3: Remove any vestiges of the xfer from the hardware. |
3323 | * The complication here is that the hardware may have executed | | 3323 | * The complication here is that the hardware may have executed |
3324 | * beyond the xfer we're trying to abort. So as we're scanning | | 3324 | * beyond the xfer we're trying to abort. So as we're scanning |
3325 | * the TDs of this xfer we check if the hardware points to | | 3325 | * the TDs of this xfer we check if the hardware points to |
3326 | * any of them. | | 3326 | * any of them. |
3327 | */ | | 3327 | */ |
3328 | | | 3328 | |
3329 | usb_syncmem(&sqh->dma, | | 3329 | usb_syncmem(&sqh->dma, |
3330 | sqh->offs + offsetof(ehci_qh_t, qh_curqtd), | | 3330 | sqh->offs + offsetof(ehci_qh_t, qh_curqtd), |
3331 | sizeof(sqh->qh.qh_curqtd), | | 3331 | sizeof(sqh->qh.qh_curqtd), |
3332 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 3332 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
3333 | cur = EHCI_LINK_ADDR(le32toh(sqh->qh.qh_curqtd)); | | 3333 | cur = EHCI_LINK_ADDR(le32toh(sqh->qh.qh_curqtd)); |
3334 | hit = 0; | | 3334 | hit = 0; |
3335 | for (sqtd = fsqtd; ; sqtd = sqtd->nextqtd) { | | 3335 | for (sqtd = fsqtd; ; sqtd = sqtd->nextqtd) { |
3336 | hit |= cur == sqtd->physaddr; | | 3336 | hit |= cur == sqtd->physaddr; |
3337 | if (sqtd == lsqtd) | | 3337 | if (sqtd == lsqtd) |
3338 | break; | | 3338 | break; |
3339 | } | | 3339 | } |
3340 | sqtd = sqtd->nextqtd; | | 3340 | sqtd = sqtd->nextqtd; |
3341 | /* Zap curqtd register if hardware pointed inside the xfer. */ | | 3341 | /* Zap curqtd register if hardware pointed inside the xfer. */ |
3342 | if (hit && sqtd != NULL) { | | 3342 | if (hit && sqtd != NULL) { |
3343 | DPRINTF("cur=0x%08jx", sqtd->physaddr, 0, 0, 0); | | 3343 | DPRINTF("cur=0x%08jx", sqtd->physaddr, 0, 0, 0); |
3344 | sqh->qh.qh_curqtd = htole32(sqtd->physaddr); /* unlink qTDs */ | | 3344 | sqh->qh.qh_curqtd = htole32(sqtd->physaddr); /* unlink qTDs */ |
3345 | usb_syncmem(&sqh->dma, | | 3345 | usb_syncmem(&sqh->dma, |
3346 | sqh->offs + offsetof(ehci_qh_t, qh_curqtd), | | 3346 | sqh->offs + offsetof(ehci_qh_t, qh_curqtd), |
3347 | sizeof(sqh->qh.qh_curqtd), | | 3347 | sizeof(sqh->qh.qh_curqtd), |
3348 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 3348 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
3349 | sqh->qh.qh_qtd.qtd_status = qhstatus; | | 3349 | sqh->qh.qh_qtd.qtd_status = qhstatus; |
3350 | usb_syncmem(&sqh->dma, | | 3350 | usb_syncmem(&sqh->dma, |
3351 | sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), | | 3351 | sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), |
3352 | sizeof(sqh->qh.qh_qtd.qtd_status), | | 3352 | sizeof(sqh->qh.qh_qtd.qtd_status), |
3353 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 3353 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
3354 | } else { | | 3354 | } else { |
3355 | DPRINTF("no hit", 0, 0, 0, 0); | | 3355 | DPRINTF("no hit", 0, 0, 0, 0); |
3356 | usb_syncmem(&sqh->dma, | | 3356 | usb_syncmem(&sqh->dma, |
3357 | sqh->offs + offsetof(ehci_qh_t, qh_curqtd), | | 3357 | sqh->offs + offsetof(ehci_qh_t, qh_curqtd), |
3358 | sizeof(sqh->qh.qh_curqtd), | | 3358 | sizeof(sqh->qh.qh_curqtd), |
3359 | BUS_DMASYNC_PREREAD); | | 3359 | BUS_DMASYNC_PREREAD); |
3360 | } | | 3360 | } |
3361 | | | 3361 | |
3362 | /* | | 3362 | /* |
3363 | * Final step: Notify completion to waiting xfers. | | 3363 | * Final step: Notify completion to waiting xfers. |
3364 | */ | | 3364 | */ |
3365 | dying: | | 3365 | dying: |
3366 | #ifdef DIAGNOSTIC | | 3366 | #ifdef DIAGNOSTIC |
3367 | exfer->ex_isdone = true; | | 3367 | exfer->ex_isdone = true; |
3368 | #endif | | 3368 | #endif |
3369 | usb_transfer_complete(xfer); | | 3369 | usb_transfer_complete(xfer); |
3370 | DPRINTFN(14, "end", 0, 0, 0, 0); | | 3370 | DPRINTFN(14, "end", 0, 0, 0, 0); |
3371 | | | 3371 | |
3372 | KASSERT(mutex_owned(&sc->sc_lock)); | | 3372 | KASSERT(mutex_owned(&sc->sc_lock)); |
3373 | } | | 3373 | } |
3374 | | | 3374 | |
3375 | Static void | | 3375 | Static void |
3376 | ehci_abort_isoc_xfer(struct usbd_xfer *xfer, usbd_status status) | | 3376 | ehci_abort_isoc_xfer(struct usbd_xfer *xfer, usbd_status status) |
3377 | { | | 3377 | { |
3378 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 3378 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
3379 | ehci_isoc_trans_t trans_status; | | 3379 | ehci_isoc_trans_t trans_status; |
3380 | struct ehci_xfer *exfer; | | 3380 | struct ehci_xfer *exfer; |
3381 | ehci_softc_t *sc; | | 3381 | ehci_softc_t *sc; |
3382 | struct ehci_soft_itd *itd; | | 3382 | struct ehci_soft_itd *itd; |
3383 | struct ehci_soft_sitd *sitd; | | 3383 | struct ehci_soft_sitd *sitd; |
3384 | int i; | | 3384 | int i; |
3385 | | | 3385 | |
3386 | KASSERTMSG(status == USBD_CANCELLED, | | 3386 | KASSERTMSG(status == USBD_CANCELLED, |
3387 | "invalid status for abort: %d", (int)status); | | 3387 | "invalid status for abort: %d", (int)status); |
3388 | | | 3388 | |
3389 | exfer = EHCI_XFER2EXFER(xfer); | | 3389 | exfer = EHCI_XFER2EXFER(xfer); |
3390 | sc = EHCI_XFER2SC(xfer); | | 3390 | sc = EHCI_XFER2SC(xfer); |
3391 | | | 3391 | |
3392 | DPRINTF("xfer %#jx pipe %#jx", (uintptr_t)xfer, | | 3392 | DPRINTF("xfer %#jx pipe %#jx", (uintptr_t)xfer, |
3393 | (uintptr_t)xfer->ux_pipe, 0, 0); | | 3393 | (uintptr_t)xfer->ux_pipe, 0, 0); |
3394 | | | 3394 | |
3395 | KASSERT(mutex_owned(&sc->sc_lock)); | | 3395 | KASSERT(mutex_owned(&sc->sc_lock)); |
3396 | ASSERT_SLEEPABLE(); | | 3396 | ASSERT_SLEEPABLE(); |
3397 | | | 3397 | |
3398 | /* No timeout or task here. */ | | 3398 | /* No timeout or task here. */ |
3399 | | | 3399 | |
3400 | /* | | 3400 | /* |
3401 | * The xfer cannot have been cancelled already. It is the | | 3401 | * The xfer cannot have been cancelled already. It is the |
3402 | * responsibility of the caller of usbd_abort_pipe not to try | | 3402 | * responsibility of the caller of usbd_abort_pipe not to try |
3403 | * to abort a pipe multiple times, whether concurrently or | | 3403 | * to abort a pipe multiple times, whether concurrently or |
3404 | * sequentially. | | 3404 | * sequentially. |
3405 | */ | | 3405 | */ |
3406 | KASSERT(xfer->ux_status != USBD_CANCELLED); | | 3406 | KASSERT(xfer->ux_status != USBD_CANCELLED); |
3407 | | | 3407 | |
3408 | /* If anyone else beat us, we're done. */ | | 3408 | /* If anyone else beat us, we're done. */ |
3409 | if (xfer->ux_status != USBD_IN_PROGRESS) | | 3409 | if (xfer->ux_status != USBD_IN_PROGRESS) |
3410 | return; | | 3410 | return; |
3411 | | | 3411 | |
3412 | /* We beat everyone else. Claim the status. */ | | 3412 | /* We beat everyone else. Claim the status. */ |
3413 | xfer->ux_status = status; | | 3413 | xfer->ux_status = status; |
3414 | | | 3414 | |
3415 | /* | | 3415 | /* |
3416 | * If we're dying, skip the hardware action and just notify the | | 3416 | * If we're dying, skip the hardware action and just notify the |
3417 | * software that we're done. | | 3417 | * software that we're done. |
3418 | */ | | 3418 | */ |
3419 | if (sc->sc_dying) { | | 3419 | if (sc->sc_dying) { |
3420 | goto dying; | | 3420 | goto dying; |
3421 | } | | 3421 | } |
3422 | | | 3422 | |
3423 | /* | | 3423 | /* |
3424 | * HC Step 1: Make interrupt routine and hardware ignore xfer. | | 3424 | * HC Step 1: Make interrupt routine and hardware ignore xfer. |
3425 | */ | | 3425 | */ |
3426 | ehci_del_intr_list(sc, exfer); | | 3426 | ehci_del_intr_list(sc, exfer); |
3427 | | | 3427 | |
3428 | if (xfer->ux_pipe->up_dev->ud_speed == USB_SPEED_HIGH) { | | 3428 | if (xfer->ux_pipe->up_dev->ud_speed == USB_SPEED_HIGH) { |
3429 | for (itd = exfer->ex_itdstart; itd != NULL; | | 3429 | for (itd = exfer->ex_itdstart; itd != NULL; |
3430 | itd = itd->xfer_next) { | | 3430 | itd = itd->xfer_next) { |
3431 | usb_syncmem(&itd->dma, | | 3431 | usb_syncmem(&itd->dma, |
3432 | itd->offs + offsetof(ehci_itd_t, itd_ctl), | | 3432 | itd->offs + offsetof(ehci_itd_t, itd_ctl), |
3433 | sizeof(itd->itd.itd_ctl), | | 3433 | sizeof(itd->itd.itd_ctl), |
3434 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 3434 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
3435 | | | 3435 | |
3436 | for (i = 0; i < 8; i++) { | | 3436 | for (i = 0; i < 8; i++) { |
3437 | trans_status = le32toh(itd->itd.itd_ctl[i]); | | 3437 | trans_status = le32toh(itd->itd.itd_ctl[i]); |
3438 | trans_status &= ~EHCI_ITD_ACTIVE; | | 3438 | trans_status &= ~EHCI_ITD_ACTIVE; |
3439 | itd->itd.itd_ctl[i] = htole32(trans_status); | | 3439 | itd->itd.itd_ctl[i] = htole32(trans_status); |
3440 | } | | 3440 | } |
3441 | | | 3441 | |
3442 | usb_syncmem(&itd->dma, | | 3442 | usb_syncmem(&itd->dma, |
3443 | itd->offs + offsetof(ehci_itd_t, itd_ctl), | | 3443 | itd->offs + offsetof(ehci_itd_t, itd_ctl), |
3444 | sizeof(itd->itd.itd_ctl), | | 3444 | sizeof(itd->itd.itd_ctl), |
3445 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 3445 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
3446 | } | | 3446 | } |
3447 | } else { | | 3447 | } else { |
3448 | for (sitd = exfer->ex_sitdstart; sitd != NULL; | | 3448 | for (sitd = exfer->ex_sitdstart; sitd != NULL; |
3449 | sitd = sitd->xfer_next) { | | 3449 | sitd = sitd->xfer_next) { |
3450 | usb_syncmem(&sitd->dma, | | 3450 | usb_syncmem(&sitd->dma, |
3451 | sitd->offs + offsetof(ehci_sitd_t, sitd_buffer), | | 3451 | sitd->offs + offsetof(ehci_sitd_t, sitd_buffer), |
3452 | sizeof(sitd->sitd.sitd_buffer), | | 3452 | sizeof(sitd->sitd.sitd_buffer), |
3453 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 3453 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
3454 | | | 3454 | |
3455 | trans_status = le32toh(sitd->sitd.sitd_trans); | | 3455 | trans_status = le32toh(sitd->sitd.sitd_trans); |
3456 | trans_status &= ~EHCI_SITD_ACTIVE; | | 3456 | trans_status &= ~EHCI_SITD_ACTIVE; |
3457 | sitd->sitd.sitd_trans = htole32(trans_status); | | 3457 | sitd->sitd.sitd_trans = htole32(trans_status); |
3458 | | | 3458 | |
3459 | usb_syncmem(&sitd->dma, | | 3459 | usb_syncmem(&sitd->dma, |
3460 | sitd->offs + offsetof(ehci_sitd_t, sitd_buffer), | | 3460 | sitd->offs + offsetof(ehci_sitd_t, sitd_buffer), |
3461 | sizeof(sitd->sitd.sitd_buffer), | | 3461 | sizeof(sitd->sitd.sitd_buffer), |
3462 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 3462 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
3463 | } | | 3463 | } |
3464 | } | | 3464 | } |
3465 | | | 3465 | |
3466 | dying: | | 3466 | dying: |
3467 | #ifdef DIAGNOSTIC | | 3467 | #ifdef DIAGNOSTIC |
3468 | exfer->ex_isdone = true; | | 3468 | exfer->ex_isdone = true; |
3469 | #endif | | 3469 | #endif |
3470 | usb_transfer_complete(xfer); | | 3470 | usb_transfer_complete(xfer); |
3471 | DPRINTFN(14, "end", 0, 0, 0, 0); | | 3471 | DPRINTFN(14, "end", 0, 0, 0, 0); |
3472 | | | 3472 | |
3473 | KASSERT(mutex_owned(&sc->sc_lock)); | | 3473 | KASSERT(mutex_owned(&sc->sc_lock)); |
3474 | } | | 3474 | } |
3475 | | | 3475 | |
3476 | Static void | | 3476 | Static void |
3477 | ehci_timeout(void *addr) | | 3477 | ehci_timeout(void *addr) |
3478 | { | | 3478 | { |
3479 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 3479 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
3480 | struct usbd_xfer *xfer = addr; | | 3480 | struct usbd_xfer *xfer = addr; |
3481 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); | | 3481 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
3482 | struct usbd_device *dev = xfer->ux_pipe->up_dev; | | 3482 | struct usbd_device *dev = xfer->ux_pipe->up_dev; |
3483 | | | 3483 | |
3484 | DPRINTF("xfer %#jx", (uintptr_t)xfer, 0, 0, 0); | | 3484 | DPRINTF("xfer %#jx", (uintptr_t)xfer, 0, 0, 0); |
3485 | #ifdef EHCI_DEBUG | | 3485 | #ifdef EHCI_DEBUG |
3486 | if (ehcidebug >= 2) { | | 3486 | if (ehcidebug >= 2) { |
3487 | struct usbd_pipe *pipe = xfer->ux_pipe; | | 3487 | struct usbd_pipe *pipe = xfer->ux_pipe; |
3488 | usbd_dump_pipe(pipe); | | 3488 | usbd_dump_pipe(pipe); |
3489 | } | | 3489 | } |
3490 | #endif | | 3490 | #endif |
3491 | | | 3491 | |
3492 | mutex_enter(&sc->sc_lock); | | 3492 | mutex_enter(&sc->sc_lock); |
3493 | if (!sc->sc_dying && xfer->ux_status == USBD_IN_PROGRESS) | | 3493 | if (!sc->sc_dying && xfer->ux_status == USBD_IN_PROGRESS) |
3494 | usb_add_task(dev, &xfer->ux_aborttask, USB_TASKQ_HC); | | 3494 | usb_add_task(dev, &xfer->ux_aborttask, USB_TASKQ_HC); |
3495 | mutex_exit(&sc->sc_lock); | | 3495 | mutex_exit(&sc->sc_lock); |
3496 | } | | 3496 | } |
3497 | | | 3497 | |
3498 | Static void | | 3498 | Static void |
3499 | ehci_timeout_task(void *addr) | | 3499 | ehci_timeout_task(void *addr) |
3500 | { | | 3500 | { |
3501 | struct usbd_xfer *xfer = addr; | | 3501 | struct usbd_xfer *xfer = addr; |
3502 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); | | 3502 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
3503 | | | 3503 | |
3504 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); | | 3504 | EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
3505 | | | 3505 | |
3506 | DPRINTF("xfer=%#jx", (uintptr_t)xfer, 0, 0, 0); | | 3506 | DPRINTF("xfer=%#jx", (uintptr_t)xfer, 0, 0, 0); |
3507 | | | 3507 | |
3508 | mutex_enter(&sc->sc_lock); | | 3508 | mutex_enter(&sc->sc_lock); |
3509 | ehci_abort_xfer(xfer, USBD_TIMEOUT); | | 3509 | ehci_abort_xfer(xfer, USBD_TIMEOUT); |
3510 | mutex_exit(&sc->sc_lock); | | 3510 | mutex_exit(&sc->sc_lock); |
3511 | } | | 3511 | } |
3512 | | | 3512 | |
3513 | /************************/ | | 3513 | /************************/ |
3514 | | | 3514 | |
3515 | Static int | | 3515 | Static int |
3516 | ehci_device_ctrl_init(struct usbd_xfer *xfer) | | 3516 | ehci_device_ctrl_init(struct usbd_xfer *xfer) |
3517 | { | | 3517 | { |
3518 | struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); | | 3518 | struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
3519 | struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); | | 3519 | struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
3520 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); | | 3520 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
3521 | usb_device_request_t *req = &xfer->ux_request; | | 3521 | usb_device_request_t *req = &xfer->ux_request; |
3522 | ehci_soft_qtd_t *setup, *status, *next; | | 3522 | ehci_soft_qtd_t *setup, *status, *next; |
3523 | int isread = req->bmRequestType & UT_READ; | | 3523 | int isread = req->bmRequestType & UT_READ; |
3524 | int len = xfer->ux_bufsize; | | 3524 | int len = xfer->ux_bufsize; |
3525 | int err; | | 3525 | int err; |
3526 | | | 3526 | |
3527 | exfer->ex_type = EX_CTRL; | | 3527 | exfer->ex_type = EX_CTRL; |
3528 | exfer->ex_status = NULL; | | 3528 | exfer->ex_status = NULL; |
3529 | exfer->ex_data = NULL; | | 3529 | exfer->ex_data = NULL; |
3530 | exfer->ex_setup = ehci_alloc_sqtd(sc); | | 3530 | exfer->ex_setup = ehci_alloc_sqtd(sc); |
3531 | if (exfer->ex_setup == NULL) { | | 3531 | if (exfer->ex_setup == NULL) { |
3532 | err = ENOMEM; | | 3532 | err = ENOMEM; |
3533 | goto bad1; | | 3533 | goto bad1; |
3534 | } | | 3534 | } |
3535 | exfer->ex_status = ehci_alloc_sqtd(sc); | | 3535 | exfer->ex_status = ehci_alloc_sqtd(sc); |
3536 | if (exfer->ex_status == NULL) { | | 3536 | if (exfer->ex_status == NULL) { |
3537 | err = ENOMEM; | | 3537 | err = ENOMEM; |
3538 | goto bad2; | | 3538 | goto bad2; |
3539 | } | | 3539 | } |
3540 | setup = exfer->ex_setup; | | 3540 | setup = exfer->ex_setup; |
3541 | status = exfer->ex_status; | | 3541 | status = exfer->ex_status; |
3542 | exfer->ex_nsqtd = 0; | | 3542 | exfer->ex_nsqtd = 0; |
3543 | next = status; | | 3543 | next = status; |
3544 | /* Set up data transaction */ | | 3544 | /* Set up data transaction */ |
3545 | if (len != 0) { | | 3545 | if (len != 0) { |
3546 | err = ehci_alloc_sqtd_chain(sc, xfer, len, isread, | | 3546 | err = ehci_alloc_sqtd_chain(sc, xfer, len, isread, |
3547 | &exfer->ex_data); | | 3547 | &exfer->ex_data); |
3548 | if (err) | | 3548 | if (err) |
3549 | goto bad3; | | 3549 | goto bad3; |
3550 | next = exfer->ex_data; | | 3550 | next = exfer->ex_data; |
3551 | } | | 3551 | } |
3552 | | | 3552 | |
3553 | /* Clear toggle */ | | 3553 | /* Clear toggle */ |
3554 | setup->qtd.qtd_status = htole32( | | 3554 | setup->qtd.qtd_status = htole32( |
3555 | EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | | | 3555 | EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | |
3556 | EHCI_QTD_SET_TOGGLE(0) | | | 3556 | EHCI_QTD_SET_TOGGLE(0) | |
3557 | EHCI_QTD_SET_BYTES(sizeof(*req)) | | 3557 | EHCI_QTD_SET_BYTES(sizeof(*req)) |
3558 | ); | | 3558 | ); |
3559 | setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->ctrl.reqdma, 0)); | | 3559 | setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->ctrl.reqdma, 0)); |
3560 | setup->qtd.qtd_buffer_hi[0] = 0; | | 3560 | setup->qtd.qtd_buffer_hi[0] = 0; |
3561 | setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr); | | 3561 | setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr); |
3562 | setup->nextqtd = next; | | 3562 | setup->nextqtd = next; |
3563 | setup->xfer = xfer; | | 3563 | setup->xfer = xfer; |
3564 | setup->len = sizeof(*req); | | 3564 | setup->len = sizeof(*req); |
3565 | | | 3565 | |
3566 | status->qtd.qtd_status = htole32( | | 3566 | status->qtd.qtd_status = htole32( |
3567 | EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | | | 3567 | EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | |
3568 | EHCI_QTD_SET_TOGGLE(1) | | | 3568 | EHCI_QTD_SET_TOGGLE(1) | |
3569 | EHCI_QTD_IOC | | 3569 | EHCI_QTD_IOC |
3570 | ); | | 3570 | ); |
3571 | status->qtd.qtd_buffer[0] = 0; | | 3571 | status->qtd.qtd_buffer[0] = 0; |
3572 | status->qtd.qtd_buffer_hi[0] = 0; | | 3572 | status->qtd.qtd_buffer_hi[0] = 0; |
3573 | status->qtd.qtd_next = status->qtd.qtd_altnext = EHCI_NULL; | | 3573 | status->qtd.qtd_next = status->qtd.qtd_altnext = EHCI_NULL; |
3574 | status->nextqtd = NULL; | | 3574 | status->nextqtd = NULL; |
3575 | status->xfer = xfer; | | 3575 | status->xfer = xfer; |
3576 | status->len = 0; | | 3576 | status->len = 0; |
3577 | | | 3577 | |
3578 | return 0; | | 3578 | return 0; |
3579 | bad3: | | 3579 | bad3: |
3580 | ehci_free_sqtd(sc, exfer->ex_status); | | 3580 | ehci_free_sqtd(sc, exfer->ex_status); |
3581 | bad2: | | 3581 | bad2: |
3582 | ehci_free_sqtd(sc, exfer->ex_setup); | | 3582 | ehci_free_sqtd(sc, exfer->ex_setup); |
3583 | bad1: | | 3583 | bad1: |
3584 | return err; | | 3584 | return err; |
3585 | } | | 3585 | } |
3586 | | | 3586 | |
3587 | Static void | | 3587 | Static void |
3588 | ehci_device_ctrl_fini(struct usbd_xfer *xfer) | | 3588 | ehci_device_ctrl_fini(struct usbd_xfer *xfer) |
3589 | { | | 3589 | { |
3590 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); | | 3590 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
3591 | struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer); | | 3591 | struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer); |
3592 | | | 3592 | |
3593 | KASSERT(ex->ex_type == EX_CTRL); | | 3593 | KASSERT(ex->ex_type == EX_CTRL); |
3594 | | | 3594 | |
3595 | ehci_free_sqtd(sc, ex->ex_setup); | | 3595 | ehci_free_sqtd(sc, ex->ex_setup); |
3596 | ehci_free_sqtd(sc, ex->ex_status); | | 3596 | ehci_free_sqtd(sc, ex->ex_status); |
3597 | ehci_free_sqtds(sc, ex); | | 3597 | ehci_free_sqtds(sc, ex); |
3598 | if (ex->ex_nsqtd) | | 3598 | if (ex->ex_nsqtd) |
3599 | kmem_free(ex->ex_sqtds, | | 3599 | kmem_free(ex->ex_sqtds, |
3600 | sizeof(ehci_soft_qtd_t *) * ex->ex_nsqtd); | | 3600 | sizeof(ehci_soft_qtd_t *) * ex->ex_nsqtd); |
3601 | } | | 3601 | } |
3602 | | | 3602 | |
3603 | Static usbd_status | | 3603 | Static usbd_status |
3604 | ehci_device_ctrl_transfer(struct usbd_xfer *xfer) | | 3604 | ehci_device_ctrl_transfer(struct usbd_xfer *xfer) |
3605 | { | | 3605 | { |
3606 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); | | 3606 | ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
3607 | usbd_status err; | | 3607 | usbd_status err; |
3608 | | | 3608 | |
3609 | /* Insert last in queue. */ | | 3609 | /* Insert last in queue. */ |
3610 | mutex_enter(&sc->sc_lock); | | 3610 | mutex_enter(&sc->sc_lock); |
3611 | err = usb_insert_transfer(xfer); | | 3611 | err = usb_insert_transfer(xfer); |