1258223a3SMatthew Dillon /* 2258223a3SMatthew Dillon * Copyright (c) 2009 The DragonFly Project. All rights reserved. 3258223a3SMatthew Dillon * 4258223a3SMatthew Dillon * This code is derived from software contributed to The DragonFly Project 5258223a3SMatthew Dillon * by Matthew Dillon <dillon@backplane.com> 6258223a3SMatthew Dillon * 7258223a3SMatthew Dillon * Redistribution and use in source and binary forms, with or without 8258223a3SMatthew Dillon * modification, are permitted provided that the following conditions 9258223a3SMatthew Dillon * are met: 10258223a3SMatthew Dillon * 11258223a3SMatthew Dillon * 1. Redistributions of source code must retain the above copyright 12258223a3SMatthew Dillon * notice, this list of conditions and the following disclaimer. 13258223a3SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 14258223a3SMatthew Dillon * notice, this list of conditions and the following disclaimer in 15258223a3SMatthew Dillon * the documentation and/or other materials provided with the 16258223a3SMatthew Dillon * distribution. 17258223a3SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 18258223a3SMatthew Dillon * contributors may be used to endorse or promote products derived 19258223a3SMatthew Dillon * from this software without specific, prior written permission. 20258223a3SMatthew Dillon * 21258223a3SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22258223a3SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23258223a3SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24258223a3SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25258223a3SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26258223a3SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27258223a3SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28258223a3SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29258223a3SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30258223a3SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31258223a3SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32258223a3SMatthew Dillon * SUCH DAMAGE. 33258223a3SMatthew Dillon */ 34258223a3SMatthew Dillon /* 35258223a3SMatthew Dillon * Primary device and CAM interface to OpenBSD AHCI driver, for DragonFly 36258223a3SMatthew Dillon */ 37258223a3SMatthew Dillon 38258223a3SMatthew Dillon #include "ahci.h" 39258223a3SMatthew Dillon 40afa796d2SMatthew Dillon u_int32_t AhciForceGen1 = 0; 41afa796d2SMatthew Dillon u_int32_t AhciNoFeatures = 0; 42afa796d2SMatthew Dillon 43258223a3SMatthew Dillon /* 44258223a3SMatthew Dillon * Device bus methods 45258223a3SMatthew Dillon */ 46258223a3SMatthew Dillon 47258223a3SMatthew Dillon static int ahci_probe (device_t dev); 48258223a3SMatthew Dillon static int ahci_attach (device_t dev); 49258223a3SMatthew Dillon static int ahci_detach (device_t dev); 50*f17a0cedSMatthew Dillon static int ahci_systcl_link_pwr_mgmt (SYSCTL_HANDLER_ARGS); 51258223a3SMatthew Dillon #if 0 52258223a3SMatthew Dillon static int ahci_shutdown (device_t dev); 53258223a3SMatthew Dillon static int ahci_suspend (device_t dev); 54258223a3SMatthew Dillon static int ahci_resume (device_t dev); 55258223a3SMatthew Dillon #endif 56258223a3SMatthew Dillon 57f4553de1SMatthew Dillon static void ahci_port_thread(void *arg); 58f4553de1SMatthew Dillon 59258223a3SMatthew Dillon static device_method_t ahci_methods[] = { 60258223a3SMatthew Dillon DEVMETHOD(device_probe, ahci_probe), 61258223a3SMatthew Dillon DEVMETHOD(device_attach, ahci_attach), 62258223a3SMatthew Dillon DEVMETHOD(device_detach, ahci_detach), 63258223a3SMatthew Dillon #if 0 64258223a3SMatthew Dillon DEVMETHOD(device_shutdown, ahci_shutdown), 65258223a3SMatthew Dillon DEVMETHOD(device_suspend, ahci_suspend), 66258223a3SMatthew Dillon DEVMETHOD(device_resume, ahci_resume), 67258223a3SMatthew Dillon #endif 68258223a3SMatthew Dillon 69258223a3SMatthew Dillon DEVMETHOD(bus_print_child, bus_generic_print_child), 70258223a3SMatthew Dillon DEVMETHOD(bus_driver_added, bus_generic_driver_added), 71258223a3SMatthew Dillon {0, 0} 72258223a3SMatthew Dillon }; 73258223a3SMatthew Dillon 74258223a3SMatthew Dillon static devclass_t ahci_devclass; 75258223a3SMatthew Dillon 76258223a3SMatthew Dillon static driver_t ahci_driver = { 77258223a3SMatthew Dillon "ahci", 78258223a3SMatthew Dillon ahci_methods, 79258223a3SMatthew Dillon sizeof(struct ahci_softc) 80258223a3SMatthew Dillon }; 81258223a3SMatthew Dillon 82258223a3SMatthew Dillon MODULE_DEPEND(ahci, cam, 1, 1, 1); 83258223a3SMatthew Dillon DRIVER_MODULE(ahci, pci, ahci_driver, ahci_devclass, 0, 0); 84258223a3SMatthew Dillon 85258223a3SMatthew Dillon /* 86258223a3SMatthew Dillon * Device bus method procedures 87258223a3SMatthew Dillon */ 88258223a3SMatthew Dillon static int 89258223a3SMatthew Dillon ahci_probe (device_t dev) 90258223a3SMatthew Dillon { 91258223a3SMatthew Dillon const struct ahci_device *ad; 92258223a3SMatthew Dillon 93c45a2365SMatthew Dillon if (kgetenv("hint.ahci.disabled")) 94c45a2365SMatthew Dillon return(ENXIO); 950700c104SMatthew Dillon if (kgetenv("hint.ahci.force150")) 960700c104SMatthew Dillon AhciForceGen1 = -1; 97afa796d2SMatthew Dillon if (kgetenv("hint.ahci.nofeatures")) 98afa796d2SMatthew Dillon AhciNoFeatures = -1; 99c45a2365SMatthew Dillon 100258223a3SMatthew Dillon ad = ahci_lookup_device(dev); 101258223a3SMatthew Dillon if (ad) { 102258223a3SMatthew Dillon device_set_desc(dev, ad->name); 103258223a3SMatthew Dillon return(-5); /* higher priority the NATA */ 104258223a3SMatthew Dillon } 105258223a3SMatthew Dillon return(ENXIO); 106258223a3SMatthew Dillon } 107258223a3SMatthew Dillon 108258223a3SMatthew Dillon static int 109258223a3SMatthew Dillon ahci_attach (device_t dev) 110258223a3SMatthew Dillon { 111258223a3SMatthew Dillon struct ahci_softc *sc = device_get_softc(dev); 112*f17a0cedSMatthew Dillon char name[16]; 113258223a3SMatthew Dillon int error; 114258223a3SMatthew Dillon 115258223a3SMatthew Dillon sc->sc_ad = ahci_lookup_device(dev); 116258223a3SMatthew Dillon if (sc->sc_ad == NULL) 117258223a3SMatthew Dillon return(ENXIO); 118*f17a0cedSMatthew Dillon 119*f17a0cedSMatthew Dillon sysctl_ctx_init(&sc->sysctl_ctx); 120*f17a0cedSMatthew Dillon ksnprintf(name, sizeof(name), "%s%d", 121*f17a0cedSMatthew Dillon device_get_name(dev), device_get_unit(dev)); 122*f17a0cedSMatthew Dillon sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 123*f17a0cedSMatthew Dillon SYSCTL_STATIC_CHILDREN(_hw), 124*f17a0cedSMatthew Dillon OID_AUTO, name, CTLFLAG_RD, 0, ""); 125*f17a0cedSMatthew Dillon 126258223a3SMatthew Dillon error = sc->sc_ad->ad_attach(dev); 127*f17a0cedSMatthew Dillon if (error) { 128*f17a0cedSMatthew Dillon sysctl_ctx_free(&sc->sysctl_ctx); 129*f17a0cedSMatthew Dillon sc->sysctl_tree = NULL; 130*f17a0cedSMatthew Dillon } 131258223a3SMatthew Dillon return (error); 132258223a3SMatthew Dillon } 133258223a3SMatthew Dillon 134258223a3SMatthew Dillon static int 135258223a3SMatthew Dillon ahci_detach (device_t dev) 136258223a3SMatthew Dillon { 137258223a3SMatthew Dillon struct ahci_softc *sc = device_get_softc(dev); 138258223a3SMatthew Dillon int error = 0; 139258223a3SMatthew Dillon 140*f17a0cedSMatthew Dillon if (sc->sysctl_tree) { 141*f17a0cedSMatthew Dillon sysctl_ctx_free(&sc->sysctl_ctx); 142*f17a0cedSMatthew Dillon sc->sysctl_tree = NULL; 143*f17a0cedSMatthew Dillon } 144258223a3SMatthew Dillon if (sc->sc_ad) { 145258223a3SMatthew Dillon error = sc->sc_ad->ad_detach(dev); 146258223a3SMatthew Dillon sc->sc_ad = NULL; 147258223a3SMatthew Dillon } 148258223a3SMatthew Dillon return(error); 149258223a3SMatthew Dillon } 150258223a3SMatthew Dillon 151*f17a0cedSMatthew Dillon static int 152*f17a0cedSMatthew Dillon ahci_systcl_link_pwr_mgmt (SYSCTL_HANDLER_ARGS) 153*f17a0cedSMatthew Dillon { 154*f17a0cedSMatthew Dillon struct ahci_port *ap = arg1; 155*f17a0cedSMatthew Dillon int error, link_pwr_mgmt; 156*f17a0cedSMatthew Dillon 157*f17a0cedSMatthew Dillon link_pwr_mgmt = ap->link_pwr_mgmt; 158*f17a0cedSMatthew Dillon error = sysctl_handle_int(oidp, &link_pwr_mgmt, 0, req); 159*f17a0cedSMatthew Dillon if (error || req->newptr == NULL) 160*f17a0cedSMatthew Dillon return error; 161*f17a0cedSMatthew Dillon 162*f17a0cedSMatthew Dillon ahci_port_link_pwr_mgmt(ap, link_pwr_mgmt); 163*f17a0cedSMatthew Dillon return 0; 164*f17a0cedSMatthew Dillon } 165*f17a0cedSMatthew Dillon 166258223a3SMatthew Dillon #if 0 167258223a3SMatthew Dillon 168258223a3SMatthew Dillon static int 169258223a3SMatthew Dillon ahci_shutdown (device_t dev) 170258223a3SMatthew Dillon { 171258223a3SMatthew Dillon return (0); 172258223a3SMatthew Dillon } 173258223a3SMatthew Dillon 174258223a3SMatthew Dillon static int 175258223a3SMatthew Dillon ahci_suspend (device_t dev) 176258223a3SMatthew Dillon { 177258223a3SMatthew Dillon return (0); 178258223a3SMatthew Dillon } 179258223a3SMatthew Dillon 180258223a3SMatthew Dillon static int 181258223a3SMatthew Dillon ahci_resume (device_t dev) 182258223a3SMatthew Dillon { 183258223a3SMatthew Dillon return (0); 184258223a3SMatthew Dillon } 185258223a3SMatthew Dillon 186258223a3SMatthew Dillon #endif 1873209f581SMatthew Dillon 188831bc9e3SMatthew Dillon /* 189831bc9e3SMatthew Dillon * Sleep (ms) milliseconds, error on the side of caution. 190831bc9e3SMatthew Dillon */ 1913209f581SMatthew Dillon void 1923209f581SMatthew Dillon ahci_os_sleep(int ms) 1933209f581SMatthew Dillon { 1943209f581SMatthew Dillon int ticks; 1953209f581SMatthew Dillon 1963209f581SMatthew Dillon ticks = hz * ms / 1000 + 1; 1973209f581SMatthew Dillon tsleep(&ticks, 0, "ahslp", ticks); 1983209f581SMatthew Dillon } 199831bc9e3SMatthew Dillon 200831bc9e3SMatthew Dillon /* 201831bc9e3SMatthew Dillon * Sleep for a minimum interval and return the number of milliseconds 202831bc9e3SMatthew Dillon * that was. The minimum value returned is 1 203831bc9e3SMatthew Dillon */ 204831bc9e3SMatthew Dillon int 205831bc9e3SMatthew Dillon ahci_os_softsleep(void) 206831bc9e3SMatthew Dillon { 207831bc9e3SMatthew Dillon if (hz >= 1000) { 208831bc9e3SMatthew Dillon tsleep(&ticks, 0, "ahslp", hz / 1000); 209831bc9e3SMatthew Dillon return(1); 210831bc9e3SMatthew Dillon } else { 211831bc9e3SMatthew Dillon tsleep(&ticks, 0, "ahslp", 1); 212831bc9e3SMatthew Dillon return(1000 / hz); 213831bc9e3SMatthew Dillon } 214831bc9e3SMatthew Dillon } 215831bc9e3SMatthew Dillon 216831bc9e3SMatthew Dillon void 217831bc9e3SMatthew Dillon ahci_os_hardsleep(int us) 218831bc9e3SMatthew Dillon { 219831bc9e3SMatthew Dillon DELAY(us); 2203209f581SMatthew Dillon } 221f4553de1SMatthew Dillon 222f4553de1SMatthew Dillon /* 223f4553de1SMatthew Dillon * Create the OS-specific port helper thread and per-port lock. 224f4553de1SMatthew Dillon */ 225f4553de1SMatthew Dillon void 226f4553de1SMatthew Dillon ahci_os_start_port(struct ahci_port *ap) 227f4553de1SMatthew Dillon { 228*f17a0cedSMatthew Dillon char name[16]; 229*f17a0cedSMatthew Dillon 230e8cf3f55SMatthew Dillon atomic_set_int(&ap->ap_signal, AP_SIGF_INIT | AP_SIGF_THREAD_SYNC); 231f4553de1SMatthew Dillon lockinit(&ap->ap_lock, "ahcipo", 0, 0); 232*f17a0cedSMatthew Dillon sysctl_ctx_init(&ap->sysctl_ctx); 233*f17a0cedSMatthew Dillon ksnprintf(name, sizeof(name), "%d", ap->ap_num); 234*f17a0cedSMatthew Dillon ap->sysctl_tree = SYSCTL_ADD_NODE(&ap->sysctl_ctx, 235*f17a0cedSMatthew Dillon SYSCTL_CHILDREN(ap->ap_sc->sysctl_tree), 236*f17a0cedSMatthew Dillon OID_AUTO, name, CTLFLAG_RD, 0, ""); 237*f17a0cedSMatthew Dillon 238*f17a0cedSMatthew Dillon if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SALP) && 239*f17a0cedSMatthew Dillon (ap->ap_sc->sc_cap & (AHCI_REG_CAP_PSC | AHCI_REG_CAP_SSC))) { 240*f17a0cedSMatthew Dillon SYSCTL_ADD_PROC(&ap->sysctl_ctx, 241*f17a0cedSMatthew Dillon SYSCTL_CHILDREN(ap->sysctl_tree), OID_AUTO, 242*f17a0cedSMatthew Dillon "link_pwr_mgmt", CTLTYPE_INT | CTLFLAG_RW, ap, 0, 243*f17a0cedSMatthew Dillon ahci_systcl_link_pwr_mgmt, "I", 244*f17a0cedSMatthew Dillon "Link power management " 245*f17a0cedSMatthew Dillon "(0 = disabled, 1 = medium, 2 = aggressive)"); 246*f17a0cedSMatthew Dillon 247*f17a0cedSMatthew Dillon } 248*f17a0cedSMatthew Dillon 249f4553de1SMatthew Dillon kthread_create(ahci_port_thread, ap, &ap->ap_thread, 250f4553de1SMatthew Dillon "%s", PORTNAME(ap)); 251f4553de1SMatthew Dillon } 252f4553de1SMatthew Dillon 253f4553de1SMatthew Dillon /* 254f4553de1SMatthew Dillon * Stop the OS-specific port helper thread and kill the per-port lock. 255f4553de1SMatthew Dillon */ 256f4553de1SMatthew Dillon void 257f4553de1SMatthew Dillon ahci_os_stop_port(struct ahci_port *ap) 258f4553de1SMatthew Dillon { 259*f17a0cedSMatthew Dillon if (ap->sysctl_tree) { 260*f17a0cedSMatthew Dillon sysctl_ctx_free(&ap->sysctl_ctx); 261*f17a0cedSMatthew Dillon ap->sysctl_tree = NULL; 262*f17a0cedSMatthew Dillon } 263*f17a0cedSMatthew Dillon 264f4553de1SMatthew Dillon if (ap->ap_thread) { 265f4553de1SMatthew Dillon ahci_os_signal_port_thread(ap, AP_SIGF_STOP); 266f4553de1SMatthew Dillon ahci_os_sleep(10); 267f4553de1SMatthew Dillon if (ap->ap_thread) { 268f4553de1SMatthew Dillon kprintf("%s: Waiting for thread to terminate\n", 269f4553de1SMatthew Dillon PORTNAME(ap)); 270f4553de1SMatthew Dillon while (ap->ap_thread) 271f4553de1SMatthew Dillon ahci_os_sleep(100); 272f4553de1SMatthew Dillon kprintf("%s: thread terminated\n", 273f4553de1SMatthew Dillon PORTNAME(ap)); 274f4553de1SMatthew Dillon } 275f4553de1SMatthew Dillon } 276f4553de1SMatthew Dillon lockuninit(&ap->ap_lock); 277f4553de1SMatthew Dillon } 278f4553de1SMatthew Dillon 279f4553de1SMatthew Dillon /* 280f4553de1SMatthew Dillon * Add (mask) to the set of bits being sent to the per-port thread helper 281f4553de1SMatthew Dillon * and wake the helper up if necessary. 282f4553de1SMatthew Dillon */ 283f4553de1SMatthew Dillon void 284f4553de1SMatthew Dillon ahci_os_signal_port_thread(struct ahci_port *ap, int mask) 285f4553de1SMatthew Dillon { 286f4553de1SMatthew Dillon atomic_set_int(&ap->ap_signal, mask); 287f4553de1SMatthew Dillon wakeup(&ap->ap_thread); 288f4553de1SMatthew Dillon } 289f4553de1SMatthew Dillon 290f4553de1SMatthew Dillon /* 291f4553de1SMatthew Dillon * Unconditionally lock the port structure for access. 292f4553de1SMatthew Dillon */ 293f4553de1SMatthew Dillon void 294f4553de1SMatthew Dillon ahci_os_lock_port(struct ahci_port *ap) 295f4553de1SMatthew Dillon { 296f4553de1SMatthew Dillon lockmgr(&ap->ap_lock, LK_EXCLUSIVE); 297f4553de1SMatthew Dillon } 298f4553de1SMatthew Dillon 299f4553de1SMatthew Dillon /* 300f4553de1SMatthew Dillon * Conditionally lock the port structure for access. 301f4553de1SMatthew Dillon * 302f4553de1SMatthew Dillon * Returns 0 on success, non-zero on failure. 303f4553de1SMatthew Dillon */ 304f4553de1SMatthew Dillon int 305f4553de1SMatthew Dillon ahci_os_lock_port_nb(struct ahci_port *ap) 306f4553de1SMatthew Dillon { 307f4553de1SMatthew Dillon return (lockmgr(&ap->ap_lock, LK_EXCLUSIVE | LK_NOWAIT)); 308f4553de1SMatthew Dillon } 309f4553de1SMatthew Dillon 310f4553de1SMatthew Dillon /* 311f4553de1SMatthew Dillon * Unlock a previously locked port. 312f4553de1SMatthew Dillon */ 313f4553de1SMatthew Dillon void 314f4553de1SMatthew Dillon ahci_os_unlock_port(struct ahci_port *ap) 315f4553de1SMatthew Dillon { 316f4553de1SMatthew Dillon lockmgr(&ap->ap_lock, LK_RELEASE); 317f4553de1SMatthew Dillon } 318f4553de1SMatthew Dillon 319f4553de1SMatthew Dillon /* 320f4553de1SMatthew Dillon * Per-port thread helper. This helper thread is responsible for 321f4553de1SMatthew Dillon * atomically retrieving and clearing the signal mask and calling 322f4553de1SMatthew Dillon * the machine-independant driver core. 323f4553de1SMatthew Dillon */ 324f4553de1SMatthew Dillon static 325f4553de1SMatthew Dillon void 326f4553de1SMatthew Dillon ahci_port_thread(void *arg) 327f4553de1SMatthew Dillon { 328f4553de1SMatthew Dillon struct ahci_port *ap = arg; 329f4553de1SMatthew Dillon int mask; 330f4553de1SMatthew Dillon 331f4553de1SMatthew Dillon /* 332f4553de1SMatthew Dillon * The helper thread is responsible for the initial port init, 333f4553de1SMatthew Dillon * so all the ports can be inited in parallel. 334f4553de1SMatthew Dillon * 335f4553de1SMatthew Dillon * We also run the state machine which should do all probes. 336f4553de1SMatthew Dillon * Since CAM is not attached yet we will not get out-of-order 337f4553de1SMatthew Dillon * SCSI attachments. 338f4553de1SMatthew Dillon */ 339f4553de1SMatthew Dillon ahci_os_lock_port(ap); 34012feb904SMatthew Dillon ahci_port_init(ap); 341e8cf3f55SMatthew Dillon atomic_clear_int(&ap->ap_signal, AP_SIGF_THREAD_SYNC); 342e8cf3f55SMatthew Dillon wakeup(&ap->ap_signal); 343831bc9e3SMatthew Dillon ahci_port_state_machine(ap, 1); 344f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 345f4553de1SMatthew Dillon atomic_clear_int(&ap->ap_signal, AP_SIGF_INIT); 346f4553de1SMatthew Dillon wakeup(&ap->ap_signal); 347f4553de1SMatthew Dillon 348f4553de1SMatthew Dillon /* 349f4553de1SMatthew Dillon * Then loop on the helper core. 350f4553de1SMatthew Dillon */ 351f4553de1SMatthew Dillon mask = ap->ap_signal; 352f4553de1SMatthew Dillon while ((mask & AP_SIGF_STOP) == 0) { 353f4553de1SMatthew Dillon atomic_clear_int(&ap->ap_signal, mask); 354f4553de1SMatthew Dillon ahci_port_thread_core(ap, mask); 355ae8e83e6SMatthew Dillon tsleep_interlock(&ap->ap_thread, 0); 356f4553de1SMatthew Dillon if (ap->ap_signal == 0) 357d9345d3aSMatthew Dillon tsleep(&ap->ap_thread, PINTERLOCKED, "ahport", 0); 358f4553de1SMatthew Dillon mask = ap->ap_signal; 359f4553de1SMatthew Dillon } 360f4553de1SMatthew Dillon ap->ap_thread = NULL; 361f4553de1SMatthew Dillon } 362