12aedd662SScott Long /*- 2aad970f1SDavid E. O'Brien * Written by: David Jeffery 32aedd662SScott Long * Copyright (c) 2002 Adaptec Inc. 42aedd662SScott Long * All rights reserved. 52aedd662SScott Long * 62aedd662SScott Long * Redistribution and use in source and binary forms, with or without 72aedd662SScott Long * modification, are permitted provided that the following conditions 82aedd662SScott Long * are met: 92aedd662SScott Long * 1. Redistributions of source code must retain the above copyright 102aedd662SScott Long * notice, this list of conditions and the following disclaimer. 112aedd662SScott Long * 2. Redistributions in binary form must reproduce the above copyright 122aedd662SScott Long * notice, this list of conditions and the following disclaimer in the 132aedd662SScott Long * documentation and/or other materials provided with the distribution. 142aedd662SScott Long * 152aedd662SScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 162aedd662SScott Long * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 172aedd662SScott Long * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 182aedd662SScott Long * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 192aedd662SScott Long * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 202aedd662SScott Long * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 212aedd662SScott Long * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 222aedd662SScott Long * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 232aedd662SScott Long * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 242aedd662SScott Long * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 252aedd662SScott Long * SUCH DAMAGE. 262aedd662SScott Long */ 272aedd662SScott Long 28aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 29aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 302aedd662SScott Long 312aedd662SScott Long #include <dev/ips/ips.h> 322aedd662SScott Long #include <sys/stat.h> 332aedd662SScott Long #include <sys/time.h> 342aedd662SScott Long #include <machine/clock.h> 352aedd662SScott Long 362aedd662SScott Long static d_open_t ips_open; 372aedd662SScott Long static d_close_t ips_close; 382aedd662SScott Long static d_ioctl_t ips_ioctl; 392aedd662SScott Long 402aedd662SScott Long static struct cdevsw ips_cdevsw = { 41dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 42dc08ffecSPoul-Henning Kamp .d_flags = D_NEEDGIANT, 432aedd662SScott Long .d_open = ips_open, 442aedd662SScott Long .d_close = ips_close, 452aedd662SScott Long .d_ioctl = ips_ioctl, 462aedd662SScott Long .d_name = "ips", 472aedd662SScott Long }; 482aedd662SScott Long 497633e7f1SMartin Blapp static const char* ips_adapter_name[] = { 507633e7f1SMartin Blapp "N/A", 517633e7f1SMartin Blapp "ServeRAID (copperhead)", 527633e7f1SMartin Blapp "ServeRAID II (copperhead refresh)", 537633e7f1SMartin Blapp "ServeRAID onboard (copperhead)", 547633e7f1SMartin Blapp "ServeRAID onboard (copperhead)", 557633e7f1SMartin Blapp "ServeRAID 3H (clarinet)", 567633e7f1SMartin Blapp "ServeRAID 3L (clarinet lite)", 577633e7f1SMartin Blapp "ServeRAID 4H (trombone)", 587633e7f1SMartin Blapp "ServeRAID 4M (morpheus)", 597633e7f1SMartin Blapp "ServeRAID 4L (morpheus lite)", 607633e7f1SMartin Blapp "ServeRAID 4Mx (neo)", 617633e7f1SMartin Blapp "ServeRAID 4Lx (neo lite)", 627633e7f1SMartin Blapp "ServeRAID 5i II (sarasota)", 637633e7f1SMartin Blapp "ServeRAID 5i (sarasota)", 647633e7f1SMartin Blapp "ServeRAID 6M (marco)", 657633e7f1SMartin Blapp "ServeRAID 6i (sebring)" 667633e7f1SMartin Blapp }; 677633e7f1SMartin Blapp 682aedd662SScott Long 692aedd662SScott Long static int ips_open(dev_t dev, int flags, int fmt, struct thread *td) 702aedd662SScott Long { 712aedd662SScott Long ips_softc_t *sc = dev->si_drv1; 722aedd662SScott Long sc->state |= IPS_DEV_OPEN; 732aedd662SScott Long return 0; 742aedd662SScott Long } 752aedd662SScott Long 762aedd662SScott Long static int ips_close(dev_t dev, int flags, int fmt, struct thread *td) 772aedd662SScott Long { 782aedd662SScott Long ips_softc_t *sc = dev->si_drv1; 792aedd662SScott Long sc->state &= ~IPS_DEV_OPEN; 802aedd662SScott Long 812aedd662SScott Long return 0; 822aedd662SScott Long } 832aedd662SScott Long 842aedd662SScott Long static int ips_ioctl(dev_t dev, u_long command, caddr_t addr, int32_t flags, struct thread *td) 852aedd662SScott Long { 862aedd662SScott Long ips_softc_t *sc; 872aedd662SScott Long 882aedd662SScott Long sc = dev->si_drv1; 892aedd662SScott Long return ips_ioctl_request(sc, command, addr, flags); 902aedd662SScott Long } 912aedd662SScott Long 922aedd662SScott Long static void ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error) 932aedd662SScott Long { 942aedd662SScott Long ips_command_t *command = cmdptr; 952aedd662SScott Long PRINTF(10, "ips: in ips_cmd_dmaload\n"); 962aedd662SScott Long if(!error) 972aedd662SScott Long command->command_phys_addr = segments[0].ds_addr; 982aedd662SScott Long 992aedd662SScott Long } 1002aedd662SScott Long 1012aedd662SScott Long /* is locking needed? what locking guarentees are there on removal? */ 1022aedd662SScott Long static __inline__ int ips_cmdqueue_free(ips_softc_t *sc) 1032aedd662SScott Long { 1042aedd662SScott Long int i, error = -1; 1052aedd662SScott Long intrmask_t mask = splbio(); 1062aedd662SScott Long if(!sc->used_commands){ 1072aedd662SScott Long for(i = 0; i < sc->max_cmds; i++){ 1082aedd662SScott Long if(!(sc->commandarray[i].command_phys_addr)) 1092aedd662SScott Long continue; 1102aedd662SScott Long bus_dmamap_unload(sc->command_dmatag, 1112aedd662SScott Long sc->commandarray[i].command_dmamap); 1122aedd662SScott Long bus_dmamem_free(sc->command_dmatag, 1132aedd662SScott Long sc->commandarray[i].command_buffer, 1142aedd662SScott Long sc->commandarray[i].command_dmamap); 1152aedd662SScott Long } 1162aedd662SScott Long error = 0; 1172aedd662SScott Long sc->state |= IPS_OFFLINE; 1182aedd662SScott Long } 1192aedd662SScott Long splx(mask); 1202aedd662SScott Long return error; 1212aedd662SScott Long } 1222aedd662SScott Long 1232aedd662SScott Long /* places all ips command structs on the free command queue. No locking as if someone else tries 1242aedd662SScott Long * to access this during init, we have bigger problems */ 1252aedd662SScott Long static __inline__ int ips_cmdqueue_init(ips_softc_t *sc) 1262aedd662SScott Long { 1272aedd662SScott Long int i; 1282aedd662SScott Long ips_command_t *command; 1292aedd662SScott Long SLIST_INIT(&sc->free_cmd_list); 1302aedd662SScott Long STAILQ_INIT(&sc->cmd_wait_list); 1312aedd662SScott Long for(i = 0; i < sc->max_cmds; i++){ 1322aedd662SScott Long sc->commandarray[i].id = i; 1332aedd662SScott Long sc->commandarray[i].sc = sc; 1342aedd662SScott Long SLIST_INSERT_HEAD(&sc->free_cmd_list, &sc->commandarray[i], 1352aedd662SScott Long next); 1362aedd662SScott Long } 1372aedd662SScott Long for(i = 0; i < sc->max_cmds; i++){ 1382aedd662SScott Long command = &sc->commandarray[i]; 1392aedd662SScott Long if(bus_dmamem_alloc(sc->command_dmatag,&command->command_buffer, 1402aedd662SScott Long BUS_DMA_NOWAIT, &command->command_dmamap)) 1412aedd662SScott Long goto error; 1422aedd662SScott Long bus_dmamap_load(sc->command_dmatag, command->command_dmamap, 1432aedd662SScott Long command->command_buffer,IPS_COMMAND_LEN, 1442aedd662SScott Long ips_cmd_dmaload, command, BUS_DMA_NOWAIT); 1452aedd662SScott Long if(!command->command_phys_addr){ 1462aedd662SScott Long bus_dmamem_free(sc->command_dmatag, 1472aedd662SScott Long command->command_buffer, command->command_dmamap); 1482aedd662SScott Long goto error; 1492aedd662SScott Long } 1502aedd662SScott Long } 1512aedd662SScott Long sc->state &= ~IPS_OFFLINE; 1522aedd662SScott Long return 0; 1532aedd662SScott Long error: 1542aedd662SScott Long ips_cmdqueue_free(sc); 1552aedd662SScott Long return ENOMEM; 1562aedd662SScott Long } 1572aedd662SScott Long 1582aedd662SScott Long static int ips_add_waiting_command(ips_softc_t *sc, int (*callback)(ips_command_t *), void *data, unsigned long flags) 1592aedd662SScott Long { 1602aedd662SScott Long intrmask_t mask; 1612aedd662SScott Long ips_command_t *command; 1622aedd662SScott Long ips_wait_list_t *waiter; 1632aedd662SScott Long unsigned long memflags = 0; 1642aedd662SScott Long if(IPS_NOWAIT_FLAG & flags) 1652aedd662SScott Long memflags = M_NOWAIT; 1662aedd662SScott Long waiter = malloc(sizeof(ips_wait_list_t), M_DEVBUF, memflags); 1672aedd662SScott Long if(!waiter) 1682aedd662SScott Long return ENOMEM; 1692aedd662SScott Long mask = splbio(); 1702aedd662SScott Long if(sc->state & IPS_OFFLINE){ 1712aedd662SScott Long splx(mask); 1727287c40cSRuslan Ermilov free(waiter, M_DEVBUF); 1732aedd662SScott Long return EIO; 1742aedd662SScott Long } 1752aedd662SScott Long command = SLIST_FIRST(&sc->free_cmd_list); 1762aedd662SScott Long if(command && !(sc->state & IPS_TIMEOUT)){ 1772aedd662SScott Long SLIST_REMOVE_HEAD(&sc->free_cmd_list, next); 1782aedd662SScott Long (sc->used_commands)++; 1792aedd662SScott Long splx(mask); 1802aedd662SScott Long clear_ips_command(command); 1812aedd662SScott Long bzero(command->command_buffer, IPS_COMMAND_LEN); 1822aedd662SScott Long free(waiter, M_DEVBUF); 1832aedd662SScott Long command->arg = data; 1842aedd662SScott Long return callback(command); 1852aedd662SScott Long } 1862aedd662SScott Long DEVICE_PRINTF(1, sc->dev, "adding command to the wait queue\n"); 1872aedd662SScott Long waiter->callback = callback; 1882aedd662SScott Long waiter->data = data; 1892aedd662SScott Long STAILQ_INSERT_TAIL(&sc->cmd_wait_list, waiter, next); 1902aedd662SScott Long splx(mask); 1912aedd662SScott Long return 0; 1922aedd662SScott Long } 1932aedd662SScott Long 1942aedd662SScott Long static void ips_run_waiting_command(ips_softc_t *sc) 1952aedd662SScott Long { 1962aedd662SScott Long ips_wait_list_t *waiter; 1972aedd662SScott Long ips_command_t *command; 1982aedd662SScott Long int (*callback)(ips_command_t*); 1992aedd662SScott Long intrmask_t mask; 2002aedd662SScott Long 2012aedd662SScott Long mask = splbio(); 2022aedd662SScott Long waiter = STAILQ_FIRST(&sc->cmd_wait_list); 2032aedd662SScott Long command = SLIST_FIRST(&sc->free_cmd_list); 2042aedd662SScott Long if(!waiter || !command){ 2052aedd662SScott Long splx(mask); 2062aedd662SScott Long return; 2072aedd662SScott Long } 2082aedd662SScott Long DEVICE_PRINTF(1, sc->dev, "removing command from wait queue\n"); 2092aedd662SScott Long SLIST_REMOVE_HEAD(&sc->free_cmd_list, next); 2102aedd662SScott Long STAILQ_REMOVE_HEAD(&sc->cmd_wait_list, next); 2112aedd662SScott Long (sc->used_commands)++; 2122aedd662SScott Long splx(mask); 2132aedd662SScott Long clear_ips_command(command); 2142aedd662SScott Long bzero(command->command_buffer, IPS_COMMAND_LEN); 2152aedd662SScott Long command->arg = waiter->data; 2162aedd662SScott Long callback = waiter->callback; 2172aedd662SScott Long free(waiter, M_DEVBUF); 2182aedd662SScott Long callback(command); 2192aedd662SScott Long return; 2202aedd662SScott Long } 2212aedd662SScott Long /* returns a free command struct if one is available. 2222aedd662SScott Long * It also blanks out anything that may be a wild pointer/value. 2232aedd662SScott Long * Also, command buffers are not freed. They are 2242aedd662SScott Long * small so they are saved and kept dmamapped and loaded. 2252aedd662SScott Long */ 2262aedd662SScott Long int ips_get_free_cmd(ips_softc_t *sc, int (*callback)(ips_command_t *), void *data, unsigned long flags) 2272aedd662SScott Long { 2282aedd662SScott Long intrmask_t mask; 2292aedd662SScott Long ips_command_t *command; 2302aedd662SScott Long mask = splbio(); 2312aedd662SScott Long 2322aedd662SScott Long if(sc->state & IPS_OFFLINE){ 2332aedd662SScott Long splx(mask); 2342aedd662SScott Long return EIO; 2352aedd662SScott Long } 2362aedd662SScott Long command = SLIST_FIRST(&sc->free_cmd_list); 2372aedd662SScott Long if(!command || (sc->state & IPS_TIMEOUT)){ 2382aedd662SScott Long splx(mask); 2392aedd662SScott Long if(flags & IPS_NOWAIT_FLAG) 2402aedd662SScott Long return EAGAIN; 2412aedd662SScott Long return ips_add_waiting_command(sc, callback, data, flags); 2422aedd662SScott Long } 2432aedd662SScott Long SLIST_REMOVE_HEAD(&sc->free_cmd_list, next); 2442aedd662SScott Long (sc->used_commands)++; 2452aedd662SScott Long splx(mask); 2462aedd662SScott Long clear_ips_command(command); 2472aedd662SScott Long bzero(command->command_buffer, IPS_COMMAND_LEN); 2482aedd662SScott Long command->arg = data; 2492aedd662SScott Long return callback(command); 2502aedd662SScott Long } 2512aedd662SScott Long 2522aedd662SScott Long /* adds a command back to the free command queue */ 2532aedd662SScott Long void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command) 2542aedd662SScott Long { 2552aedd662SScott Long intrmask_t mask; 2562aedd662SScott Long mask = splbio(); 2572aedd662SScott Long SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next); 2582aedd662SScott Long (sc->used_commands)--; 2592aedd662SScott Long splx(mask); 2602aedd662SScott Long if(!(sc->state & IPS_TIMEOUT)) 2612aedd662SScott Long ips_run_waiting_command(sc); 2622aedd662SScott Long } 2637633e7f1SMartin Blapp static const char* ips_diskdev_statename(u_int8_t state) 2647633e7f1SMartin Blapp { 2657633e7f1SMartin Blapp static char statebuf[20]; 2667633e7f1SMartin Blapp switch(state){ 2677633e7f1SMartin Blapp case IPS_LD_OFFLINE: 2687633e7f1SMartin Blapp return("OFFLINE"); 2697633e7f1SMartin Blapp break; 2707633e7f1SMartin Blapp case IPS_LD_OKAY: 2717633e7f1SMartin Blapp return("OK"); 2727633e7f1SMartin Blapp break; 2737633e7f1SMartin Blapp case IPS_LD_DEGRADED: 2747633e7f1SMartin Blapp return("DEGRADED"); 2757633e7f1SMartin Blapp break; 2767633e7f1SMartin Blapp case IPS_LD_FREE: 2777633e7f1SMartin Blapp return("FREE"); 2787633e7f1SMartin Blapp break; 2797633e7f1SMartin Blapp case IPS_LD_SYS: 2807633e7f1SMartin Blapp return("SYS"); 2817633e7f1SMartin Blapp break; 2827633e7f1SMartin Blapp case IPS_LD_CRS: 2837633e7f1SMartin Blapp return("CRS"); 2847633e7f1SMartin Blapp break; 2857633e7f1SMartin Blapp } 2867633e7f1SMartin Blapp sprintf(statebuf,"UNKNOWN(0x%02x)", state); 2877633e7f1SMartin Blapp return(statebuf); 2887633e7f1SMartin Blapp } 2892aedd662SScott Long 2902aedd662SScott Long static int ips_diskdev_init(ips_softc_t *sc) 2912aedd662SScott Long { 2922aedd662SScott Long int i; 2932aedd662SScott Long for(i=0; i < IPS_MAX_NUM_DRIVES; i++){ 2947633e7f1SMartin Blapp if(sc->drives[i].state == IPS_LD_FREE) continue; 2957633e7f1SMartin Blapp device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n", 2967633e7f1SMartin Blapp i, sc->drives[i].raid_lvl, 2977633e7f1SMartin Blapp sc->drives[i].sector_count, 2987633e7f1SMartin Blapp ips_diskdev_statename(sc->drives[i].state)); 2997633e7f1SMartin Blapp if(sc->drives[i].state == IPS_LD_OKAY || 3007633e7f1SMartin Blapp sc->drives[i].state == IPS_LD_DEGRADED){ 3012aedd662SScott Long sc->diskdev[i] = device_add_child(sc->dev, NULL, -1); 302f472527cSPeter Wemm device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i); 3032aedd662SScott Long } 3042aedd662SScott Long } 3052aedd662SScott Long if(bus_generic_attach(sc->dev)){ 3062aedd662SScott Long device_printf(sc->dev, "Attaching bus failed\n"); 3072aedd662SScott Long } 3082aedd662SScott Long return 0; 3092aedd662SScott Long } 3102aedd662SScott Long 3112aedd662SScott Long static int ips_diskdev_free(ips_softc_t *sc) 3122aedd662SScott Long { 3132aedd662SScott Long int i; 3142aedd662SScott Long int error = 0; 3152aedd662SScott Long for(i = 0; i < IPS_MAX_NUM_DRIVES; i++){ 3162aedd662SScott Long if(sc->diskdev[i]) 3172aedd662SScott Long error = device_delete_child(sc->dev, sc->diskdev[i]); 3182aedd662SScott Long if(error) 3192aedd662SScott Long return error; 3202aedd662SScott Long } 3212aedd662SScott Long bus_generic_detach(sc->dev); 3222aedd662SScott Long return 0; 3232aedd662SScott Long } 3242aedd662SScott Long 3252aedd662SScott Long /* ips_timeout is periodically called to make sure no commands sent 3262aedd662SScott Long * to the card have become stuck. If it finds a stuck command, it 3272aedd662SScott Long * sets a flag so the driver won't start any more commands and then 3282aedd662SScott Long * is periodically called to see if all outstanding commands have 3292aedd662SScott Long * either finished or timed out. Once timed out, an attempt to 3302aedd662SScott Long * reinitialize the card is made. If that fails, the driver gives 3312aedd662SScott Long * up and declares the card dead. */ 3322aedd662SScott Long static void ips_timeout(void *arg) 3332aedd662SScott Long { 3342aedd662SScott Long intrmask_t mask; 3352aedd662SScott Long ips_softc_t *sc = arg; 3362aedd662SScott Long int i, state = 0; 3372aedd662SScott Long ips_command_t *command; 3382aedd662SScott Long command = &sc->commandarray[0]; 3392aedd662SScott Long mask = splbio(); 3402aedd662SScott Long for(i = 0; i < sc->max_cmds; i++){ 3412aedd662SScott Long if(!command[i].timeout){ 3422aedd662SScott Long continue; 3432aedd662SScott Long } 3442aedd662SScott Long command[i].timeout--; 3452aedd662SScott Long if(!command[i].timeout){ 3462aedd662SScott Long if(!(sc->state & IPS_TIMEOUT)){ 3472aedd662SScott Long sc->state |= IPS_TIMEOUT; 3482aedd662SScott Long device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n"); 3492aedd662SScott Long } 3502aedd662SScott Long command[i].status.value = IPS_ERROR_STATUS; 3512aedd662SScott Long command[i].callback(&command[i]); 3522aedd662SScott Long /* hmm, this should be enough cleanup */ 3532aedd662SScott Long } else 3542aedd662SScott Long state = 1; 3552aedd662SScott Long } 3562aedd662SScott Long if(!state && (sc->state & IPS_TIMEOUT)){ 3572aedd662SScott Long if(sc->ips_adapter_reinit(sc, 1)){ 3582aedd662SScott Long device_printf(sc->dev, "AIEE! adapter reset failed, giving up and going home! Have a nice day.\n"); 3592aedd662SScott Long sc->state |= IPS_OFFLINE; 3602aedd662SScott Long sc->state &= ~IPS_TIMEOUT; 3612aedd662SScott Long /* Grr, I hate this solution. I run waiting commands 3622aedd662SScott Long one at a time and error them out just before they 3632aedd662SScott Long would go to the card. This sucks. */ 3642aedd662SScott Long } else 3652aedd662SScott Long sc->state &= ~IPS_TIMEOUT; 3662aedd662SScott Long ips_run_waiting_command(sc); 3672aedd662SScott Long } 3682aedd662SScott Long if (sc->state != IPS_OFFLINE) 3692aedd662SScott Long sc->timer = timeout(ips_timeout, sc, 10*hz); 3702aedd662SScott Long splx(mask); 3712aedd662SScott Long } 3722aedd662SScott Long 3732aedd662SScott Long /* check card and initialize it */ 3742aedd662SScott Long int ips_adapter_init(ips_softc_t *sc) 3752aedd662SScott Long { 376dea4622dSScott Long int i; 3772aedd662SScott Long DEVICE_PRINTF(1,sc->dev, "initializing\n"); 3782aedd662SScott Long if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, 3792aedd662SScott Long /* alignemnt */ 1, 3802aedd662SScott Long /* boundary */ 0, 3812aedd662SScott Long /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 3822aedd662SScott Long /* highaddr */ BUS_SPACE_MAXADDR, 3832aedd662SScott Long /* filter */ NULL, 3842aedd662SScott Long /* filterarg */ NULL, 3852aedd662SScott Long /* maxsize */ IPS_COMMAND_LEN + 3862aedd662SScott Long IPS_MAX_SG_LEN, 3872aedd662SScott Long /* numsegs */ 1, 3882aedd662SScott Long /* maxsegsize*/ IPS_COMMAND_LEN + 3892aedd662SScott Long IPS_MAX_SG_LEN, 3902aedd662SScott Long /* flags */ 0, 391f6b1c44dSScott Long /* lockfunc */ busdma_lock_mutex, 392f6b1c44dSScott Long /* lockarg */ &Giant, 3932aedd662SScott Long &sc->command_dmatag) != 0) { 3942aedd662SScott Long device_printf(sc->dev, "can't alloc command dma tag\n"); 3952aedd662SScott Long goto error; 3962aedd662SScott Long } 3972aedd662SScott Long if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, 3982aedd662SScott Long /* alignemnt */ 1, 3992aedd662SScott Long /* boundary */ 0, 4002aedd662SScott Long /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 4012aedd662SScott Long /* highaddr */ BUS_SPACE_MAXADDR, 4022aedd662SScott Long /* filter */ NULL, 4032aedd662SScott Long /* filterarg */ NULL, 4042aedd662SScott Long /* maxsize */ IPS_MAX_IOBUF_SIZE, 4052aedd662SScott Long /* numsegs */ IPS_MAX_SG_ELEMENTS, 4062aedd662SScott Long /* maxsegsize*/ IPS_MAX_IOBUF_SIZE, 4072aedd662SScott Long /* flags */ 0, 408f6b1c44dSScott Long /* lockfunc */ busdma_lock_mutex, 409f6b1c44dSScott Long /* lockarg */ &Giant, 4102aedd662SScott Long &sc->sg_dmatag) != 0) { 4112aedd662SScott Long device_printf(sc->dev, "can't alloc SG dma tag\n"); 4122aedd662SScott Long goto error; 4132aedd662SScott Long } 4142aedd662SScott Long /* create one command buffer until we know how many commands this card 4152aedd662SScott Long can handle */ 4162aedd662SScott Long sc->max_cmds = 1; 4172aedd662SScott Long ips_cmdqueue_init(sc); 418dea4622dSScott Long callout_handle_init(&sc->timer); 4192aedd662SScott Long 4202aedd662SScott Long if(sc->ips_adapter_reinit(sc, 0)) 4212aedd662SScott Long goto error; 4222aedd662SScott Long 4232aedd662SScott Long mtx_init(&sc->cmd_mtx, "ips command mutex", NULL, MTX_DEF); 4247633e7f1SMartin Blapp 4257633e7f1SMartin Blapp /* initialize ffdc values */ 4267633e7f1SMartin Blapp microtime(&sc->ffdc_resettime); 4277633e7f1SMartin Blapp sc->ffdc_resetcount = 1; 4287633e7f1SMartin Blapp if ((i = ips_ffdc_reset(sc)) != 0) { 4297633e7f1SMartin Blapp device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i); 4307633e7f1SMartin Blapp goto error; 4317633e7f1SMartin Blapp } 432dea4622dSScott Long if ((i = ips_get_adapter_info(sc)) != 0) { 433dea4622dSScott Long device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i); 434dea4622dSScott Long goto error; 435dea4622dSScott Long } 4367633e7f1SMartin Blapp ips_update_nvram(sc); /* no error check as failure doesn't matter */ 4377633e7f1SMartin Blapp if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){ 4387633e7f1SMartin Blapp device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]); 4397633e7f1SMartin Blapp } 440dea4622dSScott Long if ((i = ips_get_drive_info(sc)) != 0) { 441dea4622dSScott Long device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i); 4422aedd662SScott Long goto error; 4432aedd662SScott Long } 4442aedd662SScott Long 4452aedd662SScott Long ips_cmdqueue_free(sc); 4462aedd662SScott Long if(sc->adapter_info.max_concurrent_cmds) 4472aedd662SScott Long sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds); 4482aedd662SScott Long else 4492aedd662SScott Long sc->max_cmds = 32; 4502aedd662SScott Long if(ips_cmdqueue_init(sc)){ 4512aedd662SScott Long device_printf(sc->dev, "failed to initialize command buffers\n"); 4522aedd662SScott Long goto error; 4532aedd662SScott Long } 4542aedd662SScott Long sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev), UID_ROOT, GID_OPERATOR, 4552aedd662SScott Long S_IRUSR | S_IWUSR, "ips%d", device_get_unit(sc->dev)); 4562aedd662SScott Long sc->device_file->si_drv1 = sc; 4572aedd662SScott Long ips_diskdev_init(sc); 4582aedd662SScott Long sc->timer = timeout(ips_timeout, sc, 10*hz); 4592aedd662SScott Long return 0; 4602aedd662SScott Long 4612aedd662SScott Long error: 4622aedd662SScott Long ips_adapter_free(sc); 4632aedd662SScott Long return ENXIO; 4642aedd662SScott Long } 4652aedd662SScott Long 4662aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization */ 4672aedd662SScott Long int ips_morpheus_reinit(ips_softc_t *sc, int force) 4682aedd662SScott Long { 4692aedd662SScott Long u_int32_t tmp; 4702aedd662SScott Long int i; 4712aedd662SScott Long 4722aedd662SScott Long tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 4732aedd662SScott Long if(!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) && 4742aedd662SScott Long (ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp){ 4752aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_OIMR, 0); 4762aedd662SScott Long return 0; 4772aedd662SScott Long } 4782aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff); 4792aedd662SScott Long ips_read_4(sc, MORPHEUS_REG_OIMR); 4802aedd662SScott Long 4812aedd662SScott Long device_printf(sc->dev, "resetting adapter, this may take up to 5 minutes\n"); 4822aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000); 4832aedd662SScott Long DELAY(5000000); 4842aedd662SScott Long pci_read_config(sc->dev, 0, 4); 4852aedd662SScott Long 4862aedd662SScott Long tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 4872aedd662SScott Long for(i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++){ 4882aedd662SScott Long DELAY(1000000); 4892aedd662SScott Long DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i); 4902aedd662SScott Long tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 4912aedd662SScott Long } 4922aedd662SScott Long if(tmp & MORPHEUS_BIT_POST1) 4932aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1); 4942aedd662SScott Long 4952aedd662SScott Long if( i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK){ 4962aedd662SScott Long device_printf(sc->dev,"Adapter error during initialization.\n"); 4972aedd662SScott Long return 1; 4982aedd662SScott Long } 4992aedd662SScott Long for(i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++){ 5002aedd662SScott Long DELAY(1000000); 5012aedd662SScott Long DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i); 5022aedd662SScott Long tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 5032aedd662SScott Long } 5042aedd662SScott Long if(tmp & MORPHEUS_BIT_POST2) 5052aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2); 5062aedd662SScott Long 5072aedd662SScott Long if(i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)){ 5082aedd662SScott Long device_printf(sc->dev, "adapter failed config check\n"); 5092aedd662SScott Long return 1; 5102aedd662SScott Long } 5112aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_OIMR, 0); 5122aedd662SScott Long if(force && ips_clear_adapter(sc)){ 5132aedd662SScott Long device_printf(sc->dev, "adapter clear failed\n"); 5142aedd662SScott Long return 1; 5152aedd662SScott Long } 5162aedd662SScott Long return 0; 5172aedd662SScott Long } 5182aedd662SScott Long 5192aedd662SScott Long /* clean up so we can unload the driver. */ 5202aedd662SScott Long int ips_adapter_free(ips_softc_t *sc) 5212aedd662SScott Long { 5222aedd662SScott Long int error = 0; 5232aedd662SScott Long intrmask_t mask; 5242aedd662SScott Long if(sc->state & IPS_DEV_OPEN) 5252aedd662SScott Long return EBUSY; 5262aedd662SScott Long if((error = ips_diskdev_free(sc))) 5272aedd662SScott Long return error; 5282aedd662SScott Long if(ips_cmdqueue_free(sc)){ 5292aedd662SScott Long device_printf(sc->dev, 5302aedd662SScott Long "trying to exit when command queue is not empty!\n"); 5312aedd662SScott Long return EBUSY; 5322aedd662SScott Long } 5332aedd662SScott Long DEVICE_PRINTF(1, sc->dev, "free\n"); 5342aedd662SScott Long mask = splbio(); 5352aedd662SScott Long untimeout(ips_timeout, sc, sc->timer); 5362aedd662SScott Long splx(mask); 5372aedd662SScott Long if (mtx_initialized(&sc->cmd_mtx)) 5382aedd662SScott Long mtx_destroy(&sc->cmd_mtx); 5392aedd662SScott Long 5402aedd662SScott Long if(sc->sg_dmatag) 5412aedd662SScott Long bus_dma_tag_destroy(sc->sg_dmatag); 5422aedd662SScott Long if(sc->command_dmatag) 5432aedd662SScott Long bus_dma_tag_destroy(sc->command_dmatag); 5442aedd662SScott Long if(sc->device_file) 5452aedd662SScott Long destroy_dev(sc->device_file); 5462aedd662SScott Long return 0; 5472aedd662SScott Long } 5482aedd662SScott Long 5492aedd662SScott Long void ips_morpheus_intr(void *void_sc) 5502aedd662SScott Long { 5512aedd662SScott Long ips_softc_t *sc = (ips_softc_t *)void_sc; 5522aedd662SScott Long u_int32_t oisr, iisr; 5532aedd662SScott Long int cmdnumber; 5542aedd662SScott Long ips_cmd_status_t status; 5552aedd662SScott Long 5562aedd662SScott Long iisr =ips_read_4(sc, MORPHEUS_REG_IISR); 5572aedd662SScott Long oisr =ips_read_4(sc, MORPHEUS_REG_OISR); 5582aedd662SScott Long PRINTF(9,"interrupt registers in:%x out:%x\n",iisr, oisr); 5592aedd662SScott Long if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){ 5602aedd662SScott Long DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n"); 5612aedd662SScott Long return; 5622aedd662SScott Long } 5632aedd662SScott Long while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){ 5642aedd662SScott Long cmdnumber = status.fields.command_id; 5652aedd662SScott Long sc->commandarray[cmdnumber].status.value = status.value; 5662aedd662SScott Long sc->commandarray[cmdnumber].timeout = 0; 5672aedd662SScott Long sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber])); 5682aedd662SScott Long 5692aedd662SScott Long 5702aedd662SScott Long DEVICE_PRINTF(9,sc->dev, "got command %d\n", cmdnumber); 5712aedd662SScott Long } 5722aedd662SScott Long return; 5732aedd662SScott Long } 5742aedd662SScott Long 5752aedd662SScott Long void ips_issue_morpheus_cmd(ips_command_t *command) 5762aedd662SScott Long { 5772aedd662SScott Long intrmask_t mask = splbio(); 5782aedd662SScott Long /* hmmm, is there a cleaner way to do this? */ 5792aedd662SScott Long if(command->sc->state & IPS_OFFLINE){ 5802aedd662SScott Long splx(mask); 5812aedd662SScott Long command->status.value = IPS_ERROR_STATUS; 5822aedd662SScott Long command->callback(command); 5832aedd662SScott Long return; 5842aedd662SScott Long } 5852aedd662SScott Long command->timeout = 10; 5862aedd662SScott Long ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr); 5872aedd662SScott Long splx(mask); 5882aedd662SScott Long } 5892aedd662SScott Long 5902aedd662SScott Long static void ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,int segnum, int error) 5912aedd662SScott Long { 5922aedd662SScott Long ips_copper_queue_t *queue = queueptr; 5932aedd662SScott Long if(error){ 5942aedd662SScott Long return; 5952aedd662SScott Long } 5962aedd662SScott Long queue->base_phys_addr = segments[0].ds_addr; 5972aedd662SScott Long } 5982aedd662SScott Long 5992aedd662SScott Long static int ips_copperhead_queue_init(ips_softc_t *sc) 6002aedd662SScott Long { 6012aedd662SScott Long int error; 6022aedd662SScott Long bus_dma_tag_t dmatag; 6032aedd662SScott Long bus_dmamap_t dmamap; 6042aedd662SScott Long if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, 6052aedd662SScott Long /* alignemnt */ 1, 6062aedd662SScott Long /* boundary */ 0, 6072aedd662SScott Long /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 6082aedd662SScott Long /* highaddr */ BUS_SPACE_MAXADDR, 6092aedd662SScott Long /* filter */ NULL, 6102aedd662SScott Long /* filterarg */ NULL, 6112aedd662SScott Long /* maxsize */ sizeof(ips_copper_queue_t), 6122aedd662SScott Long /* numsegs */ 1, 6132aedd662SScott Long /* maxsegsize*/ sizeof(ips_copper_queue_t), 6142aedd662SScott Long /* flags */ 0, 615f6b1c44dSScott Long /* lockfunc */ busdma_lock_mutex, 616f6b1c44dSScott Long /* lockarg */ &Giant, 6172aedd662SScott Long &dmatag) != 0) { 6182aedd662SScott Long device_printf(sc->dev, "can't alloc dma tag for statue queue\n"); 6192aedd662SScott Long error = ENOMEM; 6202aedd662SScott Long goto exit; 6212aedd662SScott Long } 6222aedd662SScott Long if(bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue), 6232aedd662SScott Long BUS_DMA_NOWAIT, &dmamap)){ 6242aedd662SScott Long error = ENOMEM; 6252aedd662SScott Long goto exit; 6262aedd662SScott Long } 6272aedd662SScott Long bzero(sc->copper_queue, sizeof(ips_copper_queue_t)); 6282aedd662SScott Long sc->copper_queue->dmatag = dmatag; 6292aedd662SScott Long sc->copper_queue->dmamap = dmamap; 6302aedd662SScott Long sc->copper_queue->nextstatus = 1; 6312aedd662SScott Long bus_dmamap_load(dmatag, dmamap, 6322aedd662SScott Long &(sc->copper_queue->status[0]), IPS_MAX_CMD_NUM * 4, 6332aedd662SScott Long ips_copperhead_queue_callback, sc->copper_queue, 6342aedd662SScott Long BUS_DMA_NOWAIT); 6352aedd662SScott Long if(sc->copper_queue->base_phys_addr == 0){ 6362aedd662SScott Long error = ENOMEM; 6372aedd662SScott Long goto exit; 6382aedd662SScott Long } 6392aedd662SScott Long ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr); 6402aedd662SScott Long ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr + 6412aedd662SScott Long IPS_MAX_CMD_NUM * 4); 6422aedd662SScott Long ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4); 6432aedd662SScott Long ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr); 6442aedd662SScott Long 6452aedd662SScott Long 6462aedd662SScott Long return 0; 6472aedd662SScott Long exit: 6482aedd662SScott Long bus_dmamem_free(dmatag, sc->copper_queue, dmamap); 6492aedd662SScott Long bus_dma_tag_destroy(dmatag); 6502aedd662SScott Long return error; 6512aedd662SScott Long } 6522aedd662SScott Long 6532aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization FIXME */ 6542aedd662SScott Long int ips_copperhead_reinit(ips_softc_t *sc, int force) 6552aedd662SScott Long { 6562aedd662SScott Long int i, j; 6572aedd662SScott Long u_int32_t postcode = 0, configstatus = 0; 6582aedd662SScott Long ips_write_1(sc, COPPER_REG_SCPR, 0x80); 6592aedd662SScott Long ips_write_1(sc, COPPER_REG_SCPR, 0); 6602aedd662SScott Long device_printf(sc->dev, "reinitializing adapter, this could take several minutes.\n"); 6612aedd662SScott Long for(j = 0; j < 2; j++){ 6622aedd662SScott Long postcode <<= 8; 6632aedd662SScott Long for(i = 0; i < 45; i++){ 6642aedd662SScott Long if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){ 6652aedd662SScott Long postcode |= ips_read_1(sc, COPPER_REG_ISPR); 6662aedd662SScott Long ips_write_1(sc, COPPER_REG_HISR, 6672aedd662SScott Long COPPER_GHI_BIT); 6682aedd662SScott Long break; 6692aedd662SScott Long } else 6702aedd662SScott Long DELAY(1000000); 6712aedd662SScott Long } 6722aedd662SScott Long if(i == 45) 6732aedd662SScott Long return 1; 6742aedd662SScott Long } 6752aedd662SScott Long for(j = 0; j < 2; j++){ 6762aedd662SScott Long configstatus <<= 8; 6772aedd662SScott Long for(i = 0; i < 240; i++){ 6782aedd662SScott Long if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){ 6792aedd662SScott Long configstatus |= ips_read_1(sc, COPPER_REG_ISPR); 6802aedd662SScott Long ips_write_1(sc, COPPER_REG_HISR, 6812aedd662SScott Long COPPER_GHI_BIT); 6822aedd662SScott Long break; 6832aedd662SScott Long } else 6842aedd662SScott Long DELAY(1000000); 6852aedd662SScott Long } 6862aedd662SScott Long if(i == 240) 6872aedd662SScott Long return 1; 6882aedd662SScott Long } 6892aedd662SScott Long for(i = 0; i < 240; i++){ 6902aedd662SScott Long if(!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT)){ 6912aedd662SScott Long break; 6922aedd662SScott Long } else 6932aedd662SScott Long DELAY(1000000); 6942aedd662SScott Long } 6952aedd662SScott Long if(i == 240) 6962aedd662SScott Long return 1; 6972aedd662SScott Long ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT); 6982aedd662SScott Long ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT); 6992aedd662SScott Long ips_copperhead_queue_init(sc); 7002aedd662SScott Long ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT); 7012aedd662SScott Long i = ips_read_1(sc, COPPER_REG_SCPR); 7022aedd662SScott Long ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT); 7032aedd662SScott Long if(!configstatus){ 7042aedd662SScott Long device_printf(sc->dev, "adapter initialization failed\n"); 7052aedd662SScott Long return 1; 7062aedd662SScott Long } 7072aedd662SScott Long if(force && ips_clear_adapter(sc)){ 7082aedd662SScott Long device_printf(sc->dev, "adapter clear failed\n"); 7092aedd662SScott Long return 1; 7102aedd662SScott Long } 7112aedd662SScott Long return 0; 7122aedd662SScott Long } 7132aedd662SScott Long static u_int32_t ips_copperhead_cmd_status(ips_softc_t *sc) 7142aedd662SScott Long { 7152aedd662SScott Long intrmask_t mask; 7162aedd662SScott Long u_int32_t value; 7172aedd662SScott Long int statnum = sc->copper_queue->nextstatus++; 7182aedd662SScott Long if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM) 7192aedd662SScott Long sc->copper_queue->nextstatus = 0; 7202aedd662SScott Long mask = splbio(); 7212aedd662SScott Long value = sc->copper_queue->status[statnum]; 7222aedd662SScott Long ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr + 7232aedd662SScott Long 4 * statnum); 7242aedd662SScott Long splx(mask); 7252aedd662SScott Long return value; 7262aedd662SScott Long } 7272aedd662SScott Long 7282aedd662SScott Long 7292aedd662SScott Long void ips_copperhead_intr(void *void_sc) 7302aedd662SScott Long { 7312aedd662SScott Long ips_softc_t *sc = (ips_softc_t *)void_sc; 7322aedd662SScott Long int cmdnumber; 7332aedd662SScott Long ips_cmd_status_t status; 7342aedd662SScott Long 7352aedd662SScott Long while(ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT){ 7362aedd662SScott Long status.value = ips_copperhead_cmd_status(sc); 7372aedd662SScott Long cmdnumber = status.fields.command_id; 7382aedd662SScott Long sc->commandarray[cmdnumber].status.value = status.value; 7392aedd662SScott Long sc->commandarray[cmdnumber].timeout = 0; 7402aedd662SScott Long sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber])); 7412aedd662SScott Long PRINTF(9, "ips: got command %d\n", cmdnumber); 7422aedd662SScott Long } 7432aedd662SScott Long return; 7442aedd662SScott Long } 7452aedd662SScott Long 7462aedd662SScott Long void ips_issue_copperhead_cmd(ips_command_t *command) 7472aedd662SScott Long { 7482aedd662SScott Long int i; 7492aedd662SScott Long intrmask_t mask = splbio(); 7502aedd662SScott Long /* hmmm, is there a cleaner way to do this? */ 7512aedd662SScott Long if(command->sc->state & IPS_OFFLINE){ 7522aedd662SScott Long splx(mask); 7532aedd662SScott Long command->status.value = IPS_ERROR_STATUS; 7542aedd662SScott Long command->callback(command); 7552aedd662SScott Long return; 7562aedd662SScott Long } 7572aedd662SScott Long command->timeout = 10; 7582aedd662SScott Long for(i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT; 7592aedd662SScott Long i++ ){ 7602aedd662SScott Long if( i == 20){ 7612aedd662SScott Long printf("sem bit still set, can't send a command\n"); 7622aedd662SScott Long splx(mask); 7632aedd662SScott Long return; 7642aedd662SScott Long } 7652aedd662SScott Long DELAY(500);/* need to do a delay here */ 7662aedd662SScott Long } 7672aedd662SScott Long ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr); 7682aedd662SScott Long ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START); 7692aedd662SScott Long splx(mask); 7702aedd662SScott Long } 7712aedd662SScott Long 772