FreeBSD kernel usb device Code
ehci_fsl.c
Go to the documentation of this file.
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2010-2012 Semihalf
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include "opt_bus.h"
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/module.h>
38#include <sys/bus.h>
39#include <sys/queue.h>
40#include <sys/lock.h>
41#include <sys/lockmgr.h>
42#include <sys/condvar.h>
43#include <sys/rman.h>
44
45#include <dev/ofw/ofw_bus.h>
46#include <dev/ofw/ofw_bus_subr.h>
47
48#include <dev/usb/usb.h>
49#include <dev/usb/usbdi.h>
50#include <dev/usb/usb_core.h>
51#include <dev/usb/usb_busdma.h>
52#include <dev/usb/usb_process.h>
53#include <dev/usb/usb_util.h>
55#include <dev/usb/usb_bus.h>
58
59#include <machine/bus.h>
60#include <machine/clock.h>
61#include <machine/resource.h>
62
63#include <powerpc/include/tlb.h>
64
65#include "opt_platform.h"
66
67/*
68 * Register the driver
69 */
70/* Forward declarations */
71static int fsl_ehci_attach(device_t self);
72static int fsl_ehci_detach(device_t self);
73static int fsl_ehci_probe(device_t self);
74
75static device_method_t ehci_methods[] = {
76 /* Device interface */
77 DEVMETHOD(device_probe, fsl_ehci_probe),
78 DEVMETHOD(device_attach, fsl_ehci_attach),
79 DEVMETHOD(device_detach, fsl_ehci_detach),
80 DEVMETHOD(device_suspend, bus_generic_suspend),
81 DEVMETHOD(device_resume, bus_generic_resume),
82 DEVMETHOD(device_shutdown, bus_generic_shutdown),
83
84 /* Bus interface */
85 DEVMETHOD(bus_print_child, bus_generic_print_child),
86 { 0, 0 }
87};
88
89/* kobj_class definition */
90static driver_t ehci_driver = {
91 "ehci",
93 sizeof(struct ehci_softc)
94};
95
96static devclass_t ehci_devclass;
97
98DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0);
99MODULE_DEPEND(ehci, usb, 1, 1, 1);
100
101/*
102 * Private defines
103 */
104#define FSL_EHCI_REG_OFF 0x100
105#define FSL_EHCI_REG_SIZE 0x300
106
107/*
108 * Internal interface registers' offsets.
109 * Offsets from 0x000 ehci dev space, big-endian access.
110 */
112 SNOOP1 = 0x400,
113 SNOOP2 = 0x404,
115 SI_CTRL = 0x410,
116 CONTROL = 0x500
118
119/* CONTROL register bit flags */
121 USB_EN = 0x00000004,
122 UTMI_PHY_EN = 0x00000200,
123 ULPI_INT_EN = 0x00000001
125
126/* SI_CTRL register bit flags */
129 FETCH_64 = 0
131
132#define SNOOP_RANGE_2GB 0x1E
133
134/*
135 * Operational registers' offsets.
136 * Offsets from USBCMD register, little-endian access.
137 */
139 USBMODE = 0x0A8,
140 PORTSC = 0x084,
141 ULPI_VIEWPORT = 0x70
143
144/* USBMODE register bit flags */
147 DEVICE_MODE = 0x2
149
150#define PORT_POWER_MASK 0x00001000
151
152/*
153 * Private methods
154 */
155
156static void
158{
159 int tmp;
160
161 tmp = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, USBMODE);
162 bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, USBMODE, tmp | HOST_MODE);
163}
164
165static void
166enable_usb(device_t dev, bus_space_tag_t iot, bus_space_handle_t ioh)
167{
168 int tmp;
169 phandle_t node;
170 char *phy_type;
171
172 phy_type = NULL;
173 tmp = bus_space_read_4(iot, ioh, CONTROL) | USB_EN;
174
175 node = ofw_bus_get_node(dev);
176 if ((node != 0) &&
177 (OF_getprop_alloc(node, "phy_type", (void **)&phy_type) > 0)) {
178 if (strncasecmp(phy_type, "utmi", strlen("utmi")) == 0)
179 tmp |= UTMI_PHY_EN;
180 OF_prop_free(phy_type);
181 }
182 bus_space_write_4(iot, ioh, CONTROL, tmp);
183}
184
185static void
186set_32b_prefetch(bus_space_tag_t iot, bus_space_handle_t ioh)
187{
188
189 bus_space_write_4(iot, ioh, SI_CTRL, FETCH_32);
190}
191
192static void
193set_snooping(bus_space_tag_t iot, bus_space_handle_t ioh)
194{
195
196 bus_space_write_4(iot, ioh, SNOOP1, SNOOP_RANGE_2GB);
197 bus_space_write_4(iot, ioh, SNOOP2, 0x80000000 | SNOOP_RANGE_2GB);
198}
199
200static void
202{
203 int tmp;
204
205 tmp = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, PORTSC);
206 bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, PORTSC, tmp & ~PORT_POWER_MASK);
207}
208
209/*
210 * Public methods
211 */
212static int
213fsl_ehci_probe(device_t dev)
214{
215
216 if (!ofw_bus_status_okay(dev))
217 return (ENXIO);
218
219 if (((ofw_bus_is_compatible(dev, "fsl-usb2-dr")) == 0) &&
220 ((ofw_bus_is_compatible(dev, "fsl-usb2-mph")) == 0))
221 return (ENXIO);
222
223 device_set_desc(dev, "Freescale integrated EHCI controller");
224
225 return (BUS_PROBE_DEFAULT);
226}
227
228static int
229fsl_ehci_attach(device_t self)
230{
231 ehci_softc_t *sc;
232 int rid;
233 int err;
234 bus_space_handle_t ioh;
235 bus_space_tag_t iot;
236
237 sc = device_get_softc(self);
238 rid = 0;
239
240 sc->sc_bus.parent = self;
241 sc->sc_bus.devices = sc->sc_devices;
243 sc->sc_bus.dma_bits = 32;
244
247 return (ENOMEM);
248
249 /* Allocate io resource for EHCI */
250 sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
251 RF_ACTIVE);
252 if (sc->sc_io_res == NULL) {
253 err = fsl_ehci_detach(self);
254 if (err) {
255 device_printf(self,
256 "Detach of the driver failed with error %d\n",
257 err);
258 }
259 return (ENXIO);
260 }
261 iot = rman_get_bustag(sc->sc_io_res);
262
263 /*
264 * Set handle to USB related registers subregion used by generic
265 * EHCI driver
266 */
267 ioh = rman_get_bushandle(sc->sc_io_res);
268
269 err = bus_space_subregion(iot, ioh, FSL_EHCI_REG_OFF, FSL_EHCI_REG_SIZE,
270 &sc->sc_io_hdl);
271 if (err != 0) {
272 err = fsl_ehci_detach(self);
273 if (err) {
274 device_printf(self,
275 "Detach of the driver failed with error %d\n",
276 err);
277 }
278 return (ENXIO);
279 }
280
281 /* Set little-endian tag for use by the generic EHCI driver */
282 sc->sc_io_tag = &bs_le_tag;
283
284 /* Allocate irq */
285 sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
286 RF_ACTIVE);
287 if (sc->sc_irq_res == NULL) {
288 err = fsl_ehci_detach(self);
289 if (err) {
290 device_printf(self,
291 "Detach of the driver failed with error %d\n",
292 err);
293 }
294 return (ENXIO);
295 }
296
297 /* Setup interrupt handler */
298 err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
299 NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl);
300 if (err) {
301 device_printf(self, "Could not setup irq, %d\n", err);
302 sc->sc_intr_hdl = NULL;
303 err = fsl_ehci_detach(self);
304 if (err) {
305 device_printf(self,
306 "Detach of the driver failed with error %d\n",
307 err);
308 }
309 return (ENXIO);
310 }
311
312 /* Add USB device */
313 sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
314 if (!sc->sc_bus.bdev) {
315 device_printf(self, "Could not add USB device\n");
316 err = fsl_ehci_detach(self);
317 if (err) {
318 device_printf(self,
319 "Detach of the driver failed with error %d\n",
320 err);
321 }
322 return (ENOMEM);
323 }
324 device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
325
326 sc->sc_id_vendor = 0x1234;
327 strlcpy(sc->sc_vendor, "Freescale", sizeof(sc->sc_vendor));
328
329 /* Enable USB */
330 err = ehci_reset(sc);
331 if (err) {
332 device_printf(self, "Could not reset the controller\n");
333 err = fsl_ehci_detach(self);
334 if (err) {
335 device_printf(self,
336 "Detach of the driver failed with error %d\n",
337 err);
338 }
339 return (ENXIO);
340 }
341
342 enable_usb(self, iot, ioh);
343 set_snooping(iot, ioh);
345 set_32b_prefetch(iot, ioh);
346
347 /*
348 * If usb subsystem is enabled in U-Boot, port power has to be turned
349 * off to allow proper discovery of devices during boot up.
350 */
352
353 /* Set flags */
355
356 err = ehci_init(sc);
357 if (!err) {
359 err = device_probe_and_attach(sc->sc_bus.bdev);
360 }
361
362 if (err) {
363 device_printf(self, "USB init failed err=%d\n", err);
364 err = fsl_ehci_detach(self);
365 if (err) {
366 device_printf(self,
367 "Detach of the driver failed with error %d\n",
368 err);
369 }
370 return (EIO);
371 }
372
373 return (0);
374}
375
376static int
377fsl_ehci_detach(device_t self)
378{
379
380 int err;
381 ehci_softc_t *sc;
382
383 sc = device_get_softc(self);
384 /*
385 * only call ehci_detach() after ehci_init()
386 */
387 if (sc->sc_flags & EHCI_SCFLG_DONEINIT) {
388 ehci_detach(sc);
389 sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
390 }
391
392 /* Disable interrupts that might have been switched on in ehci_init */
393 if (sc->sc_io_tag && sc->sc_io_hdl)
394 bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, EHCI_USBINTR, 0);
395
396 if (sc->sc_irq_res && sc->sc_intr_hdl) {
397 err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
398 if (err) {
399 device_printf(self, "Could not tear down irq, %d\n",
400 err);
401 return (err);
402 }
403 sc->sc_intr_hdl = NULL;
404 }
405
406 if (sc->sc_bus.bdev) {
407 device_delete_child(self, sc->sc_bus.bdev);
408 sc->sc_bus.bdev = NULL;
409 }
410
411 /* During module unload there are lots of children leftover */
412 device_delete_children(self);
413
414 if (sc->sc_irq_res) {
415 bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
416 sc->sc_irq_res = NULL;
417 }
418
419 if (sc->sc_io_res) {
420 bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res);
421 sc->sc_io_res = NULL;
422 sc->sc_io_tag = 0;
423 sc->sc_io_hdl = 0;
424 }
425
426 return (0);
427}
void ehci_interrupt(ehci_softc_t *sc)
Definition: ehci.c:1451
void ehci_detach(ehci_softc_t *sc)
Definition: ehci.c:553
usb_error_t ehci_reset(ehci_softc_t *sc)
Definition: ehci.c:184
usb_error_t ehci_init(ehci_softc_t *sc)
Definition: ehci.c:281
#define EHCI_SCFLG_DONEINIT
Definition: ehci.h:347
#define EHCI_SCFLG_DONTRESET
Definition: ehci.h:346
#define EHCI_SCFLG_NORESTERM
Definition: ehci.h:341
usb_bus_mem_cb_t ehci_iterate_hw_softc
Definition: ehci.h:445
#define EHCI_MAX_DEVICES
Definition: ehci.h:36
static void set_snooping(bus_space_tag_t iot, bus_space_handle_t ioh)
Definition: ehci_fsl.c:193
static void set_32b_prefetch(bus_space_tag_t iot, bus_space_handle_t ioh)
Definition: ehci_fsl.c:186
internal_reg
Definition: ehci_fsl.c:111
@ SNOOP2
Definition: ehci_fsl.c:113
@ SNOOP1
Definition: ehci_fsl.c:112
@ SI_CTRL
Definition: ehci_fsl.c:115
@ CONTROL
Definition: ehci_fsl.c:116
@ AGE_CNT_THRESH
Definition: ehci_fsl.c:114
static int fsl_ehci_attach(device_t self)
Definition: ehci_fsl.c:229
static int fsl_ehci_probe(device_t self)
Definition: ehci_fsl.c:213
MODULE_DEPEND(ehci, usb, 1, 1, 1)
#define FSL_EHCI_REG_OFF
Definition: ehci_fsl.c:104
static void clear_port_power(ehci_softc_t *sc)
Definition: ehci_fsl.c:201
si_ctrl_flags
Definition: ehci_fsl.c:127
@ FETCH_64
Definition: ehci_fsl.c:129
@ FETCH_32
Definition: ehci_fsl.c:128
#define PORT_POWER_MASK
Definition: ehci_fsl.c:150
__FBSDID("$FreeBSD$")
static void set_to_host_mode(ehci_softc_t *sc)
Definition: ehci_fsl.c:157
static int fsl_ehci_detach(device_t self)
Definition: ehci_fsl.c:377
static devclass_t ehci_devclass
Definition: ehci_fsl.c:96
usbmode_flags
Definition: ehci_fsl.c:145
@ DEVICE_MODE
Definition: ehci_fsl.c:147
@ HOST_MODE
Definition: ehci_fsl.c:146
DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0)
static void enable_usb(device_t dev, bus_space_tag_t iot, bus_space_handle_t ioh)
Definition: ehci_fsl.c:166
static device_method_t ehci_methods[]
Definition: ehci_fsl.c:75
#define SNOOP_RANGE_2GB
Definition: ehci_fsl.c:132
#define FSL_EHCI_REG_SIZE
Definition: ehci_fsl.c:105
special_op_reg
Definition: ehci_fsl.c:138
@ ULPI_VIEWPORT
Definition: ehci_fsl.c:141
@ PORTSC
Definition: ehci_fsl.c:140
@ USBMODE
Definition: ehci_fsl.c:139
control_flags
Definition: ehci_fsl.c:120
@ USB_EN
Definition: ehci_fsl.c:121
@ ULPI_INT_EN
Definition: ehci_fsl.c:123
@ UTMI_PHY_EN
Definition: ehci_fsl.c:122
static driver_t ehci_driver
Definition: ehci_fsl.c:90
#define EHCI_USBINTR
Definition: ehcireg.h:117
uint16_t rid
device_t dev
struct usb_device * sc_devices[EHCI_MAX_DEVICES]
Definition: ehci.h:323
char sc_vendor[16]
Definition: ehci.h:357
struct resource * sc_irq_res
Definition: ehci.h:325
void * sc_intr_hdl
Definition: ehci.h:330
bus_space_handle_t sc_io_hdl
Definition: ehci.h:333
struct resource * sc_io_res
Definition: ehci.h:324
uint16_t sc_id_vendor
Definition: ehci.h:339
bus_space_tag_t sc_io_tag
Definition: ehci.h:332
uint16_t sc_flags
Definition: ehci.h:340
struct usb_bus sc_bus
Definition: ehci.h:318
device_t bdev
Definition: usb_bus.h:101
device_t parent
Definition: usb_bus.h:100
struct usb_device ** devices
Definition: usb_bus.h:108
uint8_t devices_max
Definition: usb_bus.h:121
uint8_t dma_bits
Definition: usb_bus.h:124
#define USB_GET_DMA_TAG(dev)
Definition: usb_busdma.h:46
uint8_t usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, usb_bus_mem_cb_t *cb)
INTERFACE usb
Definition: usb_if.m:35