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