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