11ac8d5baSMatthew Dillon /*
2fb00c6edSMatthew Dillon * (MPSAFE)
3fb00c6edSMatthew Dillon *
41ac8d5baSMatthew Dillon * Copyright (c) 2009 The DragonFly Project. All rights reserved.
51ac8d5baSMatthew Dillon *
61ac8d5baSMatthew Dillon * This code is derived from software contributed to The DragonFly Project
71ac8d5baSMatthew Dillon * by Matthew Dillon <dillon@backplane.com>
81ac8d5baSMatthew Dillon *
91ac8d5baSMatthew Dillon * Redistribution and use in source and binary forms, with or without
101ac8d5baSMatthew Dillon * modification, are permitted provided that the following conditions
111ac8d5baSMatthew Dillon * are met:
121ac8d5baSMatthew Dillon *
131ac8d5baSMatthew Dillon * 1. Redistributions of source code must retain the above copyright
141ac8d5baSMatthew Dillon * notice, this list of conditions and the following disclaimer.
151ac8d5baSMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
161ac8d5baSMatthew Dillon * notice, this list of conditions and the following disclaimer in
171ac8d5baSMatthew Dillon * the documentation and/or other materials provided with the
181ac8d5baSMatthew Dillon * distribution.
191ac8d5baSMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its
201ac8d5baSMatthew Dillon * contributors may be used to endorse or promote products derived
211ac8d5baSMatthew Dillon * from this software without specific, prior written permission.
221ac8d5baSMatthew Dillon *
231ac8d5baSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
241ac8d5baSMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
251ac8d5baSMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
261ac8d5baSMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
271ac8d5baSMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
281ac8d5baSMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
291ac8d5baSMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
301ac8d5baSMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
311ac8d5baSMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
321ac8d5baSMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
331ac8d5baSMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341ac8d5baSMatthew Dillon * SUCH DAMAGE.
351ac8d5baSMatthew Dillon */
361ac8d5baSMatthew Dillon /*
371ac8d5baSMatthew Dillon * Primary device and CAM interface to OpenBSD SILI driver, for DragonFly
381ac8d5baSMatthew Dillon */
391ac8d5baSMatthew Dillon
401ac8d5baSMatthew Dillon #include "sili.h"
411ac8d5baSMatthew Dillon
421ac8d5baSMatthew Dillon u_int32_t SiliForceGen1 = 0;
431ac8d5baSMatthew Dillon u_int32_t SiliNoFeatures = 0;
441ac8d5baSMatthew Dillon
451ac8d5baSMatthew Dillon /*
461ac8d5baSMatthew Dillon * Device bus methods
471ac8d5baSMatthew Dillon */
481ac8d5baSMatthew Dillon
491ac8d5baSMatthew Dillon static int sili_probe (device_t dev);
501ac8d5baSMatthew Dillon static int sili_attach (device_t dev);
511ac8d5baSMatthew Dillon static int sili_detach (device_t dev);
521ac8d5baSMatthew Dillon #if 0
531ac8d5baSMatthew Dillon static int sili_shutdown (device_t dev);
541ac8d5baSMatthew Dillon static int sili_suspend (device_t dev);
551ac8d5baSMatthew Dillon static int sili_resume (device_t dev);
561ac8d5baSMatthew Dillon #endif
571ac8d5baSMatthew Dillon
581ac8d5baSMatthew Dillon static void sili_port_thread(void *arg);
591ac8d5baSMatthew Dillon
601ac8d5baSMatthew Dillon static device_method_t sili_methods[] = {
611ac8d5baSMatthew Dillon DEVMETHOD(device_probe, sili_probe),
621ac8d5baSMatthew Dillon DEVMETHOD(device_attach, sili_attach),
631ac8d5baSMatthew Dillon DEVMETHOD(device_detach, sili_detach),
641ac8d5baSMatthew Dillon #if 0
651ac8d5baSMatthew Dillon DEVMETHOD(device_shutdown, sili_shutdown),
661ac8d5baSMatthew Dillon DEVMETHOD(device_suspend, sili_suspend),
671ac8d5baSMatthew Dillon DEVMETHOD(device_resume, sili_resume),
681ac8d5baSMatthew Dillon #endif
691ac8d5baSMatthew Dillon
701ac8d5baSMatthew Dillon DEVMETHOD(bus_print_child, bus_generic_print_child),
711ac8d5baSMatthew Dillon DEVMETHOD(bus_driver_added, bus_generic_driver_added),
72*d3c9c58eSSascha Wildner DEVMETHOD_END
731ac8d5baSMatthew Dillon };
741ac8d5baSMatthew Dillon
751ac8d5baSMatthew Dillon static devclass_t sili_devclass;
761ac8d5baSMatthew Dillon
771ac8d5baSMatthew Dillon static driver_t sili_driver = {
781ac8d5baSMatthew Dillon "sili",
791ac8d5baSMatthew Dillon sili_methods,
801ac8d5baSMatthew Dillon sizeof(struct sili_softc)
811ac8d5baSMatthew Dillon };
821ac8d5baSMatthew Dillon
831ac8d5baSMatthew Dillon MODULE_DEPEND(sili, cam, 1, 1, 1);
84aa2b9d05SSascha Wildner DRIVER_MODULE(sili, pci, sili_driver, sili_devclass, NULL, NULL);
851ac8d5baSMatthew Dillon
861ac8d5baSMatthew Dillon /*
871ac8d5baSMatthew Dillon * Device bus method procedures
881ac8d5baSMatthew Dillon */
891ac8d5baSMatthew Dillon static int
sili_probe(device_t dev)901ac8d5baSMatthew Dillon sili_probe (device_t dev)
911ac8d5baSMatthew Dillon {
921ac8d5baSMatthew Dillon const struct sili_device *ad;
931ac8d5baSMatthew Dillon
941ac8d5baSMatthew Dillon if (kgetenv("hint.sili.disabled"))
951ac8d5baSMatthew Dillon return(ENXIO);
961ac8d5baSMatthew Dillon if (kgetenv("hint.sili.force150"))
971ac8d5baSMatthew Dillon SiliForceGen1 = -1;
981ac8d5baSMatthew Dillon if (kgetenv("hint.sili.nofeatures"))
991ac8d5baSMatthew Dillon SiliNoFeatures = -1;
1001ac8d5baSMatthew Dillon
1011ac8d5baSMatthew Dillon ad = sili_lookup_device(dev);
1021ac8d5baSMatthew Dillon if (ad) {
1031ac8d5baSMatthew Dillon device_set_desc(dev, ad->name);
1041ac8d5baSMatthew Dillon return(-5); /* higher priority the NATA */
1051ac8d5baSMatthew Dillon }
1061ac8d5baSMatthew Dillon return(ENXIO);
1071ac8d5baSMatthew Dillon }
1081ac8d5baSMatthew Dillon
1091ac8d5baSMatthew Dillon static int
sili_attach(device_t dev)1101ac8d5baSMatthew Dillon sili_attach (device_t dev)
1111ac8d5baSMatthew Dillon {
1121ac8d5baSMatthew Dillon struct sili_softc *sc = device_get_softc(dev);
1131ac8d5baSMatthew Dillon int error;
1141ac8d5baSMatthew Dillon
1151ac8d5baSMatthew Dillon sc->sc_ad = sili_lookup_device(dev);
1161ac8d5baSMatthew Dillon if (sc->sc_ad == NULL)
1171ac8d5baSMatthew Dillon return(ENXIO);
1181ac8d5baSMatthew Dillon error = sc->sc_ad->ad_attach(dev);
1191ac8d5baSMatthew Dillon return (error);
1201ac8d5baSMatthew Dillon }
1211ac8d5baSMatthew Dillon
1221ac8d5baSMatthew Dillon static int
sili_detach(device_t dev)1231ac8d5baSMatthew Dillon sili_detach (device_t dev)
1241ac8d5baSMatthew Dillon {
1251ac8d5baSMatthew Dillon struct sili_softc *sc = device_get_softc(dev);
1261ac8d5baSMatthew Dillon int error = 0;
1271ac8d5baSMatthew Dillon
1281ac8d5baSMatthew Dillon if (sc->sc_ad) {
1291ac8d5baSMatthew Dillon error = sc->sc_ad->ad_detach(dev);
1301ac8d5baSMatthew Dillon sc->sc_ad = NULL;
1311ac8d5baSMatthew Dillon }
1321ac8d5baSMatthew Dillon return(error);
1331ac8d5baSMatthew Dillon }
1341ac8d5baSMatthew Dillon
1351ac8d5baSMatthew Dillon #if 0
1361ac8d5baSMatthew Dillon
1371ac8d5baSMatthew Dillon static int
1381ac8d5baSMatthew Dillon sili_shutdown (device_t dev)
1391ac8d5baSMatthew Dillon {
1401ac8d5baSMatthew Dillon return (0);
1411ac8d5baSMatthew Dillon }
1421ac8d5baSMatthew Dillon
1431ac8d5baSMatthew Dillon static int
1441ac8d5baSMatthew Dillon sili_suspend (device_t dev)
1451ac8d5baSMatthew Dillon {
1461ac8d5baSMatthew Dillon return (0);
1471ac8d5baSMatthew Dillon }
1481ac8d5baSMatthew Dillon
1491ac8d5baSMatthew Dillon static int
1501ac8d5baSMatthew Dillon sili_resume (device_t dev)
1511ac8d5baSMatthew Dillon {
1521ac8d5baSMatthew Dillon return (0);
1531ac8d5baSMatthew Dillon }
1541ac8d5baSMatthew Dillon
1551ac8d5baSMatthew Dillon #endif
1561ac8d5baSMatthew Dillon
1571ac8d5baSMatthew Dillon /*
1581ac8d5baSMatthew Dillon * Sleep (ms) milliseconds, error on the side of caution.
1591ac8d5baSMatthew Dillon */
1601ac8d5baSMatthew Dillon void
sili_os_sleep(int ms)1611ac8d5baSMatthew Dillon sili_os_sleep(int ms)
1621ac8d5baSMatthew Dillon {
1631ac8d5baSMatthew Dillon int ticks;
1641ac8d5baSMatthew Dillon
1651ac8d5baSMatthew Dillon ticks = hz * ms / 1000 + 1;
1661ac8d5baSMatthew Dillon tsleep(&ticks, 0, "ahslp", ticks);
1671ac8d5baSMatthew Dillon }
1681ac8d5baSMatthew Dillon
1691ac8d5baSMatthew Dillon /*
1701ac8d5baSMatthew Dillon * Sleep for a minimum interval and return the number of milliseconds
1711ac8d5baSMatthew Dillon * that was. The minimum value returned is 1
1721ac8d5baSMatthew Dillon */
1731ac8d5baSMatthew Dillon int
sili_os_softsleep(void)1741ac8d5baSMatthew Dillon sili_os_softsleep(void)
1751ac8d5baSMatthew Dillon {
1761ac8d5baSMatthew Dillon if (hz >= 1000) {
1771ac8d5baSMatthew Dillon tsleep(&ticks, 0, "ahslp", hz / 1000);
1781ac8d5baSMatthew Dillon return(1);
1791ac8d5baSMatthew Dillon } else {
1801ac8d5baSMatthew Dillon tsleep(&ticks, 0, "ahslp", 1);
1811ac8d5baSMatthew Dillon return(1000 / hz);
1821ac8d5baSMatthew Dillon }
1831ac8d5baSMatthew Dillon }
1841ac8d5baSMatthew Dillon
1851ac8d5baSMatthew Dillon void
sili_os_hardsleep(int us)1861ac8d5baSMatthew Dillon sili_os_hardsleep(int us)
1871ac8d5baSMatthew Dillon {
1881ac8d5baSMatthew Dillon DELAY(us);
1891ac8d5baSMatthew Dillon }
1901ac8d5baSMatthew Dillon
1911ac8d5baSMatthew Dillon /*
1921ac8d5baSMatthew Dillon * Create the OS-specific port helper thread and per-port lock.
1931ac8d5baSMatthew Dillon */
1941ac8d5baSMatthew Dillon void
sili_os_start_port(struct sili_port * ap)1951ac8d5baSMatthew Dillon sili_os_start_port(struct sili_port *ap)
1961ac8d5baSMatthew Dillon {
1971ac8d5baSMatthew Dillon atomic_set_int(&ap->ap_signal, AP_SIGF_INIT);
1986bac9ae4SMatthew Dillon lockinit(&ap->ap_lock, "silipo", 0, LK_CANRECURSE);
199fb00c6edSMatthew Dillon lockinit(&ap->ap_sim_lock, "silicam", 0, LK_CANRECURSE);
200fb00c6edSMatthew Dillon lockinit(&ap->ap_sig_lock, "siport", 0, 0);
2011ac8d5baSMatthew Dillon kthread_create(sili_port_thread, ap, &ap->ap_thread,
2021ac8d5baSMatthew Dillon "%s", PORTNAME(ap));
2031ac8d5baSMatthew Dillon }
2041ac8d5baSMatthew Dillon
2051ac8d5baSMatthew Dillon /*
2061ac8d5baSMatthew Dillon * Stop the OS-specific port helper thread and kill the per-port lock.
2071ac8d5baSMatthew Dillon */
2081ac8d5baSMatthew Dillon void
sili_os_stop_port(struct sili_port * ap)2091ac8d5baSMatthew Dillon sili_os_stop_port(struct sili_port *ap)
2101ac8d5baSMatthew Dillon {
2111ac8d5baSMatthew Dillon if (ap->ap_thread) {
2121ac8d5baSMatthew Dillon sili_os_signal_port_thread(ap, AP_SIGF_STOP);
2131ac8d5baSMatthew Dillon sili_os_sleep(10);
2141ac8d5baSMatthew Dillon if (ap->ap_thread) {
2151ac8d5baSMatthew Dillon kprintf("%s: Waiting for thread to terminate\n",
2161ac8d5baSMatthew Dillon PORTNAME(ap));
2171ac8d5baSMatthew Dillon while (ap->ap_thread)
2181ac8d5baSMatthew Dillon sili_os_sleep(100);
2191ac8d5baSMatthew Dillon kprintf("%s: thread terminated\n",
2201ac8d5baSMatthew Dillon PORTNAME(ap));
2211ac8d5baSMatthew Dillon }
2221ac8d5baSMatthew Dillon }
2231ac8d5baSMatthew Dillon lockuninit(&ap->ap_lock);
2241ac8d5baSMatthew Dillon }
2251ac8d5baSMatthew Dillon
2261ac8d5baSMatthew Dillon /*
2271ac8d5baSMatthew Dillon * Add (mask) to the set of bits being sent to the per-port thread helper
2281ac8d5baSMatthew Dillon * and wake the helper up if necessary.
2291ac8d5baSMatthew Dillon */
2301ac8d5baSMatthew Dillon void
sili_os_signal_port_thread(struct sili_port * ap,int mask)2311ac8d5baSMatthew Dillon sili_os_signal_port_thread(struct sili_port *ap, int mask)
2321ac8d5baSMatthew Dillon {
233fb00c6edSMatthew Dillon lockmgr(&ap->ap_sig_lock, LK_EXCLUSIVE);
2341ac8d5baSMatthew Dillon atomic_set_int(&ap->ap_signal, mask);
2351ac8d5baSMatthew Dillon wakeup(&ap->ap_thread);
236fb00c6edSMatthew Dillon lockmgr(&ap->ap_sig_lock, LK_RELEASE);
2371ac8d5baSMatthew Dillon }
2381ac8d5baSMatthew Dillon
2391ac8d5baSMatthew Dillon /*
2401ac8d5baSMatthew Dillon * Unconditionally lock the port structure for access.
2411ac8d5baSMatthew Dillon */
2421ac8d5baSMatthew Dillon void
sili_os_lock_port(struct sili_port * ap)2431ac8d5baSMatthew Dillon sili_os_lock_port(struct sili_port *ap)
2441ac8d5baSMatthew Dillon {
2451ac8d5baSMatthew Dillon lockmgr(&ap->ap_lock, LK_EXCLUSIVE);
2461ac8d5baSMatthew Dillon }
2471ac8d5baSMatthew Dillon
2481ac8d5baSMatthew Dillon /*
2491ac8d5baSMatthew Dillon * Conditionally lock the port structure for access.
2501ac8d5baSMatthew Dillon *
2511ac8d5baSMatthew Dillon * Returns 0 on success, non-zero on failure.
2521ac8d5baSMatthew Dillon */
2531ac8d5baSMatthew Dillon int
sili_os_lock_port_nb(struct sili_port * ap)2541ac8d5baSMatthew Dillon sili_os_lock_port_nb(struct sili_port *ap)
2551ac8d5baSMatthew Dillon {
2561ac8d5baSMatthew Dillon return (lockmgr(&ap->ap_lock, LK_EXCLUSIVE | LK_NOWAIT));
2571ac8d5baSMatthew Dillon }
2581ac8d5baSMatthew Dillon
2591ac8d5baSMatthew Dillon /*
2601ac8d5baSMatthew Dillon * Unlock a previously locked port.
2611ac8d5baSMatthew Dillon */
2621ac8d5baSMatthew Dillon void
sili_os_unlock_port(struct sili_port * ap)2631ac8d5baSMatthew Dillon sili_os_unlock_port(struct sili_port *ap)
2641ac8d5baSMatthew Dillon {
2651ac8d5baSMatthew Dillon lockmgr(&ap->ap_lock, LK_RELEASE);
2661ac8d5baSMatthew Dillon }
2671ac8d5baSMatthew Dillon
2681ac8d5baSMatthew Dillon /*
2691ac8d5baSMatthew Dillon * Per-port thread helper. This helper thread is responsible for
2701ac8d5baSMatthew Dillon * atomically retrieving and clearing the signal mask and calling
2711ac8d5baSMatthew Dillon * the machine-independant driver core.
272cd8ab232SMatthew Dillon *
273cd8ab232SMatthew Dillon * MPSAFE
2741ac8d5baSMatthew Dillon */
2751ac8d5baSMatthew Dillon static
2761ac8d5baSMatthew Dillon void
sili_port_thread(void * arg)2771ac8d5baSMatthew Dillon sili_port_thread(void *arg)
2781ac8d5baSMatthew Dillon {
2791ac8d5baSMatthew Dillon struct sili_port *ap = arg;
2801ac8d5baSMatthew Dillon int mask;
2811ac8d5baSMatthew Dillon
2821ac8d5baSMatthew Dillon /*
2831ac8d5baSMatthew Dillon * The helper thread is responsible for the initial port init,
2841ac8d5baSMatthew Dillon * so all the ports can be inited in parallel.
2851ac8d5baSMatthew Dillon *
2861ac8d5baSMatthew Dillon * We also run the state machine which should do all probes.
2871ac8d5baSMatthew Dillon * Since CAM is not attached yet we will not get out-of-order
2881ac8d5baSMatthew Dillon * SCSI attachments.
2891ac8d5baSMatthew Dillon */
2901ac8d5baSMatthew Dillon sili_os_lock_port(ap);
291a35ddbb4SMatthew Dillon sili_port_init(ap);
2921ac8d5baSMatthew Dillon sili_port_state_machine(ap, 1);
2931ac8d5baSMatthew Dillon sili_os_unlock_port(ap);
2941ac8d5baSMatthew Dillon atomic_clear_int(&ap->ap_signal, AP_SIGF_INIT);
2951ac8d5baSMatthew Dillon wakeup(&ap->ap_signal);
2961ac8d5baSMatthew Dillon
2971ac8d5baSMatthew Dillon /*
2981ac8d5baSMatthew Dillon * Then loop on the helper core.
2991ac8d5baSMatthew Dillon */
3001ac8d5baSMatthew Dillon mask = ap->ap_signal;
3011ac8d5baSMatthew Dillon while ((mask & AP_SIGF_STOP) == 0) {
3021ac8d5baSMatthew Dillon atomic_clear_int(&ap->ap_signal, mask);
3031ac8d5baSMatthew Dillon sili_port_thread_core(ap, mask);
304fb00c6edSMatthew Dillon lockmgr(&ap->ap_sig_lock, LK_EXCLUSIVE);
305fb00c6edSMatthew Dillon if (ap->ap_signal == 0) {
306fb00c6edSMatthew Dillon lksleep(&ap->ap_thread, &ap->ap_sig_lock, 0,
307fb00c6edSMatthew Dillon "siport", 0);
308fb00c6edSMatthew Dillon }
309fb00c6edSMatthew Dillon lockmgr(&ap->ap_sig_lock, LK_RELEASE);
3101ac8d5baSMatthew Dillon mask = ap->ap_signal;
3111ac8d5baSMatthew Dillon }
3121ac8d5baSMatthew Dillon ap->ap_thread = NULL;
3131ac8d5baSMatthew Dillon }
314