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