xref: /openbsd/sys/dev/fdt/ohci_fdt.c (revision d415bd75)
1 /*	$OpenBSD: ohci_fdt.c,v 1.3 2021/10/24 17:52:26 mpi Exp $ */
2 
3 /*
4  * Copyright (c) 2005, 2019 David Gwynne <dlg@openbsd.org>
5  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/systm.h>
22 #include <sys/device.h>
23 #include <sys/malloc.h>
24 
25 #include <machine/intr.h>
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28 
29 #include <dev/ofw/openfirm.h>
30 #include <dev/ofw/ofw_clock.h>
31 #include <dev/ofw/ofw_pinctrl.h>
32 #include <dev/ofw/ofw_regulator.h>
33 #include <dev/ofw/fdt.h>
34 
35 #include <dev/usb/usb.h>
36 #include <dev/usb/usbdi.h>
37 #include <dev/usb/usbdivar.h>
38 #include <dev/usb/usb_mem.h>
39 
40 #include <dev/usb/ohcireg.h>
41 #include <dev/usb/ohcivar.h>
42 
43 struct ohci_fdt_softc {
44 	struct ohci_softc	sc;
45 	int			sc_node;
46 	void			*sc_ih;
47 };
48 
49 int	ohci_fdt_match(struct device *, void *, void *);
50 void	ohci_fdt_attach(struct device *, struct device *, void *);
51 int	ohci_fdt_detach(struct device *, int);
52 
53 const struct cfattach ohci_fdt_ca = {
54 	sizeof(struct ohci_fdt_softc),
55 	ohci_fdt_match,
56 	ohci_fdt_attach,
57 	ohci_fdt_detach,
58 	ohci_activate
59 };
60 
61 void	ohci_fdt_attach_deferred(struct device *);
62 
63 int
64 ohci_fdt_match(struct device *parent, void *match, void *aux)
65 {
66 	struct fdt_attach_args *faa = aux;
67 
68 	return OF_is_compatible(faa->fa_node, "generic-ohci");
69 }
70 
71 void
72 ohci_fdt_attach(struct device *parent, struct device *self, void *aux)
73 {
74 	struct ohci_fdt_softc *sc = (struct ohci_fdt_softc *)self;
75 	struct fdt_attach_args *faa = aux;
76 	char *devname = sc->sc.sc_bus.bdev.dv_xname;
77 
78 	if (faa->fa_nreg < 1) {
79 		printf(": no registers\n");
80 		return;
81 	}
82 
83 	sc->sc_node = faa->fa_node;
84 	sc->sc.iot = faa->fa_iot;
85 	sc->sc.sc_bus.dmatag = faa->fa_dmat;
86 	sc->sc.sc_size = faa->fa_reg[0].size;
87 
88 	if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
89 	    faa->fa_reg[0].size, 0, &sc->sc.ioh)) {
90 		printf(": can't map registers\n");
91 		goto out;
92 	}
93 
94 	pinctrl_byname(sc->sc_node, "default");
95 
96 	clock_enable_all(sc->sc_node);
97 	reset_deassert_all(sc->sc_node);
98 
99 	/* Record what interrupts were enabled by SMM/BIOS. */
100 	sc->sc.sc_intre = bus_space_read_4(sc->sc.iot, sc->sc.ioh,
101 	    OHCI_INTERRUPT_ENABLE);
102 
103 	/* Disable interrupts, so we don't get any spurious ones. */
104 	bus_space_write_4(sc->sc.iot, sc->sc.ioh, OHCI_INTERRUPT_DISABLE,
105 	    OHCI_MIE);
106 
107 	bus_space_barrier(sc->sc.iot, sc->sc.ioh, 0, sc->sc.sc_size,
108 	    BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
109 	bus_space_write_4(sc->sc.iot, sc->sc.ioh,
110 	    OHCI_INTERRUPT_DISABLE, OHCI_MIE);
111 
112 	/* Map and establish the interrupt. */
113 	splassert(IPL_USB);
114 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB,
115 	    ohci_intr, &sc->sc, devname);
116 	if (sc->sc_ih == NULL) {
117 		printf(": can't establish interrupt\n");
118 		goto disable_clocks;
119 	}
120 	printf(": ");
121 
122 	strlcpy(sc->sc.sc_vendor, "Generic", sizeof(sc->sc.sc_vendor));
123 
124 	/* Display revision. Legacy handover isn't needed as there's no smm */
125 	if (ohci_checkrev(&sc->sc) != USBD_NORMAL_COMPLETION) {
126 		goto disestablish_intr;
127 	}
128 
129 	/* Ignore interrupts for now */
130 	sc->sc.sc_bus.dying = 1;
131 
132 	config_defer(self, ohci_fdt_attach_deferred);
133 
134 	return;
135 
136 disestablish_intr:
137 	fdt_intr_disestablish(sc->sc_ih);
138 	sc->sc_ih = NULL;
139 disable_clocks:
140 	clock_disable_all(sc->sc_node);
141 
142 	bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
143 	sc->sc.sc_size = 0;
144 out:
145 	return;
146 }
147 
148 void
149 ohci_fdt_attach_deferred(struct device *self)
150 {
151 	struct ohci_fdt_softc *sc = (struct ohci_fdt_softc *)self;
152 	usbd_status r;
153 	int s;
154 
155 	s = splusb();
156 
157 	sc->sc.sc_bus.dying = 0;
158 
159 	r = ohci_init(&sc->sc);
160 
161 	splx(s);
162 	if (r != USBD_NORMAL_COMPLETION) {
163 		printf("%s: init failed, error=%d\n",
164 		    sc->sc.sc_bus.bdev.dv_xname, r);
165 		fdt_intr_disestablish(sc->sc_ih);
166 		sc->sc_ih = NULL;
167 		clock_disable_all(sc->sc_node);
168 		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
169 		sc->sc.sc_size = 0;
170 		return;
171 	}
172 
173 	/* Attach usb device. */
174 	config_found(self, &sc->sc.sc_bus, usbctlprint);
175 }
176 
177 int
178 ohci_fdt_detach(struct device *self, int flags)
179 {
180 	struct ohci_fdt_softc *sc = (struct ohci_fdt_softc *)self;
181 	int rv;
182 
183 	rv = ohci_detach(self, flags);
184 	if (rv)
185 		return rv;
186 
187 	if (sc->sc_ih != NULL) {
188 		fdt_intr_disestablish(sc->sc_ih);
189 		sc->sc_ih = NULL;
190 	}
191 
192 	if (sc->sc.sc_size) {
193 		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
194 		sc->sc.sc_size = 0;
195 	}
196 
197 	clock_disable_all(sc->sc_node);
198 	return 0;
199 }
200