1 /*
2 * (MPSAFE)
3 *
4 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
5 *
6 * This code is derived from software contributed to The DragonFly Project
7 * by Matthew Dillon <dillon@backplane.com>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 * 3. Neither the name of The DragonFly Project nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific, prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36 /*
37 * Primary device and CAM interface to OpenBSD AHCI driver, for DragonFly
38 */
39
40 #include "ahci.h"
41
42 u_int32_t AhciForceGen = 0;
43 u_int32_t AhciNoFeatures = 0;
44
45 /*
46 * Device bus methods
47 */
48
49 static int ahci_probe (device_t dev);
50 static int ahci_attach (device_t dev);
51 static int ahci_detach (device_t dev);
52 static int ahci_sysctl_link_pwr_mgmt (SYSCTL_HANDLER_ARGS);
53 #if 0
54 static int ahci_shutdown (device_t dev);
55 static int ahci_suspend (device_t dev);
56 static int ahci_resume (device_t dev);
57 #endif
58
59 static void ahci_port_thread(void *arg);
60
61 static device_method_t ahci_methods[] = {
62 DEVMETHOD(device_probe, ahci_probe),
63 DEVMETHOD(device_attach, ahci_attach),
64 DEVMETHOD(device_detach, ahci_detach),
65 #if 0
66 DEVMETHOD(device_shutdown, ahci_shutdown),
67 DEVMETHOD(device_suspend, ahci_suspend),
68 DEVMETHOD(device_resume, ahci_resume),
69 #endif
70
71 DEVMETHOD(bus_print_child, bus_generic_print_child),
72 DEVMETHOD(bus_driver_added, bus_generic_driver_added),
73 DEVMETHOD_END
74 };
75
76 static devclass_t ahci_devclass;
77
78 static driver_t ahci_driver = {
79 "ahci",
80 ahci_methods,
81 sizeof(struct ahci_softc)
82 };
83
84 MODULE_DEPEND(ahci, cam, 1, 1, 1);
85 DRIVER_MODULE(ahci, pci, ahci_driver, ahci_devclass, NULL, NULL);
86 MODULE_VERSION(ahci, 1);
87
88 /*
89 * Device bus method procedures
90 */
91 static int
ahci_probe(device_t dev)92 ahci_probe (device_t dev)
93 {
94 const struct ahci_device *ad;
95
96 if (kgetenv("hint.ahci.disabled"))
97 return(ENXIO);
98
99 ad = ahci_lookup_device(dev);
100 if (ad) {
101 device_set_desc(dev, ad->name);
102 return(-5); /* higher priority the NATA */
103 }
104 return(ENXIO);
105 }
106
107 static int
ahci_attach(device_t dev)108 ahci_attach (device_t dev)
109 {
110 struct ahci_softc *sc = device_get_softc(dev);
111
112 sc->sc_ad = ahci_lookup_device(dev);
113 if (sc->sc_ad == NULL)
114 return(ENXIO);
115
116 /*
117 * Some chipsets do not properly implement the AHCI spec and may
118 * require the link speed to be specifically requested.
119 */
120 if (kgetenv("hint.ahci.force150"))
121 AhciForceGen = 1;
122 if (kgetenv("hint.ahci.force300"))
123 AhciForceGen = 2;
124 if (kgetenv("hint.ahci.force600"))
125 AhciForceGen = 3;
126
127 if (kgetenv("hint.ahci.nofeatures"))
128 AhciNoFeatures = -1;
129
130 if (kgetenv("hint.ahci.forcefbss"))
131 sc->sc_flags |= AHCI_F_FORCE_FBSS;
132
133 return (sc->sc_ad->ad_attach(dev));
134 }
135
136 static int
ahci_detach(device_t dev)137 ahci_detach (device_t dev)
138 {
139 struct ahci_softc *sc = device_get_softc(dev);
140 int error = 0;
141
142 if (sc->sc_ad) {
143 error = sc->sc_ad->ad_detach(dev);
144 sc->sc_ad = NULL;
145 }
146 return(error);
147 }
148
149 static int
ahci_sysctl_link_pwr_mgmt(SYSCTL_HANDLER_ARGS)150 ahci_sysctl_link_pwr_mgmt (SYSCTL_HANDLER_ARGS)
151 {
152 struct ahci_port *ap = arg1;
153 int error, link_pwr_mgmt;
154
155 link_pwr_mgmt = ap->link_pwr_mgmt;
156 error = sysctl_handle_int(oidp, &link_pwr_mgmt, 0, req);
157 if (error || req->newptr == NULL)
158 return error;
159
160 ahci_port_link_pwr_mgmt(ap, link_pwr_mgmt);
161 return 0;
162 }
163
164 static int
ahci_sysctl_link_pwr_state(SYSCTL_HANDLER_ARGS)165 ahci_sysctl_link_pwr_state (SYSCTL_HANDLER_ARGS)
166 {
167 struct ahci_port *ap = arg1;
168 const char *state_names[] =
169 {"unknown", "active", "partial", "slumber", "devsleep"};
170 char buf[16];
171 int state;
172
173 state = ahci_port_link_pwr_state(ap);
174 if (state < 0 || state >= NELEM(state_names))
175 state = 0;
176
177 ksnprintf(buf, sizeof(buf), "%s", state_names[state]);
178 return sysctl_handle_string(oidp, buf, sizeof(buf), req);
179 }
180
181 #if 0
182
183 static int
184 ahci_shutdown (device_t dev)
185 {
186 return (0);
187 }
188
189 static int
190 ahci_suspend (device_t dev)
191 {
192 return (0);
193 }
194
195 static int
196 ahci_resume (device_t dev)
197 {
198 return (0);
199 }
200
201 #endif
202
203 /*
204 * Sleep (ms) milliseconds, error on the side of caution.
205 */
206 void
ahci_os_sleep(int ms)207 ahci_os_sleep(int ms)
208 {
209 int ticks;
210
211 ticks = hz * ms / 1000 + 1;
212 tsleep(&ticks, 0, "ahslp", ticks);
213 }
214
215 /*
216 * Sleep for a minimum interval and return the number of milliseconds
217 * that was. The minimum value returned is 1
218 */
219 int
ahci_os_softsleep(void)220 ahci_os_softsleep(void)
221 {
222 if (hz >= 1000) {
223 tsleep(&ticks, 0, "ahslp", hz / 1000);
224 return(1);
225 } else {
226 tsleep(&ticks, 0, "ahslp", 1);
227 return(1000 / hz);
228 }
229 }
230
231 void
ahci_os_hardsleep(int us)232 ahci_os_hardsleep(int us)
233 {
234 DELAY(us);
235 }
236
237 /*
238 * Create the OS-specific port helper thread and per-port lock.
239 */
240 void
ahci_os_start_port(struct ahci_port * ap)241 ahci_os_start_port(struct ahci_port *ap)
242 {
243 struct sysctl_oid *soid;
244 char name[16];
245
246 atomic_set_int(&ap->ap_signal, AP_SIGF_INIT | AP_SIGF_THREAD_SYNC);
247 lockinit(&ap->ap_lock, "ahcipo", 0, LK_CANRECURSE);
248 lockinit(&ap->ap_sim_lock, "ahcicam", 0, LK_CANRECURSE);
249 lockinit(&ap->ap_sig_lock, "ahport", 0, 0);
250 sysctl_ctx_init(&ap->sysctl_ctx);
251 ksnprintf(name, sizeof(name), "%d", ap->ap_num);
252 soid = device_get_sysctl_tree(ap->ap_sc->sc_dev);
253 ap->sysctl_tree = SYSCTL_ADD_NODE(&ap->sysctl_ctx,
254 SYSCTL_CHILDREN(soid),
255 OID_AUTO, name, CTLFLAG_RD, 0, "");
256
257 if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SALP) &&
258 (ap->ap_sc->sc_cap & (AHCI_REG_CAP_PSC | AHCI_REG_CAP_SSC))) {
259 SYSCTL_ADD_PROC(&ap->sysctl_ctx,
260 SYSCTL_CHILDREN(ap->sysctl_tree), OID_AUTO,
261 "link_pwr_mgmt", CTLTYPE_INT | CTLFLAG_RW, ap, 0,
262 ahci_sysctl_link_pwr_mgmt, "I",
263 "Link power management policy "
264 "(0 = disabled, 1 = medium, 2 = aggressive)");
265 SYSCTL_ADD_PROC(&ap->sysctl_ctx,
266 SYSCTL_CHILDREN(ap->sysctl_tree), OID_AUTO,
267 "link_pwr_state", CTLTYPE_STRING | CTLFLAG_RD, ap, 0,
268 ahci_sysctl_link_pwr_state, "A",
269 "Link power management state");
270
271 }
272
273 kthread_create(ahci_port_thread, ap, &ap->ap_thread,
274 "%s", PORTNAME(ap));
275 }
276
277 /*
278 * Stop the OS-specific port helper thread and kill the per-port lock.
279 */
280 void
ahci_os_stop_port(struct ahci_port * ap)281 ahci_os_stop_port(struct ahci_port *ap)
282 {
283 if (ap->sysctl_tree) {
284 sysctl_ctx_free(&ap->sysctl_ctx);
285 ap->sysctl_tree = NULL;
286 }
287
288 if (ap->ap_thread) {
289 ahci_os_signal_port_thread(ap, AP_SIGF_STOP);
290 ahci_os_sleep(10);
291 if (ap->ap_thread) {
292 kprintf("%s: Waiting for thread to terminate\n",
293 PORTNAME(ap));
294 while (ap->ap_thread)
295 ahci_os_sleep(100);
296 kprintf("%s: thread terminated\n",
297 PORTNAME(ap));
298 }
299 }
300 lockuninit(&ap->ap_lock);
301 }
302
303 /*
304 * Add (mask) to the set of bits being sent to the per-port thread helper
305 * and wake the helper up if necessary.
306 */
307 void
ahci_os_signal_port_thread(struct ahci_port * ap,int mask)308 ahci_os_signal_port_thread(struct ahci_port *ap, int mask)
309 {
310 lockmgr(&ap->ap_sig_lock, LK_EXCLUSIVE);
311 atomic_set_int(&ap->ap_signal, mask);
312 lockmgr(&ap->ap_sig_lock, LK_RELEASE);
313 wakeup(&ap->ap_thread);
314 }
315
316 /*
317 * Unconditionally lock the port structure for access.
318 */
319 void
ahci_os_lock_port(struct ahci_port * ap)320 ahci_os_lock_port(struct ahci_port *ap)
321 {
322 lockmgr(&ap->ap_lock, LK_EXCLUSIVE);
323 }
324
325 /*
326 * Conditionally lock the port structure for access.
327 *
328 * Returns 0 on success, non-zero on failure.
329 */
330 int
ahci_os_lock_port_nb(struct ahci_port * ap)331 ahci_os_lock_port_nb(struct ahci_port *ap)
332 {
333 return (lockmgr(&ap->ap_lock, LK_EXCLUSIVE | LK_NOWAIT));
334 }
335
336 /*
337 * Unlock a previously locked port.
338 */
339 void
ahci_os_unlock_port(struct ahci_port * ap)340 ahci_os_unlock_port(struct ahci_port *ap)
341 {
342 lockmgr(&ap->ap_lock, LK_RELEASE);
343 }
344
345 /*
346 * Per-port thread helper. This helper thread is responsible for
347 * atomically retrieving and clearing the signal mask and calling
348 * the machine-independant driver core.
349 *
350 * MPSAFE
351 */
352 static
353 void
ahci_port_thread(void * arg)354 ahci_port_thread(void *arg)
355 {
356 struct ahci_port *ap = arg;
357 int mask;
358
359 /*
360 * Sets us up as an interrupt support thread, meaning we are
361 * given a higher priority and we can preempt normal threads.
362 */
363 lwkt_set_interrupt_support_thread();
364
365 /*
366 * The helper thread is responsible for the initial port init,
367 * so all the ports can be inited in parallel.
368 *
369 * We also run the state machine which should do all probes.
370 * Since CAM is not attached yet we will not get out-of-order
371 * SCSI attachments.
372 */
373 ahci_os_lock_port(ap);
374 ahci_port_init(ap);
375 atomic_clear_int(&ap->ap_signal, AP_SIGF_THREAD_SYNC);
376 wakeup(&ap->ap_signal);
377 ahci_port_state_machine(ap, 1);
378 atomic_clear_int(&ap->ap_signal, AP_SIGF_INIT);
379
380 /*
381 * If attaching synchronous wakeup the frontend (device attach code),
382 * otherwise attach asynchronously. We haven't probed anything yet
383 * so the ahci_cam_attach() won't try to probe.
384 */
385 if (ahci_synchronous_boot) {
386 wakeup(&ap->ap_signal);
387 } else {
388 if (ahci_cam_attach(ap) == 0)
389 ahci_cam_changed(ap, NULL, -1);
390 }
391 ahci_os_unlock_port(ap);
392
393 /*
394 * Then loop on the helper core.
395 */
396 mask = ap->ap_signal;
397 while ((mask & AP_SIGF_STOP) == 0) {
398 ahci_port_thread_core(ap, mask);
399 lockmgr(&ap->ap_sig_lock, LK_EXCLUSIVE);
400 if (ap->ap_signal == 0) {
401 lksleep(&ap->ap_thread, &ap->ap_sig_lock, 0,
402 "ahport", 10 * hz);
403 }
404 mask = ap->ap_signal;
405 atomic_clear_int(&ap->ap_signal, mask);
406 lockmgr(&ap->ap_sig_lock, LK_RELEASE);
407 }
408 ap->ap_thread = NULL;
409 }
410