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 {0, 0} 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 87 /* 88 * Device bus method procedures 89 */ 90 static int 91 ahci_probe (device_t dev) 92 { 93 const struct ahci_device *ad; 94 95 if (kgetenv("hint.ahci.disabled")) 96 return(ENXIO); 97 98 ad = ahci_lookup_device(dev); 99 if (ad) { 100 device_set_desc(dev, ad->name); 101 return(-5); /* higher priority the NATA */ 102 } 103 return(ENXIO); 104 } 105 106 static int 107 ahci_attach (device_t dev) 108 { 109 struct ahci_softc *sc = device_get_softc(dev); 110 char name[16]; 111 int error; 112 113 sc->sc_ad = ahci_lookup_device(dev); 114 if (sc->sc_ad == NULL) 115 return(ENXIO); 116 117 /* 118 * Some chipsets do not properly implement the AHCI spec and may 119 * require the link speed to be specifically requested. 120 */ 121 if (kgetenv("hint.ahci.force150")) 122 AhciForceGen = 1; 123 if (kgetenv("hint.ahci.force300")) 124 AhciForceGen = 2; 125 if (kgetenv("hint.ahci.force600")) 126 AhciForceGen = 3; 127 128 if (kgetenv("hint.ahci.nofeatures")) 129 AhciNoFeatures = -1; 130 131 sysctl_ctx_init(&sc->sysctl_ctx); 132 ksnprintf(name, sizeof(name), "%s%d", 133 device_get_name(dev), device_get_unit(dev)); 134 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 135 SYSCTL_STATIC_CHILDREN(_hw), 136 OID_AUTO, name, CTLFLAG_RD, 0, ""); 137 138 error = sc->sc_ad->ad_attach(dev); 139 if (error) { 140 sysctl_ctx_free(&sc->sysctl_ctx); 141 sc->sysctl_tree = NULL; 142 } 143 return (error); 144 } 145 146 static int 147 ahci_detach (device_t dev) 148 { 149 struct ahci_softc *sc = device_get_softc(dev); 150 int error = 0; 151 152 if (sc->sysctl_tree) { 153 sysctl_ctx_free(&sc->sysctl_ctx); 154 sc->sysctl_tree = NULL; 155 } 156 if (sc->sc_ad) { 157 error = sc->sc_ad->ad_detach(dev); 158 sc->sc_ad = NULL; 159 } 160 return(error); 161 } 162 163 static int 164 ahci_sysctl_link_pwr_mgmt (SYSCTL_HANDLER_ARGS) 165 { 166 struct ahci_port *ap = arg1; 167 int error, link_pwr_mgmt; 168 169 link_pwr_mgmt = ap->link_pwr_mgmt; 170 error = sysctl_handle_int(oidp, &link_pwr_mgmt, 0, req); 171 if (error || req->newptr == NULL) 172 return error; 173 174 ahci_port_link_pwr_mgmt(ap, link_pwr_mgmt); 175 return 0; 176 } 177 178 static int 179 ahci_sysctl_link_pwr_state (SYSCTL_HANDLER_ARGS) 180 { 181 struct ahci_port *ap = arg1; 182 const char *state_names[] = {"unknown", "active", "partial", "slumber"}; 183 char buf[16]; 184 int state; 185 186 state = ahci_port_link_pwr_state(ap); 187 if (state < 0 || state >= NELEM(state_names)) 188 state = 0; 189 190 ksnprintf(buf, sizeof(buf), "%s", state_names[state]); 191 return sysctl_handle_string(oidp, buf, sizeof(buf), req); 192 } 193 194 #if 0 195 196 static int 197 ahci_shutdown (device_t dev) 198 { 199 return (0); 200 } 201 202 static int 203 ahci_suspend (device_t dev) 204 { 205 return (0); 206 } 207 208 static int 209 ahci_resume (device_t dev) 210 { 211 return (0); 212 } 213 214 #endif 215 216 /* 217 * Sleep (ms) milliseconds, error on the side of caution. 218 */ 219 void 220 ahci_os_sleep(int ms) 221 { 222 int ticks; 223 224 ticks = hz * ms / 1000 + 1; 225 tsleep(&ticks, 0, "ahslp", ticks); 226 } 227 228 /* 229 * Sleep for a minimum interval and return the number of milliseconds 230 * that was. The minimum value returned is 1 231 */ 232 int 233 ahci_os_softsleep(void) 234 { 235 if (hz >= 1000) { 236 tsleep(&ticks, 0, "ahslp", hz / 1000); 237 return(1); 238 } else { 239 tsleep(&ticks, 0, "ahslp", 1); 240 return(1000 / hz); 241 } 242 } 243 244 void 245 ahci_os_hardsleep(int us) 246 { 247 DELAY(us); 248 } 249 250 /* 251 * Create the OS-specific port helper thread and per-port lock. 252 */ 253 void 254 ahci_os_start_port(struct ahci_port *ap) 255 { 256 char name[16]; 257 258 atomic_set_int(&ap->ap_signal, AP_SIGF_INIT | AP_SIGF_THREAD_SYNC); 259 lockinit(&ap->ap_lock, "ahcipo", 0, 0); 260 lockinit(&ap->ap_sim_lock, "ahcicam", 0, LK_CANRECURSE); 261 lockinit(&ap->ap_sig_lock, "ahport", 0, 0); 262 sysctl_ctx_init(&ap->sysctl_ctx); 263 ksnprintf(name, sizeof(name), "%d", ap->ap_num); 264 ap->sysctl_tree = SYSCTL_ADD_NODE(&ap->sysctl_ctx, 265 SYSCTL_CHILDREN(ap->ap_sc->sysctl_tree), 266 OID_AUTO, name, CTLFLAG_RD, 0, ""); 267 268 if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SALP) && 269 (ap->ap_sc->sc_cap & (AHCI_REG_CAP_PSC | AHCI_REG_CAP_SSC))) { 270 SYSCTL_ADD_PROC(&ap->sysctl_ctx, 271 SYSCTL_CHILDREN(ap->sysctl_tree), OID_AUTO, 272 "link_pwr_mgmt", CTLTYPE_INT | CTLFLAG_RW, ap, 0, 273 ahci_sysctl_link_pwr_mgmt, "I", 274 "Link power management policy " 275 "(0 = disabled, 1 = medium, 2 = aggressive)"); 276 SYSCTL_ADD_PROC(&ap->sysctl_ctx, 277 SYSCTL_CHILDREN(ap->sysctl_tree), OID_AUTO, 278 "link_pwr_state", CTLTYPE_STRING | CTLFLAG_RD, ap, 0, 279 ahci_sysctl_link_pwr_state, "A", 280 "Link power management state"); 281 282 } 283 284 kthread_create(ahci_port_thread, ap, &ap->ap_thread, 285 "%s", PORTNAME(ap)); 286 } 287 288 /* 289 * Stop the OS-specific port helper thread and kill the per-port lock. 290 */ 291 void 292 ahci_os_stop_port(struct ahci_port *ap) 293 { 294 if (ap->sysctl_tree) { 295 sysctl_ctx_free(&ap->sysctl_ctx); 296 ap->sysctl_tree = NULL; 297 } 298 299 if (ap->ap_thread) { 300 ahci_os_signal_port_thread(ap, AP_SIGF_STOP); 301 ahci_os_sleep(10); 302 if (ap->ap_thread) { 303 kprintf("%s: Waiting for thread to terminate\n", 304 PORTNAME(ap)); 305 while (ap->ap_thread) 306 ahci_os_sleep(100); 307 kprintf("%s: thread terminated\n", 308 PORTNAME(ap)); 309 } 310 } 311 lockuninit(&ap->ap_lock); 312 } 313 314 /* 315 * Add (mask) to the set of bits being sent to the per-port thread helper 316 * and wake the helper up if necessary. 317 */ 318 void 319 ahci_os_signal_port_thread(struct ahci_port *ap, int mask) 320 { 321 lockmgr(&ap->ap_sig_lock, LK_EXCLUSIVE); 322 atomic_set_int(&ap->ap_signal, mask); 323 wakeup(&ap->ap_thread); 324 lockmgr(&ap->ap_sig_lock, LK_RELEASE); 325 } 326 327 /* 328 * Unconditionally lock the port structure for access. 329 */ 330 void 331 ahci_os_lock_port(struct ahci_port *ap) 332 { 333 lockmgr(&ap->ap_lock, LK_EXCLUSIVE); 334 } 335 336 /* 337 * Conditionally lock the port structure for access. 338 * 339 * Returns 0 on success, non-zero on failure. 340 */ 341 int 342 ahci_os_lock_port_nb(struct ahci_port *ap) 343 { 344 return (lockmgr(&ap->ap_lock, LK_EXCLUSIVE | LK_NOWAIT)); 345 } 346 347 /* 348 * Unlock a previously locked port. 349 */ 350 void 351 ahci_os_unlock_port(struct ahci_port *ap) 352 { 353 lockmgr(&ap->ap_lock, LK_RELEASE); 354 } 355 356 /* 357 * Per-port thread helper. This helper thread is responsible for 358 * atomically retrieving and clearing the signal mask and calling 359 * the machine-independant driver core. 360 * 361 * MPSAFE 362 */ 363 static 364 void 365 ahci_port_thread(void *arg) 366 { 367 struct ahci_port *ap = arg; 368 int mask; 369 370 /* 371 * The helper thread is responsible for the initial port init, 372 * so all the ports can be inited in parallel. 373 * 374 * We also run the state machine which should do all probes. 375 * Since CAM is not attached yet we will not get out-of-order 376 * SCSI attachments. 377 */ 378 ahci_os_lock_port(ap); 379 ahci_port_init(ap); 380 atomic_clear_int(&ap->ap_signal, AP_SIGF_THREAD_SYNC); 381 wakeup(&ap->ap_signal); 382 ahci_port_state_machine(ap, 1); 383 ahci_os_unlock_port(ap); 384 atomic_clear_int(&ap->ap_signal, AP_SIGF_INIT); 385 wakeup(&ap->ap_signal); 386 387 /* 388 * Then loop on the helper core. 389 */ 390 mask = ap->ap_signal; 391 while ((mask & AP_SIGF_STOP) == 0) { 392 atomic_clear_int(&ap->ap_signal, mask); 393 ahci_port_thread_core(ap, mask); 394 lockmgr(&ap->ap_sig_lock, LK_EXCLUSIVE); 395 if (ap->ap_signal == 0) { 396 lksleep(&ap->ap_thread, &ap->ap_sig_lock, 0, 397 "ahport", 0); 398 } 399 lockmgr(&ap->ap_sig_lock, LK_RELEASE); 400 mask = ap->ap_signal; 401 } 402 ap->ap_thread = NULL; 403 } 404