12aedd662SScott Long /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 4aad970f1SDavid E. O'Brien * Written by: David Jeffery 52aedd662SScott Long * Copyright (c) 2002 Adaptec Inc. 62aedd662SScott Long * All rights reserved. 72aedd662SScott Long * 82aedd662SScott Long * Redistribution and use in source and binary forms, with or without 92aedd662SScott Long * modification, are permitted provided that the following conditions 102aedd662SScott Long * are met: 112aedd662SScott Long * 1. Redistributions of source code must retain the above copyright 122aedd662SScott Long * notice, this list of conditions and the following disclaimer. 132aedd662SScott Long * 2. Redistributions in binary form must reproduce the above copyright 142aedd662SScott Long * notice, this list of conditions and the following disclaimer in the 152aedd662SScott Long * documentation and/or other materials provided with the distribution. 162aedd662SScott Long * 172aedd662SScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 182aedd662SScott Long * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 192aedd662SScott Long * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 202aedd662SScott Long * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 212aedd662SScott Long * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 222aedd662SScott Long * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 232aedd662SScott Long * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 242aedd662SScott Long * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 252aedd662SScott Long * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 262aedd662SScott Long * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 272aedd662SScott Long * SUCH DAMAGE. 282aedd662SScott Long */ 292aedd662SScott Long 30aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 31aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 322aedd662SScott Long 33e2e050c8SConrad Meyer #include <sys/types.h> 34e2e050c8SConrad Meyer #include <sys/lock.h> 35e2e050c8SConrad Meyer #include <sys/mutex.h> 362aedd662SScott Long #include <sys/stat.h> 372aedd662SScott Long #include <sys/time.h> 382aedd662SScott Long 39e2e050c8SConrad Meyer #include <dev/ips/ipsreg.h> 40e2e050c8SConrad Meyer #include <dev/ips/ips.h> 41e2e050c8SConrad Meyer 422aedd662SScott Long static d_open_t ips_open; 432aedd662SScott Long static d_close_t ips_close; 442aedd662SScott Long static d_ioctl_t ips_ioctl; 452aedd662SScott Long 46dd83a01eSScott Long MALLOC_DEFINE(M_IPSBUF, "ipsbuf","IPS driver buffer"); 47dd83a01eSScott Long 482aedd662SScott Long static struct cdevsw ips_cdevsw = { 49dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 502aedd662SScott Long .d_open = ips_open, 512aedd662SScott Long .d_close = ips_close, 522aedd662SScott Long .d_ioctl = ips_ioctl, 532aedd662SScott Long .d_name = "ips", 542aedd662SScott Long }; 552aedd662SScott Long 567633e7f1SMartin Blapp static const char* ips_adapter_name[] = { 577633e7f1SMartin Blapp "N/A", 587633e7f1SMartin Blapp "ServeRAID (copperhead)", 597633e7f1SMartin Blapp "ServeRAID II (copperhead refresh)", 607633e7f1SMartin Blapp "ServeRAID onboard (copperhead)", 617633e7f1SMartin Blapp "ServeRAID onboard (copperhead)", 627633e7f1SMartin Blapp "ServeRAID 3H (clarinet)", 637633e7f1SMartin Blapp "ServeRAID 3L (clarinet lite)", 647633e7f1SMartin Blapp "ServeRAID 4H (trombone)", 657633e7f1SMartin Blapp "ServeRAID 4M (morpheus)", 667633e7f1SMartin Blapp "ServeRAID 4L (morpheus lite)", 677633e7f1SMartin Blapp "ServeRAID 4Mx (neo)", 687633e7f1SMartin Blapp "ServeRAID 4Lx (neo lite)", 697633e7f1SMartin Blapp "ServeRAID 5i II (sarasota)", 707633e7f1SMartin Blapp "ServeRAID 5i (sarasota)", 717633e7f1SMartin Blapp "ServeRAID 6M (marco)", 72bd4fb874SMaxim Konovalov "ServeRAID 6i (sebring)", 73bd4fb874SMaxim Konovalov "ServeRAID 7t", 74bd4fb874SMaxim Konovalov "ServeRAID 7k", 75bd4fb874SMaxim Konovalov "ServeRAID 7M" 767633e7f1SMartin Blapp }; 777633e7f1SMartin Blapp 782aedd662SScott Long 7989c9c53dSPoul-Henning Kamp static int ips_open(struct cdev *dev, int flags, int fmt, struct thread *td) 802aedd662SScott Long { 812aedd662SScott Long ips_softc_t *sc = dev->si_drv1; 82352176c8SJohn Baldwin mtx_lock(&sc->queue_mtx); 832aedd662SScott Long sc->state |= IPS_DEV_OPEN; 84352176c8SJohn Baldwin mtx_unlock(&sc->queue_mtx); 852aedd662SScott Long return 0; 862aedd662SScott Long } 872aedd662SScott Long 8889c9c53dSPoul-Henning Kamp static int ips_close(struct cdev *dev, int flags, int fmt, struct thread *td) 892aedd662SScott Long { 902aedd662SScott Long ips_softc_t *sc = dev->si_drv1; 91352176c8SJohn Baldwin 92352176c8SJohn Baldwin mtx_lock(&sc->queue_mtx); 932aedd662SScott Long sc->state &= ~IPS_DEV_OPEN; 94352176c8SJohn Baldwin mtx_unlock(&sc->queue_mtx); 952aedd662SScott Long 962aedd662SScott Long return 0; 972aedd662SScott Long } 982aedd662SScott Long 9989c9c53dSPoul-Henning Kamp static int ips_ioctl(struct cdev *dev, u_long command, caddr_t addr, int32_t flags, struct thread *td) 1002aedd662SScott Long { 1012aedd662SScott Long ips_softc_t *sc; 1022aedd662SScott Long 1032aedd662SScott Long sc = dev->si_drv1; 1042aedd662SScott Long return ips_ioctl_request(sc, command, addr, flags); 1052aedd662SScott Long } 1062aedd662SScott Long 1072aedd662SScott Long static void ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error) 1082aedd662SScott Long { 1092aedd662SScott Long ips_command_t *command = cmdptr; 1102aedd662SScott Long PRINTF(10, "ips: in ips_cmd_dmaload\n"); 1112aedd662SScott Long if(!error) 1122aedd662SScott Long command->command_phys_addr = segments[0].ds_addr; 1132aedd662SScott Long 1142aedd662SScott Long } 1152aedd662SScott Long 116453130d9SPedro F. Giffuni /* is locking needed? what locking guarantees are there on removal? */ 11703a908f2SScott Long static int ips_cmdqueue_free(ips_softc_t *sc) 1182aedd662SScott Long { 1192aedd662SScott Long int i, error = -1; 120b234a120SScott Long ips_command_t *command; 121b234a120SScott Long 1222aedd662SScott Long if(!sc->used_commands){ 1232aedd662SScott Long for(i = 0; i < sc->max_cmds; i++){ 124b234a120SScott Long 125b234a120SScott Long command = &sc->commandarray[i]; 126b234a120SScott Long 127b234a120SScott Long if(command->command_phys_addr == 0) 1282aedd662SScott Long continue; 1292aedd662SScott Long bus_dmamap_unload(sc->command_dmatag, 130b234a120SScott Long command->command_dmamap); 1312aedd662SScott Long bus_dmamem_free(sc->command_dmatag, 132b234a120SScott Long command->command_buffer, 133b234a120SScott Long command->command_dmamap); 13403a908f2SScott Long if (command->data_dmamap != NULL) 13503a908f2SScott Long bus_dmamap_destroy(command->data_dmatag, 13603a908f2SScott Long command->data_dmamap); 1372aedd662SScott Long } 1382aedd662SScott Long error = 0; 1392aedd662SScott Long sc->state |= IPS_OFFLINE; 1402aedd662SScott Long } 14103a908f2SScott Long sc->staticcmd = NULL; 14203a908f2SScott Long free(sc->commandarray, M_DEVBUF); 1432aedd662SScott Long return error; 1442aedd662SScott Long } 1452aedd662SScott Long 1462aedd662SScott Long /* places all ips command structs on the free command queue. No locking as if someone else tries 1472aedd662SScott Long * to access this during init, we have bigger problems */ 14803a908f2SScott Long static int ips_cmdqueue_init(ips_softc_t *sc) 1492aedd662SScott Long { 1502aedd662SScott Long int i; 1512aedd662SScott Long ips_command_t *command; 15203a908f2SScott Long 15303a908f2SScott Long sc->commandarray = (ips_command_t *)malloc(sizeof(ips_command_t) * 15403a908f2SScott Long sc->max_cmds, M_DEVBUF, M_NOWAIT|M_ZERO); 15503a908f2SScott Long if (sc->commandarray == NULL) 15603a908f2SScott Long return (ENOMEM); 15703a908f2SScott Long 1582aedd662SScott Long SLIST_INIT(&sc->free_cmd_list); 1592aedd662SScott Long for(i = 0; i < sc->max_cmds; i++){ 1602aedd662SScott Long command = &sc->commandarray[i]; 161b234a120SScott Long command->id = i; 162b234a120SScott Long command->sc = sc; 163b234a120SScott Long 1642aedd662SScott Long if(bus_dmamem_alloc(sc->command_dmatag,&command->command_buffer, 1652aedd662SScott Long BUS_DMA_NOWAIT, &command->command_dmamap)) 1662aedd662SScott Long goto error; 1672aedd662SScott Long bus_dmamap_load(sc->command_dmatag, command->command_dmamap, 1682aedd662SScott Long command->command_buffer,IPS_COMMAND_LEN, 1692aedd662SScott Long ips_cmd_dmaload, command, BUS_DMA_NOWAIT); 1702aedd662SScott Long if(!command->command_phys_addr){ 1712aedd662SScott Long bus_dmamem_free(sc->command_dmatag, 1722aedd662SScott Long command->command_buffer, command->command_dmamap); 1732aedd662SScott Long goto error; 1742aedd662SScott Long } 175b234a120SScott Long 17603a908f2SScott Long if (i != 0) { 17703a908f2SScott Long command->data_dmatag = sc->sg_dmatag; 17803a908f2SScott Long if (bus_dmamap_create(command->data_dmatag, 0, 17903a908f2SScott Long &command->data_dmamap)) 18003a908f2SScott Long goto error; 181b234a120SScott Long SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next); 18203a908f2SScott Long } else 18303a908f2SScott Long sc->staticcmd = command; 1842aedd662SScott Long } 1852aedd662SScott Long sc->state &= ~IPS_OFFLINE; 1862aedd662SScott Long return 0; 1872aedd662SScott Long error: 1882aedd662SScott Long ips_cmdqueue_free(sc); 1892aedd662SScott Long return ENOMEM; 1902aedd662SScott Long } 1912aedd662SScott Long 1922aedd662SScott Long /* returns a free command struct if one is available. 1932aedd662SScott Long * It also blanks out anything that may be a wild pointer/value. 1942aedd662SScott Long * Also, command buffers are not freed. They are 1952aedd662SScott Long * small so they are saved and kept dmamapped and loaded. 1962aedd662SScott Long */ 19703a908f2SScott Long int ips_get_free_cmd(ips_softc_t *sc, ips_command_t **cmd, unsigned long flags) 1982aedd662SScott Long { 1992aedd662SScott Long ips_command_t *command; 2002aedd662SScott Long 2012aedd662SScott Long if(sc->state & IPS_OFFLINE){ 2022aedd662SScott Long return EIO; 2032aedd662SScott Long } 20403a908f2SScott Long if ((flags & IPS_STATIC_FLAG) == 0) { 2052aedd662SScott Long command = SLIST_FIRST(&sc->free_cmd_list); 2062aedd662SScott Long if(!command || (sc->state & IPS_TIMEOUT)){ 20703a908f2SScott Long return EBUSY; 2082aedd662SScott Long } 2092aedd662SScott Long SLIST_REMOVE_HEAD(&sc->free_cmd_list, next); 2102aedd662SScott Long (sc->used_commands)++; 21103a908f2SScott Long } else { 21203a908f2SScott Long if (sc->state & IPS_STATIC_BUSY) 21303a908f2SScott Long return EAGAIN; 21403a908f2SScott Long command = sc->staticcmd; 21503a908f2SScott Long sc->state |= IPS_STATIC_BUSY; 21603a908f2SScott Long } 2172aedd662SScott Long clear_ips_command(command); 2182aedd662SScott Long bzero(command->command_buffer, IPS_COMMAND_LEN); 21903a908f2SScott Long *cmd = command; 22003a908f2SScott Long return 0; 2212aedd662SScott Long } 2222aedd662SScott Long 2232aedd662SScott Long /* adds a command back to the free command queue */ 2242aedd662SScott Long void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command) 2252aedd662SScott Long { 226b234a120SScott Long 22703a908f2SScott Long if (sema_value(&sc->cmd_sema) != 0) 228b234a120SScott Long panic("ips: command returned non-zero semaphore"); 229b234a120SScott Long 23003a908f2SScott Long if (command != sc->staticcmd) { 2312aedd662SScott Long SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next); 2322aedd662SScott Long (sc->used_commands)--; 23303a908f2SScott Long } else { 23403a908f2SScott Long sc->state &= ~IPS_STATIC_BUSY; 23503a908f2SScott Long } 2362aedd662SScott Long } 2377633e7f1SMartin Blapp static const char* ips_diskdev_statename(u_int8_t state) 2387633e7f1SMartin Blapp { 2397633e7f1SMartin Blapp static char statebuf[20]; 2407633e7f1SMartin Blapp switch(state){ 2417633e7f1SMartin Blapp case IPS_LD_OFFLINE: 2427633e7f1SMartin Blapp return("OFFLINE"); 2437633e7f1SMartin Blapp break; 2447633e7f1SMartin Blapp case IPS_LD_OKAY: 2457633e7f1SMartin Blapp return("OK"); 2467633e7f1SMartin Blapp break; 2477633e7f1SMartin Blapp case IPS_LD_DEGRADED: 2487633e7f1SMartin Blapp return("DEGRADED"); 2497633e7f1SMartin Blapp break; 2507633e7f1SMartin Blapp case IPS_LD_FREE: 2517633e7f1SMartin Blapp return("FREE"); 2527633e7f1SMartin Blapp break; 2537633e7f1SMartin Blapp case IPS_LD_SYS: 2547633e7f1SMartin Blapp return("SYS"); 2557633e7f1SMartin Blapp break; 2567633e7f1SMartin Blapp case IPS_LD_CRS: 2577633e7f1SMartin Blapp return("CRS"); 2587633e7f1SMartin Blapp break; 2597633e7f1SMartin Blapp } 2607633e7f1SMartin Blapp sprintf(statebuf,"UNKNOWN(0x%02x)", state); 2617633e7f1SMartin Blapp return(statebuf); 2627633e7f1SMartin Blapp } 2632aedd662SScott Long 2642aedd662SScott Long static int ips_diskdev_init(ips_softc_t *sc) 2652aedd662SScott Long { 2662aedd662SScott Long int i; 2672aedd662SScott Long for(i=0; i < IPS_MAX_NUM_DRIVES; i++){ 2687633e7f1SMartin Blapp if(sc->drives[i].state == IPS_LD_FREE) continue; 2697633e7f1SMartin Blapp device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n", 2707633e7f1SMartin Blapp i, sc->drives[i].raid_lvl, 2717633e7f1SMartin Blapp sc->drives[i].sector_count, 2727633e7f1SMartin Blapp ips_diskdev_statename(sc->drives[i].state)); 2737633e7f1SMartin Blapp if(sc->drives[i].state == IPS_LD_OKAY || 2747633e7f1SMartin Blapp sc->drives[i].state == IPS_LD_DEGRADED){ 2752aedd662SScott Long sc->diskdev[i] = device_add_child(sc->dev, NULL, -1); 276f472527cSPeter Wemm device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i); 2772aedd662SScott Long } 2782aedd662SScott Long } 2792aedd662SScott Long if(bus_generic_attach(sc->dev)){ 2802aedd662SScott Long device_printf(sc->dev, "Attaching bus failed\n"); 2812aedd662SScott Long } 2822aedd662SScott Long return 0; 2832aedd662SScott Long } 2842aedd662SScott Long 2852aedd662SScott Long static int ips_diskdev_free(ips_softc_t *sc) 2862aedd662SScott Long { 2872aedd662SScott Long int i; 2882aedd662SScott Long int error = 0; 2892aedd662SScott Long for(i = 0; i < IPS_MAX_NUM_DRIVES; i++){ 2906c9e56b2SLi-Wen Hsu if(sc->diskdev[i]) { 2912aedd662SScott Long error = device_delete_child(sc->dev, sc->diskdev[i]); 2922aedd662SScott Long if(error) 2932aedd662SScott Long return error; 2942aedd662SScott Long } 2956c9e56b2SLi-Wen Hsu } 2962aedd662SScott Long bus_generic_detach(sc->dev); 2972aedd662SScott Long return 0; 2982aedd662SScott Long } 2992aedd662SScott Long 3002aedd662SScott Long /* ips_timeout is periodically called to make sure no commands sent 3012aedd662SScott Long * to the card have become stuck. If it finds a stuck command, it 3022aedd662SScott Long * sets a flag so the driver won't start any more commands and then 3032aedd662SScott Long * is periodically called to see if all outstanding commands have 3042aedd662SScott Long * either finished or timed out. Once timed out, an attempt to 3052aedd662SScott Long * reinitialize the card is made. If that fails, the driver gives 3062aedd662SScott Long * up and declares the card dead. */ 3072aedd662SScott Long static void ips_timeout(void *arg) 3082aedd662SScott Long { 3092aedd662SScott Long ips_softc_t *sc = arg; 3102aedd662SScott Long int i, state = 0; 3112aedd662SScott Long ips_command_t *command; 31203a908f2SScott Long 313352176c8SJohn Baldwin mtx_assert(&sc->queue_mtx, MA_OWNED); 3142aedd662SScott Long command = &sc->commandarray[0]; 3152aedd662SScott Long for(i = 0; i < sc->max_cmds; i++){ 3162aedd662SScott Long if(!command[i].timeout){ 3172aedd662SScott Long continue; 3182aedd662SScott Long } 3192aedd662SScott Long command[i].timeout--; 3202aedd662SScott Long if(!command[i].timeout){ 3212aedd662SScott Long if(!(sc->state & IPS_TIMEOUT)){ 3222aedd662SScott Long sc->state |= IPS_TIMEOUT; 3232aedd662SScott Long device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n"); 3242aedd662SScott Long } 3252eea7051SScott Long ips_set_error(&command[i], ETIMEDOUT); 3262aedd662SScott Long command[i].callback(&command[i]); 3272aedd662SScott Long /* hmm, this should be enough cleanup */ 3282aedd662SScott Long } else 3292aedd662SScott Long state = 1; 3302aedd662SScott Long } 3312aedd662SScott Long if(!state && (sc->state & IPS_TIMEOUT)){ 3322aedd662SScott Long if(sc->ips_adapter_reinit(sc, 1)){ 3332aedd662SScott Long device_printf(sc->dev, "AIEE! adapter reset failed, giving up and going home! Have a nice day.\n"); 3342aedd662SScott Long sc->state |= IPS_OFFLINE; 3352aedd662SScott Long sc->state &= ~IPS_TIMEOUT; 3362aedd662SScott Long /* Grr, I hate this solution. I run waiting commands 3372aedd662SScott Long one at a time and error them out just before they 3382aedd662SScott Long would go to the card. This sucks. */ 3392aedd662SScott Long } else 3402aedd662SScott Long sc->state &= ~IPS_TIMEOUT; 3412aedd662SScott Long } 3422aedd662SScott Long if (sc->state != IPS_OFFLINE) 343352176c8SJohn Baldwin callout_reset(&sc->timer, 10 * hz, ips_timeout, sc); 3442aedd662SScott Long } 3452aedd662SScott Long 3462aedd662SScott Long /* check card and initialize it */ 3472aedd662SScott Long int ips_adapter_init(ips_softc_t *sc) 3482aedd662SScott Long { 349dea4622dSScott Long int i; 3502aedd662SScott Long DEVICE_PRINTF(1,sc->dev, "initializing\n"); 351b234a120SScott Long 3522aedd662SScott Long if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, 3532aedd662SScott Long /* alignemnt */ 1, 3542aedd662SScott Long /* boundary */ 0, 3552aedd662SScott Long /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 3562aedd662SScott Long /* highaddr */ BUS_SPACE_MAXADDR, 3572aedd662SScott Long /* filter */ NULL, 3582aedd662SScott Long /* filterarg */ NULL, 3592aedd662SScott Long /* maxsize */ IPS_COMMAND_LEN + 3602aedd662SScott Long IPS_MAX_SG_LEN, 3612aedd662SScott Long /* numsegs */ 1, 3622aedd662SScott Long /* maxsegsize*/ IPS_COMMAND_LEN + 3632aedd662SScott Long IPS_MAX_SG_LEN, 3642aedd662SScott Long /* flags */ 0, 36503a908f2SScott Long /* lockfunc */ NULL, 36603a908f2SScott Long /* lockarg */ NULL, 3672aedd662SScott Long &sc->command_dmatag) != 0) { 3682aedd662SScott Long device_printf(sc->dev, "can't alloc command dma tag\n"); 3692aedd662SScott Long goto error; 3702aedd662SScott Long } 3712aedd662SScott Long if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, 3722aedd662SScott Long /* alignemnt */ 1, 3732aedd662SScott Long /* boundary */ 0, 3742aedd662SScott Long /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 3752aedd662SScott Long /* highaddr */ BUS_SPACE_MAXADDR, 3762aedd662SScott Long /* filter */ NULL, 3772aedd662SScott Long /* filterarg */ NULL, 3782aedd662SScott Long /* maxsize */ IPS_MAX_IOBUF_SIZE, 3792aedd662SScott Long /* numsegs */ IPS_MAX_SG_ELEMENTS, 3802aedd662SScott Long /* maxsegsize*/ IPS_MAX_IOBUF_SIZE, 3812aedd662SScott Long /* flags */ 0, 382f6b1c44dSScott Long /* lockfunc */ busdma_lock_mutex, 38303a908f2SScott Long /* lockarg */ &sc->queue_mtx, 3842aedd662SScott Long &sc->sg_dmatag) != 0) { 3852aedd662SScott Long device_printf(sc->dev, "can't alloc SG dma tag\n"); 3862aedd662SScott Long goto error; 3872aedd662SScott Long } 3882aedd662SScott Long /* create one command buffer until we know how many commands this card 3892aedd662SScott Long can handle */ 3902aedd662SScott Long sc->max_cmds = 1; 3912aedd662SScott Long ips_cmdqueue_init(sc); 3922aedd662SScott Long 3932aedd662SScott Long if(sc->ips_adapter_reinit(sc, 0)) 3942aedd662SScott Long goto error; 3952aedd662SScott Long 3967633e7f1SMartin Blapp /* initialize ffdc values */ 3977633e7f1SMartin Blapp microtime(&sc->ffdc_resettime); 3987633e7f1SMartin Blapp sc->ffdc_resetcount = 1; 3997633e7f1SMartin Blapp if ((i = ips_ffdc_reset(sc)) != 0) { 4007633e7f1SMartin Blapp device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i); 4017633e7f1SMartin Blapp goto error; 4027633e7f1SMartin Blapp } 403dea4622dSScott Long if ((i = ips_get_adapter_info(sc)) != 0) { 404dea4622dSScott Long device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i); 405dea4622dSScott Long goto error; 406dea4622dSScott Long } 4077633e7f1SMartin Blapp ips_update_nvram(sc); /* no error check as failure doesn't matter */ 4087633e7f1SMartin Blapp if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){ 4097633e7f1SMartin Blapp device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]); 4107633e7f1SMartin Blapp } 411dea4622dSScott Long if ((i = ips_get_drive_info(sc)) != 0) { 412dea4622dSScott Long device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i); 4132aedd662SScott Long goto error; 4142aedd662SScott Long } 4152aedd662SScott Long 4162aedd662SScott Long ips_cmdqueue_free(sc); 4172aedd662SScott Long if(sc->adapter_info.max_concurrent_cmds) 4182aedd662SScott Long sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds); 4192aedd662SScott Long else 4202aedd662SScott Long sc->max_cmds = 32; 4212aedd662SScott Long if(ips_cmdqueue_init(sc)){ 4222aedd662SScott Long device_printf(sc->dev, "failed to initialize command buffers\n"); 4232aedd662SScott Long goto error; 4242aedd662SScott Long } 4252aedd662SScott Long sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev), UID_ROOT, GID_OPERATOR, 4262aedd662SScott Long S_IRUSR | S_IWUSR, "ips%d", device_get_unit(sc->dev)); 4272aedd662SScott Long sc->device_file->si_drv1 = sc; 4282aedd662SScott Long ips_diskdev_init(sc); 429352176c8SJohn Baldwin callout_reset(&sc->timer, 10 * hz, ips_timeout, sc); 4302aedd662SScott Long return 0; 4312aedd662SScott Long 4322aedd662SScott Long error: 4332aedd662SScott Long ips_adapter_free(sc); 4342aedd662SScott Long return ENXIO; 4352aedd662SScott Long } 4362aedd662SScott Long 4372aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization */ 4382aedd662SScott Long int ips_morpheus_reinit(ips_softc_t *sc, int force) 4392aedd662SScott Long { 4402aedd662SScott Long u_int32_t tmp; 4412aedd662SScott Long int i; 4422aedd662SScott Long 4432aedd662SScott Long tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 4442aedd662SScott Long if(!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) && 4452aedd662SScott Long (ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp){ 4462aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_OIMR, 0); 4472aedd662SScott Long return 0; 4482aedd662SScott Long } 4492aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff); 4502aedd662SScott Long ips_read_4(sc, MORPHEUS_REG_OIMR); 4512aedd662SScott Long 4522aedd662SScott Long device_printf(sc->dev, "resetting adapter, this may take up to 5 minutes\n"); 4532aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000); 4542aedd662SScott Long DELAY(5000000); 455f94dfeb4SScott Long ips_read_4(sc, MORPHEUS_REG_OIMR); 4562aedd662SScott Long 4572aedd662SScott Long tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 4582aedd662SScott Long for(i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++){ 4592aedd662SScott Long DELAY(1000000); 4602aedd662SScott Long DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i); 4612aedd662SScott Long tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 4622aedd662SScott Long } 4632aedd662SScott Long if(tmp & MORPHEUS_BIT_POST1) 4642aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1); 4652aedd662SScott Long 4662aedd662SScott Long if( i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK){ 4672aedd662SScott Long device_printf(sc->dev,"Adapter error during initialization.\n"); 4682aedd662SScott Long return 1; 4692aedd662SScott Long } 4702aedd662SScott Long for(i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++){ 4712aedd662SScott Long DELAY(1000000); 4722aedd662SScott Long DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i); 4732aedd662SScott Long tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 4742aedd662SScott Long } 4752aedd662SScott Long if(tmp & MORPHEUS_BIT_POST2) 4762aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2); 4772aedd662SScott Long 4782aedd662SScott Long if(i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)){ 4792aedd662SScott Long device_printf(sc->dev, "adapter failed config check\n"); 4802aedd662SScott Long return 1; 4812aedd662SScott Long } 4822aedd662SScott Long ips_write_4(sc, MORPHEUS_REG_OIMR, 0); 4832aedd662SScott Long if(force && ips_clear_adapter(sc)){ 4842aedd662SScott Long device_printf(sc->dev, "adapter clear failed\n"); 4852aedd662SScott Long return 1; 4862aedd662SScott Long } 4872aedd662SScott Long return 0; 4882aedd662SScott Long } 4892aedd662SScott Long 4902aedd662SScott Long /* clean up so we can unload the driver. */ 4912aedd662SScott Long int ips_adapter_free(ips_softc_t *sc) 4922aedd662SScott Long { 4932aedd662SScott Long int error = 0; 4942aedd662SScott Long if(sc->state & IPS_DEV_OPEN) 4952aedd662SScott Long return EBUSY; 4962aedd662SScott Long if((error = ips_diskdev_free(sc))) 4972aedd662SScott Long return error; 4982aedd662SScott Long if(ips_cmdqueue_free(sc)){ 4992aedd662SScott Long device_printf(sc->dev, 5002aedd662SScott Long "trying to exit when command queue is not empty!\n"); 5012aedd662SScott Long return EBUSY; 5022aedd662SScott Long } 5032aedd662SScott Long DEVICE_PRINTF(1, sc->dev, "free\n"); 504352176c8SJohn Baldwin callout_drain(&sc->timer); 5052aedd662SScott Long 5062aedd662SScott Long if(sc->sg_dmatag) 5072aedd662SScott Long bus_dma_tag_destroy(sc->sg_dmatag); 5082aedd662SScott Long if(sc->command_dmatag) 5092aedd662SScott Long bus_dma_tag_destroy(sc->command_dmatag); 5102aedd662SScott Long if(sc->device_file) 5112aedd662SScott Long destroy_dev(sc->device_file); 5122aedd662SScott Long return 0; 5132aedd662SScott Long } 5142aedd662SScott Long 5157765040eSScott Long static __inline int ips_morpheus_check_intr(ips_softc_t *sc) 5162aedd662SScott Long { 5172aedd662SScott Long int cmdnumber; 5182aedd662SScott Long ips_cmd_status_t status; 5197765040eSScott Long ips_command_t *command; 5207765040eSScott Long int found = 0; 5217765040eSScott Long u_int32_t oisr; 5222aedd662SScott Long 5232aedd662SScott Long oisr = ips_read_4(sc, MORPHEUS_REG_OISR); 5247765040eSScott Long PRINTF(9, "interrupt registers out:%x\n", oisr); 5252aedd662SScott Long if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){ 5262aedd662SScott Long DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n"); 5277765040eSScott Long return (0); 5282aedd662SScott Long } 5292aedd662SScott Long while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){ 5302aedd662SScott Long cmdnumber = status.fields.command_id; 5317765040eSScott Long command = &sc->commandarray[cmdnumber]; 5327765040eSScott Long command->status.value = status.value; 5337765040eSScott Long command->timeout = 0; 5347765040eSScott Long command->callback(command); 5352aedd662SScott Long 5367765040eSScott Long found = 1; 5372aedd662SScott Long } 5387765040eSScott Long return (found); 5397765040eSScott Long } 5407765040eSScott Long 5417765040eSScott Long void ips_morpheus_intr(void *void_sc) 5427765040eSScott Long { 5437765040eSScott Long ips_softc_t *sc = void_sc; 5447765040eSScott Long 5457765040eSScott Long mtx_lock(&sc->queue_mtx); 5467765040eSScott Long ips_morpheus_check_intr(sc); 54703a908f2SScott Long mtx_unlock(&sc->queue_mtx); 5487765040eSScott Long } 5497765040eSScott Long 5507765040eSScott Long void ips_morpheus_poll(ips_command_t *command) 5517765040eSScott Long { 5527765040eSScott Long uint32_t ts; 5537765040eSScott Long 5544ef63badSScott Long /* 5554ef63badSScott Long * Locks are not used here because this is only called during 5564ef63badSScott Long * crashdumps. 5574ef63badSScott Long */ 5587765040eSScott Long ts = time_second + command->timeout; 5597765040eSScott Long while ((command->timeout != 0) 5607765040eSScott Long && (ips_morpheus_check_intr(command->sc) == 0) 5617765040eSScott Long && (ts > time_second)) 5627765040eSScott Long DELAY(1000); 5632aedd662SScott Long } 5642aedd662SScott Long 5652aedd662SScott Long void ips_issue_morpheus_cmd(ips_command_t *command) 5662aedd662SScott Long { 5672aedd662SScott Long /* hmmm, is there a cleaner way to do this? */ 5682aedd662SScott Long if(command->sc->state & IPS_OFFLINE){ 5692eea7051SScott Long ips_set_error(command, EINVAL); 5702aedd662SScott Long command->callback(command); 5712aedd662SScott Long return; 5722aedd662SScott Long } 5732aedd662SScott Long command->timeout = 10; 5742aedd662SScott Long ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr); 5752aedd662SScott Long } 5762aedd662SScott Long 5772aedd662SScott Long static void ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,int segnum, int error) 5782aedd662SScott Long { 5792aedd662SScott Long ips_copper_queue_t *queue = queueptr; 5802aedd662SScott Long if(error){ 5812aedd662SScott Long return; 5822aedd662SScott Long } 5832aedd662SScott Long queue->base_phys_addr = segments[0].ds_addr; 5842aedd662SScott Long } 5852aedd662SScott Long 5862aedd662SScott Long static int ips_copperhead_queue_init(ips_softc_t *sc) 5872aedd662SScott Long { 5882aedd662SScott Long int error; 5892aedd662SScott Long bus_dma_tag_t dmatag; 59076fe16bbSHiren Panchasara bus_dmamap_t dmamap; 5912aedd662SScott Long if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, 5922aedd662SScott Long /* alignemnt */ 1, 5932aedd662SScott Long /* boundary */ 0, 5942aedd662SScott Long /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 5952aedd662SScott Long /* highaddr */ BUS_SPACE_MAXADDR, 5962aedd662SScott Long /* filter */ NULL, 5972aedd662SScott Long /* filterarg */ NULL, 5982aedd662SScott Long /* maxsize */ sizeof(ips_copper_queue_t), 5992aedd662SScott Long /* numsegs */ 1, 6002aedd662SScott Long /* maxsegsize*/ sizeof(ips_copper_queue_t), 6012aedd662SScott Long /* flags */ 0, 60203a908f2SScott Long /* lockfunc */ NULL, 60303a908f2SScott Long /* lockarg */ NULL, 6042aedd662SScott Long &dmatag) != 0) { 6052aedd662SScott Long device_printf(sc->dev, "can't alloc dma tag for statue queue\n"); 6062aedd662SScott Long error = ENOMEM; 60776fe16bbSHiren Panchasara return error; 6082aedd662SScott Long } 6092aedd662SScott Long if(bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue), 6102aedd662SScott Long BUS_DMA_NOWAIT, &dmamap)){ 6112aedd662SScott Long error = ENOMEM; 6122aedd662SScott Long goto exit; 6132aedd662SScott Long } 6142aedd662SScott Long bzero(sc->copper_queue, sizeof(ips_copper_queue_t)); 6152aedd662SScott Long sc->copper_queue->dmatag = dmatag; 6162aedd662SScott Long sc->copper_queue->dmamap = dmamap; 6172aedd662SScott Long sc->copper_queue->nextstatus = 1; 6182aedd662SScott Long bus_dmamap_load(dmatag, dmamap, 6192aedd662SScott Long &(sc->copper_queue->status[0]), IPS_MAX_CMD_NUM * 4, 6202aedd662SScott Long ips_copperhead_queue_callback, sc->copper_queue, 6212aedd662SScott Long BUS_DMA_NOWAIT); 6222aedd662SScott Long if(sc->copper_queue->base_phys_addr == 0){ 6232aedd662SScott Long error = ENOMEM; 6242aedd662SScott Long goto exit; 6252aedd662SScott Long } 6262aedd662SScott Long ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr); 6272aedd662SScott Long ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr + 6282aedd662SScott Long IPS_MAX_CMD_NUM * 4); 6292aedd662SScott Long ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4); 6302aedd662SScott Long ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr); 6312aedd662SScott Long 6322aedd662SScott Long 6332aedd662SScott Long return 0; 6342aedd662SScott Long exit: 63576fe16bbSHiren Panchasara if (sc->copper_queue != NULL) 6362aedd662SScott Long bus_dmamem_free(dmatag, sc->copper_queue, dmamap); 6372aedd662SScott Long bus_dma_tag_destroy(dmatag); 6382aedd662SScott Long return error; 6392aedd662SScott Long } 6402aedd662SScott Long 6412aedd662SScott Long /* see if we should reinitialize the card and wait for it to timeout or complete initialization FIXME */ 6422aedd662SScott Long int ips_copperhead_reinit(ips_softc_t *sc, int force) 6432aedd662SScott Long { 6442aedd662SScott Long int i, j; 645dec703c7SScott Long u_int32_t configstatus = 0; 6462aedd662SScott Long ips_write_1(sc, COPPER_REG_SCPR, 0x80); 6472aedd662SScott Long ips_write_1(sc, COPPER_REG_SCPR, 0); 6482aedd662SScott Long device_printf(sc->dev, "reinitializing adapter, this could take several minutes.\n"); 6492aedd662SScott Long for(j = 0; j < 2; j++){ 6502aedd662SScott Long for(i = 0; i < 45; i++){ 6512aedd662SScott Long if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){ 6522aedd662SScott Long ips_write_1(sc, COPPER_REG_HISR, 6532aedd662SScott Long COPPER_GHI_BIT); 6542aedd662SScott Long break; 6552aedd662SScott Long } else 6562aedd662SScott Long DELAY(1000000); 6572aedd662SScott Long } 6582aedd662SScott Long if(i == 45) 6592aedd662SScott Long return 1; 6602aedd662SScott Long } 6612aedd662SScott Long for(j = 0; j < 2; j++){ 6622aedd662SScott Long configstatus <<= 8; 6632aedd662SScott Long for(i = 0; i < 240; i++){ 6642aedd662SScott Long if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){ 6652aedd662SScott Long configstatus |= 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 == 240) 6732aedd662SScott Long return 1; 6742aedd662SScott Long } 6752aedd662SScott Long for(i = 0; i < 240; i++){ 6762aedd662SScott Long if(!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT)){ 6772aedd662SScott Long break; 6782aedd662SScott Long } else 6792aedd662SScott Long DELAY(1000000); 6802aedd662SScott Long } 6812aedd662SScott Long if(i == 240) 6822aedd662SScott Long return 1; 6832aedd662SScott Long ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT); 6842aedd662SScott Long ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT); 6852aedd662SScott Long ips_copperhead_queue_init(sc); 6862aedd662SScott Long ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT); 6872aedd662SScott Long i = ips_read_1(sc, COPPER_REG_SCPR); 6882aedd662SScott Long ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT); 6892aedd662SScott Long if(!configstatus){ 6902aedd662SScott Long device_printf(sc->dev, "adapter initialization failed\n"); 6912aedd662SScott Long return 1; 6922aedd662SScott Long } 6932aedd662SScott Long if(force && ips_clear_adapter(sc)){ 6942aedd662SScott Long device_printf(sc->dev, "adapter clear failed\n"); 6952aedd662SScott Long return 1; 6962aedd662SScott Long } 6972aedd662SScott Long return 0; 6982aedd662SScott Long } 6992aedd662SScott Long static u_int32_t ips_copperhead_cmd_status(ips_softc_t *sc) 7002aedd662SScott Long { 7012aedd662SScott Long u_int32_t value; 7022aedd662SScott Long int statnum = sc->copper_queue->nextstatus++; 7032aedd662SScott Long if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM) 7042aedd662SScott Long sc->copper_queue->nextstatus = 0; 7052aedd662SScott Long value = sc->copper_queue->status[statnum]; 7062aedd662SScott Long ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr + 7072aedd662SScott Long 4 * statnum); 7082aedd662SScott Long return value; 7092aedd662SScott Long } 7102aedd662SScott Long 7112aedd662SScott Long 7122aedd662SScott Long void ips_copperhead_intr(void *void_sc) 7132aedd662SScott Long { 7142aedd662SScott Long ips_softc_t *sc = (ips_softc_t *)void_sc; 7152aedd662SScott Long int cmdnumber; 7162aedd662SScott Long ips_cmd_status_t status; 7172aedd662SScott Long 71803a908f2SScott Long mtx_lock(&sc->queue_mtx); 7192aedd662SScott Long while(ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT){ 7202aedd662SScott Long status.value = ips_copperhead_cmd_status(sc); 7212aedd662SScott Long cmdnumber = status.fields.command_id; 7222aedd662SScott Long sc->commandarray[cmdnumber].status.value = status.value; 7232aedd662SScott Long sc->commandarray[cmdnumber].timeout = 0; 7242aedd662SScott Long sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber])); 7252aedd662SScott Long PRINTF(9, "ips: got command %d\n", cmdnumber); 7262aedd662SScott Long } 72703a908f2SScott Long mtx_unlock(&sc->queue_mtx); 7282aedd662SScott Long return; 7292aedd662SScott Long } 7302aedd662SScott Long 7312aedd662SScott Long void ips_issue_copperhead_cmd(ips_command_t *command) 7322aedd662SScott Long { 7332aedd662SScott Long int i; 7342aedd662SScott Long /* hmmm, is there a cleaner way to do this? */ 7352aedd662SScott Long if(command->sc->state & IPS_OFFLINE){ 7362eea7051SScott Long ips_set_error(command, EINVAL); 7372aedd662SScott Long command->callback(command); 7382aedd662SScott Long return; 7392aedd662SScott Long } 7402aedd662SScott Long command->timeout = 10; 7412aedd662SScott Long for(i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT; 7422aedd662SScott Long i++ ){ 7432aedd662SScott Long if( i == 20){ 7442aedd662SScott Long printf("sem bit still set, can't send a command\n"); 7452aedd662SScott Long return; 7462aedd662SScott Long } 7472aedd662SScott Long DELAY(500);/* need to do a delay here */ 7482aedd662SScott Long } 7492aedd662SScott Long ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr); 7502aedd662SScott Long ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START); 7512aedd662SScott Long } 7522aedd662SScott Long 7537765040eSScott Long void ips_copperhead_poll(ips_command_t *command) 7547765040eSScott Long { 7557765040eSScott Long 7567765040eSScott Long printf("ips: cmd polling not implemented for copperhead devices\n"); 7577765040eSScott Long } 758