diff options
Diffstat (limited to 'sys/dev/ips/ips.c')
-rw-r--r-- | sys/dev/ips/ips.c | 697 |
1 files changed, 697 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); +} + |