diff options
Diffstat (limited to 'sys/dev/ips')
| -rw-r--r-- | sys/dev/ips/ips.c | 749 | ||||
| -rw-r--r-- | sys/dev/ips/ips.h | 468 | ||||
| -rw-r--r-- | sys/dev/ips/ips_commands.c | 670 | ||||
| -rw-r--r-- | sys/dev/ips/ips_disk.c | 302 | ||||
| -rw-r--r-- | sys/dev/ips/ips_disk.h | 65 | ||||
| -rw-r--r-- | sys/dev/ips/ips_ioctl.c | 166 | ||||
| -rw-r--r-- | sys/dev/ips/ips_ioctl.h | 61 | ||||
| -rw-r--r-- | sys/dev/ips/ips_pci.c | 233 |
8 files changed, 2714 insertions, 0 deletions
diff --git a/sys/dev/ips/ips.c b/sys/dev/ips/ips.c new file mode 100644 index 000000000000..5f2f8f65ede0 --- /dev/null +++ b/sys/dev/ips/ips.c @@ -0,0 +1,749 @@ +/*- + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dev/ips/ips.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <machine/clock.h> + +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; + sc->state |= IPS_DEV_OPEN; + return 0; +} + +static int ips_close(struct cdev *dev, int flags, int fmt, struct thread *td) +{ + ips_softc_t *sc = dev->si_drv1; + sc->state &= ~IPS_DEV_OPEN; + + 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); + 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"); + } + command[i].status.value = IPS_ERROR_STATUS; + 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); +} + +/* 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); + 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); + pci_read_config(sc->dev, 0, 4); + + 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); + + 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){ + command->status.value = IPS_ERROR_STATUS; + 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; + goto exit; + } + 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: + 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){ + command->status.value = IPS_ERROR_STATUS; + 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"); +} diff --git a/sys/dev/ips/ips.h b/sys/dev/ips/ips.h new file mode 100644 index 000000000000..53a30819e2e0 --- /dev/null +++ b/sys/dev/ips/ips.h @@ -0,0 +1,468 @@ +/*- + * 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$ + */ + + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/bio.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/sema.h> +#include <sys/time.h> + +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +MALLOC_DECLARE(M_IPSBUF); + +/* + * IPS CONSTANTS + */ +#define IPS_VENDOR_ID 0x1014 +#define IPS_VENDOR_ID_ADAPTEC 0x9005 +#define IPS_MORPHEUS_DEVICE_ID 0x01BD +#define IPS_COPPERHEAD_DEVICE_ID 0x002E +#define IPS_MARCO_DEVICE_ID 0x0250 +#define IPS_CSL 0xff +#define IPS_POCL 0x30 + +/* amounts of memory to allocate for certain commands */ +#define IPS_ADAPTER_INFO_LEN (sizeof(ips_adapter_info_t)) +#define IPS_DRIVE_INFO_LEN (sizeof(ips_drive_info_t)) +#define IPS_COMMAND_LEN 24 +#define IPS_MAX_SG_LEN (sizeof(ips_sg_element_t) * IPS_MAX_SG_ELEMENTS) +#define IPS_NVRAM_PAGE_SIZE 128 +/* various flags */ +#define IPS_STATIC_FLAG 0x01 + +/* states for the card to be in */ +#define IPS_DEV_OPEN 0x01 +#define IPS_TIMEOUT 0x02 /* command time out, need reset */ +#define IPS_OFFLINE 0x04 /* can't reset card/card failure */ +#define IPS_STATIC_BUSY 0x08 + +/* max number of commands set to something low for now */ +#define IPS_MAX_CMD_NUM 128 +#define IPS_MAX_NUM_DRIVES 8 +#define IPS_MAX_SG_ELEMENTS 32 +#define IPS_MAX_IOBUF_SIZE (64 * 1024) +#define IPS_BLKSIZE 512 + +/* logical drive states */ + +#define IPS_LD_OFFLINE 0x02 +#define IPS_LD_OKAY 0x03 +#define IPS_LD_DEGRADED 0x04 +#define IPS_LD_FREE 0x00 +#define IPS_LD_SYS 0x06 +#define IPS_LD_CRS 0x24 + +/* register offsets */ +#define MORPHEUS_REG_OMR0 0x0018 /* Outbound Msg. Reg. 0 */ +#define MORPHEUS_REG_OMR1 0x001C /* Outbound Msg. Reg. 1 */ +#define MORPHEUS_REG_IDR 0x0020 /* Inbound Doorbell Reg. */ +#define MORPHEUS_REG_IISR 0x0024 /* Inbound IRQ Status Reg. */ +#define MORPHEUS_REG_IIMR 0x0028 /* Inbound IRQ Mask Reg. */ +#define MORPHEUS_REG_OISR 0x0030 /* Outbound IRQ Status Reg. */ +#define MORPHEUS_REG_OIMR 0x0034 /* Outbound IRQ Status Reg. */ +#define MORPHEUS_REG_IQPR 0x0040 /* Inbound Queue Port Reg. */ +#define MORPHEUS_REG_OQPR 0x0044 /* Outbound Queue Port Reg. */ + +#define COPPER_REG_SCPR 0x05 /* Subsystem Ctrl. Port Reg. */ +#define COPPER_REG_ISPR 0x06 /* IRQ Status Port Reg. */ +#define COPPER_REG_CBSP 0x07 /* ? Reg. */ +#define COPPER_REG_HISR 0x08 /* Host IRQ Status Reg. */ +#define COPPER_REG_CCSAR 0x10 /* Cmd. Channel Sys Addr Reg.*/ +#define COPPER_REG_CCCR 0x14 /* Cmd. Channel Ctrl. Reg. */ +#define COPPER_REG_SQHR 0x20 /* Status Queue Head Reg. */ +#define COPPER_REG_SQTR 0x24 /* Status Queue Tail Reg. */ +#define COPPER_REG_SQER 0x28 /* Status Queue End Reg. */ +#define COPPER_REG_SQSR 0x2C /* Status Queue Start Reg. */ + +/* bit definitions */ +#define MORPHEUS_BIT_POST1 0x01 +#define MORPHEUS_BIT_POST2 0x02 +#define MORPHEUS_BIT_CMD_IRQ 0x08 + +#define COPPER_CMD_START 0x101A +#define COPPER_SEM_BIT 0x08 +#define COPPER_EI_BIT 0x80 +#define COPPER_EBM_BIT 0x02 +#define COPPER_RESET_BIT 0x80 +#define COPPER_GHI_BIT 0x04 +#define COPPER_SCE_BIT 0x01 +#define COPPER_OP_BIT 0x01 +#define COPPER_ILE_BIT 0x10 + +/* status defines */ +#define IPS_POST1_OK 0x8000 +#define IPS_POST2_OK 0x000f + +/* command op codes */ +#define IPS_READ_CMD 0x02 +#define IPS_WRITE_CMD 0x03 +#define IPS_ADAPTER_INFO_CMD 0x05 +#define IPS_CACHE_FLUSH_CMD 0x0A +#define IPS_REBUILD_STATUS_CMD 0x0C +#define IPS_ERROR_TABLE_CMD 0x17 +#define IPS_DRIVE_INFO_CMD 0x19 +#define IPS_SUBSYS_PARAM_CMD 0x40 +#define IPS_CONFIG_SYNC_CMD 0x58 +#define IPS_SG_READ_CMD 0x82 +#define IPS_SG_WRITE_CMD 0x83 +#define IPS_RW_NVRAM_CMD 0xBC +#define IPS_FFDC_CMD 0xD7 + +/* error information returned by the adapter */ +#define IPS_MIN_ERROR 0x02 +#define IPS_ERROR_STATUS 0x13000200 /* ahh, magic numbers */ + +#define IPS_OS_FREEBSD 8 +#define IPS_VERSION_MAJOR "0.90" +#define IPS_VERSION_MINOR ".10" + +/* Adapter Types */ +#define IPS_ADAPTER_COPPERHEAD 0x01 +#define IPS_ADAPTER_COPPERHEAD2 0x02 +#define IPS_ADAPTER_COPPERHEADOB1 0x03 +#define IPS_ADAPTER_COPPERHEADOB2 0x04 +#define IPS_ADAPTER_CLARINET 0x05 +#define IPS_ADAPTER_CLARINETLITE 0x06 +#define IPS_ADAPTER_TROMBONE 0x07 +#define IPS_ADAPTER_MORPHEUS 0x08 +#define IPS_ADAPTER_MORPHEUSLITE 0x09 +#define IPS_ADAPTER_NEO 0x0A +#define IPS_ADAPTER_NEOLITE 0x0B +#define IPS_ADAPTER_SARASOTA2 0x0C +#define IPS_ADAPTER_SARASOTA1 0x0D +#define IPS_ADAPTER_MARCO 0x0E +#define IPS_ADAPTER_SEBRING 0x0F +#define IPS_ADAPTER_7T 0x10 +#define IPS_ADAPTER_7K 0x11 +#define IPS_ADAPTER_7M 0x12 +#define IPS_ADAPTER_MAX_T IPS_ADAPTER_7M + +/* values for ffdc_settime (from gmtime) */ +#define IPS_SECSPERMIN 60 +#define IPS_MINSPERHOUR 60 +#define IPS_HOURSPERDAY 24 +#define IPS_DAYSPERWEEK 7 +#define IPS_DAYSPERNYEAR 365 +#define IPS_DAYSPERLYEAR 366 +#define IPS_SECSPERHOUR (IPS_SECSPERMIN * IPS_MINSPERHOUR) +#define IPS_SECSPERDAY ((long) IPS_SECSPERHOUR * IPS_HOURSPERDAY) +#define IPS_MONSPERYEAR 12 +#define IPS_EPOCH_YEAR 1970 +#define IPS_LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) +#define ips_isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) + +/* + * 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_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) + +/* 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(status) (((status)->fields.basic_status & 0x0f) >= IPS_MIN_ERROR) + +#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 +/* + * IPS STRUCTS + */ + +struct ips_softc; + +typedef struct{ + u_int8_t command; + u_int8_t id; + u_int8_t drivenum; + u_int8_t reserve2; + u_int32_t lba; + u_int32_t buffaddr; + u_int32_t reserve3; +} __attribute__ ((packed)) ips_generic_cmd; + +typedef struct{ + u_int8_t command; + u_int8_t id; + u_int8_t drivenum; + u_int8_t segnum; + u_int32_t lba; + u_int32_t buffaddr; + u_int16_t length; + u_int16_t reserve1; +} __attribute__ ((packed)) ips_io_cmd; + +typedef struct{ + u_int8_t command; + u_int8_t id; + u_int8_t pagenum; + u_int8_t rw; + u_int32_t reserve1; + u_int32_t buffaddr; + u_int32_t reserve3; +} __attribute__ ((packed)) ips_rw_nvram_cmd; + +typedef struct{ + u_int8_t command; + u_int8_t id; + u_int8_t drivenum; + u_int8_t reserve1; + u_int32_t reserve2; + u_int32_t buffaddr; + u_int32_t reserve3; +} __attribute__ ((packed)) ips_drive_cmd; + +typedef struct{ + u_int8_t command; + u_int8_t id; + u_int8_t reserve1; + u_int8_t commandtype; + u_int32_t reserve2; + u_int32_t buffaddr; + u_int32_t reserve3; +} __attribute__((packed)) ips_adapter_info_cmd; + +typedef struct{ + u_int8_t command; + u_int8_t id; + u_int8_t reset_count; + u_int8_t reset_type; + u_int8_t second; + u_int8_t minute; + u_int8_t hour; + u_int8_t day; + u_int8_t reserve1[4]; + u_int8_t month; + u_int8_t yearH; + u_int8_t yearL; + u_int8_t reserve2; +} __attribute__((packed)) ips_adapter_ffdc_cmd; + +typedef union{ + ips_generic_cmd generic_cmd; + ips_drive_cmd drive_cmd; + ips_adapter_info_cmd adapter_info_cmd; +} ips_cmd_buff_t; + +typedef struct { + u_int32_t signature; + u_int8_t reserved; + u_int8_t adapter_slot; + u_int16_t adapter_type; + u_int8_t bios_high[4]; + u_int8_t bios_low[4]; + u_int16_t reserve2; + u_int8_t reserve3; + u_int8_t operating_system; + u_int8_t driver_high[4]; + u_int8_t driver_low[4]; + u_int8_t reserve4[100]; +}__attribute__((packed)) ips_nvram_page5; + +typedef struct{ + u_int32_t addr; + u_int32_t len; +} ips_sg_element_t; + +typedef struct{ + u_int8_t drivenum; + u_int8_t merge_id; + u_int8_t raid_lvl; + u_int8_t state; + u_int32_t sector_count; +} __attribute__((packed)) ips_drive_t; + +typedef struct{ + u_int8_t drivecount; + u_int8_t reserve1; + u_int16_t reserve2; + ips_drive_t drives[IPS_MAX_NUM_DRIVES]; +}__attribute__((packed)) ips_drive_info_t; + +typedef struct{ + u_int8_t drivecount; + u_int8_t miscflags; + u_int8_t SLTflags; + u_int8_t BSTflags; + u_int8_t pwr_chg_count; + u_int8_t wrong_addr_count; + u_int8_t unident_count; + u_int8_t nvram_dev_chg_count; + u_int8_t codeblock_version[8]; + u_int8_t bootblock_version[8]; + u_int32_t drive_sector_count[IPS_MAX_NUM_DRIVES]; + u_int8_t max_concurrent_cmds; + u_int8_t max_phys_devices; + u_int16_t flash_prog_count; + u_int8_t defunct_disks; + u_int8_t rebuildflags; + u_int8_t offline_drivecount; + u_int8_t critical_drivecount; + u_int16_t config_update_count; + u_int8_t blockedflags; + u_int8_t psdn_error; + u_int16_t addr_dead_disk[4*16];/* ugly, max # channels * max # scsi devices per channel */ +}__attribute__((packed)) ips_adapter_info_t; + +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; + +typedef union { + struct { + u_int8_t reserved; + u_int8_t command_id; + u_int8_t basic_status; + u_int8_t extended_status; + } fields; + volatile u_int32_t value; +} ips_cmd_status_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; + 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); + diff --git a/sys/dev/ips/ips_commands.c b/sys/dev/ips/ips_commands.c new file mode 100644 index 000000000000..bbaed7168bc0 --- /dev/null +++ b/sys/dev/ips/ips_commands.c @@ -0,0 +1,670 @@ +/*- + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dev/ips/ips.h> + +/* + * This is an interrupt callback. It is called from + * interrupt context when the adapter has completed the + * command. This very generic callback simply stores + * the command's return value in command->arg and wake's + * up anyone waiting on the command. + */ +static void ips_wakeup_callback(ips_command_t *command) +{ + bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_POSTWRITE); + sema_post(&command->sc->cmd_sema); +} +/* Below are a series of functions for sending an IO request + * to the adapter. The flow order is: start, send, callback, finish. + * The caller must have already assembled an iorequest struct to hold + * the details of the IO request. */ +static void ips_io_request_finish(ips_command_t *command) +{ + + struct bio *iobuf = command->arg; + if(ips_read_request(iobuf)) { + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_POSTREAD); + } else { + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_POSTWRITE); + } + bus_dmamap_unload(command->data_dmatag, command->data_dmamap); + if(COMMAND_ERROR(&command->status)){ + iobuf->bio_flags |=BIO_ERROR; + iobuf->bio_error = EIO; + } + ips_insert_free_cmd(command->sc, command); + ipsd_finish(iobuf); +} + +static void ips_io_request_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error) +{ + ips_softc_t *sc; + ips_command_t *command = cmdptr; + ips_sg_element_t *sg_list; + ips_io_cmd *command_struct; + struct bio *iobuf = command->arg; + int i, length = 0; + u_int8_t cmdtype; + + sc = command->sc; + if(error){ + printf("ips: error = %d in ips_sg_request_callback\n", error); + bus_dmamap_unload(command->data_dmatag, command->data_dmamap); + iobuf->bio_flags |= BIO_ERROR; + iobuf->bio_error = ENOMEM; + ips_insert_free_cmd(sc, command); + ipsd_finish(iobuf); + return; + } + command_struct = (ips_io_cmd *)command->command_buffer; + command_struct->id = command->id; + command_struct->drivenum = (uintptr_t)iobuf->bio_driver1; + if(segnum != 1){ + if(ips_read_request(iobuf)) + cmdtype = IPS_SG_READ_CMD; + else + cmdtype = IPS_SG_WRITE_CMD; + command_struct->segnum = segnum; + sg_list = (ips_sg_element_t *)((u_int8_t *) + command->command_buffer + IPS_COMMAND_LEN); + for(i = 0; i < segnum; i++){ + sg_list[i].addr = segments[i].ds_addr; + sg_list[i].len = segments[i].ds_len; + length += segments[i].ds_len; + } + command_struct->buffaddr = + (u_int32_t)command->command_phys_addr + IPS_COMMAND_LEN; + } else { + if(ips_read_request(iobuf)) + cmdtype = IPS_READ_CMD; + else + cmdtype = IPS_WRITE_CMD; + command_struct->buffaddr = segments[0].ds_addr; + length = segments[0].ds_len; + } + command_struct->command = cmdtype; + command_struct->lba = iobuf->bio_pblkno; + length = (length + IPS_BLKSIZE - 1)/IPS_BLKSIZE; + command_struct->length = length; + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + if(ips_read_request(iobuf)) { + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_PREREAD); + } else { + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_PREWRITE); + } + PRINTF(10, "ips test: command id: %d segments: %d " + "pblkno: %lld length: %d, ds_len: %d\n", command->id, segnum, + iobuf->bio_pblkno, + length, segments[0].ds_len); + + sc->ips_issue_cmd(command); + return; +} + +static int ips_send_io_request(ips_command_t *command, struct bio *iobuf) +{ + command->callback = ips_io_request_finish; + command->arg = iobuf; + PRINTF(10, "ips test: : bcount %ld\n", iobuf->bio_bcount); + bus_dmamap_load(command->data_dmatag, command->data_dmamap, + iobuf->bio_data, iobuf->bio_bcount, + ips_io_request_callback, command, 0); + return 0; +} + +void ips_start_io_request(ips_softc_t *sc) +{ + struct bio *iobuf; + ips_command_t *command; + + iobuf = bioq_first(&sc->queue); + if(!iobuf) + return; + + if (ips_get_free_cmd(sc, &command, 0)) + return; + + bioq_remove(&sc->queue, iobuf); + ips_send_io_request(command, iobuf); + return; +} + +/* Below are a series of functions for sending an adapter info request + * to the adapter. The flow order is: get, send, callback. It uses + * the generic finish callback at the top of this file. + * This can be used to get configuration/status info from the card */ +static void ips_adapter_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error) +{ + ips_softc_t *sc; + ips_command_t *command = cmdptr; + ips_adapter_info_cmd *command_struct; + sc = command->sc; + if(error){ + command->status.value = IPS_ERROR_STATUS; /* a lovely error value */ + ips_insert_free_cmd(sc, command); + printf("ips: error = %d in ips_get_adapter_info\n", error); + return; + } + command_struct = (ips_adapter_info_cmd *)command->command_buffer; + command_struct->command = IPS_ADAPTER_INFO_CMD; + command_struct->id = command->id; + command_struct->buffaddr = segments[0].ds_addr; + + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_PREREAD); + sc->ips_issue_cmd(command); +} + + + +static int ips_send_adapter_info_cmd(ips_command_t *command) +{ + int error = 0; + ips_softc_t *sc = command->sc; + + 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_ADAPTER_INFO_LEN, + /* numsegs */ 1, + /* maxsegsize*/ IPS_ADAPTER_INFO_LEN, + /* flags */ 0, + /* lockfunc */ NULL, + /* lockarg */ NULL, + &command->data_dmatag) != 0) { + printf("ips: can't alloc dma tag for adapter status\n"); + error = ENOMEM; + goto exit; + } + if(bus_dmamem_alloc(command->data_dmatag, &command->data_buffer, + BUS_DMA_NOWAIT, &command->data_dmamap)){ + error = ENOMEM; + goto exit; + } + command->callback = ips_wakeup_callback; + bus_dmamap_load(command->data_dmatag, command->data_dmamap, + command->data_buffer,IPS_ADAPTER_INFO_LEN, + ips_adapter_info_callback, command, BUS_DMA_NOWAIT); + + if ((command->status.value == IPS_ERROR_STATUS) || + (sema_timedwait(&sc->cmd_sema, 30*hz) != 0)) + error = ETIMEDOUT; + + if (error == 0) { + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_POSTREAD); + memcpy(&(sc->adapter_info), command->data_buffer, + IPS_ADAPTER_INFO_LEN); + } + bus_dmamap_unload(command->data_dmatag, command->data_dmamap); + +exit: + /* I suppose I should clean up my memory allocations */ + bus_dmamem_free(command->data_dmatag, command->data_buffer, + command->data_dmamap); + bus_dma_tag_destroy(command->data_dmatag); + ips_insert_free_cmd(sc, command); + return error; +} + +int ips_get_adapter_info(ips_softc_t *sc) +{ + ips_command_t *command; + int error = 0; + + if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) > 0){ + device_printf(sc->dev, "unable to get adapter configuration\n"); + return ENXIO; + } + ips_send_adapter_info_cmd(command); + if (COMMAND_ERROR(&command->status)){ + error = ENXIO; + } + return error; +} + +/* Below are a series of functions for sending a drive info request + * to the adapter. The flow order is: get, send, callback. It uses + * the generic finish callback at the top of this file. + * This can be used to get drive status info from the card */ +static void ips_drive_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error) +{ + ips_softc_t *sc; + ips_command_t *command = cmdptr; + ips_drive_cmd *command_struct; + sc = command->sc; + if(error){ + command->status.value = IPS_ERROR_STATUS; + ips_insert_free_cmd(sc, command); + printf("ips: error = %d in ips_get_drive_info\n", error); + return; + } + command_struct = (ips_drive_cmd *)command->command_buffer; + command_struct->command = IPS_DRIVE_INFO_CMD; + command_struct->id = command->id; + command_struct->buffaddr = segments[0].ds_addr; + + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_PREREAD); + sc->ips_issue_cmd(command); +} + +static int ips_send_drive_info_cmd(ips_command_t *command) +{ + int error = 0; + ips_softc_t *sc = command->sc; + ips_drive_info_t *driveinfo; + + 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_DRIVE_INFO_LEN, + /* numsegs */ 1, + /* maxsegsize*/ IPS_DRIVE_INFO_LEN, + /* flags */ 0, + /* lockfunc */ NULL, + /* lockarg */ NULL, + &command->data_dmatag) != 0) { + printf("ips: can't alloc dma tag for drive status\n"); + error = ENOMEM; + goto exit; + } + if(bus_dmamem_alloc(command->data_dmatag, &command->data_buffer, + BUS_DMA_NOWAIT, &command->data_dmamap)){ + error = ENOMEM; + goto exit; + } + command->callback = ips_wakeup_callback; + bus_dmamap_load(command->data_dmatag, command->data_dmamap, + command->data_buffer,IPS_DRIVE_INFO_LEN, + ips_drive_info_callback, command, BUS_DMA_NOWAIT); + if ((command->status.value == IPS_ERROR_STATUS) || + (sema_timedwait(&sc->cmd_sema, 10*hz) != 0)) + error = ETIMEDOUT; + + if (error == 0) { + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_POSTREAD); + driveinfo = command->data_buffer; + memcpy(sc->drives, driveinfo->drives, sizeof(ips_drive_t) * 8); + sc->drivecount = driveinfo->drivecount; + device_printf(sc->dev, "logical drives: %d\n",sc->drivecount); + } + bus_dmamap_unload(command->data_dmatag, command->data_dmamap); + +exit: + /* I suppose I should clean up my memory allocations */ + bus_dmamem_free(command->data_dmatag, command->data_buffer, + command->data_dmamap); + bus_dma_tag_destroy(command->data_dmatag); + ips_insert_free_cmd(sc, command); + return error; + +} +int ips_get_drive_info(ips_softc_t *sc) +{ + int error = 0; + ips_command_t *command; + + if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG) > 0){ + device_printf(sc->dev, "unable to get drive configuration\n"); + return ENXIO; + } + ips_send_drive_info_cmd(command); + if(COMMAND_ERROR(&command->status)){ + error = ENXIO; + } + return error; +} + +/* Below is a pair of functions for making sure data is safely + * on disk by flushing the adapter's cache. */ +static int ips_send_flush_cache_cmd(ips_command_t *command) +{ + ips_softc_t *sc = command->sc; + ips_generic_cmd *command_struct; + + PRINTF(10,"ips test: got a command, building flush command\n"); + command->callback = ips_wakeup_callback; + command_struct = (ips_generic_cmd *)command->command_buffer; + command_struct->command = IPS_CACHE_FLUSH_CMD; + command_struct->id = command->id; + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + sc->ips_issue_cmd(command); + if (command->status.value != IPS_ERROR_STATUS) + sema_wait(&sc->cmd_sema); + ips_insert_free_cmd(sc, command); + return 0; +} + +int ips_flush_cache(ips_softc_t *sc) +{ + ips_command_t *command; + + device_printf(sc->dev, "flushing cache\n"); + if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG)){ + device_printf(sc->dev, "ERROR: unable to get a command! can't flush cache!\n"); + } + ips_send_flush_cache_cmd(command); + if(COMMAND_ERROR(&command->status)){ + device_printf(sc->dev, "ERROR: cache flush command failed!\n"); + } + return 0; +} + +/* Simplified localtime to provide timevalues for ffdc. + * Taken from libc/stdtime/localtime.c + */ +void static ips_ffdc_settime(ips_adapter_ffdc_cmd *command, time_t sctime) +{ + long days, rem, y; + int yleap, *ip, month; + int year_lengths[2] = { IPS_DAYSPERNYEAR, IPS_DAYSPERLYEAR }; + int mon_lengths[2][IPS_MONSPERYEAR] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + }; + + days = sctime / IPS_SECSPERDAY; + rem = sctime % IPS_SECSPERDAY; + + command->hour = rem / IPS_SECSPERHOUR; + rem = rem % IPS_SECSPERHOUR; + + command->minute = rem / IPS_SECSPERMIN; + command->second = rem % IPS_SECSPERMIN; + + y = IPS_EPOCH_YEAR; + while (days < 0 || days >= (long) year_lengths[yleap = ips_isleap(y)]) { + long newy; + + newy = y + days / IPS_DAYSPERNYEAR; + if (days < 0) + --newy; + days -= (newy - y) * IPS_DAYSPERNYEAR + + IPS_LEAPS_THRU_END_OF(newy - 1) - + IPS_LEAPS_THRU_END_OF(y - 1); + y = newy; + } + command->yearH = y / 100; + command->yearL = y % 100; + ip = mon_lengths[yleap]; + for(month = 0; days >= (long) ip[month]; ++month) + days = days - (long) ip[month]; + command->month = month + 1; + command->day = days + 1; +} + +static int ips_send_ffdc_reset_cmd(ips_command_t *command) +{ + ips_softc_t *sc = command->sc; + ips_adapter_ffdc_cmd *command_struct; + + PRINTF(10,"ips test: got a command, building ffdc reset command\n"); + command->callback = ips_wakeup_callback; + command_struct = (ips_adapter_ffdc_cmd *)command->command_buffer; + command_struct->command = IPS_FFDC_CMD; + command_struct->id = command->id; + command_struct->reset_count = sc->ffdc_resetcount; + command_struct->reset_type = 0x0; + ips_ffdc_settime(command_struct, sc->ffdc_resettime.tv_sec); + + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + sc->ips_issue_cmd(command); + if (command->status.value != IPS_ERROR_STATUS) + sema_wait(&sc->cmd_sema); + ips_insert_free_cmd(sc, command); + return 0; +} + +int ips_ffdc_reset(ips_softc_t *sc) +{ + ips_command_t *command; + + if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG)){ + device_printf(sc->dev, "ERROR: unable to get a command! can't send ffdc reset!\n"); + } + ips_send_ffdc_reset_cmd(command); + if(COMMAND_ERROR(&command->status)){ + device_printf(sc->dev, "ERROR: ffdc reset command failed!\n"); + } + return 0; +} + +static void ips_write_nvram(ips_command_t *command){ + ips_softc_t *sc = command->sc; + ips_rw_nvram_cmd *command_struct; + ips_nvram_page5 *nvram; + + /*FIXME check for error */ + command->callback = ips_wakeup_callback; + command_struct = (ips_rw_nvram_cmd *)command->command_buffer; + command_struct->command = IPS_RW_NVRAM_CMD; + command_struct->id = command->id; + command_struct->pagenum = 5; + command_struct->rw = 1; /*write*/ + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_POSTREAD); + nvram = command->data_buffer; + /* retrieve adapter info and save in sc */ + sc->adapter_type = nvram->adapter_type; + + strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4); + strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4); + nvram->operating_system = IPS_OS_FREEBSD; + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + sc->ips_issue_cmd(command); +} + +static void ips_read_nvram_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error) +{ + ips_softc_t *sc; + ips_command_t *command = cmdptr; + ips_rw_nvram_cmd *command_struct; + sc = command->sc; + if(error){ + command->status.value = IPS_ERROR_STATUS; + ips_insert_free_cmd(sc, command); + printf("ips: error = %d in ips_read_nvram_callback\n", error); + return; + } + command_struct = (ips_rw_nvram_cmd *)command->command_buffer; + command_struct->command = IPS_RW_NVRAM_CMD; + command_struct->id = command->id; + command_struct->pagenum = 5; + command_struct->rw = 0; + command_struct->buffaddr = segments[0].ds_addr; + + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_PREREAD); + sc->ips_issue_cmd(command); +} + +static int ips_read_nvram(ips_command_t *command) +{ + int error = 0; + ips_softc_t *sc = command->sc; + + 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_NVRAM_PAGE_SIZE, + /* numsegs */ 1, + /* maxsegsize*/ IPS_NVRAM_PAGE_SIZE, + /* flags */ 0, + /* lockfunc */ NULL, + /* lockarg */ NULL, + &command->data_dmatag) != 0) { + printf("ips: can't alloc dma tag for nvram\n"); + error = ENOMEM; + goto exit; + } + if(bus_dmamem_alloc(command->data_dmatag, &command->data_buffer, + BUS_DMA_NOWAIT, &command->data_dmamap)){ + error = ENOMEM; + goto exit; + } + command->callback = ips_write_nvram; + bus_dmamap_load(command->data_dmatag, command->data_dmamap, + command->data_buffer,IPS_NVRAM_PAGE_SIZE, + ips_read_nvram_callback, command, BUS_DMA_NOWAIT); + if ((command->status.value == IPS_ERROR_STATUS) || + (sema_timedwait(&sc->cmd_sema, 30*hz) != 0)) + error = ETIMEDOUT; + + if (error == 0) { + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_POSTWRITE); + } + bus_dmamap_unload(command->data_dmatag, command->data_dmamap); + +exit: + bus_dmamem_free(command->data_dmatag, command->data_buffer, + command->data_dmamap); + bus_dma_tag_destroy(command->data_dmatag); + ips_insert_free_cmd(sc, command); + return error; +} + +int ips_update_nvram(ips_softc_t *sc) +{ + ips_command_t *command; + + if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG)){ + device_printf(sc->dev, "ERROR: unable to get a command! can't update nvram\n"); + return 1; + } + ips_read_nvram(command); + if(COMMAND_ERROR(&command->status)){ + device_printf(sc->dev, "ERROR: nvram update command failed!\n"); + } + return 0; + + +} + + +static int ips_send_config_sync_cmd(ips_command_t *command) +{ + ips_softc_t *sc = command->sc; + ips_generic_cmd *command_struct; + + PRINTF(10,"ips test: got a command, building flush command\n"); + command->callback = ips_wakeup_callback; + command_struct = (ips_generic_cmd *)command->command_buffer; + command_struct->command = IPS_CONFIG_SYNC_CMD; + command_struct->id = command->id; + command_struct->reserve2 = IPS_POCL; + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + sc->ips_issue_cmd(command); + if (command->status.value != IPS_ERROR_STATUS) + sema_wait(&sc->cmd_sema); + ips_insert_free_cmd(sc, command); + return 0; +} + +static int ips_send_error_table_cmd(ips_command_t *command) +{ + ips_softc_t *sc = command->sc; + ips_generic_cmd *command_struct; + + PRINTF(10,"ips test: got a command, building errortable command\n"); + command->callback = ips_wakeup_callback; + command_struct = (ips_generic_cmd *)command->command_buffer; + command_struct->command = IPS_ERROR_TABLE_CMD; + command_struct->id = command->id; + command_struct->reserve2 = IPS_CSL; + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + sc->ips_issue_cmd(command); + if (command->status.value != IPS_ERROR_STATUS) + sema_wait(&sc->cmd_sema); + ips_insert_free_cmd(sc, command); + return 0; +} + + +int ips_clear_adapter(ips_softc_t *sc) +{ + ips_command_t *command; + + device_printf(sc->dev, "syncing config\n"); + if (ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG)){ + device_printf(sc->dev, "ERROR: unable to get a command! can't sync cache!\n"); + return 1; + } + ips_send_config_sync_cmd(command); + if(COMMAND_ERROR(&command->status)){ + device_printf(sc->dev, "ERROR: cache sync command failed!\n"); + return 1; + } + + device_printf(sc->dev, "clearing error table\n"); + if(ips_get_free_cmd(sc, &command, IPS_STATIC_FLAG)){ + device_printf(sc->dev, "ERROR: unable to get a command! can't sync cache!\n"); + return 1; + } + ips_send_error_table_cmd(command); + if(COMMAND_ERROR(&command->status)){ + device_printf(sc->dev, "ERROR: etable command failed!\n"); + return 1; + } + + return 0; +} diff --git a/sys/dev/ips/ips_disk.c b/sys/dev/ips/ips_disk.c new file mode 100644 index 000000000000..7a8f5fd85ac8 --- /dev/null +++ b/sys/dev/ips/ips_disk.c @@ -0,0 +1,302 @@ +/*- + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dev/ips/ips.h> +#include <dev/ips/ips_disk.h> +#include <sys/stat.h> + +static int ipsd_probe(device_t dev); +static int ipsd_attach(device_t dev); +static int ipsd_detach(device_t dev); + +static int ipsd_dump(void *arg, void *virtual, vm_offset_t physical, + off_t offset, size_t length); +static void ipsd_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, + int error); +static void ipsd_dump_block_complete(ips_command_t *command); + +static disk_open_t ipsd_open; +static disk_close_t ipsd_close; +static disk_strategy_t ipsd_strategy; + +static device_method_t ipsd_methods[] = { + DEVMETHOD(device_probe, ipsd_probe), + DEVMETHOD(device_attach, ipsd_attach), + DEVMETHOD(device_detach, ipsd_detach), + { 0, 0 } +}; + +static driver_t ipsd_driver = { + "ipsd", + ipsd_methods, + sizeof(ipsdisk_softc_t) +}; + +static devclass_t ipsd_devclass; +DRIVER_MODULE(ipsd, ips, ipsd_driver, ipsd_devclass, 0, 0); + +/* handle opening of disk device. It must set up all + information about the geometry and size of the disk */ +static int ipsd_open(struct disk *dp) +{ + ipsdisk_softc_t *dsc = dp->d_drv1; + + dsc->state |= IPS_DEV_OPEN; + DEVICE_PRINTF(2, dsc->dev, "I'm open\n"); + return 0; +} + +static int ipsd_close(struct disk *dp) +{ + ipsdisk_softc_t *dsc = dp->d_drv1; + dsc->state &= ~IPS_DEV_OPEN; + DEVICE_PRINTF(2, dsc->dev, "I'm closed for the day\n"); + return 0; +} + +/* ipsd_finish is called to clean up and return a completed IO request */ +void ipsd_finish(struct bio *iobuf) +{ + ipsdisk_softc_t *dsc; + dsc = iobuf->bio_disk->d_drv1; + + if (iobuf->bio_flags & BIO_ERROR) { + ipsdisk_softc_t *dsc; + dsc = iobuf->bio_disk->d_drv1; + device_printf(dsc->dev, "iobuf error %d\n", iobuf->bio_error); + } else + iobuf->bio_resid = 0; + + biodone(iobuf); + ips_start_io_request(dsc->sc); +} + + +static void ipsd_strategy(struct bio *iobuf) +{ + ipsdisk_softc_t *dsc; + + dsc = iobuf->bio_disk->d_drv1; + DEVICE_PRINTF(8,dsc->dev,"in strategy\n"); + iobuf->bio_driver1 = (void *)(uintptr_t)dsc->sc->drives[dsc->disk_number].drivenum; + mtx_lock(&dsc->sc->queue_mtx); + bioq_insert_tail(&dsc->sc->queue, iobuf); + ips_start_io_request(dsc->sc); + mtx_unlock(&dsc->sc->queue_mtx); +} + +static int ipsd_probe(device_t dev) +{ + DEVICE_PRINTF(2,dev, "in probe\n"); + device_set_desc(dev, "Logical Drive"); + return 0; +} + +static int ipsd_attach(device_t dev) +{ + device_t adapter; + ipsdisk_softc_t *dsc; + u_int totalsectors; + + DEVICE_PRINTF(2,dev, "in attach\n"); + + dsc = (ipsdisk_softc_t *)device_get_softc(dev); + bzero(dsc, sizeof(ipsdisk_softc_t)); + adapter = device_get_parent(dev); + dsc->dev = dev; + dsc->sc = device_get_softc(adapter); + dsc->unit = device_get_unit(dev); + dsc->disk_number = (uintptr_t) device_get_ivars(dev); + dsc->ipsd_disk = disk_alloc(); + dsc->ipsd_disk->d_drv1 = dsc; + dsc->ipsd_disk->d_name = "ipsd"; + dsc->ipsd_disk->d_maxsize = IPS_MAX_IO_SIZE; + dsc->ipsd_disk->d_open = ipsd_open; + dsc->ipsd_disk->d_close = ipsd_close; + dsc->ipsd_disk->d_strategy = ipsd_strategy; + dsc->ipsd_disk->d_dump = ipsd_dump; + + totalsectors = dsc->sc->drives[dsc->disk_number].sector_count; + if ((totalsectors > 0x400000) && + ((dsc->sc->adapter_info.miscflags & 0x8) == 0)) { + dsc->ipsd_disk->d_fwheads = IPS_NORM_HEADS; + dsc->ipsd_disk->d_fwsectors = IPS_NORM_SECTORS; + } else { + dsc->ipsd_disk->d_fwheads = IPS_COMP_HEADS; + dsc->ipsd_disk->d_fwsectors = IPS_COMP_SECTORS; + } + dsc->ipsd_disk->d_sectorsize = IPS_BLKSIZE; + dsc->ipsd_disk->d_mediasize = (off_t)totalsectors * IPS_BLKSIZE; + dsc->ipsd_disk->d_unit = dsc->unit; + dsc->ipsd_disk->d_flags = 0; + disk_create(dsc->ipsd_disk, DISK_VERSION); + + device_printf(dev, "Logical Drive (%dMB)\n", + dsc->sc->drives[dsc->disk_number].sector_count >> 11); + return 0; +} + +static int ipsd_detach(device_t dev) +{ + ipsdisk_softc_t *dsc; + + DEVICE_PRINTF(2, dev,"in detach\n"); + dsc = (ipsdisk_softc_t *)device_get_softc(dev); + if(dsc->state & IPS_DEV_OPEN) + return (EBUSY); + disk_destroy(dsc->ipsd_disk); + return 0; +} + +static int +ipsd_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, + size_t length) +{ + ipsdisk_softc_t *dsc; + ips_softc_t *sc; + ips_command_t *command; + ips_io_cmd *command_struct; + struct disk *dp; + void *va; + off_t off; + size_t len; + int error = 0; + + dp = arg; + dsc = dp->d_drv1; + sc = dsc->sc; + + if (dsc == NULL) + return (EINVAL); + + if (ips_get_free_cmd(sc, &command, 0) != 0) { + printf("ipsd: failed to get cmd for dump\n"); + return (ENOMEM); + } + + command->data_dmatag = sc->sg_dmatag; + command->callback = ipsd_dump_block_complete; + + command_struct = (ips_io_cmd *)command->command_buffer; + command_struct->id = command->id; + command_struct->drivenum= sc->drives[dsc->disk_number].drivenum; + + off = offset; + va = virtual; + + while (length > 0) { + len = + (length > IPS_MAX_IO_SIZE) ? IPS_MAX_IO_SIZE : length; + + command_struct->lba = off / IPS_BLKSIZE; + + if (bus_dmamap_load(command->data_dmatag, command->data_dmamap, + va, len, ipsd_dump_map_sg, command, BUS_DMA_NOWAIT) != 0) { + error = EIO; + break; + } + if (COMMAND_ERROR(&command->status)) { + error = EIO; + break; + } + + length -= len; + off += len; + va = (uint8_t *)va + len; + } + + ips_insert_free_cmd(command->sc, command); + return (error); +} + +static void +ipsd_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + ips_softc_t *sc; + ips_command_t *command; + ips_sg_element_t *sg_list; + ips_io_cmd *command_struct; + int i, length; + + command = (ips_command_t *)arg; + sc = command->sc; + length = 0; + + if (error) { + printf("ipsd_dump_map_sg: error %d\n", error); + command->status.value = IPS_ERROR_STATUS; + return; + } + + command_struct = (ips_io_cmd *)command->command_buffer; + + if (nsegs != 1) { + command_struct->segnum = nsegs; + sg_list = (ips_sg_element_t *)((uint8_t *) + command->command_buffer + IPS_COMMAND_LEN); + for (i = 0; i < nsegs; i++) { + sg_list[i].addr = segs[i].ds_addr; + sg_list[i].len = segs[i].ds_len; + length += segs[i].ds_len; + } + command_struct->buffaddr = + (uint32_t)command->command_phys_addr + IPS_COMMAND_LEN; + command_struct->command = IPS_SG_WRITE_CMD; + } else { + command_struct->buffaddr = segs[0].ds_addr; + length = segs[0].ds_len; + command_struct->segnum = 0; + command_struct->command = IPS_WRITE_CMD; + } + + length = (length + IPS_BLKSIZE - 1) / IPS_BLKSIZE; + command_struct->length = length; + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_PREWRITE); + + sc->ips_issue_cmd(command); + sc->ips_poll_cmd(command); + return; +} + +static void +ipsd_dump_block_complete(ips_command_t *command) +{ + + if (COMMAND_ERROR(&command->status)) + printf("ipsd_dump completion error= 0x%x\n", + command->status.value); + + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(command->data_dmatag, command->data_dmamap); +} diff --git a/sys/dev/ips/ips_disk.h b/sys/dev/ips/ips_disk.h new file mode 100644 index 000000000000..b841eeaeea2f --- /dev/null +++ b/sys/dev/ips/ips_disk.h @@ -0,0 +1,65 @@ +/*- + * 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$ + */ + + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/disk.h> +#include <sys/bio.h> +#include <sys/disk.h> +#include <geom/geom_disk.h> + +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#define IPS_MAX_IO_SIZE 0x10000 + +#define IPS_COMP_HEADS 128 +#define IPS_COMP_SECTORS 32 +#define IPS_NORM_HEADS 254 +#define IPS_NORM_SECTORS 63 + +typedef struct ipsdisk_softc { + device_t dev; + int unit; + int disk_number; + u_int32_t state; + struct disk *ipsd_disk; + ips_softc_t *sc; +}ipsdisk_softc_t; diff --git a/sys/dev/ips/ips_ioctl.c b/sys/dev/ips/ips_ioctl.c new file mode 100644 index 000000000000..d3cee20ebdaa --- /dev/null +++ b/sys/dev/ips/ips_ioctl.c @@ -0,0 +1,166 @@ +/*- + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dev/ips/ips.h> +#include <dev/ips/ips_ioctl.h> + +static void ips_ioctl_finish(ips_command_t *command) +{ + ips_ioctl_t *ioctl_cmd = command->arg; + if(ioctl_cmd->readwrite & IPS_IOCTL_READ){ + bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap, + BUS_DMASYNC_POSTREAD); + } else if(ioctl_cmd->readwrite & IPS_IOCTL_WRITE){ + bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap, + BUS_DMASYNC_POSTWRITE); + } + bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ioctl_cmd->dmatag, ioctl_cmd->dmamap); + ioctl_cmd->status.value = command->status.value; + ips_insert_free_cmd(command->sc, command); +} + +static void ips_ioctl_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum, int error) +{ + ips_command_t *command = cmdptr; + ips_ioctl_t *ioctl_cmd = command->arg; + ips_generic_cmd *command_buffer = command->command_buffer; + if(error){ + ioctl_cmd->status.value = IPS_ERROR_STATUS; + ips_insert_free_cmd(command->sc, command); + return; + } + command_buffer->id = command->id; + command_buffer->buffaddr = segments[0].ds_addr; + if(ioctl_cmd->readwrite & IPS_IOCTL_WRITE){ + bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap, + BUS_DMASYNC_PREWRITE); + } else if(ioctl_cmd->readwrite & IPS_IOCTL_READ){ + bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap, + BUS_DMASYNC_PREREAD); + } + bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + command->sc->ips_issue_cmd(command); +} +static int ips_ioctl_start(ips_command_t *command) +{ + ips_ioctl_t *ioctl_cmd = command->arg; + memcpy(command->command_buffer, ioctl_cmd->command_buffer, + sizeof(ips_generic_cmd)); + command->callback = ips_ioctl_finish; + bus_dmamap_load(ioctl_cmd->dmatag, ioctl_cmd->dmamap, + ioctl_cmd->data_buffer,ioctl_cmd->datasize, + ips_ioctl_callback, command, 0); + return 0; +} + +static int ips_ioctl_cmd(ips_softc_t *sc, ips_ioctl_t *ioctl_cmd, ips_user_request *user_request) +{ + ips_command_t *command; + int error = EINVAL; + + if (bus_dma_tag_create( /* parent */ sc->adapter_dmatag, + /* alignment */ 1, + /* boundary */ 0, + /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, + /* highaddr */ BUS_SPACE_MAXADDR, + /* filter */ NULL, + /* filterarg */ NULL, + /* maxsize */ ioctl_cmd->datasize, + /* numsegs */ 1, + /* maxsegsize*/ ioctl_cmd->datasize, + /* flags */ 0, + /* lockfunc */ NULL, + /* lockarg */ NULL, + &ioctl_cmd->dmatag) != 0) { + return ENOMEM; + } + if(bus_dmamem_alloc(ioctl_cmd->dmatag, &ioctl_cmd->data_buffer, + 0, &ioctl_cmd->dmamap)){ + error = ENOMEM; + goto exit; + } + if(copyin(user_request->data_buffer,ioctl_cmd->data_buffer, + ioctl_cmd->datasize)) + goto exit; + ioctl_cmd->status.value = 0xffffffff; + mtx_lock(&sc->queue_mtx); + if((error = ips_get_free_cmd(sc, &command, 0)) > 0){ + error = ENOMEM; + mtx_unlock(&sc->queue_mtx); + goto exit; + } + command->arg = ioctl_cmd; + ips_ioctl_start(command); + while( ioctl_cmd->status.value == 0xffffffff) + msleep(ioctl_cmd, &sc->queue_mtx, 0, "ips", hz/10); + if(COMMAND_ERROR(&ioctl_cmd->status)) + error = EIO; + else + error = 0; + mtx_unlock(&sc->queue_mtx); + if(copyout(ioctl_cmd->data_buffer, user_request->data_buffer, + ioctl_cmd->datasize)) + error = EINVAL; +exit: bus_dmamem_free(ioctl_cmd->dmatag, ioctl_cmd->data_buffer, + ioctl_cmd->dmamap); + bus_dma_tag_destroy(ioctl_cmd->dmatag); + return error; +} + + +int ips_ioctl_request(ips_softc_t *sc, u_long ioctl_request, caddr_t addr, int32_t flags){ + int error = EINVAL; + ips_ioctl_t *ioctl_cmd; + ips_user_request *user_request; + switch(ioctl_request){ + case IPS_USER_CMD: + user_request = (ips_user_request *)addr; + ioctl_cmd = malloc(sizeof(ips_ioctl_t), M_IPSBUF, M_WAITOK); + ioctl_cmd->command_buffer = malloc(sizeof(ips_generic_cmd), + M_IPSBUF, M_WAITOK); + if(copyin(user_request->command_buffer, + ioctl_cmd->command_buffer, sizeof(ips_generic_cmd))){ + free(ioctl_cmd->command_buffer, M_IPSBUF); + free(ioctl_cmd, M_IPSBUF); + break; + } + ioctl_cmd->readwrite = IPS_IOCTL_READ | IPS_IOCTL_WRITE; + ioctl_cmd->datasize = IPS_IOCTL_BUFFER_SIZE; + error = ips_ioctl_cmd(sc, ioctl_cmd, user_request); + free(ioctl_cmd->command_buffer, M_IPSBUF); + free(ioctl_cmd, M_IPSBUF); + break; + } + + return error; +} diff --git a/sys/dev/ips/ips_ioctl.h b/sys/dev/ips/ips_ioctl.h new file mode 100644 index 000000000000..ef0691e92846 --- /dev/null +++ b/sys/dev/ips/ips_ioctl.h @@ -0,0 +1,61 @@ +/*- + * 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$ + */ + + +#include <sys/ioccom.h> + +#define IPS_USER_CMD _IOWR(0x81,0x21,ips_user_request) + +#define IPS_IOCTL_READ 1 +#define IPS_IOCTL_WRITE 2 + +#define IPS_REBUILD_STAT_SIZE 116 +#define IPS_SUBSYS_PARAM_SIZE 128 +#define IPS_RW_NVRAM_SIZE 128 + +#define IPS_IOCTL_BUFFER_SIZE 4096 + + +typedef struct ips_ioctl{ + ips_generic_cmd * command_buffer; + void * data_buffer; + ips_cmd_status_t status; + int datasize; + int readwrite; + bus_dma_tag_t dmatag; + bus_dmamap_t dmamap; +}ips_ioctl_t; + +typedef struct ips_user_request{ + void * command_buffer; + void * data_buffer; + u_int32_t status; +}ips_user_request; + diff --git a/sys/dev/ips/ips_pci.c b/sys/dev/ips/ips_pci.c new file mode 100644 index 000000000000..569c60f09825 --- /dev/null +++ b/sys/dev/ips/ips_pci.c @@ -0,0 +1,233 @@ +/*- + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dev/ips/ips.h> + +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) +{ + u_int32_t command; + 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; + + 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 */ + command = pci_read_config(dev, PCIR_COMMAND, 1); + command |= PCIM_CMD_BUSMASTEREN; + pci_write_config(dev, PCIR_COMMAND, command, 1); + /* seting up io space */ + sc->iores = NULL; + if(command & PCIM_CMD_MEMEN){ + 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 && command & PCIM_CMD_PORTEN){ + 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, sc->ips_adapter_intr, sc, &sc->irqcookie)){ + device_printf(dev, "irq setup failed\n"); + goto error; + } + if (bus_dma_tag_create( /* parent */ NULL, + /* 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"); + goto error; + } + sc->ips_ich.ich_func = ips_intrhook; + sc->ips_ich.ich_arg = sc; + mtx_init(&sc->queue_mtx, "IPS bioqueue lock", MTX_DEF, 0); + 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); |
