1114902Sscottl/*- 2119418Sobrien * Written by: David Jeffery 3114902Sscottl * Copyright (c) 2002 Adaptec Inc. 4114902Sscottl * All rights reserved. 5114902Sscottl * 6114902Sscottl * Redistribution and use in source and binary forms, with or without 7114902Sscottl * modification, are permitted provided that the following conditions 8114902Sscottl * are met: 9114902Sscottl * 1. Redistributions of source code must retain the above copyright 10114902Sscottl * notice, this list of conditions and the following disclaimer. 11114902Sscottl * 2. Redistributions in binary form must reproduce the above copyright 12114902Sscottl * notice, this list of conditions and the following disclaimer in the 13114902Sscottl * documentation and/or other materials provided with the distribution. 14114902Sscottl * 15114902Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16114902Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17114902Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18114902Sscottl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19114902Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20114902Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21114902Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22114902Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23114902Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24114902Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25114902Sscottl * SUCH DAMAGE. 26114902Sscottl */ 27114902Sscottl 28119418Sobrien#include <sys/cdefs.h> 29119418Sobrien__FBSDID("$FreeBSD$"); 30114902Sscottl 31152919Sscottl#include <dev/ips/ipsreg.h> 32114902Sscottl#include <dev/ips/ips.h> 33114902Sscottl#include <sys/stat.h> 34114902Sscottl#include <sys/time.h> 35114902Sscottl 36114902Sscottlstatic d_open_t ips_open; 37114902Sscottlstatic d_close_t ips_close; 38114902Sscottlstatic d_ioctl_t ips_ioctl; 39114902Sscottl 40129859SscottlMALLOC_DEFINE(M_IPSBUF, "ipsbuf","IPS driver buffer"); 41129859Sscottl 42114902Sscottlstatic struct cdevsw ips_cdevsw = { 43126080Sphk .d_version = D_VERSION, 44126080Sphk .d_flags = D_NEEDGIANT, 45125808Sphk .d_open = ips_open, 46125808Sphk .d_close = ips_close, 47125808Sphk .d_ioctl = ips_ioctl, 48125808Sphk .d_name = "ips", 49114902Sscottl}; 50114902Sscottl 51122999Smbrstatic const char* ips_adapter_name[] = { 52122999Smbr "N/A", 53122999Smbr "ServeRAID (copperhead)", 54122999Smbr "ServeRAID II (copperhead refresh)", 55122999Smbr "ServeRAID onboard (copperhead)", 56122999Smbr "ServeRAID onboard (copperhead)", 57122999Smbr "ServeRAID 3H (clarinet)", 58122999Smbr "ServeRAID 3L (clarinet lite)", 59122999Smbr "ServeRAID 4H (trombone)", 60122999Smbr "ServeRAID 4M (morpheus)", 61122999Smbr "ServeRAID 4L (morpheus lite)", 62122999Smbr "ServeRAID 4Mx (neo)", 63122999Smbr "ServeRAID 4Lx (neo lite)", 64122999Smbr "ServeRAID 5i II (sarasota)", 65122999Smbr "ServeRAID 5i (sarasota)", 66122999Smbr "ServeRAID 6M (marco)", 67163024Smaxim "ServeRAID 6i (sebring)", 68163024Smaxim "ServeRAID 7t", 69163024Smaxim "ServeRAID 7k", 70163024Smaxim "ServeRAID 7M" 71122999Smbr}; 72114902Sscottl 73122999Smbr 74130585Sphkstatic int ips_open(struct cdev *dev, int flags, int fmt, struct thread *td) 75114902Sscottl{ 76114902Sscottl ips_softc_t *sc = dev->si_drv1; 77114902Sscottl sc->state |= IPS_DEV_OPEN; 78114902Sscottl return 0; 79114902Sscottl} 80114902Sscottl 81130585Sphkstatic int ips_close(struct cdev *dev, int flags, int fmt, struct thread *td) 82114902Sscottl{ 83114902Sscottl ips_softc_t *sc = dev->si_drv1; 84114902Sscottl sc->state &= ~IPS_DEV_OPEN; 85114902Sscottl 86114902Sscottl return 0; 87114902Sscottl} 88114902Sscottl 89130585Sphkstatic int ips_ioctl(struct cdev *dev, u_long command, caddr_t addr, int32_t flags, struct thread *td) 90114902Sscottl{ 91114902Sscottl ips_softc_t *sc; 92114902Sscottl 93114902Sscottl sc = dev->si_drv1; 94114902Sscottl return ips_ioctl_request(sc, command, addr, flags); 95114902Sscottl} 96114902Sscottl 97114902Sscottlstatic void ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error) 98114902Sscottl{ 99114902Sscottl ips_command_t *command = cmdptr; 100114902Sscottl PRINTF(10, "ips: in ips_cmd_dmaload\n"); 101114902Sscottl if(!error) 102114902Sscottl command->command_phys_addr = segments[0].ds_addr; 103114902Sscottl 104114902Sscottl} 105114902Sscottl 106114902Sscottl/* is locking needed? what locking guarentees are there on removal? */ 107140923Sscottlstatic int ips_cmdqueue_free(ips_softc_t *sc) 108114902Sscottl{ 109114902Sscottl int i, error = -1; 110126364Sscottl ips_command_t *command; 111126364Sscottl 112114902Sscottl if(!sc->used_commands){ 113114902Sscottl for(i = 0; i < sc->max_cmds; i++){ 114126364Sscottl 115126364Sscottl command = &sc->commandarray[i]; 116126364Sscottl 117126364Sscottl if(command->command_phys_addr == 0) 118114902Sscottl continue; 119114902Sscottl bus_dmamap_unload(sc->command_dmatag, 120126364Sscottl command->command_dmamap); 121114902Sscottl bus_dmamem_free(sc->command_dmatag, 122126364Sscottl command->command_buffer, 123126364Sscottl command->command_dmamap); 124140923Sscottl if (command->data_dmamap != NULL) 125140923Sscottl bus_dmamap_destroy(command->data_dmatag, 126140923Sscottl command->data_dmamap); 127114902Sscottl } 128114902Sscottl error = 0; 129114902Sscottl sc->state |= IPS_OFFLINE; 130114902Sscottl } 131140923Sscottl sc->staticcmd = NULL; 132140923Sscottl free(sc->commandarray, M_DEVBUF); 133114902Sscottl return error; 134114902Sscottl} 135114902Sscottl 136114902Sscottl/* places all ips command structs on the free command queue. No locking as if someone else tries 137114902Sscottl * to access this during init, we have bigger problems */ 138140923Sscottlstatic int ips_cmdqueue_init(ips_softc_t *sc) 139114902Sscottl{ 140114902Sscottl int i; 141114902Sscottl ips_command_t *command; 142140923Sscottl 143140923Sscottl sc->commandarray = (ips_command_t *)malloc(sizeof(ips_command_t) * 144140923Sscottl sc->max_cmds, M_DEVBUF, M_NOWAIT|M_ZERO); 145140923Sscottl if (sc->commandarray == NULL) 146140923Sscottl return (ENOMEM); 147140923Sscottl 148114902Sscottl SLIST_INIT(&sc->free_cmd_list); 149114902Sscottl for(i = 0; i < sc->max_cmds; i++){ 150114902Sscottl command = &sc->commandarray[i]; 151126364Sscottl command->id = i; 152126364Sscottl command->sc = sc; 153126364Sscottl 154114902Sscottl if(bus_dmamem_alloc(sc->command_dmatag,&command->command_buffer, 155114902Sscottl BUS_DMA_NOWAIT, &command->command_dmamap)) 156114902Sscottl goto error; 157114902Sscottl bus_dmamap_load(sc->command_dmatag, command->command_dmamap, 158114902Sscottl command->command_buffer,IPS_COMMAND_LEN, 159114902Sscottl ips_cmd_dmaload, command, BUS_DMA_NOWAIT); 160114902Sscottl if(!command->command_phys_addr){ 161114902Sscottl bus_dmamem_free(sc->command_dmatag, 162114902Sscottl command->command_buffer, command->command_dmamap); 163114902Sscottl goto error; 164114902Sscottl } 165126364Sscottl 166140923Sscottl if (i != 0) { 167140923Sscottl command->data_dmatag = sc->sg_dmatag; 168140923Sscottl if (bus_dmamap_create(command->data_dmatag, 0, 169140923Sscottl &command->data_dmamap)) 170140923Sscottl goto error; 171140923Sscottl SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next); 172140923Sscottl } else 173140923Sscottl sc->staticcmd = command; 174114902Sscottl } 175114902Sscottl sc->state &= ~IPS_OFFLINE; 176114902Sscottl return 0; 177114902Sscottlerror: 178126364Sscottl ips_cmdqueue_free(sc); 179126364Sscottl return ENOMEM; 180114902Sscottl} 181114902Sscottl 182114902Sscottl/* returns a free command struct if one is available. 183114902Sscottl * It also blanks out anything that may be a wild pointer/value. 184114902Sscottl * Also, command buffers are not freed. They are 185114902Sscottl * small so they are saved and kept dmamapped and loaded. 186114902Sscottl */ 187140923Sscottlint ips_get_free_cmd(ips_softc_t *sc, ips_command_t **cmd, unsigned long flags) 188114902Sscottl{ 189114902Sscottl ips_command_t *command; 190114902Sscottl 191114902Sscottl if(sc->state & IPS_OFFLINE){ 192114902Sscottl return EIO; 193114902Sscottl } 194140923Sscottl if ((flags & IPS_STATIC_FLAG) == 0) { 195140923Sscottl command = SLIST_FIRST(&sc->free_cmd_list); 196140923Sscottl if(!command || (sc->state & IPS_TIMEOUT)){ 197140923Sscottl return EBUSY; 198140923Sscottl } 199140923Sscottl SLIST_REMOVE_HEAD(&sc->free_cmd_list, next); 200140923Sscottl (sc->used_commands)++; 201140923Sscottl } else { 202140923Sscottl if (sc->state & IPS_STATIC_BUSY) 203114902Sscottl return EAGAIN; 204140923Sscottl command = sc->staticcmd; 205140923Sscottl sc->state |= IPS_STATIC_BUSY; 206114902Sscottl } 207114902Sscottl clear_ips_command(command); 208114902Sscottl bzero(command->command_buffer, IPS_COMMAND_LEN); 209140923Sscottl *cmd = command; 210140923Sscottl return 0; 211114902Sscottl} 212114902Sscottl 213114902Sscottl/* adds a command back to the free command queue */ 214114902Sscottlvoid ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command) 215114902Sscottl{ 216126364Sscottl 217140923Sscottl if (sema_value(&sc->cmd_sema) != 0) 218126364Sscottl panic("ips: command returned non-zero semaphore"); 219126364Sscottl 220140923Sscottl if (command != sc->staticcmd) { 221140923Sscottl SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next); 222140923Sscottl (sc->used_commands)--; 223140923Sscottl } else { 224140923Sscottl sc->state &= ~IPS_STATIC_BUSY; 225140923Sscottl } 226114902Sscottl} 227122999Smbrstatic const char* ips_diskdev_statename(u_int8_t state) 228122999Smbr{ 229122999Smbr static char statebuf[20]; 230122999Smbr switch(state){ 231122999Smbr case IPS_LD_OFFLINE: 232122999Smbr return("OFFLINE"); 233122999Smbr break; 234122999Smbr case IPS_LD_OKAY: 235122999Smbr return("OK"); 236122999Smbr break; 237122999Smbr case IPS_LD_DEGRADED: 238122999Smbr return("DEGRADED"); 239122999Smbr break; 240122999Smbr case IPS_LD_FREE: 241122999Smbr return("FREE"); 242122999Smbr break; 243122999Smbr case IPS_LD_SYS: 244122999Smbr return("SYS"); 245122999Smbr break; 246122999Smbr case IPS_LD_CRS: 247122999Smbr return("CRS"); 248122999Smbr break; 249122999Smbr } 250122999Smbr sprintf(statebuf,"UNKNOWN(0x%02x)", state); 251122999Smbr return(statebuf); 252122999Smbr} 253114902Sscottl 254114902Sscottlstatic int ips_diskdev_init(ips_softc_t *sc) 255114902Sscottl{ 256114902Sscottl int i; 257114902Sscottl for(i=0; i < IPS_MAX_NUM_DRIVES; i++){ 258122999Smbr if(sc->drives[i].state == IPS_LD_FREE) continue; 259122999Smbr device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n", 260122999Smbr i, sc->drives[i].raid_lvl, 261122999Smbr sc->drives[i].sector_count, 262122999Smbr ips_diskdev_statename(sc->drives[i].state)); 263122999Smbr if(sc->drives[i].state == IPS_LD_OKAY || 264122999Smbr sc->drives[i].state == IPS_LD_DEGRADED){ 265114902Sscottl sc->diskdev[i] = device_add_child(sc->dev, NULL, -1); 266116931Speter device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i); 267114902Sscottl } 268114902Sscottl } 269114902Sscottl if(bus_generic_attach(sc->dev)){ 270114902Sscottl device_printf(sc->dev, "Attaching bus failed\n"); 271114902Sscottl } 272114902Sscottl return 0; 273114902Sscottl} 274114902Sscottl 275114902Sscottlstatic int ips_diskdev_free(ips_softc_t *sc) 276114902Sscottl{ 277114902Sscottl int i; 278114902Sscottl int error = 0; 279114902Sscottl for(i = 0; i < IPS_MAX_NUM_DRIVES; i++){ 280114902Sscottl if(sc->diskdev[i]) 281114902Sscottl error = device_delete_child(sc->dev, sc->diskdev[i]); 282114902Sscottl if(error) 283114902Sscottl return error; 284114902Sscottl } 285114902Sscottl bus_generic_detach(sc->dev); 286114902Sscottl return 0; 287114902Sscottl} 288114902Sscottl 289114902Sscottl/* ips_timeout is periodically called to make sure no commands sent 290114902Sscottl * to the card have become stuck. If it finds a stuck command, it 291114902Sscottl * sets a flag so the driver won't start any more commands and then 292114902Sscottl * is periodically called to see if all outstanding commands have 293114902Sscottl * either finished or timed out. Once timed out, an attempt to 294114902Sscottl * reinitialize the card is made. If that fails, the driver gives 295114902Sscottl * up and declares the card dead. */ 296114902Sscottlstatic void ips_timeout(void *arg) 297114902Sscottl{ 298114902Sscottl ips_softc_t *sc = arg; 299114902Sscottl int i, state = 0; 300114902Sscottl ips_command_t *command; 301140923Sscottl 302140923Sscottl mtx_lock(&sc->queue_mtx); 303114902Sscottl command = &sc->commandarray[0]; 304114902Sscottl for(i = 0; i < sc->max_cmds; i++){ 305114902Sscottl if(!command[i].timeout){ 306114902Sscottl continue; 307114902Sscottl } 308114902Sscottl command[i].timeout--; 309114902Sscottl if(!command[i].timeout){ 310114902Sscottl if(!(sc->state & IPS_TIMEOUT)){ 311114902Sscottl sc->state |= IPS_TIMEOUT; 312114902Sscottl device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n"); 313114902Sscottl } 314150535Sscottl ips_set_error(&command[i], ETIMEDOUT); 315114902Sscottl command[i].callback(&command[i]); 316114902Sscottl /* hmm, this should be enough cleanup */ 317114902Sscottl } else 318114902Sscottl state = 1; 319114902Sscottl } 320114902Sscottl if(!state && (sc->state & IPS_TIMEOUT)){ 321114902Sscottl if(sc->ips_adapter_reinit(sc, 1)){ 322114902Sscottl device_printf(sc->dev, "AIEE! adapter reset failed, giving up and going home! Have a nice day.\n"); 323114902Sscottl sc->state |= IPS_OFFLINE; 324114902Sscottl sc->state &= ~IPS_TIMEOUT; 325114902Sscottl /* Grr, I hate this solution. I run waiting commands 326114902Sscottl one at a time and error them out just before they 327114902Sscottl would go to the card. This sucks. */ 328114902Sscottl } else 329114902Sscottl sc->state &= ~IPS_TIMEOUT; 330114902Sscottl } 331114902Sscottl if (sc->state != IPS_OFFLINE) 332114902Sscottl sc->timer = timeout(ips_timeout, sc, 10*hz); 333140923Sscottl mtx_unlock(&sc->queue_mtx); 334114902Sscottl} 335114902Sscottl 336114902Sscottl/* check card and initialize it */ 337114902Sscottlint ips_adapter_init(ips_softc_t *sc) 338114902Sscottl{ 339116852Sscottl int i; 340114902Sscottl DEVICE_PRINTF(1,sc->dev, "initializing\n"); 341126364Sscottl 342114902Sscottl if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, 343114902Sscottl /* alignemnt */ 1, 344114902Sscottl /* boundary */ 0, 345114902Sscottl /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 346114902Sscottl /* highaddr */ BUS_SPACE_MAXADDR, 347114902Sscottl /* filter */ NULL, 348114902Sscottl /* filterarg */ NULL, 349114902Sscottl /* maxsize */ IPS_COMMAND_LEN + 350114902Sscottl IPS_MAX_SG_LEN, 351114902Sscottl /* numsegs */ 1, 352114902Sscottl /* maxsegsize*/ IPS_COMMAND_LEN + 353114902Sscottl IPS_MAX_SG_LEN, 354114902Sscottl /* flags */ 0, 355140923Sscottl /* lockfunc */ NULL, 356140923Sscottl /* lockarg */ NULL, 357114902Sscottl &sc->command_dmatag) != 0) { 358114902Sscottl device_printf(sc->dev, "can't alloc command dma tag\n"); 359114902Sscottl goto error; 360114902Sscottl } 361114902Sscottl if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, 362114902Sscottl /* alignemnt */ 1, 363114902Sscottl /* boundary */ 0, 364114902Sscottl /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 365114902Sscottl /* highaddr */ BUS_SPACE_MAXADDR, 366114902Sscottl /* filter */ NULL, 367114902Sscottl /* filterarg */ NULL, 368114902Sscottl /* maxsize */ IPS_MAX_IOBUF_SIZE, 369114902Sscottl /* numsegs */ IPS_MAX_SG_ELEMENTS, 370114902Sscottl /* maxsegsize*/ IPS_MAX_IOBUF_SIZE, 371114902Sscottl /* flags */ 0, 372117126Sscottl /* lockfunc */ busdma_lock_mutex, 373140923Sscottl /* lockarg */ &sc->queue_mtx, 374114902Sscottl &sc->sg_dmatag) != 0) { 375114902Sscottl device_printf(sc->dev, "can't alloc SG dma tag\n"); 376114902Sscottl goto error; 377114902Sscottl } 378114902Sscottl /* create one command buffer until we know how many commands this card 379114902Sscottl can handle */ 380114902Sscottl sc->max_cmds = 1; 381114902Sscottl ips_cmdqueue_init(sc); 382116852Sscottl callout_handle_init(&sc->timer); 383114902Sscottl 384114902Sscottl if(sc->ips_adapter_reinit(sc, 0)) 385114902Sscottl goto error; 386114902Sscottl 387122999Smbr /* initialize ffdc values */ 388122999Smbr microtime(&sc->ffdc_resettime); 389122999Smbr sc->ffdc_resetcount = 1; 390122999Smbr if ((i = ips_ffdc_reset(sc)) != 0) { 391122999Smbr device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i); 392122999Smbr goto error; 393122999Smbr } 394116852Sscottl if ((i = ips_get_adapter_info(sc)) != 0) { 395116852Sscottl device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i); 396114902Sscottl goto error; 397114902Sscottl } 398122999Smbr ips_update_nvram(sc); /* no error check as failure doesn't matter */ 399122999Smbr if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){ 400122999Smbr device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]); 401122999Smbr } 402116852Sscottl if ((i = ips_get_drive_info(sc)) != 0) { 403116852Sscottl device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i); 404116852Sscottl goto error; 405116852Sscottl } 406114902Sscottl 407114902Sscottl ips_cmdqueue_free(sc); 408114902Sscottl if(sc->adapter_info.max_concurrent_cmds) 409114902Sscottl sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds); 410114902Sscottl else 411114902Sscottl sc->max_cmds = 32; 412114902Sscottl if(ips_cmdqueue_init(sc)){ 413114902Sscottl device_printf(sc->dev, "failed to initialize command buffers\n"); 414114902Sscottl goto error; 415114902Sscottl } 416114902Sscottl sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev), UID_ROOT, GID_OPERATOR, 417114902Sscottl S_IRUSR | S_IWUSR, "ips%d", device_get_unit(sc->dev)); 418114902Sscottl sc->device_file->si_drv1 = sc; 419114902Sscottl ips_diskdev_init(sc); 420114902Sscottl sc->timer = timeout(ips_timeout, sc, 10*hz); 421114902Sscottl return 0; 422114902Sscottl 423114902Sscottlerror: 424114902Sscottl ips_adapter_free(sc); 425114902Sscottl return ENXIO; 426114902Sscottl} 427114902Sscottl 428114902Sscottl/* see if we should reinitialize the card and wait for it to timeout or complete initialization */ 429114902Sscottlint ips_morpheus_reinit(ips_softc_t *sc, int force) 430114902Sscottl{ 431114902Sscottl u_int32_t tmp; 432114902Sscottl int i; 433114902Sscottl 434114902Sscottl tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 435114902Sscottl if(!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) && 436114902Sscottl (ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp){ 437114902Sscottl ips_write_4(sc, MORPHEUS_REG_OIMR, 0); 438114902Sscottl return 0; 439114902Sscottl } 440114902Sscottl ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff); 441114902Sscottl ips_read_4(sc, MORPHEUS_REG_OIMR); 442114902Sscottl 443114902Sscottl device_printf(sc->dev, "resetting adapter, this may take up to 5 minutes\n"); 444114902Sscottl ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000); 445114902Sscottl DELAY(5000000); 446152919Sscottl ips_read_4(sc, MORPHEUS_REG_OIMR); 447114902Sscottl 448114902Sscottl tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 449114902Sscottl for(i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++){ 450114902Sscottl DELAY(1000000); 451114902Sscottl DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i); 452114902Sscottl tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 453114902Sscottl } 454114902Sscottl if(tmp & MORPHEUS_BIT_POST1) 455114902Sscottl ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1); 456114902Sscottl 457114902Sscottl if( i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK){ 458114902Sscottl device_printf(sc->dev,"Adapter error during initialization.\n"); 459114902Sscottl return 1; 460114902Sscottl } 461114902Sscottl for(i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++){ 462114902Sscottl DELAY(1000000); 463114902Sscottl DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i); 464114902Sscottl tmp = ips_read_4(sc, MORPHEUS_REG_OISR); 465114902Sscottl } 466114902Sscottl if(tmp & MORPHEUS_BIT_POST2) 467114902Sscottl ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2); 468114902Sscottl 469114902Sscottl if(i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)){ 470114902Sscottl device_printf(sc->dev, "adapter failed config check\n"); 471114902Sscottl return 1; 472114902Sscottl } 473114902Sscottl ips_write_4(sc, MORPHEUS_REG_OIMR, 0); 474114902Sscottl if(force && ips_clear_adapter(sc)){ 475114902Sscottl device_printf(sc->dev, "adapter clear failed\n"); 476114902Sscottl return 1; 477114902Sscottl } 478114902Sscottl return 0; 479114902Sscottl} 480114902Sscottl 481114902Sscottl/* clean up so we can unload the driver. */ 482114902Sscottlint ips_adapter_free(ips_softc_t *sc) 483114902Sscottl{ 484114902Sscottl int error = 0; 485114902Sscottl if(sc->state & IPS_DEV_OPEN) 486114902Sscottl return EBUSY; 487114902Sscottl if((error = ips_diskdev_free(sc))) 488114902Sscottl return error; 489114902Sscottl if(ips_cmdqueue_free(sc)){ 490114902Sscottl device_printf(sc->dev, 491114902Sscottl "trying to exit when command queue is not empty!\n"); 492114902Sscottl return EBUSY; 493114902Sscottl } 494114902Sscottl DEVICE_PRINTF(1, sc->dev, "free\n"); 495114902Sscottl untimeout(ips_timeout, sc, sc->timer); 496114902Sscottl 497114902Sscottl if(sc->sg_dmatag) 498114902Sscottl bus_dma_tag_destroy(sc->sg_dmatag); 499114902Sscottl if(sc->command_dmatag) 500114902Sscottl bus_dma_tag_destroy(sc->command_dmatag); 501114902Sscottl if(sc->device_file) 502114902Sscottl destroy_dev(sc->device_file); 503114902Sscottl return 0; 504114902Sscottl} 505114902Sscottl 506141062Sscottlstatic __inline int ips_morpheus_check_intr(ips_softc_t *sc) 507114902Sscottl{ 508114902Sscottl int cmdnumber; 509114902Sscottl ips_cmd_status_t status; 510141062Sscottl ips_command_t *command; 511141062Sscottl int found = 0; 512141062Sscottl u_int32_t oisr; 513114902Sscottl 514141062Sscottl oisr = ips_read_4(sc, MORPHEUS_REG_OISR); 515141062Sscottl PRINTF(9, "interrupt registers out:%x\n", oisr); 516114902Sscottl if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){ 517114902Sscottl DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n"); 518141062Sscottl return (0); 519114902Sscottl } 520114902Sscottl while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){ 521114902Sscottl cmdnumber = status.fields.command_id; 522141062Sscottl command = &sc->commandarray[cmdnumber]; 523141062Sscottl command->status.value = status.value; 524141062Sscottl command->timeout = 0; 525141062Sscottl command->callback(command); 526114902Sscottl 527141062Sscottl found = 1; 528114902Sscottl } 529141062Sscottl return (found); 530141062Sscottl} 531141062Sscottl 532141062Sscottlvoid ips_morpheus_intr(void *void_sc) 533141062Sscottl{ 534141062Sscottl ips_softc_t *sc = void_sc; 535141062Sscottl 536141062Sscottl mtx_lock(&sc->queue_mtx); 537141062Sscottl ips_morpheus_check_intr(sc); 538140923Sscottl mtx_unlock(&sc->queue_mtx); 539114902Sscottl} 540114902Sscottl 541141062Sscottlvoid ips_morpheus_poll(ips_command_t *command) 542141062Sscottl{ 543141062Sscottl uint32_t ts; 544141062Sscottl 545145545Sscottl /* 546145545Sscottl * Locks are not used here because this is only called during 547145545Sscottl * crashdumps. 548145545Sscottl */ 549141062Sscottl ts = time_second + command->timeout; 550141062Sscottl while ((command->timeout != 0) 551141062Sscottl && (ips_morpheus_check_intr(command->sc) == 0) 552141062Sscottl && (ts > time_second)) 553141062Sscottl DELAY(1000); 554141062Sscottl} 555141062Sscottl 556114902Sscottlvoid ips_issue_morpheus_cmd(ips_command_t *command) 557114902Sscottl{ 558114902Sscottl /* hmmm, is there a cleaner way to do this? */ 559114902Sscottl if(command->sc->state & IPS_OFFLINE){ 560150535Sscottl ips_set_error(command, EINVAL); 561114902Sscottl command->callback(command); 562114902Sscottl return; 563114902Sscottl } 564114902Sscottl command->timeout = 10; 565114902Sscottl ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr); 566114902Sscottl} 567114902Sscottl 568114902Sscottlstatic void ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,int segnum, int error) 569114902Sscottl{ 570114902Sscottl ips_copper_queue_t *queue = queueptr; 571114902Sscottl if(error){ 572114902Sscottl return; 573114902Sscottl } 574114902Sscottl queue->base_phys_addr = segments[0].ds_addr; 575114902Sscottl} 576114902Sscottl 577114902Sscottlstatic int ips_copperhead_queue_init(ips_softc_t *sc) 578114902Sscottl{ 579114902Sscottl int error; 580114902Sscottl bus_dma_tag_t dmatag; 581114902Sscottl bus_dmamap_t dmamap; 582114902Sscottl if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, 583114902Sscottl /* alignemnt */ 1, 584114902Sscottl /* boundary */ 0, 585114902Sscottl /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 586114902Sscottl /* highaddr */ BUS_SPACE_MAXADDR, 587114902Sscottl /* filter */ NULL, 588114902Sscottl /* filterarg */ NULL, 589114902Sscottl /* maxsize */ sizeof(ips_copper_queue_t), 590114902Sscottl /* numsegs */ 1, 591114902Sscottl /* maxsegsize*/ sizeof(ips_copper_queue_t), 592114902Sscottl /* flags */ 0, 593140923Sscottl /* lockfunc */ NULL, 594140923Sscottl /* lockarg */ NULL, 595114902Sscottl &dmatag) != 0) { 596114902Sscottl device_printf(sc->dev, "can't alloc dma tag for statue queue\n"); 597114902Sscottl error = ENOMEM; 598114902Sscottl goto exit; 599114902Sscottl } 600114902Sscottl if(bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue), 601114902Sscottl BUS_DMA_NOWAIT, &dmamap)){ 602114902Sscottl error = ENOMEM; 603114902Sscottl goto exit; 604114902Sscottl } 605114902Sscottl bzero(sc->copper_queue, sizeof(ips_copper_queue_t)); 606114902Sscottl sc->copper_queue->dmatag = dmatag; 607114902Sscottl sc->copper_queue->dmamap = dmamap; 608114902Sscottl sc->copper_queue->nextstatus = 1; 609114902Sscottl bus_dmamap_load(dmatag, dmamap, 610114902Sscottl &(sc->copper_queue->status[0]), IPS_MAX_CMD_NUM * 4, 611114902Sscottl ips_copperhead_queue_callback, sc->copper_queue, 612114902Sscottl BUS_DMA_NOWAIT); 613114902Sscottl if(sc->copper_queue->base_phys_addr == 0){ 614114902Sscottl error = ENOMEM; 615114902Sscottl goto exit; 616114902Sscottl } 617114902Sscottl ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr); 618114902Sscottl ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr + 619114902Sscottl IPS_MAX_CMD_NUM * 4); 620114902Sscottl ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4); 621114902Sscottl ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr); 622114902Sscottl 623114902Sscottl 624114902Sscottl return 0; 625114902Sscottlexit: 626114902Sscottl bus_dmamem_free(dmatag, sc->copper_queue, dmamap); 627114902Sscottl bus_dma_tag_destroy(dmatag); 628114902Sscottl return error; 629114902Sscottl} 630114902Sscottl 631114902Sscottl/* see if we should reinitialize the card and wait for it to timeout or complete initialization FIXME */ 632114902Sscottlint ips_copperhead_reinit(ips_softc_t *sc, int force) 633114902Sscottl{ 634114902Sscottl int i, j; 635114902Sscottl u_int32_t postcode = 0, configstatus = 0; 636114902Sscottl ips_write_1(sc, COPPER_REG_SCPR, 0x80); 637114902Sscottl ips_write_1(sc, COPPER_REG_SCPR, 0); 638114902Sscottl device_printf(sc->dev, "reinitializing adapter, this could take several minutes.\n"); 639114902Sscottl for(j = 0; j < 2; j++){ 640114902Sscottl postcode <<= 8; 641114902Sscottl for(i = 0; i < 45; i++){ 642114902Sscottl if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){ 643114902Sscottl postcode |= ips_read_1(sc, COPPER_REG_ISPR); 644114902Sscottl ips_write_1(sc, COPPER_REG_HISR, 645114902Sscottl COPPER_GHI_BIT); 646114902Sscottl break; 647114902Sscottl } else 648114902Sscottl DELAY(1000000); 649114902Sscottl } 650114902Sscottl if(i == 45) 651114902Sscottl return 1; 652114902Sscottl } 653114902Sscottl for(j = 0; j < 2; j++){ 654114902Sscottl configstatus <<= 8; 655114902Sscottl for(i = 0; i < 240; i++){ 656114902Sscottl if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){ 657114902Sscottl configstatus |= ips_read_1(sc, COPPER_REG_ISPR); 658114902Sscottl ips_write_1(sc, COPPER_REG_HISR, 659114902Sscottl COPPER_GHI_BIT); 660114902Sscottl break; 661114902Sscottl } else 662114902Sscottl DELAY(1000000); 663114902Sscottl } 664114902Sscottl if(i == 240) 665114902Sscottl return 1; 666114902Sscottl } 667114902Sscottl for(i = 0; i < 240; i++){ 668114902Sscottl if(!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT)){ 669114902Sscottl break; 670114902Sscottl } else 671114902Sscottl DELAY(1000000); 672114902Sscottl } 673114902Sscottl if(i == 240) 674114902Sscottl return 1; 675114902Sscottl ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT); 676114902Sscottl ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT); 677114902Sscottl ips_copperhead_queue_init(sc); 678114902Sscottl ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT); 679114902Sscottl i = ips_read_1(sc, COPPER_REG_SCPR); 680114902Sscottl ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT); 681114902Sscottl if(!configstatus){ 682114902Sscottl device_printf(sc->dev, "adapter initialization failed\n"); 683114902Sscottl return 1; 684114902Sscottl } 685114902Sscottl if(force && ips_clear_adapter(sc)){ 686114902Sscottl device_printf(sc->dev, "adapter clear failed\n"); 687114902Sscottl return 1; 688114902Sscottl } 689114902Sscottl return 0; 690114902Sscottl} 691114902Sscottlstatic u_int32_t ips_copperhead_cmd_status(ips_softc_t *sc) 692114902Sscottl{ 693114902Sscottl u_int32_t value; 694114902Sscottl int statnum = sc->copper_queue->nextstatus++; 695114902Sscottl if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM) 696114902Sscottl sc->copper_queue->nextstatus = 0; 697114902Sscottl value = sc->copper_queue->status[statnum]; 698114902Sscottl ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr + 699114902Sscottl 4 * statnum); 700114902Sscottl return value; 701114902Sscottl} 702114902Sscottl 703114902Sscottl 704114902Sscottlvoid ips_copperhead_intr(void *void_sc) 705114902Sscottl{ 706114902Sscottl ips_softc_t *sc = (ips_softc_t *)void_sc; 707114902Sscottl int cmdnumber; 708114902Sscottl ips_cmd_status_t status; 709114902Sscottl 710140923Sscottl mtx_lock(&sc->queue_mtx); 711114902Sscottl while(ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT){ 712114902Sscottl status.value = ips_copperhead_cmd_status(sc); 713114902Sscottl cmdnumber = status.fields.command_id; 714114902Sscottl sc->commandarray[cmdnumber].status.value = status.value; 715114902Sscottl sc->commandarray[cmdnumber].timeout = 0; 716114902Sscottl sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber])); 717114902Sscottl PRINTF(9, "ips: got command %d\n", cmdnumber); 718114902Sscottl } 719140923Sscottl mtx_unlock(&sc->queue_mtx); 720114902Sscottl return; 721114902Sscottl} 722114902Sscottl 723114902Sscottlvoid ips_issue_copperhead_cmd(ips_command_t *command) 724114902Sscottl{ 725114902Sscottl int i; 726114902Sscottl /* hmmm, is there a cleaner way to do this? */ 727114902Sscottl if(command->sc->state & IPS_OFFLINE){ 728150535Sscottl ips_set_error(command, EINVAL); 729114902Sscottl command->callback(command); 730114902Sscottl return; 731114902Sscottl } 732114902Sscottl command->timeout = 10; 733114902Sscottl for(i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT; 734114902Sscottl i++ ){ 735114902Sscottl if( i == 20){ 736114902Sscottlprintf("sem bit still set, can't send a command\n"); 737114902Sscottl return; 738114902Sscottl } 739114902Sscottl DELAY(500);/* need to do a delay here */ 740114902Sscottl } 741114902Sscottl ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr); 742114902Sscottl ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START); 743114902Sscottl} 744114902Sscottl 745141062Sscottlvoid ips_copperhead_poll(ips_command_t *command) 746141062Sscottl{ 747141062Sscottl 748141062Sscottl printf("ips: cmd polling not implemented for copperhead devices\n"); 749141062Sscottl} 750