Index: head/sys/dev/ips/ips.c =================================================================== --- head/sys/dev/ips/ips.c (revision 274487) +++ head/sys/dev/ips/ips.c (revision 274488) @@ -1,750 +1,752 @@ /*- * Written by: David Jeffery * Copyright (c) 2002 Adaptec Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include static d_open_t ips_open; static d_close_t ips_close; static d_ioctl_t ips_ioctl; MALLOC_DEFINE(M_IPSBUF, "ipsbuf","IPS driver buffer"); static struct cdevsw ips_cdevsw = { .d_version = D_VERSION, - .d_flags = D_NEEDGIANT, .d_open = ips_open, .d_close = ips_close, .d_ioctl = ips_ioctl, .d_name = "ips", }; static const char* ips_adapter_name[] = { "N/A", "ServeRAID (copperhead)", "ServeRAID II (copperhead refresh)", "ServeRAID onboard (copperhead)", "ServeRAID onboard (copperhead)", "ServeRAID 3H (clarinet)", "ServeRAID 3L (clarinet lite)", "ServeRAID 4H (trombone)", "ServeRAID 4M (morpheus)", "ServeRAID 4L (morpheus lite)", "ServeRAID 4Mx (neo)", "ServeRAID 4Lx (neo lite)", "ServeRAID 5i II (sarasota)", "ServeRAID 5i (sarasota)", "ServeRAID 6M (marco)", "ServeRAID 6i (sebring)", "ServeRAID 7t", "ServeRAID 7k", "ServeRAID 7M" }; static int ips_open(struct cdev *dev, int flags, int fmt, struct thread *td) { ips_softc_t *sc = dev->si_drv1; + mtx_lock(&sc->queue_mtx); sc->state |= IPS_DEV_OPEN; + mtx_unlock(&sc->queue_mtx); return 0; } static int ips_close(struct cdev *dev, int flags, int fmt, struct thread *td) { ips_softc_t *sc = dev->si_drv1; + + mtx_lock(&sc->queue_mtx); sc->state &= ~IPS_DEV_OPEN; + mtx_unlock(&sc->queue_mtx); return 0; } static int ips_ioctl(struct cdev *dev, u_long command, caddr_t addr, int32_t flags, struct thread *td) { ips_softc_t *sc; sc = dev->si_drv1; return ips_ioctl_request(sc, command, addr, flags); } static void ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error) { ips_command_t *command = cmdptr; PRINTF(10, "ips: in ips_cmd_dmaload\n"); if(!error) command->command_phys_addr = segments[0].ds_addr; } /* is locking needed? what locking guarentees are there on removal? */ static int ips_cmdqueue_free(ips_softc_t *sc) { int i, error = -1; ips_command_t *command; if(!sc->used_commands){ for(i = 0; i < sc->max_cmds; i++){ command = &sc->commandarray[i]; if(command->command_phys_addr == 0) continue; bus_dmamap_unload(sc->command_dmatag, command->command_dmamap); bus_dmamem_free(sc->command_dmatag, command->command_buffer, command->command_dmamap); if (command->data_dmamap != NULL) bus_dmamap_destroy(command->data_dmatag, command->data_dmamap); } error = 0; sc->state |= IPS_OFFLINE; } sc->staticcmd = NULL; free(sc->commandarray, M_DEVBUF); return error; } /* places all ips command structs on the free command queue. No locking as if someone else tries * to access this during init, we have bigger problems */ static int ips_cmdqueue_init(ips_softc_t *sc) { int i; ips_command_t *command; sc->commandarray = (ips_command_t *)malloc(sizeof(ips_command_t) * sc->max_cmds, M_DEVBUF, M_NOWAIT|M_ZERO); if (sc->commandarray == NULL) return (ENOMEM); SLIST_INIT(&sc->free_cmd_list); for(i = 0; i < sc->max_cmds; i++){ command = &sc->commandarray[i]; command->id = i; command->sc = sc; if(bus_dmamem_alloc(sc->command_dmatag,&command->command_buffer, BUS_DMA_NOWAIT, &command->command_dmamap)) goto error; bus_dmamap_load(sc->command_dmatag, command->command_dmamap, command->command_buffer,IPS_COMMAND_LEN, ips_cmd_dmaload, command, BUS_DMA_NOWAIT); if(!command->command_phys_addr){ bus_dmamem_free(sc->command_dmatag, command->command_buffer, command->command_dmamap); goto error; } if (i != 0) { command->data_dmatag = sc->sg_dmatag; if (bus_dmamap_create(command->data_dmatag, 0, &command->data_dmamap)) goto error; SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next); } else sc->staticcmd = command; } sc->state &= ~IPS_OFFLINE; return 0; error: ips_cmdqueue_free(sc); return ENOMEM; } /* returns a free command struct if one is available. * It also blanks out anything that may be a wild pointer/value. * Also, command buffers are not freed. They are * small so they are saved and kept dmamapped and loaded. */ int ips_get_free_cmd(ips_softc_t *sc, ips_command_t **cmd, unsigned long flags) { ips_command_t *command; if(sc->state & IPS_OFFLINE){ return EIO; } if ((flags & IPS_STATIC_FLAG) == 0) { command = SLIST_FIRST(&sc->free_cmd_list); if(!command || (sc->state & IPS_TIMEOUT)){ return EBUSY; } SLIST_REMOVE_HEAD(&sc->free_cmd_list, next); (sc->used_commands)++; } else { if (sc->state & IPS_STATIC_BUSY) return EAGAIN; command = sc->staticcmd; sc->state |= IPS_STATIC_BUSY; } clear_ips_command(command); bzero(command->command_buffer, IPS_COMMAND_LEN); *cmd = command; return 0; } /* adds a command back to the free command queue */ void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command) { if (sema_value(&sc->cmd_sema) != 0) panic("ips: command returned non-zero semaphore"); if (command != sc->staticcmd) { SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next); (sc->used_commands)--; } else { sc->state &= ~IPS_STATIC_BUSY; } } static const char* ips_diskdev_statename(u_int8_t state) { static char statebuf[20]; switch(state){ case IPS_LD_OFFLINE: return("OFFLINE"); break; case IPS_LD_OKAY: return("OK"); break; case IPS_LD_DEGRADED: return("DEGRADED"); break; case IPS_LD_FREE: return("FREE"); break; case IPS_LD_SYS: return("SYS"); break; case IPS_LD_CRS: return("CRS"); break; } sprintf(statebuf,"UNKNOWN(0x%02x)", state); return(statebuf); } static int ips_diskdev_init(ips_softc_t *sc) { int i; for(i=0; i < IPS_MAX_NUM_DRIVES; i++){ if(sc->drives[i].state == IPS_LD_FREE) continue; device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n", i, sc->drives[i].raid_lvl, sc->drives[i].sector_count, ips_diskdev_statename(sc->drives[i].state)); if(sc->drives[i].state == IPS_LD_OKAY || sc->drives[i].state == IPS_LD_DEGRADED){ sc->diskdev[i] = device_add_child(sc->dev, NULL, -1); device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i); } } if(bus_generic_attach(sc->dev)){ device_printf(sc->dev, "Attaching bus failed\n"); } return 0; } static int ips_diskdev_free(ips_softc_t *sc) { int i; int error = 0; for(i = 0; i < IPS_MAX_NUM_DRIVES; i++){ if(sc->diskdev[i]) error = device_delete_child(sc->dev, sc->diskdev[i]); if(error) return error; } bus_generic_detach(sc->dev); return 0; } /* ips_timeout is periodically called to make sure no commands sent * to the card have become stuck. If it finds a stuck command, it * sets a flag so the driver won't start any more commands and then * is periodically called to see if all outstanding commands have * either finished or timed out. Once timed out, an attempt to * reinitialize the card is made. If that fails, the driver gives * up and declares the card dead. */ static void ips_timeout(void *arg) { ips_softc_t *sc = arg; int i, state = 0; ips_command_t *command; - mtx_lock(&sc->queue_mtx); + mtx_assert(&sc->queue_mtx, MA_OWNED); command = &sc->commandarray[0]; for(i = 0; i < sc->max_cmds; i++){ if(!command[i].timeout){ continue; } command[i].timeout--; if(!command[i].timeout){ if(!(sc->state & IPS_TIMEOUT)){ sc->state |= IPS_TIMEOUT; device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n"); } ips_set_error(&command[i], ETIMEDOUT); command[i].callback(&command[i]); /* hmm, this should be enough cleanup */ } else state = 1; } if(!state && (sc->state & IPS_TIMEOUT)){ if(sc->ips_adapter_reinit(sc, 1)){ device_printf(sc->dev, "AIEE! adapter reset failed, giving up and going home! Have a nice day.\n"); sc->state |= IPS_OFFLINE; sc->state &= ~IPS_TIMEOUT; /* Grr, I hate this solution. I run waiting commands one at a time and error them out just before they would go to the card. This sucks. */ } else sc->state &= ~IPS_TIMEOUT; } if (sc->state != IPS_OFFLINE) - sc->timer = timeout(ips_timeout, sc, 10*hz); - mtx_unlock(&sc->queue_mtx); + callout_reset(&sc->timer, 10 * hz, ips_timeout, sc); } /* check card and initialize it */ int ips_adapter_init(ips_softc_t *sc) { int i; DEVICE_PRINTF(1,sc->dev, "initializing\n"); if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, /* alignemnt */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ IPS_COMMAND_LEN + IPS_MAX_SG_LEN, /* numsegs */ 1, /* maxsegsize*/ IPS_COMMAND_LEN + IPS_MAX_SG_LEN, /* flags */ 0, /* lockfunc */ NULL, /* lockarg */ NULL, &sc->command_dmatag) != 0) { device_printf(sc->dev, "can't alloc command dma tag\n"); goto error; } if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, /* alignemnt */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ IPS_MAX_IOBUF_SIZE, /* numsegs */ IPS_MAX_SG_ELEMENTS, /* maxsegsize*/ IPS_MAX_IOBUF_SIZE, /* flags */ 0, /* lockfunc */ busdma_lock_mutex, /* lockarg */ &sc->queue_mtx, &sc->sg_dmatag) != 0) { device_printf(sc->dev, "can't alloc SG dma tag\n"); goto error; } /* create one command buffer until we know how many commands this card can handle */ sc->max_cmds = 1; ips_cmdqueue_init(sc); - callout_handle_init(&sc->timer); if(sc->ips_adapter_reinit(sc, 0)) goto error; /* initialize ffdc values */ microtime(&sc->ffdc_resettime); sc->ffdc_resetcount = 1; if ((i = ips_ffdc_reset(sc)) != 0) { device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i); goto error; } if ((i = ips_get_adapter_info(sc)) != 0) { device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i); goto error; } ips_update_nvram(sc); /* no error check as failure doesn't matter */ if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){ device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]); } if ((i = ips_get_drive_info(sc)) != 0) { device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i); goto error; } ips_cmdqueue_free(sc); if(sc->adapter_info.max_concurrent_cmds) sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds); else sc->max_cmds = 32; if(ips_cmdqueue_init(sc)){ device_printf(sc->dev, "failed to initialize command buffers\n"); goto error; } sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev), UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "ips%d", device_get_unit(sc->dev)); sc->device_file->si_drv1 = sc; ips_diskdev_init(sc); - sc->timer = timeout(ips_timeout, sc, 10*hz); + callout_reset(&sc->timer, 10 * hz, ips_timeout, sc); return 0; error: ips_adapter_free(sc); return ENXIO; } /* see if we should reinitialize the card and wait for it to timeout or complete initialization */ int ips_morpheus_reinit(ips_softc_t *sc, int force) { u_int32_t tmp; int i; tmp = ips_read_4(sc, MORPHEUS_REG_OISR); if(!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) && (ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp){ ips_write_4(sc, MORPHEUS_REG_OIMR, 0); return 0; } ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff); ips_read_4(sc, MORPHEUS_REG_OIMR); device_printf(sc->dev, "resetting adapter, this may take up to 5 minutes\n"); ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000); DELAY(5000000); ips_read_4(sc, MORPHEUS_REG_OIMR); tmp = ips_read_4(sc, MORPHEUS_REG_OISR); for(i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++){ DELAY(1000000); DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i); tmp = ips_read_4(sc, MORPHEUS_REG_OISR); } if(tmp & MORPHEUS_BIT_POST1) ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1); if( i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK){ device_printf(sc->dev,"Adapter error during initialization.\n"); return 1; } for(i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++){ DELAY(1000000); DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i); tmp = ips_read_4(sc, MORPHEUS_REG_OISR); } if(tmp & MORPHEUS_BIT_POST2) ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2); if(i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)){ device_printf(sc->dev, "adapter failed config check\n"); return 1; } ips_write_4(sc, MORPHEUS_REG_OIMR, 0); if(force && ips_clear_adapter(sc)){ device_printf(sc->dev, "adapter clear failed\n"); return 1; } return 0; } /* clean up so we can unload the driver. */ int ips_adapter_free(ips_softc_t *sc) { int error = 0; if(sc->state & IPS_DEV_OPEN) return EBUSY; if((error = ips_diskdev_free(sc))) return error; if(ips_cmdqueue_free(sc)){ device_printf(sc->dev, "trying to exit when command queue is not empty!\n"); return EBUSY; } DEVICE_PRINTF(1, sc->dev, "free\n"); - untimeout(ips_timeout, sc, sc->timer); + callout_drain(&sc->timer); if(sc->sg_dmatag) bus_dma_tag_destroy(sc->sg_dmatag); if(sc->command_dmatag) bus_dma_tag_destroy(sc->command_dmatag); if(sc->device_file) destroy_dev(sc->device_file); return 0; } static __inline int ips_morpheus_check_intr(ips_softc_t *sc) { int cmdnumber; ips_cmd_status_t status; ips_command_t *command; int found = 0; u_int32_t oisr; oisr = ips_read_4(sc, MORPHEUS_REG_OISR); PRINTF(9, "interrupt registers out:%x\n", oisr); if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){ DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n"); return (0); } while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){ cmdnumber = status.fields.command_id; command = &sc->commandarray[cmdnumber]; command->status.value = status.value; command->timeout = 0; command->callback(command); found = 1; } return (found); } void ips_morpheus_intr(void *void_sc) { ips_softc_t *sc = void_sc; mtx_lock(&sc->queue_mtx); ips_morpheus_check_intr(sc); mtx_unlock(&sc->queue_mtx); } void ips_morpheus_poll(ips_command_t *command) { uint32_t ts; /* * Locks are not used here because this is only called during * crashdumps. */ ts = time_second + command->timeout; while ((command->timeout != 0) && (ips_morpheus_check_intr(command->sc) == 0) && (ts > time_second)) DELAY(1000); } void ips_issue_morpheus_cmd(ips_command_t *command) { /* hmmm, is there a cleaner way to do this? */ if(command->sc->state & IPS_OFFLINE){ ips_set_error(command, EINVAL); command->callback(command); return; } command->timeout = 10; ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr); } static void ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,int segnum, int error) { ips_copper_queue_t *queue = queueptr; if(error){ return; } queue->base_phys_addr = segments[0].ds_addr; } static int ips_copperhead_queue_init(ips_softc_t *sc) { int error; bus_dma_tag_t dmatag; bus_dmamap_t dmamap; if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, /* alignemnt */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ sizeof(ips_copper_queue_t), /* numsegs */ 1, /* maxsegsize*/ sizeof(ips_copper_queue_t), /* flags */ 0, /* lockfunc */ NULL, /* lockarg */ NULL, &dmatag) != 0) { device_printf(sc->dev, "can't alloc dma tag for statue queue\n"); error = ENOMEM; return error; } if(bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue), BUS_DMA_NOWAIT, &dmamap)){ error = ENOMEM; goto exit; } bzero(sc->copper_queue, sizeof(ips_copper_queue_t)); sc->copper_queue->dmatag = dmatag; sc->copper_queue->dmamap = dmamap; sc->copper_queue->nextstatus = 1; bus_dmamap_load(dmatag, dmamap, &(sc->copper_queue->status[0]), IPS_MAX_CMD_NUM * 4, ips_copperhead_queue_callback, sc->copper_queue, BUS_DMA_NOWAIT); if(sc->copper_queue->base_phys_addr == 0){ error = ENOMEM; goto exit; } ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr); ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr + IPS_MAX_CMD_NUM * 4); ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4); ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr); return 0; exit: if (sc->copper_queue != NULL) bus_dmamem_free(dmatag, sc->copper_queue, dmamap); bus_dma_tag_destroy(dmatag); return error; } /* see if we should reinitialize the card and wait for it to timeout or complete initialization FIXME */ int ips_copperhead_reinit(ips_softc_t *sc, int force) { int i, j; u_int32_t postcode = 0, configstatus = 0; ips_write_1(sc, COPPER_REG_SCPR, 0x80); ips_write_1(sc, COPPER_REG_SCPR, 0); device_printf(sc->dev, "reinitializing adapter, this could take several minutes.\n"); for(j = 0; j < 2; j++){ postcode <<= 8; for(i = 0; i < 45; i++){ if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){ postcode |= ips_read_1(sc, COPPER_REG_ISPR); ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT); break; } else DELAY(1000000); } if(i == 45) return 1; } for(j = 0; j < 2; j++){ configstatus <<= 8; for(i = 0; i < 240; i++){ if(ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT){ configstatus |= ips_read_1(sc, COPPER_REG_ISPR); ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT); break; } else DELAY(1000000); } if(i == 240) return 1; } for(i = 0; i < 240; i++){ if(!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT)){ break; } else DELAY(1000000); } if(i == 240) return 1; ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT); ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT); ips_copperhead_queue_init(sc); ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT); i = ips_read_1(sc, COPPER_REG_SCPR); ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT); if(!configstatus){ device_printf(sc->dev, "adapter initialization failed\n"); return 1; } if(force && ips_clear_adapter(sc)){ device_printf(sc->dev, "adapter clear failed\n"); return 1; } return 0; } static u_int32_t ips_copperhead_cmd_status(ips_softc_t *sc) { u_int32_t value; int statnum = sc->copper_queue->nextstatus++; if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM) sc->copper_queue->nextstatus = 0; value = sc->copper_queue->status[statnum]; ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr + 4 * statnum); return value; } void ips_copperhead_intr(void *void_sc) { ips_softc_t *sc = (ips_softc_t *)void_sc; int cmdnumber; ips_cmd_status_t status; mtx_lock(&sc->queue_mtx); while(ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT){ status.value = ips_copperhead_cmd_status(sc); cmdnumber = status.fields.command_id; sc->commandarray[cmdnumber].status.value = status.value; sc->commandarray[cmdnumber].timeout = 0; sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber])); PRINTF(9, "ips: got command %d\n", cmdnumber); } mtx_unlock(&sc->queue_mtx); return; } void ips_issue_copperhead_cmd(ips_command_t *command) { int i; /* hmmm, is there a cleaner way to do this? */ if(command->sc->state & IPS_OFFLINE){ ips_set_error(command, EINVAL); command->callback(command); return; } command->timeout = 10; for(i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT; i++ ){ if( i == 20){ printf("sem bit still set, can't send a command\n"); return; } DELAY(500);/* need to do a delay here */ } ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr); ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START); } void ips_copperhead_poll(ips_command_t *command) { printf("ips: cmd polling not implemented for copperhead devices\n"); } Index: head/sys/dev/ips/ips.h =================================================================== --- head/sys/dev/ips/ips.h (revision 274487) +++ head/sys/dev/ips/ips.h (revision 274488) @@ -1,188 +1,186 @@ /*- * Copyright (c) 2002 Adaptec Inc. * All rights reserved. * * Written by: David Jeffery * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _IPS_H #define _IPS_H #ifdef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DECLARE(M_IPSBUF); /* * IPS MACROS */ -#define ips_read_1(sc,offset) bus_space_read_1(sc->bustag, sc->bushandle, offset) -#define ips_read_2(sc,offset) bus_space_read_2(sc->bustag, sc->bushandle, offset) -#define ips_read_4(sc,offset) bus_space_read_4(sc->bustag, sc->bushandle, offset) +#define ips_read_1(sc,offset) bus_read_1(sc->iores, offset) +#define ips_read_2(sc,offset) bus_read_2(sc->iores, offset) +#define ips_read_4(sc,offset) bus_read_4(sc->iores, offset) -#define ips_write_1(sc,offset,value) bus_space_write_1(sc->bustag, sc->bushandle, offset, value) -#define ips_write_2(sc,offset,value) bus_space_write_2(sc->bustag, sc->bushandle, offset, value) -#define ips_write_4(sc,offset,value) bus_space_write_4(sc->bustag, sc->bushandle, offset, value) +#define ips_write_1(sc,offset,value) bus_write_1(sc->iores, offset, value) +#define ips_write_2(sc,offset,value) bus_write_2(sc->iores, offset, value) +#define ips_write_4(sc,offset,value) bus_write_4(sc->iores, offset, value) /* this is ugly. It zeros the end elements in an ips_command_t struct starting with the status element */ #define clear_ips_command(command) bzero(&((command)->status), (unsigned long)(&(command)[1])-(unsigned long)&((command)->status)) #define ips_read_request(iobuf) ((iobuf)->bio_cmd == BIO_READ) #define COMMAND_ERROR(command) (((command)->status.fields.basic_status & IPS_GSC_STATUS_MASK) >= IPS_MIN_ERROR) #define ips_set_error(command, error) do { \ (command)->status.fields.basic_status = IPS_DRV_ERROR; \ (command)->status.fields.reserved = ((error) & 0x0f); \ } while (0) #ifndef IPS_DEBUG #define DEVICE_PRINTF(x...) #define PRINTF(x...) #else #define DEVICE_PRINTF(level,x...) if(IPS_DEBUG >= level)device_printf(x) #define PRINTF(level,x...) if(IPS_DEBUG >= level)printf(x) #endif struct ips_softc; typedef struct { u_int32_t status[IPS_MAX_CMD_NUM]; u_int32_t base_phys_addr; int nextstatus; bus_dma_tag_t dmatag; bus_dmamap_t dmamap; } ips_copper_queue_t; /* used to keep track of current commands to the card */ typedef struct ips_command{ u_int8_t command_number; u_int8_t id; u_int8_t timeout; struct ips_softc * sc; bus_dma_tag_t data_dmatag; bus_dmamap_t data_dmamap; bus_dmamap_t command_dmamap; void * command_buffer; u_int32_t command_phys_addr;/*WARNING! must be changed if 64bit addressing ever used*/ ips_cmd_status_t status; SLIST_ENTRY(ips_command) next; void * data_buffer; void * arg; void (* callback)(struct ips_command *command); }ips_command_t; typedef struct ips_softc{ struct resource * iores; struct resource * irqres; struct intr_config_hook ips_ich; int configured; int state; int iotype; int rid; int irqrid; void * irqcookie; - bus_space_tag_t bustag; - bus_space_handle_t bushandle; bus_dma_tag_t adapter_dmatag; bus_dma_tag_t command_dmatag; bus_dma_tag_t sg_dmatag; device_t dev; struct cdev *device_file; - struct callout_handle timer; + struct callout timer; u_int16_t adapter_type; ips_adapter_info_t adapter_info; device_t diskdev[IPS_MAX_NUM_DRIVES]; ips_drive_t drives[IPS_MAX_NUM_DRIVES]; u_int8_t drivecount; u_int16_t ffdc_resetcount; struct timeval ffdc_resettime; u_int8_t next_drive; u_int8_t max_cmds; volatile u_int8_t used_commands; ips_command_t *commandarray; ips_command_t *staticcmd; SLIST_HEAD(command_list, ips_command) free_cmd_list; int (* ips_adapter_reinit)(struct ips_softc *sc, int force); void (* ips_adapter_intr)(void *sc); void (* ips_issue_cmd)(ips_command_t *command); void (* ips_poll_cmd)(ips_command_t *command); ips_copper_queue_t * copper_queue; struct mtx queue_mtx; struct bio_queue_head queue; struct sema cmd_sema; }ips_softc_t; /* function defines from ips_ioctl.c */ extern int ips_ioctl_request(ips_softc_t *sc, u_long ioctl_cmd, caddr_t addr, int32_t flags); /* function defines from ips_disk.c */ extern void ipsd_finish(struct bio *iobuf); /* function defines from ips_commands.c */ extern int ips_flush_cache(ips_softc_t *sc); extern void ips_start_io_request(ips_softc_t *sc); extern int ips_get_drive_info(ips_softc_t *sc); extern int ips_get_adapter_info(ips_softc_t *sc); extern int ips_ffdc_reset(ips_softc_t *sc); extern int ips_update_nvram(ips_softc_t *sc); extern int ips_clear_adapter(ips_softc_t *sc); /* function defines from ips.c */ extern int ips_get_free_cmd(ips_softc_t *sc, ips_command_t **command, unsigned long flags); extern void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command); extern int ips_adapter_init(ips_softc_t *sc); extern int ips_morpheus_reinit(ips_softc_t *sc, int force); extern int ips_adapter_free(ips_softc_t *sc); extern void ips_morpheus_intr(void *sc); extern void ips_issue_morpheus_cmd(ips_command_t *command); extern void ips_morpheus_poll(ips_command_t *command); extern int ips_copperhead_reinit(ips_softc_t *sc, int force); extern void ips_copperhead_intr(void *sc); extern void ips_issue_copperhead_cmd(ips_command_t *command); extern void ips_copperhead_poll(ips_command_t *command); #endif #endif Index: head/sys/dev/ips/ips_pci.c =================================================================== --- head/sys/dev/ips/ips_pci.c (revision 274487) +++ head/sys/dev/ips/ips_pci.c (revision 274488) @@ -1,233 +1,221 @@ /*- * Copyright (c) 2002 Adaptec Inc. * All rights reserved. * * Written by: David Jeffery * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include static int ips_pci_free(ips_softc_t *sc); static void ips_intrhook(void *arg); static int ips_pci_probe(device_t dev) { if ((pci_get_vendor(dev) == IPS_VENDOR_ID) && (pci_get_device(dev) == IPS_MORPHEUS_DEVICE_ID)) { device_set_desc(dev, "IBM ServeRAID Adapter"); return (BUS_PROBE_DEFAULT); } else if ((pci_get_vendor(dev) == IPS_VENDOR_ID) && (pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID)) { device_set_desc(dev, "IBM ServeRAID Adapter"); return (BUS_PROBE_DEFAULT); } else if ((pci_get_vendor(dev) == IPS_VENDOR_ID_ADAPTEC) && (pci_get_device(dev) == IPS_MARCO_DEVICE_ID)) { device_set_desc(dev, "Adaptec ServeRAID Adapter"); return (BUS_PROBE_DEFAULT); } return(ENXIO); } static int ips_pci_attach(device_t dev) { ips_softc_t *sc; - - if (resource_disabled(device_get_name(dev), device_get_unit(dev))) { - device_printf(dev, "device is disabled\n"); - /* but return 0 so the !$)$)*!$*) unit isn't reused */ - return (0); - } DEVICE_PRINTF(1, dev, "in attach.\n"); sc = (ips_softc_t *)device_get_softc(dev); - if(!sc){ - printf("how is sc NULL?!\n"); - return (ENXIO); - } - bzero(sc, sizeof(ips_softc_t)); sc->dev = dev; + mtx_init(&sc->queue_mtx, "IPS bioqueue lock", NULL, MTX_DEF); + sema_init(&sc->cmd_sema, 0, "IPS Command Semaphore"); + callout_init_mtx(&sc->timer, &sc->queue_mtx, 0); if(pci_get_device(dev) == IPS_MORPHEUS_DEVICE_ID){ sc->ips_adapter_reinit = ips_morpheus_reinit; sc->ips_adapter_intr = ips_morpheus_intr; sc->ips_issue_cmd = ips_issue_morpheus_cmd; sc->ips_poll_cmd = ips_morpheus_poll; } else if(pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID){ sc->ips_adapter_reinit = ips_copperhead_reinit; sc->ips_adapter_intr = ips_copperhead_intr; sc->ips_issue_cmd = ips_issue_copperhead_cmd; sc->ips_poll_cmd = ips_copperhead_poll; } else if (pci_get_device(dev) == IPS_MARCO_DEVICE_ID){ sc->ips_adapter_reinit = ips_morpheus_reinit; sc->ips_adapter_intr = ips_morpheus_intr; sc->ips_issue_cmd = ips_issue_morpheus_cmd; sc->ips_poll_cmd = ips_morpheus_poll; } else goto error; /* make sure busmastering is on */ pci_enable_busmaster(dev); - /* seting up io space */ + /* setting up io space */ sc->iores = NULL; PRINTF(10, "trying MEMIO\n"); if(pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID) sc->rid = PCIR_BAR(1); else sc->rid = PCIR_BAR(0); sc->iotype = SYS_RES_MEMORY; sc->iores = bus_alloc_resource_any(dev, sc->iotype, &sc->rid, RF_ACTIVE); if(!sc->iores){ PRINTF(10, "trying PORTIO\n"); sc->rid = PCIR_BAR(0); sc->iotype = SYS_RES_IOPORT; sc->iores = bus_alloc_resource_any(dev, sc->iotype, &sc->rid, RF_ACTIVE); } if(sc->iores == NULL){ device_printf(dev, "resource allocation failed\n"); return (ENXIO); } - sc->bustag = rman_get_bustag(sc->iores); - sc->bushandle = rman_get_bushandle(sc->iores); /*allocate an interrupt. when does the irq become active? after leaving attach? */ sc->irqrid = 0; if(!(sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid, RF_SHAREABLE | RF_ACTIVE))){ device_printf(dev, "irq allocation failed\n"); goto error; } if(bus_setup_intr(dev, sc->irqres, INTR_TYPE_BIO|INTR_MPSAFE, NULL, sc->ips_adapter_intr, sc, &sc->irqcookie)){ device_printf(dev, "irq setup failed\n"); goto error; } if (bus_dma_tag_create( /* PCI parent */bus_get_dma_tag(dev), /* alignemnt */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ BUS_SPACE_MAXSIZE_32BIT, /* numsegs */ IPS_MAX_SG_ELEMENTS, /* maxsegsize*/ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, /* lockfunc */ NULL, /* lockarg */ NULL, &sc->adapter_dmatag) != 0) { - printf("IPS can't alloc dma tag\n"); + device_printf(dev, "can't alloc dma tag\n"); goto error; } sc->ips_ich.ich_func = ips_intrhook; sc->ips_ich.ich_arg = sc; - mtx_init(&sc->queue_mtx, "IPS bioqueue lock", NULL, MTX_DEF); - sema_init(&sc->cmd_sema, 0, "IPS Command Semaphore"); bioq_init(&sc->queue); if (config_intrhook_establish(&sc->ips_ich) != 0) { printf("IPS can't establish configuration hook\n"); goto error; } return 0; error: ips_pci_free(sc); return (ENXIO); } static void ips_intrhook(void *arg) { struct ips_softc *sc = (struct ips_softc *)arg; config_intrhook_disestablish(&sc->ips_ich); if (ips_adapter_init(sc)) ips_pci_free(sc); else sc->configured = 1; } static int ips_pci_free(ips_softc_t *sc) { if(sc->adapter_dmatag) bus_dma_tag_destroy(sc->adapter_dmatag); if(sc->irqcookie) bus_teardown_intr(sc->dev, sc->irqres, sc->irqcookie); if(sc->irqres) bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqrid, sc->irqres); if(sc->iores) bus_release_resource(sc->dev, sc->iotype, sc->rid, sc->iores); sc->configured = 0; mtx_destroy(&sc->queue_mtx); sema_destroy(&sc->cmd_sema); return 0; } static int ips_pci_detach(device_t dev) { ips_softc_t *sc; DEVICE_PRINTF(1, dev, "detaching ServeRaid\n"); sc = (ips_softc_t *) device_get_softc(dev); if (sc->configured) { sc->configured = 0; ips_flush_cache(sc); if(ips_adapter_free(sc)) return EBUSY; ips_pci_free(sc); bioq_flush(&sc->queue, NULL, ENXIO); } return 0; } static int ips_pci_shutdown(device_t dev) { ips_softc_t *sc = (ips_softc_t *) device_get_softc(dev); if (sc->configured) { ips_flush_cache(sc); } return 0; } static device_method_t ips_driver_methods[] = { DEVMETHOD(device_probe, ips_pci_probe), DEVMETHOD(device_attach, ips_pci_attach), DEVMETHOD(device_detach, ips_pci_detach), DEVMETHOD(device_shutdown, ips_pci_shutdown), {0,0} }; static driver_t ips_pci_driver = { "ips", ips_driver_methods, sizeof(ips_softc_t), }; static devclass_t ips_devclass; DRIVER_MODULE(ips, pci, ips_pci_driver, ips_devclass, 0, 0);