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 40*afa796d2SMatthew Dillon u_int32_t AhciForceGen1 = 0; 41*afa796d2SMatthew Dillon u_int32_t AhciNoFeatures = 0; 42*afa796d2SMatthew 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); 50258223a3SMatthew Dillon #if 0 51258223a3SMatthew Dillon static int ahci_shutdown (device_t dev); 52258223a3SMatthew Dillon static int ahci_suspend (device_t dev); 53258223a3SMatthew Dillon static int ahci_resume (device_t dev); 54258223a3SMatthew Dillon #endif 55258223a3SMatthew Dillon 56f4553de1SMatthew Dillon static void ahci_port_thread(void *arg); 57f4553de1SMatthew Dillon 58258223a3SMatthew Dillon static device_method_t ahci_methods[] = { 59258223a3SMatthew Dillon DEVMETHOD(device_probe, ahci_probe), 60258223a3SMatthew Dillon DEVMETHOD(device_attach, ahci_attach), 61258223a3SMatthew Dillon DEVMETHOD(device_detach, ahci_detach), 62258223a3SMatthew Dillon #if 0 63258223a3SMatthew Dillon DEVMETHOD(device_shutdown, ahci_shutdown), 64258223a3SMatthew Dillon DEVMETHOD(device_suspend, ahci_suspend), 65258223a3SMatthew Dillon DEVMETHOD(device_resume, ahci_resume), 66258223a3SMatthew Dillon #endif 67258223a3SMatthew Dillon 68258223a3SMatthew Dillon DEVMETHOD(bus_print_child, bus_generic_print_child), 69258223a3SMatthew Dillon DEVMETHOD(bus_driver_added, bus_generic_driver_added), 70258223a3SMatthew Dillon {0, 0} 71258223a3SMatthew Dillon }; 72258223a3SMatthew Dillon 73258223a3SMatthew Dillon static devclass_t ahci_devclass; 74258223a3SMatthew Dillon 75258223a3SMatthew Dillon static driver_t ahci_driver = { 76258223a3SMatthew Dillon "ahci", 77258223a3SMatthew Dillon ahci_methods, 78258223a3SMatthew Dillon sizeof(struct ahci_softc) 79258223a3SMatthew Dillon }; 80258223a3SMatthew Dillon 81258223a3SMatthew Dillon MODULE_DEPEND(ahci, cam, 1, 1, 1); 82258223a3SMatthew Dillon DRIVER_MODULE(ahci, pci, ahci_driver, ahci_devclass, 0, 0); 83258223a3SMatthew Dillon 84258223a3SMatthew Dillon /* 85258223a3SMatthew Dillon * Device bus method procedures 86258223a3SMatthew Dillon */ 87258223a3SMatthew Dillon static int 88258223a3SMatthew Dillon ahci_probe (device_t dev) 89258223a3SMatthew Dillon { 90258223a3SMatthew Dillon const struct ahci_device *ad; 91258223a3SMatthew Dillon 92c45a2365SMatthew Dillon if (kgetenv("hint.ahci.disabled")) 93c45a2365SMatthew Dillon return(ENXIO); 940700c104SMatthew Dillon if (kgetenv("hint.ahci.force150")) 950700c104SMatthew Dillon AhciForceGen1 = -1; 96*afa796d2SMatthew Dillon if (kgetenv("hint.ahci.nofeatures")) 97*afa796d2SMatthew Dillon AhciNoFeatures = -1; 98c45a2365SMatthew Dillon 99258223a3SMatthew Dillon ad = ahci_lookup_device(dev); 100258223a3SMatthew Dillon if (ad) { 101258223a3SMatthew Dillon device_set_desc(dev, ad->name); 102258223a3SMatthew Dillon return(-5); /* higher priority the NATA */ 103258223a3SMatthew Dillon } 104258223a3SMatthew Dillon return(ENXIO); 105258223a3SMatthew Dillon } 106258223a3SMatthew Dillon 107258223a3SMatthew Dillon static int 108258223a3SMatthew Dillon ahci_attach (device_t dev) 109258223a3SMatthew Dillon { 110258223a3SMatthew Dillon struct ahci_softc *sc = device_get_softc(dev); 111258223a3SMatthew Dillon int error; 112258223a3SMatthew Dillon 113258223a3SMatthew Dillon sc->sc_ad = ahci_lookup_device(dev); 114258223a3SMatthew Dillon if (sc->sc_ad == NULL) 115258223a3SMatthew Dillon return(ENXIO); 116258223a3SMatthew Dillon error = sc->sc_ad->ad_attach(dev); 117258223a3SMatthew Dillon return (error); 118258223a3SMatthew Dillon } 119258223a3SMatthew Dillon 120258223a3SMatthew Dillon static int 121258223a3SMatthew Dillon ahci_detach (device_t dev) 122258223a3SMatthew Dillon { 123258223a3SMatthew Dillon struct ahci_softc *sc = device_get_softc(dev); 124258223a3SMatthew Dillon int error = 0; 125258223a3SMatthew Dillon 126258223a3SMatthew Dillon if (sc->sc_ad) { 127258223a3SMatthew Dillon error = sc->sc_ad->ad_detach(dev); 128258223a3SMatthew Dillon sc->sc_ad = NULL; 129258223a3SMatthew Dillon } 130258223a3SMatthew Dillon return(error); 131258223a3SMatthew Dillon } 132258223a3SMatthew Dillon 133258223a3SMatthew Dillon #if 0 134258223a3SMatthew Dillon 135258223a3SMatthew Dillon static int 136258223a3SMatthew Dillon ahci_shutdown (device_t dev) 137258223a3SMatthew Dillon { 138258223a3SMatthew Dillon return (0); 139258223a3SMatthew Dillon } 140258223a3SMatthew Dillon 141258223a3SMatthew Dillon static int 142258223a3SMatthew Dillon ahci_suspend (device_t dev) 143258223a3SMatthew Dillon { 144258223a3SMatthew Dillon return (0); 145258223a3SMatthew Dillon } 146258223a3SMatthew Dillon 147258223a3SMatthew Dillon static int 148258223a3SMatthew Dillon ahci_resume (device_t dev) 149258223a3SMatthew Dillon { 150258223a3SMatthew Dillon return (0); 151258223a3SMatthew Dillon } 152258223a3SMatthew Dillon 153258223a3SMatthew Dillon #endif 1543209f581SMatthew Dillon 155831bc9e3SMatthew Dillon /* 156831bc9e3SMatthew Dillon * Sleep (ms) milliseconds, error on the side of caution. 157831bc9e3SMatthew Dillon */ 1583209f581SMatthew Dillon void 1593209f581SMatthew Dillon ahci_os_sleep(int ms) 1603209f581SMatthew Dillon { 1613209f581SMatthew Dillon int ticks; 1623209f581SMatthew Dillon 1633209f581SMatthew Dillon ticks = hz * ms / 1000 + 1; 1643209f581SMatthew Dillon tsleep(&ticks, 0, "ahslp", ticks); 1653209f581SMatthew Dillon } 166831bc9e3SMatthew Dillon 167831bc9e3SMatthew Dillon /* 168831bc9e3SMatthew Dillon * Sleep for a minimum interval and return the number of milliseconds 169831bc9e3SMatthew Dillon * that was. The minimum value returned is 1 170831bc9e3SMatthew Dillon */ 171831bc9e3SMatthew Dillon int 172831bc9e3SMatthew Dillon ahci_os_softsleep(void) 173831bc9e3SMatthew Dillon { 174831bc9e3SMatthew Dillon if (hz >= 1000) { 175831bc9e3SMatthew Dillon tsleep(&ticks, 0, "ahslp", hz / 1000); 176831bc9e3SMatthew Dillon return(1); 177831bc9e3SMatthew Dillon } else { 178831bc9e3SMatthew Dillon tsleep(&ticks, 0, "ahslp", 1); 179831bc9e3SMatthew Dillon return(1000 / hz); 180831bc9e3SMatthew Dillon } 181831bc9e3SMatthew Dillon } 182831bc9e3SMatthew Dillon 183831bc9e3SMatthew Dillon void 184831bc9e3SMatthew Dillon ahci_os_hardsleep(int us) 185831bc9e3SMatthew Dillon { 186831bc9e3SMatthew Dillon DELAY(us); 1873209f581SMatthew Dillon } 188f4553de1SMatthew Dillon 189f4553de1SMatthew Dillon /* 190f4553de1SMatthew Dillon * Create the OS-specific port helper thread and per-port lock. 191f4553de1SMatthew Dillon */ 192f4553de1SMatthew Dillon void 193f4553de1SMatthew Dillon ahci_os_start_port(struct ahci_port *ap) 194f4553de1SMatthew Dillon { 195f4553de1SMatthew Dillon atomic_set_int(&ap->ap_signal, AP_SIGF_INIT); 196f4553de1SMatthew Dillon lockinit(&ap->ap_lock, "ahcipo", 0, 0); 197f4553de1SMatthew Dillon kthread_create(ahci_port_thread, ap, &ap->ap_thread, 198f4553de1SMatthew Dillon "%s", PORTNAME(ap)); 199f4553de1SMatthew Dillon } 200f4553de1SMatthew Dillon 201f4553de1SMatthew Dillon /* 202f4553de1SMatthew Dillon * Stop the OS-specific port helper thread and kill the per-port lock. 203f4553de1SMatthew Dillon */ 204f4553de1SMatthew Dillon void 205f4553de1SMatthew Dillon ahci_os_stop_port(struct ahci_port *ap) 206f4553de1SMatthew Dillon { 207f4553de1SMatthew Dillon if (ap->ap_thread) { 208f4553de1SMatthew Dillon ahci_os_signal_port_thread(ap, AP_SIGF_STOP); 209f4553de1SMatthew Dillon ahci_os_sleep(10); 210f4553de1SMatthew Dillon if (ap->ap_thread) { 211f4553de1SMatthew Dillon kprintf("%s: Waiting for thread to terminate\n", 212f4553de1SMatthew Dillon PORTNAME(ap)); 213f4553de1SMatthew Dillon while (ap->ap_thread) 214f4553de1SMatthew Dillon ahci_os_sleep(100); 215f4553de1SMatthew Dillon kprintf("%s: thread terminated\n", 216f4553de1SMatthew Dillon PORTNAME(ap)); 217f4553de1SMatthew Dillon } 218f4553de1SMatthew Dillon } 219f4553de1SMatthew Dillon lockuninit(&ap->ap_lock); 220f4553de1SMatthew Dillon } 221f4553de1SMatthew Dillon 222f4553de1SMatthew Dillon /* 223f4553de1SMatthew Dillon * Add (mask) to the set of bits being sent to the per-port thread helper 224f4553de1SMatthew Dillon * and wake the helper up if necessary. 225f4553de1SMatthew Dillon */ 226f4553de1SMatthew Dillon void 227f4553de1SMatthew Dillon ahci_os_signal_port_thread(struct ahci_port *ap, int mask) 228f4553de1SMatthew Dillon { 229f4553de1SMatthew Dillon atomic_set_int(&ap->ap_signal, mask); 230f4553de1SMatthew Dillon wakeup(&ap->ap_thread); 231f4553de1SMatthew Dillon } 232f4553de1SMatthew Dillon 233f4553de1SMatthew Dillon /* 234f4553de1SMatthew Dillon * Unconditionally lock the port structure for access. 235f4553de1SMatthew Dillon */ 236f4553de1SMatthew Dillon void 237f4553de1SMatthew Dillon ahci_os_lock_port(struct ahci_port *ap) 238f4553de1SMatthew Dillon { 239f4553de1SMatthew Dillon lockmgr(&ap->ap_lock, LK_EXCLUSIVE); 240f4553de1SMatthew Dillon } 241f4553de1SMatthew Dillon 242f4553de1SMatthew Dillon /* 243f4553de1SMatthew Dillon * Conditionally lock the port structure for access. 244f4553de1SMatthew Dillon * 245f4553de1SMatthew Dillon * Returns 0 on success, non-zero on failure. 246f4553de1SMatthew Dillon */ 247f4553de1SMatthew Dillon int 248f4553de1SMatthew Dillon ahci_os_lock_port_nb(struct ahci_port *ap) 249f4553de1SMatthew Dillon { 250f4553de1SMatthew Dillon return (lockmgr(&ap->ap_lock, LK_EXCLUSIVE | LK_NOWAIT)); 251f4553de1SMatthew Dillon } 252f4553de1SMatthew Dillon 253f4553de1SMatthew Dillon /* 254f4553de1SMatthew Dillon * Unlock a previously locked port. 255f4553de1SMatthew Dillon */ 256f4553de1SMatthew Dillon void 257f4553de1SMatthew Dillon ahci_os_unlock_port(struct ahci_port *ap) 258f4553de1SMatthew Dillon { 259f4553de1SMatthew Dillon lockmgr(&ap->ap_lock, LK_RELEASE); 260f4553de1SMatthew Dillon } 261f4553de1SMatthew Dillon 262f4553de1SMatthew Dillon /* 263f4553de1SMatthew Dillon * Per-port thread helper. This helper thread is responsible for 264f4553de1SMatthew Dillon * atomically retrieving and clearing the signal mask and calling 265f4553de1SMatthew Dillon * the machine-independant driver core. 266f4553de1SMatthew Dillon */ 267f4553de1SMatthew Dillon static 268f4553de1SMatthew Dillon void 269f4553de1SMatthew Dillon ahci_port_thread(void *arg) 270f4553de1SMatthew Dillon { 271f4553de1SMatthew Dillon struct ahci_port *ap = arg; 272f4553de1SMatthew Dillon int mask; 273f4553de1SMatthew Dillon 274f4553de1SMatthew Dillon /* 275f4553de1SMatthew Dillon * The helper thread is responsible for the initial port init, 276f4553de1SMatthew Dillon * so all the ports can be inited in parallel. 277f4553de1SMatthew Dillon * 278f4553de1SMatthew Dillon * We also run the state machine which should do all probes. 279f4553de1SMatthew Dillon * Since CAM is not attached yet we will not get out-of-order 280f4553de1SMatthew Dillon * SCSI attachments. 281f4553de1SMatthew Dillon */ 282f4553de1SMatthew Dillon ahci_os_lock_port(ap); 283f4553de1SMatthew Dillon ahci_port_init(ap, NULL); 284831bc9e3SMatthew Dillon ahci_port_state_machine(ap, 1); 285f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 286f4553de1SMatthew Dillon atomic_clear_int(&ap->ap_signal, AP_SIGF_INIT); 287f4553de1SMatthew Dillon wakeup(&ap->ap_signal); 288f4553de1SMatthew Dillon 289f4553de1SMatthew Dillon /* 290f4553de1SMatthew Dillon * Then loop on the helper core. 291f4553de1SMatthew Dillon */ 292f4553de1SMatthew Dillon mask = ap->ap_signal; 293f4553de1SMatthew Dillon while ((mask & AP_SIGF_STOP) == 0) { 294f4553de1SMatthew Dillon atomic_clear_int(&ap->ap_signal, mask); 295f4553de1SMatthew Dillon ahci_port_thread_core(ap, mask); 296f4553de1SMatthew Dillon crit_enter(); 297f4553de1SMatthew Dillon tsleep_interlock(&ap->ap_thread); 298f4553de1SMatthew Dillon if (ap->ap_signal == 0) 299f4553de1SMatthew Dillon tsleep(&ap->ap_thread, 0, "ahport", 0); 300f4553de1SMatthew Dillon crit_exit(); 301f4553de1SMatthew Dillon mask = ap->ap_signal; 302f4553de1SMatthew Dillon } 303f4553de1SMatthew Dillon ap->ap_thread = NULL; 304f4553de1SMatthew Dillon } 305