AHCI - Port multiplier plug/unplug work, tuning, cleanup, bug fixes.
[dragonfly.git] / sys / dev / disk / ahci / ahci_dragonfly.c
CommitLineData
258223a3
MD
1/*
2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
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 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34/*
35 * Primary device and CAM interface to OpenBSD AHCI driver, for DragonFly
36 */
37
38#include "ahci.h"
39
40/*
41 * Device bus methods
42 */
43
44static int ahci_probe (device_t dev);
45static int ahci_attach (device_t dev);
46static int ahci_detach (device_t dev);
47#if 0
48static int ahci_shutdown (device_t dev);
49static int ahci_suspend (device_t dev);
50static int ahci_resume (device_t dev);
51#endif
52
f4553de1
MD
53static void ahci_port_thread(void *arg);
54
258223a3
MD
55static device_method_t ahci_methods[] = {
56 DEVMETHOD(device_probe, ahci_probe),
57 DEVMETHOD(device_attach, ahci_attach),
58 DEVMETHOD(device_detach, ahci_detach),
59#if 0
60 DEVMETHOD(device_shutdown, ahci_shutdown),
61 DEVMETHOD(device_suspend, ahci_suspend),
62 DEVMETHOD(device_resume, ahci_resume),
63#endif
64
65 DEVMETHOD(bus_print_child, bus_generic_print_child),
66 DEVMETHOD(bus_driver_added, bus_generic_driver_added),
67 {0, 0}
68};
69
70static devclass_t ahci_devclass;
71
72static driver_t ahci_driver = {
73 "ahci",
74 ahci_methods,
75 sizeof(struct ahci_softc)
76};
77
78MODULE_DEPEND(ahci, cam, 1, 1, 1);
79DRIVER_MODULE(ahci, pci, ahci_driver, ahci_devclass, 0, 0);
80
81/*
82 * Device bus method procedures
83 */
84static int
85ahci_probe (device_t dev)
86{
87 const struct ahci_device *ad;
88
89 ad = ahci_lookup_device(dev);
90 if (ad) {
91 device_set_desc(dev, ad->name);
92 return(-5); /* higher priority the NATA */
93 }
94 return(ENXIO);
95}
96
97static int
98ahci_attach (device_t dev)
99{
100 struct ahci_softc *sc = device_get_softc(dev);
101 int error;
102
103 sc->sc_ad = ahci_lookup_device(dev);
104 if (sc->sc_ad == NULL)
105 return(ENXIO);
106 error = sc->sc_ad->ad_attach(dev);
107 return (error);
108}
109
110static int
111ahci_detach (device_t dev)
112{
113 struct ahci_softc *sc = device_get_softc(dev);
114 int error = 0;
115
116 if (sc->sc_ad) {
117 error = sc->sc_ad->ad_detach(dev);
118 sc->sc_ad = NULL;
119 }
120 return(error);
121}
122
123#if 0
124
125static int
126ahci_shutdown (device_t dev)
127{
128 return (0);
129}
130
131static int
132ahci_suspend (device_t dev)
133{
134 return (0);
135}
136
137static int
138ahci_resume (device_t dev)
139{
140 return (0);
141}
142
143#endif
3209f581 144
831bc9e3
MD
145/*
146 * Sleep (ms) milliseconds, error on the side of caution.
147 */
3209f581
MD
148void
149ahci_os_sleep(int ms)
150{
151 int ticks;
152
831bc9e3
MD
153 ticks = hz * ms / 1000 + 1;
154 tsleep(&ticks, 0, "ahslp", ticks);
155}
156
157/*
158 * Sleep for a minimum interval and return the number of milliseconds
159 * that was. The minimum value returned is 1
160 */
161int
162ahci_os_softsleep(void)
163{
164 if (hz >= 1000) {
165 tsleep(&ticks, 0, "ahslp", hz / 1000);
166 return(1);
3209f581 167 } else {
831bc9e3
MD
168 tsleep(&ticks, 0, "ahslp", 1);
169 return(1000 / hz);
3209f581
MD
170 }
171}
f4553de1 172
831bc9e3
MD
173void
174ahci_os_hardsleep(int us)
175{
176 DELAY(us);
177}
178
f4553de1
MD
179/*
180 * Create the OS-specific port helper thread and per-port lock.
181 */
182void
183ahci_os_start_port(struct ahci_port *ap)
184{
185 atomic_set_int(&ap->ap_signal, AP_SIGF_INIT);
186 lockinit(&ap->ap_lock, "ahcipo", 0, 0);
187 kthread_create(ahci_port_thread, ap, &ap->ap_thread,
188 "%s", PORTNAME(ap));
189}
190
191/*
192 * Stop the OS-specific port helper thread and kill the per-port lock.
193 */
194void
195ahci_os_stop_port(struct ahci_port *ap)
196{
197 if (ap->ap_thread) {
198 ahci_os_signal_port_thread(ap, AP_SIGF_STOP);
199 ahci_os_sleep(10);
200 if (ap->ap_thread) {
201 kprintf("%s: Waiting for thread to terminate\n",
202 PORTNAME(ap));
203 while (ap->ap_thread)
204 ahci_os_sleep(100);
205 kprintf("%s: thread terminated\n",
206 PORTNAME(ap));
207 }
208 }
209 lockuninit(&ap->ap_lock);
210}
211
212/*
213 * Add (mask) to the set of bits being sent to the per-port thread helper
214 * and wake the helper up if necessary.
215 */
216void
217ahci_os_signal_port_thread(struct ahci_port *ap, int mask)
218{
219 atomic_set_int(&ap->ap_signal, mask);
220 wakeup(&ap->ap_thread);
221}
222
223/*
224 * Unconditionally lock the port structure for access.
225 */
226void
227ahci_os_lock_port(struct ahci_port *ap)
228{
229 lockmgr(&ap->ap_lock, LK_EXCLUSIVE);
230}
231
232/*
233 * Conditionally lock the port structure for access.
234 *
235 * Returns 0 on success, non-zero on failure.
236 */
237int
238ahci_os_lock_port_nb(struct ahci_port *ap)
239{
240 return (lockmgr(&ap->ap_lock, LK_EXCLUSIVE | LK_NOWAIT));
241}
242
243/*
244 * Unlock a previously locked port.
245 */
246void
247ahci_os_unlock_port(struct ahci_port *ap)
248{
249 lockmgr(&ap->ap_lock, LK_RELEASE);
250}
251
252/*
253 * Per-port thread helper. This helper thread is responsible for
254 * atomically retrieving and clearing the signal mask and calling
255 * the machine-independant driver core.
256 */
257static
258void
259ahci_port_thread(void *arg)
260{
261 struct ahci_port *ap = arg;
262 int mask;
263
264 /*
265 * The helper thread is responsible for the initial port init,
266 * so all the ports can be inited in parallel.
267 *
268 * We also run the state machine which should do all probes.
269 * Since CAM is not attached yet we will not get out-of-order
270 * SCSI attachments.
271 */
272 ahci_os_lock_port(ap);
273 ahci_port_init(ap, NULL);
831bc9e3 274 ahci_port_state_machine(ap, 1);
f4553de1
MD
275 ahci_os_unlock_port(ap);
276 atomic_clear_int(&ap->ap_signal, AP_SIGF_INIT);
277 wakeup(&ap->ap_signal);
278
279 /*
280 * Then loop on the helper core.
281 */
282 mask = ap->ap_signal;
283 while ((mask & AP_SIGF_STOP) == 0) {
284 atomic_clear_int(&ap->ap_signal, mask);
285 ahci_port_thread_core(ap, mask);
286 crit_enter();
287 tsleep_interlock(&ap->ap_thread);
288 if (ap->ap_signal == 0)
289 tsleep(&ap->ap_thread, 0, "ahport", 0);
290 crit_exit();
291 mask = ap->ap_signal;
292 }
293 ap->ap_thread = NULL;
294}