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 SILI driver, for DragonFly 36 */ 37 38 #include "sili.h" 39 40 u_int32_t SiliForceGen1 = 0; 41 u_int32_t SiliNoFeatures = 0; 42 43 /* 44 * Device bus methods 45 */ 46 47 static int sili_probe (device_t dev); 48 static int sili_attach (device_t dev); 49 static int sili_detach (device_t dev); 50 #if 0 51 static int sili_shutdown (device_t dev); 52 static int sili_suspend (device_t dev); 53 static int sili_resume (device_t dev); 54 #endif 55 56 static void sili_port_thread(void *arg); 57 58 static device_method_t sili_methods[] = { 59 DEVMETHOD(device_probe, sili_probe), 60 DEVMETHOD(device_attach, sili_attach), 61 DEVMETHOD(device_detach, sili_detach), 62 #if 0 63 DEVMETHOD(device_shutdown, sili_shutdown), 64 DEVMETHOD(device_suspend, sili_suspend), 65 DEVMETHOD(device_resume, sili_resume), 66 #endif 67 68 DEVMETHOD(bus_print_child, bus_generic_print_child), 69 DEVMETHOD(bus_driver_added, bus_generic_driver_added), 70 {0, 0} 71 }; 72 73 static devclass_t sili_devclass; 74 75 static driver_t sili_driver = { 76 "sili", 77 sili_methods, 78 sizeof(struct sili_softc) 79 }; 80 81 MODULE_DEPEND(sili, cam, 1, 1, 1); 82 DRIVER_MODULE(sili, pci, sili_driver, sili_devclass, 0, 0); 83 84 /* 85 * Device bus method procedures 86 */ 87 static int 88 sili_probe (device_t dev) 89 { 90 const struct sili_device *ad; 91 92 if (kgetenv("hint.sili.disabled")) 93 return(ENXIO); 94 if (kgetenv("hint.sili.force150")) 95 SiliForceGen1 = -1; 96 if (kgetenv("hint.sili.nofeatures")) 97 SiliNoFeatures = -1; 98 99 ad = sili_lookup_device(dev); 100 if (ad) { 101 device_set_desc(dev, ad->name); 102 return(-5); /* higher priority the NATA */ 103 } 104 return(ENXIO); 105 } 106 107 static int 108 sili_attach (device_t dev) 109 { 110 struct sili_softc *sc = device_get_softc(dev); 111 int error; 112 113 sc->sc_ad = sili_lookup_device(dev); 114 if (sc->sc_ad == NULL) 115 return(ENXIO); 116 error = sc->sc_ad->ad_attach(dev); 117 return (error); 118 } 119 120 static int 121 sili_detach (device_t dev) 122 { 123 struct sili_softc *sc = device_get_softc(dev); 124 int error = 0; 125 126 if (sc->sc_ad) { 127 error = sc->sc_ad->ad_detach(dev); 128 sc->sc_ad = NULL; 129 } 130 return(error); 131 } 132 133 #if 0 134 135 static int 136 sili_shutdown (device_t dev) 137 { 138 return (0); 139 } 140 141 static int 142 sili_suspend (device_t dev) 143 { 144 return (0); 145 } 146 147 static int 148 sili_resume (device_t dev) 149 { 150 return (0); 151 } 152 153 #endif 154 155 /* 156 * Sleep (ms) milliseconds, error on the side of caution. 157 */ 158 void 159 sili_os_sleep(int ms) 160 { 161 int ticks; 162 163 ticks = hz * ms / 1000 + 1; 164 tsleep(&ticks, 0, "ahslp", ticks); 165 } 166 167 /* 168 * Sleep for a minimum interval and return the number of milliseconds 169 * that was. The minimum value returned is 1 170 */ 171 int 172 sili_os_softsleep(void) 173 { 174 if (hz >= 1000) { 175 tsleep(&ticks, 0, "ahslp", hz / 1000); 176 return(1); 177 } else { 178 tsleep(&ticks, 0, "ahslp", 1); 179 return(1000 / hz); 180 } 181 } 182 183 void 184 sili_os_hardsleep(int us) 185 { 186 DELAY(us); 187 } 188 189 /* 190 * Create the OS-specific port helper thread and per-port lock. 191 */ 192 void 193 sili_os_start_port(struct sili_port *ap) 194 { 195 atomic_set_int(&ap->ap_signal, AP_SIGF_INIT); 196 lockinit(&ap->ap_lock, "silipo", 0, 0); 197 kthread_create(sili_port_thread, ap, &ap->ap_thread, 198 "%s", PORTNAME(ap)); 199 } 200 201 /* 202 * Stop the OS-specific port helper thread and kill the per-port lock. 203 */ 204 void 205 sili_os_stop_port(struct sili_port *ap) 206 { 207 if (ap->ap_thread) { 208 sili_os_signal_port_thread(ap, AP_SIGF_STOP); 209 sili_os_sleep(10); 210 if (ap->ap_thread) { 211 kprintf("%s: Waiting for thread to terminate\n", 212 PORTNAME(ap)); 213 while (ap->ap_thread) 214 sili_os_sleep(100); 215 kprintf("%s: thread terminated\n", 216 PORTNAME(ap)); 217 } 218 } 219 lockuninit(&ap->ap_lock); 220 } 221 222 /* 223 * Add (mask) to the set of bits being sent to the per-port thread helper 224 * and wake the helper up if necessary. 225 */ 226 void 227 sili_os_signal_port_thread(struct sili_port *ap, int mask) 228 { 229 atomic_set_int(&ap->ap_signal, mask); 230 wakeup(&ap->ap_thread); 231 } 232 233 /* 234 * Unconditionally lock the port structure for access. 235 */ 236 void 237 sili_os_lock_port(struct sili_port *ap) 238 { 239 lockmgr(&ap->ap_lock, LK_EXCLUSIVE); 240 } 241 242 /* 243 * Conditionally lock the port structure for access. 244 * 245 * Returns 0 on success, non-zero on failure. 246 */ 247 int 248 sili_os_lock_port_nb(struct sili_port *ap) 249 { 250 return (lockmgr(&ap->ap_lock, LK_EXCLUSIVE | LK_NOWAIT)); 251 } 252 253 /* 254 * Unlock a previously locked port. 255 */ 256 void 257 sili_os_unlock_port(struct sili_port *ap) 258 { 259 lockmgr(&ap->ap_lock, LK_RELEASE); 260 } 261 262 /* 263 * Per-port thread helper. This helper thread is responsible for 264 * atomically retrieving and clearing the signal mask and calling 265 * the machine-independant driver core. 266 */ 267 static 268 void 269 sili_port_thread(void *arg) 270 { 271 struct sili_port *ap = arg; 272 int mask; 273 274 /* 275 * The helper thread is responsible for the initial port init, 276 * so all the ports can be inited in parallel. 277 * 278 * We also run the state machine which should do all probes. 279 * Since CAM is not attached yet we will not get out-of-order 280 * SCSI attachments. 281 */ 282 sili_os_lock_port(ap); 283 sili_port_init(ap); 284 sili_port_state_machine(ap, 1); 285 sili_os_unlock_port(ap); 286 atomic_clear_int(&ap->ap_signal, AP_SIGF_INIT); 287 wakeup(&ap->ap_signal); 288 289 /* 290 * Then loop on the helper core. 291 */ 292 mask = ap->ap_signal; 293 while ((mask & AP_SIGF_STOP) == 0) { 294 atomic_clear_int(&ap->ap_signal, mask); 295 sili_port_thread_core(ap, mask); 296 tsleep_interlock(&ap->ap_thread, 0); 297 if (ap->ap_signal == 0) 298 tsleep(&ap->ap_thread, PINTERLOCKED, "ahport", 0); 299 mask = ap->ap_signal; 300 } 301 ap->ap_thread = NULL; 302 } 303