diff options
-rw-r--r-- | sys/dev/ips/ips.c | 697 | ||||
-rw-r--r-- | sys/dev/ips/ips.h | 403 | ||||
-rw-r--r-- | sys/dev/ips/ips_commands.c | 636 | ||||
-rw-r--r-- | sys/dev/ips/ips_disk.c | 162 | ||||
-rw-r--r-- | sys/dev/ips/ips_disk.h | 66 | ||||
-rw-r--r-- | sys/dev/ips/ips_ioctl.c | 157 | ||||
-rw-r--r-- | sys/dev/ips/ips_ioctl.h | 61 | ||||
-rw-r--r-- | sys/dev/ips/ips_pci.c | 182 | ||||
-rw-r--r-- | sys/modules/ips/Makefile | 8 |
9 files changed, 2372 insertions, 0 deletions
diff --git a/sys/dev/ips/ips.c b/sys/dev/ips/ips.c new file mode 100644 index 0000000..984fdf9 --- /dev/null +++ b/sys/dev/ips/ips.c @@ -0,0 +1,697 @@ +/*- + * 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 <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; + +#define IPS_CDEV_MAJOR 175 +static struct cdevsw ips_cdevsw = { + .d_open = ips_open, + .d_close = ips_close, + .d_ioctl = ips_ioctl, + .d_name = "ips", + .d_maj = IPS_CDEV_MAJOR, +}; + + +static int ips_open(dev_t 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(dev_t 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(dev_t 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 __inline__ int ips_cmdqueue_free(ips_softc_t *sc) +{ + int i, error = -1; + intrmask_t mask = splbio(); + if(!sc->used_commands){ + for(i = 0; i < sc->max_cmds; i++){ + if(!(sc->commandarray[i].command_phys_addr)) + continue; + bus_dmamap_unload(sc->command_dmatag, + sc->commandarray[i].command_dmamap); + bus_dmamem_free(sc->command_dmatag, + sc->commandarray[i].command_buffer, + sc->commandarray[i].command_dmamap); + } + error = 0; + sc->state |= IPS_OFFLINE; + } + splx(mask); + 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 __inline__ int ips_cmdqueue_init(ips_softc_t *sc) +{ + int i; + ips_command_t *command; + SLIST_INIT(&sc->free_cmd_list); + STAILQ_INIT(&sc->cmd_wait_list); + for(i = 0; i < sc->max_cmds; i++){ + sc->commandarray[i].id = i; + sc->commandarray[i].sc = sc; + SLIST_INSERT_HEAD(&sc->free_cmd_list, &sc->commandarray[i], + next); + } + for(i = 0; i < sc->max_cmds; i++){ + command = &sc->commandarray[i]; + 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; + } + } + sc->state &= ~IPS_OFFLINE; + return 0; +error: + ips_cmdqueue_free(sc); + return ENOMEM; +} + +static int ips_add_waiting_command(ips_softc_t *sc, int (*callback)(ips_command_t *), void *data, unsigned long flags) +{ + intrmask_t mask; + ips_command_t *command; + ips_wait_list_t *waiter; + unsigned long memflags = 0; + if(IPS_NOWAIT_FLAG & flags) + memflags = M_NOWAIT; + waiter = malloc(sizeof(ips_wait_list_t), M_DEVBUF, memflags); + if(!waiter) + return ENOMEM; + mask = splbio(); + if(sc->state & IPS_OFFLINE){ + splx(mask); + return EIO; + } + command = SLIST_FIRST(&sc->free_cmd_list); + if(command && !(sc->state & IPS_TIMEOUT)){ + SLIST_REMOVE_HEAD(&sc->free_cmd_list, next); + (sc->used_commands)++; + splx(mask); + clear_ips_command(command); + bzero(command->command_buffer, IPS_COMMAND_LEN); + free(waiter, M_DEVBUF); + command->arg = data; + return callback(command); + } + DEVICE_PRINTF(1, sc->dev, "adding command to the wait queue\n"); + waiter->callback = callback; + waiter->data = data; + STAILQ_INSERT_TAIL(&sc->cmd_wait_list, waiter, next); + splx(mask); + return 0; +} + +static void ips_run_waiting_command(ips_softc_t *sc) +{ + ips_wait_list_t *waiter; + ips_command_t *command; + int (*callback)(ips_command_t*); + intrmask_t mask; + + mask = splbio(); + waiter = STAILQ_FIRST(&sc->cmd_wait_list); + command = SLIST_FIRST(&sc->free_cmd_list); + if(!waiter || !command){ + splx(mask); + return; + } + DEVICE_PRINTF(1, sc->dev, "removing command from wait queue\n"); + SLIST_REMOVE_HEAD(&sc->free_cmd_list, next); + STAILQ_REMOVE_HEAD(&sc->cmd_wait_list, next); + (sc->used_commands)++; + splx(mask); + clear_ips_command(command); + bzero(command->command_buffer, IPS_COMMAND_LEN); + command->arg = waiter->data; + callback = waiter->callback; + free(waiter, M_DEVBUF); + callback(command); + return; +} +/* 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, int (*callback)(ips_command_t *), void *data, unsigned long flags) +{ + intrmask_t mask; + ips_command_t *command; + mask = splbio(); + + if(sc->state & IPS_OFFLINE){ + splx(mask); + return EIO; + } + command = SLIST_FIRST(&sc->free_cmd_list); + if(!command || (sc->state & IPS_TIMEOUT)){ + splx(mask); + if(flags & IPS_NOWAIT_FLAG) + return EAGAIN; + return ips_add_waiting_command(sc, callback, data, flags); + } + SLIST_REMOVE_HEAD(&sc->free_cmd_list, next); + (sc->used_commands)++; + splx(mask); + clear_ips_command(command); + bzero(command->command_buffer, IPS_COMMAND_LEN); + command->arg = data; + return callback(command); +} + +/* adds a command back to the free command queue */ +void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command) +{ + intrmask_t mask; + mask = splbio(); + SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next); + (sc->used_commands)--; + splx(mask); + if(!(sc->state & IPS_TIMEOUT)) + ips_run_waiting_command(sc); +} + +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_OKAY){ + sc->diskdev[i] = device_add_child(sc->dev, NULL, -1); + device_set_ivars(sc->diskdev[i],(void *) 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) +{ + intrmask_t mask; + ips_softc_t *sc = arg; + int i, state = 0; + ips_command_t *command; + command = &sc->commandarray[0]; + mask = splbio(); + 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; + ips_run_waiting_command(sc); + } + if (sc->state != IPS_OFFLINE) + sc->timer = timeout(ips_timeout, sc, 10*hz); + splx(mask); +} + +/* check card and initialize it */ +int ips_adapter_init(ips_softc_t *sc) +{ + 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, + &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, + &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); + + if(sc->ips_adapter_reinit(sc, 0)) + goto error; + + mtx_init(&sc->cmd_mtx, "ips command mutex", NULL, MTX_DEF); + if(ips_get_adapter_info(sc) || ips_get_drive_info(sc)){ + device_printf(sc->dev, "failed to get configuration data from device\n"); + goto error; + } + ips_update_nvram(sc); /* no error check as failure doesn't matter */ + + 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; + intrmask_t mask; + 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"); + mask = splbio(); + untimeout(ips_timeout, sc, sc->timer); + splx(mask); + if (mtx_initialized(&sc->cmd_mtx)) + mtx_destroy(&sc->cmd_mtx); + + 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; +} + +void ips_morpheus_intr(void *void_sc) +{ + ips_softc_t *sc = (ips_softc_t *)void_sc; + u_int32_t oisr, iisr; + int cmdnumber; + ips_cmd_status_t status; + + iisr =ips_read_4(sc, MORPHEUS_REG_IISR); + oisr =ips_read_4(sc, MORPHEUS_REG_OISR); + PRINTF(9,"interrupt registers in:%x out:%x\n",iisr, oisr); + if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){ + DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n"); + return; + } + while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){ + cmdnumber = status.fields.command_id; + sc->commandarray[cmdnumber].status.value = status.value; + sc->commandarray[cmdnumber].timeout = 0; + sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber])); + + + DEVICE_PRINTF(9,sc->dev, "got command %d\n", cmdnumber); + } + return; +} + +void ips_issue_morpheus_cmd(ips_command_t *command) +{ + intrmask_t mask = splbio(); + /* hmmm, is there a cleaner way to do this? */ + if(command->sc->state & IPS_OFFLINE){ + splx(mask); + 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); + splx(mask); +} + +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, + &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) +{ + intrmask_t mask; + u_int32_t value; + int statnum = sc->copper_queue->nextstatus++; + if(sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM) + sc->copper_queue->nextstatus = 0; + mask = splbio(); + value = sc->copper_queue->status[statnum]; + ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr + + 4 * statnum); + splx(mask); + 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; + + 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); + } + return; +} + +void ips_issue_copperhead_cmd(ips_command_t *command) +{ + int i; + intrmask_t mask = splbio(); + /* hmmm, is there a cleaner way to do this? */ + if(command->sc->state & IPS_OFFLINE){ + splx(mask); + 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"); + splx(mask); + 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); + splx(mask); +} + diff --git a/sys/dev/ips/ips.h b/sys/dev/ips/ips.h new file mode 100644 index 0000000..888cc40 --- /dev/null +++ b/sys/dev/ips/ips.h @@ -0,0 +1,403 @@ +/*- + * 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/bio.h> +#include <sys/malloc.h> +#include <sys/mutex.h> + +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +/* + * IPS CONSTANTS + */ +#define IPS_VENDOR_ID 0x1014 +#define IPS_MORPHEUS_DEVICE_ID 0x01BD +#define IPS_COPPERHEAD_DEVICE_ID 0x002E +#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_NOWAIT_FLAG 1 + +/* 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 */ + +/* 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_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 + +/* error information returned by the adapter */ +#define IPS_MIN_ERROR 0x02 +#define IPS_ERROR_STATUS 0x13000200 /* ahh, magic numbers */ + +#define IPS_OS_FREEBSD 10 +#define IPS_VERSION_MAJOR "0.90" +#define IPS_VERSION_MINOR ".00" + +/* + * 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 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_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; + bus_dma_tag_t data_dmatag; + bus_dmamap_t data_dmamap; + void * data_buffer; + void * arg; + void (* callback)(struct ips_command *command); +}ips_command_t; + +typedef struct ips_wait_list{ + STAILQ_ENTRY(ips_wait_list) next; + void *data; + int (* callback)(ips_command_t *command); +}ips_wait_list_t; + +typedef struct ips_softc{ + struct resource * iores; + struct resource * irqres; + 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; + dev_t device_file; + struct callout_handle timer; + 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_int8_t next_drive; + u_int8_t max_cmds; + volatile u_int8_t used_commands; + ips_command_t commandarray[IPS_MAX_CMD_NUM]; + SLIST_HEAD(command_list, ips_command) free_cmd_list; + STAILQ_HEAD(command_wait_list,ips_wait_list) cmd_wait_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); + ips_copper_queue_t * copper_queue; + struct mtx cmd_mtx; +}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, struct bio *iobuf); +extern int ips_get_drive_info(ips_softc_t *sc); +extern int ips_get_adapter_info(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, int (*callback)(ips_command_t *), + void *data, 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 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); + diff --git a/sys/dev/ips/ips_commands.c b/sys/dev/ips/ips_commands.c new file mode 100644 index 0000000..6b47af5 --- /dev/null +++ b/sys/dev/ips/ips_commands.c @@ -0,0 +1,636 @@ +/*- + * 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 <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) +{ + ips_cmd_status_t *status; + status = command->arg; + status->value = command->status.value; + bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_POSTWRITE); + mtx_lock(&command->sc->cmd_mtx); + wakeup(status); + mtx_unlock(&command->sc->cmd_mtx); +} +/* 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); + bus_dmamap_destroy(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); + bus_dmamap_destroy(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 = (uint32_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 blkno: %lld " + "pblkno: %lld length: %d, ds_len: %d\n", command->id, segnum, + iobuf->bio_blkno, iobuf->bio_pblkno, + length, segments[0].ds_len); + + sc->ips_issue_cmd(command); + return; +} + +static int ips_send_io_request(ips_command_t *command) +{ + ips_softc_t *sc = command->sc; + struct bio *iobuf = command->arg; + command->data_dmatag = sc->sg_dmatag; + if(bus_dmamap_create(command->data_dmatag, 0, &command->data_dmamap)){ + device_printf(sc->dev, "dmamap failed\n"); + iobuf->bio_flags |= BIO_ERROR; + iobuf->bio_error = ENOMEM; + ips_insert_free_cmd(sc, command); + ipsd_finish(iobuf); + return 0; + } + command->callback = ips_io_request_finish; + 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) +{ + if(ips_get_free_cmd(sc, ips_send_io_request, iobuf, 0)){ + device_printf(sc->dev, "no mem for command slots!\n"); + iobuf->bio_flags |= BIO_ERROR; + iobuf->bio_error = ENOMEM; + ipsd_finish(iobuf); + return; + } + 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){ + ips_cmd_status_t * status = command->arg; + 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; + ips_cmd_status_t *status = command->arg; + + 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, + &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; + mtx_lock(&sc->cmd_mtx); + 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 ((status->value == IPS_ERROR_STATUS) || + (msleep(status, &sc->cmd_mtx, 0, "ips", 30*hz) == EWOULDBLOCK)) + error = ETIMEDOUT; + mtx_unlock(&sc->cmd_mtx); + + 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) +{ + int error = 0; + ips_cmd_status_t *status; + status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT); + if(!status) + return ENOMEM; + if(ips_get_free_cmd(sc, ips_send_adapter_info_cmd, status, + IPS_NOWAIT_FLAG) > 0){ + device_printf(sc->dev, "unable to get adapter configuration\n"); + free(status, M_DEVBUF); + return ENXIO; + } + if(COMMAND_ERROR(status)){ + error = ENXIO; + } + free(status, M_DEVBUF); + 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){ + ips_cmd_status_t * status = command->arg; + 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_cmd_status_t *status = command->arg; + 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, + &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; + mtx_lock(&sc->cmd_mtx); + 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 ((status->value == IPS_ERROR_STATUS) || + (msleep(status, &sc->cmd_mtx, 0, "ips", 10*hz) == EWOULDBLOCK)) + error = ETIMEDOUT; + mtx_unlock(&sc->cmd_mtx); + + 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_cmd_status_t *status; + status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT); + if(!status) + return ENOMEM; + if(ips_get_free_cmd(sc, ips_send_drive_info_cmd, status, + IPS_NOWAIT_FLAG) > 0){ + free(status, M_DEVBUF); + device_printf(sc->dev, "unable to get drive configuration\n"); + return ENXIO; + } + if(COMMAND_ERROR(status)){ + error = ENXIO; + } + free(status, M_DEVBUF); + 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_cmd_status_t *status = command->arg; + 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); + mtx_lock(&sc->cmd_mtx); + sc->ips_issue_cmd(command); + if (status->value != IPS_ERROR_STATUS) + msleep(status, &sc->cmd_mtx, 0, "flush2", 0); + mtx_unlock(&sc->cmd_mtx); + ips_insert_free_cmd(sc, command); + return 0; +} + +int ips_flush_cache(ips_softc_t *sc) +{ + ips_cmd_status_t *status; + status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT); + if(!status) + return ENOMEM; + device_printf(sc->dev, "flushing cache\n"); + if(ips_get_free_cmd(sc, ips_send_flush_cache_cmd, status, + IPS_NOWAIT_FLAG)){ + free(status, M_DEVBUF); + device_printf(sc->dev, "ERROR: unable to get a command! can't flush cache!\n"); + } + if(COMMAND_ERROR(status)){ + device_printf(sc->dev, "ERROR: cache flush command failed!\n"); + } + free(status, M_DEVBUF); + 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; + 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){ + ips_cmd_status_t * status = command->arg; + 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; + ips_cmd_status_t *status = command->arg; + + 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, + &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; + mtx_lock(&sc->cmd_mtx); + 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 ((status->value == IPS_ERROR_STATUS) || + (msleep(status, &sc->cmd_mtx, 0, "ips", 0) == EWOULDBLOCK)) + error = ETIMEDOUT; + mtx_unlock(&sc->cmd_mtx); + + 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_cmd_status_t *status; + status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT); + if(!status) + return ENOMEM; + if(ips_get_free_cmd(sc, ips_read_nvram, status, IPS_NOWAIT_FLAG)){ + free(status, M_DEVBUF); + device_printf(sc->dev, "ERROR: unable to get a command! can't update nvram\n"); + return 1; + } + if(COMMAND_ERROR(status)){ + device_printf(sc->dev, "ERROR: nvram update command failed!\n"); + } + free(status, M_DEVBUF); + return 0; + + +} + + +static int ips_send_config_sync_cmd(ips_command_t *command) +{ + ips_softc_t *sc = command->sc; + ips_cmd_status_t *status = command->arg; + 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); + mtx_lock(&sc->cmd_mtx); + sc->ips_issue_cmd(command); + if (status->value != IPS_ERROR_STATUS) + msleep(status, &sc->cmd_mtx, 0, "ipssyn", 0); + mtx_unlock(&sc->cmd_mtx); + 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_cmd_status_t *status = command->arg; + 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); + mtx_lock(&sc->cmd_mtx); + sc->ips_issue_cmd(command); + if (status->value != IPS_ERROR_STATUS) + msleep(status, &sc->cmd_mtx, 0, "ipsetc", 0); + mtx_unlock(&sc->cmd_mtx); + ips_insert_free_cmd(sc, command); + return 0; +} + + +int ips_clear_adapter(ips_softc_t *sc) +{ + ips_cmd_status_t *status; + status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT); + if(!status) + return ENOMEM; + device_printf(sc->dev, "syncing config\n"); + if(ips_get_free_cmd(sc, ips_send_config_sync_cmd, status, + IPS_NOWAIT_FLAG)){ + free(status, M_DEVBUF); + device_printf(sc->dev, "ERROR: unable to get a command! can't sync cache!\n"); + return 1; + } + if(COMMAND_ERROR(status)){ + free(status, M_DEVBUF); + 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, ips_send_error_table_cmd, status, + IPS_NOWAIT_FLAG)){ + free(status, M_DEVBUF); + device_printf(sc->dev, "ERROR: unable to get a command! can't sync cache!\n"); + return 1; + } + if(COMMAND_ERROR(status)){ + device_printf(sc->dev, "ERROR: etable command failed!\n"); + free(status, M_DEVBUF); + return 1; + } + + free(status, M_DEVBUF); + return 0; +} diff --git a/sys/dev/ips/ips_disk.c b/sys/dev/ips/ips_disk.c new file mode 100644 index 0000000..bbeaf9f --- /dev/null +++ b/sys/dev/ips/ips_disk.c @@ -0,0 +1,162 @@ +/*- + * 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 <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 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) +{ + 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); +} + + +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"); + (uint32_t)iobuf->bio_driver1 = dsc->sc->drives[dsc->disk_number].drivenum; + ips_start_io_request(dsc->sc, iobuf); +} + +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 = (int) device_get_ivars(dev); + 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; + + 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 = totalsectors * IPS_BLKSIZE; + disk_create(dsc->unit, &dsc->ipsd_disk, 0, NULL, NULL); + + 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; +} + diff --git a/sys/dev/ips/ips_disk.h b/sys/dev/ips/ips_disk.h new file mode 100644 index 0000000..6205745 --- /dev/null +++ b/sys/dev/ips/ips_disk.h @@ -0,0 +1,66 @@ +/*- + * 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_memio.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <pci/pcireg.h> +#include <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 0000000..9540a0f --- /dev/null +++ b/sys/dev/ips/ips_ioctl.c @@ -0,0 +1,157 @@ +/*- + * 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 <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) +{ + 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, + &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; + if((error = ips_get_free_cmd(sc, ips_ioctl_start, ioctl_cmd,0)) > 0){ + error = ENOMEM; + goto exit; + } + while( ioctl_cmd->status.value == 0xffffffff) + tsleep(ioctl_cmd, 0, "ips", hz/10); + if(COMMAND_ERROR(&ioctl_cmd->status)) + error = EIO; + else + error = 0; + 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_DEVBUF, M_WAITOK); + ioctl_cmd->command_buffer = malloc(sizeof(ips_generic_cmd), + M_DEVBUF, M_WAITOK); + if(copyin(user_request->command_buffer, + ioctl_cmd->command_buffer, sizeof(ips_generic_cmd))){ + free(ioctl_cmd->command_buffer, M_DEVBUF); + free(ioctl_cmd, M_DEVBUF); + 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_DEVBUF); + free(ioctl_cmd, M_DEVBUF); + break; + } + + return error; +} diff --git a/sys/dev/ips/ips_ioctl.h b/sys/dev/ips/ips_ioctl.h new file mode 100644 index 0000000..ef0691e --- /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 0000000..4999de6 --- /dev/null +++ b/sys/dev/ips/ips_pci.c @@ -0,0 +1,182 @@ +/*- + * 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 <dev/ips/ips.h> +static int ips_pci_free(ips_softc_t *sc); + +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 0; + } 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 (0); + } + return(ENXIO); +} + +static int ips_pci_attach(device_t dev) +{ + u_int32_t command; + ips_softc_t *sc; + + 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; + } 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; + } 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_MORPHEUS_DEVICE_ID) + sc->rid = PCIR_MAPS; + else + sc->rid = PCIR_MAPS + 4; + sc->iotype = SYS_RES_MEMORY; + sc->iores = bus_alloc_resource(dev, sc->iotype, &sc->rid, 0, ~0, 1, RF_ACTIVE); + } + if(!sc->iores && command & PCIM_CMD_PORTEN){ + PRINTF(10, "trying PORTIO\n"); + sc->rid = PCIR_MAPS; + sc->iotype = SYS_RES_IOPORT; + sc->iores = bus_alloc_resource(dev, sc->iotype, &sc->rid, 0, ~0, 1, 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(dev, SYS_RES_IRQ, &sc->irqrid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE))){ + device_printf(dev, "irq allocation failed\n"); + goto error; + } + if(bus_setup_intr(dev, sc->irqres, INTR_TYPE_BIO, 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, + &sc->adapter_dmatag) != 0) { + printf("IPS can't alloc dma tag\n"); + goto error; + } + if(ips_adapter_init(sc)) + goto error; + return 0; +error: + ips_pci_free(sc); + return (ENXIO); +} + +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); + 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); + ips_flush_cache(sc); + if(ips_adapter_free(sc)) + return EBUSY; + ips_pci_free(sc); + mtx_destroy(&sc->cmd_mtx); + return 0; +} + +static int ips_pci_shutdown(device_t dev) +{ + ips_softc_t *sc = (ips_softc_t *) device_get_softc(dev); + 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); diff --git a/sys/modules/ips/Makefile b/sys/modules/ips/Makefile new file mode 100644 index 0000000..4ec20a3 --- /dev/null +++ b/sys/modules/ips/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/ips +KMOD = ips +SRCS = ips.c ips_pci.c ips.h ips_disk.c ips_disk.h ips_commands.c \ + ips_ioctl.h ips_ioctl.c device_if.h bus_if.h pci_if.h + +.include <bsd.kmod.mk> |