Removed
Link Here
|
1 |
/*- |
2 |
* Copyright (c) 2006 M. Warner Losh. All rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions |
6 |
* are met: |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
* notice, this list of conditions and the following disclaimer in the |
11 |
* documentation and/or other materials provided with the distribution. |
12 |
* |
13 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
14 |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
15 |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
16 |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
17 |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
18 |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
19 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
20 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
21 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
22 |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
23 |
*/ |
24 |
|
25 |
#include <sys/cdefs.h> |
26 |
__FBSDID("$FreeBSD$"); |
27 |
|
28 |
#include <sys/stdint.h> |
29 |
#include <sys/stddef.h> |
30 |
#include <sys/param.h> |
31 |
#include <sys/queue.h> |
32 |
#include <sys/types.h> |
33 |
#include <sys/systm.h> |
34 |
#include <sys/kernel.h> |
35 |
#include <sys/bus.h> |
36 |
#include <sys/module.h> |
37 |
#include <sys/lock.h> |
38 |
#include <sys/mutex.h> |
39 |
#include <sys/condvar.h> |
40 |
#include <sys/sysctl.h> |
41 |
#include <sys/sx.h> |
42 |
#include <sys/unistd.h> |
43 |
#include <sys/callout.h> |
44 |
#include <sys/malloc.h> |
45 |
#include <sys/priv.h> |
46 |
|
47 |
#include <dev/usb/usb.h> |
48 |
#include <dev/usb/usbdi.h> |
49 |
|
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> |
54 |
|
55 |
#include <dev/usb/usb_controller.h> |
56 |
#include <dev/usb/usb_bus.h> |
57 |
#include <dev/usb/controller/ohci.h> |
58 |
#include <dev/usb/controller/ohcireg.h> |
59 |
|
60 |
#include <sys/rman.h> |
61 |
|
62 |
#include <arm/at91/at91_pmcvar.h> |
63 |
|
64 |
#include <dev/fdt/fdt_common.h> |
65 |
#include <dev/ofw/ofw_bus.h> |
66 |
#include <dev/ofw/ofw_bus_subr.h> |
67 |
|
68 |
#define MEM_RID 0 |
69 |
|
70 |
static device_probe_t ohci_at91_fdt_probe; |
71 |
static device_attach_t ohci_at91_fdt_attach; |
72 |
static device_detach_t ohci_at91_fdt_detach; |
73 |
|
74 |
struct at91_ohci_softc { |
75 |
struct ohci_softc sc_ohci; /* must be first */ |
76 |
struct at91_pmc_clock *mclk; |
77 |
struct at91_pmc_clock *iclk; |
78 |
struct at91_pmc_clock *fclk; |
79 |
}; |
80 |
|
81 |
static int |
82 |
ohci_at91_fdt_probe(device_t dev) |
83 |
{ |
84 |
if (!ofw_bus_is_compatible(dev, "atmel,at91rm9200-ohci")) |
85 |
return (ENXIO); |
86 |
device_set_desc(dev, "AT91 integrated OHCI controller"); |
87 |
|
88 |
return (BUS_PROBE_DEFAULT); |
89 |
} |
90 |
|
91 |
static int |
92 |
ohci_at91_fdt_attach(device_t dev) |
93 |
{ |
94 |
struct at91_ohci_softc *sc = device_get_softc(dev); |
95 |
int err; |
96 |
int rid; |
97 |
|
98 |
/* initialise some bus fields */ |
99 |
sc->sc_ohci.sc_bus.parent = dev; |
100 |
sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; |
101 |
sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; |
102 |
sc->sc_ohci.sc_bus.dma_bits = 32; |
103 |
|
104 |
/* get all DMA memory */ |
105 |
if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, |
106 |
USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { |
107 |
return (ENOMEM); |
108 |
} |
109 |
sc->mclk = at91_pmc_clock_ref("mck"); |
110 |
sc->iclk = at91_pmc_clock_ref("ohci_clk"); |
111 |
sc->fclk = at91_pmc_clock_ref("uhpck"); |
112 |
|
113 |
sc->sc_ohci.sc_dev = dev; |
114 |
|
115 |
rid = MEM_RID; |
116 |
sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, |
117 |
&rid, RF_ACTIVE); |
118 |
|
119 |
if (!(sc->sc_ohci.sc_io_res)) { |
120 |
err = ENOMEM; |
121 |
goto error; |
122 |
} |
123 |
sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); |
124 |
sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res); |
125 |
sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); |
126 |
|
127 |
rid = 0; |
128 |
sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, |
129 |
RF_ACTIVE); |
130 |
if (!(sc->sc_ohci.sc_irq_res)) { |
131 |
goto error; |
132 |
} |
133 |
sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); |
134 |
if (!(sc->sc_ohci.sc_bus.bdev)) { |
135 |
goto error; |
136 |
} |
137 |
device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); |
138 |
|
139 |
strlcpy(sc->sc_ohci.sc_vendor, "Atmel", sizeof(sc->sc_ohci.sc_vendor)); |
140 |
|
141 |
err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, |
142 |
NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); |
143 |
if (err) { |
144 |
sc->sc_ohci.sc_intr_hdl = NULL; |
145 |
goto error; |
146 |
} |
147 |
/* |
148 |
* turn on the clocks from the AT91's point of view. Keep the unit in reset. |
149 |
*/ |
150 |
at91_pmc_clock_enable(sc->mclk); |
151 |
at91_pmc_clock_enable(sc->iclk); |
152 |
at91_pmc_clock_enable(sc->fclk); |
153 |
bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, |
154 |
OHCI_CONTROL, 0); |
155 |
|
156 |
err = ohci_init(&sc->sc_ohci); |
157 |
if (!err) { |
158 |
err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); |
159 |
} |
160 |
if (err) { |
161 |
goto error; |
162 |
} |
163 |
return (0); |
164 |
|
165 |
error: |
166 |
ohci_at91_fdt_detach(dev); |
167 |
return (ENXIO); |
168 |
} |
169 |
|
170 |
static int |
171 |
ohci_at91_fdt_detach(device_t dev) |
172 |
{ |
173 |
struct at91_ohci_softc *sc = device_get_softc(dev); |
174 |
device_t bdev; |
175 |
int err; |
176 |
|
177 |
if (sc->sc_ohci.sc_bus.bdev) { |
178 |
bdev = sc->sc_ohci.sc_bus.bdev; |
179 |
device_detach(bdev); |
180 |
device_delete_child(dev, bdev); |
181 |
} |
182 |
/* during module unload there are lots of children leftover */ |
183 |
device_delete_children(dev); |
184 |
|
185 |
if (sc->sc_ohci.sc_io_res != NULL) { |
186 |
/* |
187 |
* Put the controller into reset, then disable clocks and do |
188 |
* the MI tear down. We have to disable the clocks/hardware |
189 |
* after we do the rest of the teardown. We also disable the |
190 |
* clocks in the opposite order we acquire them, but that |
191 |
* doesn't seem to be absolutely necessary. We free up the |
192 |
* clocks after we disable them, so the system could, in |
193 |
* theory, reuse them. |
194 |
*/ |
195 |
bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, |
196 |
OHCI_CONTROL, 0); |
197 |
|
198 |
at91_pmc_clock_disable(sc->fclk); |
199 |
at91_pmc_clock_disable(sc->iclk); |
200 |
at91_pmc_clock_disable(sc->mclk); |
201 |
at91_pmc_clock_deref(sc->fclk); |
202 |
at91_pmc_clock_deref(sc->iclk); |
203 |
at91_pmc_clock_deref(sc->mclk); |
204 |
|
205 |
if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { |
206 |
/* |
207 |
* only call ohci_detach() after ohci_init() |
208 |
*/ |
209 |
ohci_detach(&sc->sc_ohci); |
210 |
|
211 |
err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, |
212 |
sc->sc_ohci.sc_intr_hdl); |
213 |
sc->sc_ohci.sc_intr_hdl = NULL; |
214 |
} |
215 |
if (sc->sc_ohci.sc_irq_res) { |
216 |
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); |
217 |
sc->sc_ohci.sc_irq_res = NULL; |
218 |
} |
219 |
if (sc->sc_ohci.sc_io_res) { |
220 |
bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, |
221 |
sc->sc_ohci.sc_io_res); |
222 |
sc->sc_ohci.sc_io_res = NULL; |
223 |
} |
224 |
} |
225 |
usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); |
226 |
|
227 |
return (0); |
228 |
} |
229 |
|
230 |
static device_method_t ohci_methods[] = { |
231 |
/* Device interface */ |
232 |
DEVMETHOD(device_probe, ohci_at91_fdt_probe), |
233 |
DEVMETHOD(device_attach, ohci_at91_fdt_attach), |
234 |
DEVMETHOD(device_detach, ohci_at91_fdt_detach), |
235 |
DEVMETHOD(device_suspend, bus_generic_suspend), |
236 |
DEVMETHOD(device_resume, bus_generic_resume), |
237 |
DEVMETHOD(device_shutdown, bus_generic_shutdown), |
238 |
|
239 |
DEVMETHOD_END |
240 |
}; |
241 |
|
242 |
static driver_t ohci_driver = { |
243 |
.name = "ohci", |
244 |
.methods = ohci_methods, |
245 |
.size = sizeof(struct at91_ohci_softc), |
246 |
}; |
247 |
|
248 |
static devclass_t ohci_devclass; |
249 |
|
250 |
DRIVER_MODULE(ohci, simplebus, ohci_driver, ohci_devclass, 0, 0); |
251 |
MODULE_DEPEND(ohci, usb, 1, 1, 1); |