diff options
Diffstat (limited to 'sys/dev/ahb/ahb.c')
-rw-r--r-- | sys/dev/ahb/ahb.c | 1346 |
1 files changed, 0 insertions, 1346 deletions
diff --git a/sys/dev/ahb/ahb.c b/sys/dev/ahb/ahb.c deleted file mode 100644 index 7493c30942d6..000000000000 --- a/sys/dev/ahb/ahb.c +++ /dev/null @@ -1,1346 +0,0 @@ -/* - * CAM SCSI device driver for the Adaptec 174X SCSI Host adapter - * - * Copyright (c) 1998 Justin T. Gibbs - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice immediately at the beginning of the file, without modification, - * this list of conditions, and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * 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. - * - * $Id: ahb.c,v 1.3 1998/09/17 00:08:26 gibbs Exp $ - */ - -#include "eisa.h" -#if NEISA > 0 -#include <stddef.h> /* For offsetof() */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/malloc.h> - -#include <machine/bus_pio.h> -#include <machine/bus.h> -#include <machine/clock.h> - -#include <cam/cam.h> -#include <cam/cam_ccb.h> -#include <cam/cam_sim.h> -#include <cam/cam_xpt_sim.h> -#include <cam/cam_debug.h> - -#include <cam/scsi/scsi_message.h> - -#include <i386/eisa/eisaconf.h> - -#include <i386/eisa/ahbreg.h> - -#define ccb_ecb_ptr spriv_ptr0 -#define ccb_ahb_ptr spriv_ptr1 - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -#define ahb_inb(ahb, port) \ - bus_space_read_1((ahb)->tag, (ahb)->bsh, port) - -#define ahb_inl(ahb, port) \ - bus_space_read_4((ahb)->tag, (ahb)->bsh, port) - -#define ahb_outb(ahb, port, value) \ - bus_space_write_1((ahb)->tag, (ahb)->bsh, port, value) - -#define ahb_outl(ahb, port, value) \ - bus_space_write_4((ahb)->tag, (ahb)->bsh, port, value) - -static const char *ahbmatch(eisa_id_t type); -static int ahbprobe(void); -static int ahbattach(struct eisa_device *dev); -static struct ahb_softc *ahballoc(u_long unit, u_int iobase, u_int irq); -static void ahbfree(struct ahb_softc *ahb); -static int ahbreset(struct ahb_softc *ahb); -static void ahbmapecbs(void *arg, bus_dma_segment_t *segs, - int nseg, int error); -static int ahbxptattach(struct ahb_softc *ahb); -static void ahbhandleimmed(struct ahb_softc *ahb, - u_int32_t mbox, u_int intstat); -static void ahbcalcresid(struct ahb_softc *ahb, - struct ecb *ecb, union ccb *ccb); -static __inline void ahbdone(struct ahb_softc *ahb, u_int32_t mbox, - u_int intstat); -static void ahbintr(void *arg); -static bus_dmamap_callback_t ahbexecuteecb; -static void ahbaction(struct cam_sim *sim, union ccb *ccb); -static void ahbpoll(struct cam_sim *sim); - -/* Our timeout handler */ -timeout_t ahbtimeout; - -static __inline struct ecb* ahbecbget(struct ahb_softc *ahb); -static __inline void ahbecbfree(struct ahb_softc* ahb, - struct ecb* ecb); -static __inline u_int32_t ahbecbvtop(struct ahb_softc *ahb, - struct ecb *ecb); -static __inline struct ecb* ahbecbptov(struct ahb_softc *ahb, - u_int32_t ecb_addr); -static __inline u_int32_t ahbstatuspaddr(u_int32_t ecb_paddr); -static __inline u_int32_t ahbsensepaddr(u_int32_t ecb_paddr); -static __inline u_int32_t ahbsgpaddr(u_int32_t ecb_paddr); -static __inline void ahbqueuembox(struct ahb_softc *ahb, - u_int32_t mboxval, - u_int attn_code); - -static __inline struct ecb* -ahbecbget(struct ahb_softc *ahb) -{ - struct ecb* ecb; - int s; - - s = splcam(); - if ((ecb = SLIST_FIRST(&ahb->free_ecbs)) != NULL) - SLIST_REMOVE_HEAD(&ahb->free_ecbs, links); - splx(s); - - return (ecb); -} - -static __inline void -ahbecbfree(struct ahb_softc* ahb, struct ecb* ecb) -{ - int s; - - s = splcam(); - ecb->state = ECB_FREE; - SLIST_INSERT_HEAD(&ahb->free_ecbs, ecb, links); - splx(s); -} - -static __inline u_int32_t -ahbecbvtop(struct ahb_softc *ahb, struct ecb *ecb) -{ - return (ahb->ecb_physbase - + (u_int32_t)((caddr_t)ecb - (caddr_t)ahb->ecb_array)); -} - -static __inline struct ecb* -ahbecbptov(struct ahb_softc *ahb, u_int32_t ecb_addr) -{ - return (ahb->ecb_array - + ((struct ecb*)ecb_addr - (struct ecb*)ahb->ecb_physbase)); -} - -static __inline u_int32_t -ahbstatuspaddr(u_int32_t ecb_paddr) -{ - return (ecb_paddr + offsetof(struct ecb, status)); -} - -static __inline u_int32_t -ahbsensepaddr(u_int32_t ecb_paddr) -{ - return (ecb_paddr + offsetof(struct ecb, sense)); -} - -static __inline u_int32_t -ahbsgpaddr(u_int32_t ecb_paddr) -{ - return (ecb_paddr + offsetof(struct ecb, sg_list)); -} - -static __inline void -ahbqueuembox(struct ahb_softc *ahb, u_int32_t mboxval, u_int attn_code) -{ - u_int loopmax = 300; - while (--loopmax) { - u_int status; - - status = ahb_inb(ahb, HOSTSTAT); - if ((status & HOSTSTAT_MBOX_EMPTY|HOSTSTAT_BUSY) - != HOSTSTAT_MBOX_EMPTY) - break; - DELAY(20); - } - if (loopmax == 0) - panic("ahb%ld: adapter not taking commands\n", ahb->unit); - - ahb_outl(ahb, MBOXOUT0, mboxval); - ahb_outb(ahb, ATTN, attn_code); -} - -static u_long ahbunit; - -static struct eisa_driver ahb_eisa_driver = -{ - "ahb", - ahbprobe, - ahbattach, - /*shutdown*/NULL, - &ahbunit -}; - -DATA_SET (eisadriver_set, ahb_eisa_driver); - -static const char * -ahbmatch(eisa_id_t type) -{ - switch(type & 0xfffffe00) { - case EISA_DEVICE_ID_ADAPTEC_1740: - return ("Adaptec 174x SCSI host adapter"); - break; - default: - break; - } - return (NULL); -} - -static int -ahbprobe(void) -{ - struct eisa_device *e_dev = NULL; - u_int32_t iobase; - u_int32_t irq; - int count; - - count = 0; - while ((e_dev = eisa_match_dev(e_dev, ahbmatch))) { - u_int8_t intdef; - - iobase = (e_dev->ioconf.slot * EISA_SLOT_SIZE) + - AHB_EISA_SLOT_OFFSET; - - eisa_add_iospace(e_dev, iobase, AHB_EISA_IOSIZE, RESVADDR_NONE); - - intdef = inb(INTDEF + iobase); - switch (intdef & 0x7) { - case INT9: - irq = 9; - break; - case INT10: - irq = 10; - break; - case INT11: - irq = 11; - break; - case INT12: - irq = 12; - break; - case INT14: - irq = 14; - break; - case INT15: - irq = 15; - break; - default: - printf("Adaptec 174X at slot %d: illegal " - "irq setting %d\n", e_dev->ioconf.slot, - (intdef & 0x7)); - irq = 0; - break; - } - if (irq == 0) - continue; - eisa_add_intr(e_dev, irq); - eisa_registerdev(e_dev, &ahb_eisa_driver); - count++; - } - return count; -} - -static int -ahbattach(struct eisa_device *e_dev) -{ - /* - * find unit and check we have that many defined - */ - struct ahb_softc *ahb; - struct ecb* next_ecb; - resvaddr_t *iospace; - u_int irq; - - if (TAILQ_FIRST(&e_dev->ioconf.irqs) == NULL) - return (-1); - - irq = TAILQ_FIRST(&e_dev->ioconf.irqs)->irq_no; - - iospace = e_dev->ioconf.ioaddrs.lh_first; - - if (iospace == NULL) - return (-1); - - eisa_reg_start(e_dev); - if (eisa_reg_iospace(e_dev, iospace)) { - eisa_reg_end(e_dev); - return (-1); - } - - if ((ahb = ahballoc(e_dev->unit, iospace->addr, irq)) == NULL) { - eisa_reg_end(e_dev); - return (-1); - } - - if (ahbreset(ahb) != 0) - return (-1); - - if (eisa_reg_intr(e_dev, irq, ahbintr, (void *)ahb, &cam_imask, - (ahb_inb(ahb, INTDEF) & INTLEVEL) ? TRUE : FALSE)) { - eisa_reg_end(e_dev); - ahbfree(ahb); - return (-1); - } - - /* - * Create our DMA tags. These tags define the kinds of device - * accessable memory allocations and memory mappings we will - * need to perform during normal operation. - */ - /* DMA tag for mapping buffers into device visible space. */ - /* XXX Should be a child of the EISA bus dma tag */ - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/0, /*boundary*/0, - /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, - /*highaddr*/BUS_SPACE_MAXADDR, - /*filter*/NULL, /*filterarg*/NULL, - /*maxsize*/MAXBSIZE, /*nsegments*/AHB_NSEG, - /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, - /*flags*/BUS_DMA_ALLOCNOW, - &ahb->buffer_dmat) != 0) - goto error_exit; - - ahb->init_level++; - - /* DMA tag for our ccb structures and ha inquiry data */ - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/0, /*boundary*/0, - /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, - /*highaddr*/BUS_SPACE_MAXADDR, - /*filter*/NULL, /*filterarg*/NULL, - (AHB_NECB * sizeof(struct ecb)) - + sizeof(*ahb->ha_inq_data), - /*nsegments*/1, - /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, - /*flags*/0, &ahb->ecb_dmat) != 0) - goto error_exit; - - ahb->init_level++; - - /* Allocation for our ccbs */ - if (bus_dmamem_alloc(ahb->ecb_dmat, (void **)&ahb->ecb_array, - BUS_DMA_NOWAIT, &ahb->ecb_dmamap) != 0) - goto error_exit; - - ahb->ha_inq_data = (struct ha_inquiry_data *)&ahb->ecb_array[AHB_NECB]; - - ahb->init_level++; - - /* And permanently map them */ - bus_dmamap_load(ahb->ecb_dmat, ahb->ecb_dmamap, - ahb->ecb_array, AHB_NSEG * sizeof(struct ecb), - ahbmapecbs, ahb, /*flags*/0); - - ahb->init_level++; - - /* Allocate the buffer dmamaps for each of our ECBs */ - bzero(ahb->ecb_array, (AHB_NECB * sizeof(struct ecb)) - + sizeof(*ahb->ha_inq_data)); - next_ecb = ahb->ecb_array; - while (ahb->num_ecbs < AHB_NECB) { - u_int32_t ecb_paddr; - - if (bus_dmamap_create(ahb->buffer_dmat, /*flags*/0, - &next_ecb->dmamap)) - break; - ecb_paddr = ahbecbvtop(ahb, next_ecb); - next_ecb->hecb.status_ptr = ahbstatuspaddr(ecb_paddr); - next_ecb->hecb.sense_ptr = ahbsensepaddr(ecb_paddr); - ahb->num_ecbs++; - ahbecbfree(ahb, next_ecb); - next_ecb++; - } - - if (ahb->num_ecbs == 0) - goto error_exit; - - ahb->init_level++; - - eisa_reg_end(e_dev); - - /* - * Now that we know we own the resources we need, register - * our bus with the XPT. - */ - if (ahbxptattach(ahb)) - goto error_exit; - - /* Enable our interrupt */ - eisa_enable_intr(e_dev, irq); - return (0); -error_exit: - /* - * The board's IRQ line will not be left enabled - * if we can't intialize correctly, so its safe - * to release the irq. - */ - eisa_release_intr(e_dev, irq, ahbintr); - ahbfree(ahb); - return (-1); -} - -static struct ahb_softc * -ahballoc(u_long unit, u_int iobase, u_int irq) -{ - struct ahb_softc *ahb; - - /* - * Allocate a storage area for us - */ - ahb = malloc(sizeof(struct ahb_softc), M_TEMP, M_NOWAIT); - if (!ahb) { - printf("ahb%ld: cannot malloc!\n", unit); - return (NULL); - } - bzero(ahb, sizeof(struct ahb_softc)); - SLIST_INIT(&ahb->free_ecbs); - LIST_INIT(&ahb->pending_ccbs); - ahb->unit = unit; - ahb->tag = I386_BUS_SPACE_IO; - ahb->bsh = iobase; - ahb->disc_permitted = ~0; - ahb->tags_permitted = ~0; - - return (ahb); -} - -static void -ahbfree(struct ahb_softc *ahb) -{ - switch (ahb->init_level) { - default: - case 4: - bus_dmamap_unload(ahb->ecb_dmat, ahb->ecb_dmamap); - case 3: - bus_dmamem_free(ahb->ecb_dmat, ahb->ecb_array, - ahb->ecb_dmamap); - bus_dmamap_destroy(ahb->ecb_dmat, ahb->ecb_dmamap); - case 2: - bus_dma_tag_destroy(ahb->ecb_dmat); - case 1: - bus_dma_tag_destroy(ahb->buffer_dmat); - case 0: - } - free(ahb, M_DEVBUF); -} - -/* - * reset board, If it doesn't respond, return failure - */ -static int -ahbreset(struct ahb_softc *ahb) -{ - int wait = 1000; /* 1 sec enough? */ - int test; - - if ((ahb_inb(ahb, PORTADDR) & PORTADDR_ENHANCED) == 0) { - printf("ahb_reset: Controller not in enhanced mode\n"); - return (-1); - } - - ahb_outb(ahb, CONTROL, CNTRL_HARD_RST); - DELAY(1000); - ahb_outb(ahb, CONTROL, 0); - while (--wait) { - DELAY(1000); - if ((ahb_inb(ahb, HOSTSTAT) & HOSTSTAT_BUSY) == 0) - break; - } - - if (wait == 0) { - printf("ahbreset: No answer from aha1742 board\n"); - return (-1); - } - if ((test = ahb_inb(ahb, MBOXIN0)) != 0) { - printf("ahb_reset: self test failed, val = 0x%x\n", test); - return (-1); - } - while (ahb_inb(ahb, HOSTSTAT) & HOSTSTAT_INTPEND) { - ahb_outb(ahb, CONTROL, CNTRL_CLRINT); - DELAY(10000); - } - return (0); -} - -static void -ahbmapecbs(void *arg, bus_dma_segment_t *segs, int nseg, int error) -{ - struct ahb_softc* ahb; - - ahb = (struct ahb_softc*)arg; - ahb->ecb_physbase = segs->ds_addr; - /* - * Space for adapter inquiry information is on the - * tail of the ecb array. - */ - ahb->ha_inq_physbase = ahbecbvtop(ahb, &ahb->ecb_array[AHB_NECB]); -} - -static int -ahbxptattach(struct ahb_softc *ahb) -{ - struct cam_devq *devq; - struct ecb *ecb; - u_int i; - - /* Remeber who are we on the scsi bus */ - ahb->scsi_id = ahb_inb(ahb, SCSIDEF) & HSCSIID; - - /* Use extended translation?? */ - ahb->extended_trans = ahb_inb(ahb, RESV1) & EXTENDED_TRANS; - - /* Fetch adapter inquiry data */ - ecb = ahbecbget(ahb); /* Always succeeds - no outstanding commands */ - ecb->hecb.opcode = ECBOP_READ_HA_INQDATA; - ecb->hecb.flag_word1 = FW1_SUPPRESS_URUN_ERR|FW1_ERR_STATUS_BLK_ONLY; - ecb->hecb.data_ptr = ahb->ha_inq_physbase; - ecb->hecb.data_len = sizeof(struct ha_inquiry_data); - ecb->hecb.sense_ptr = 0; - ecb->state = ECB_ACTIVE; - - /* Tell the adapter about this command */ - ahbqueuembox(ahb, ahbecbvtop(ahb, ecb), - ATTN_STARTECB|ahb->scsi_id); - - /* Poll for interrupt completion */ - for (i = 1000; ecb->state != ECB_FREE && i != 0; i--) { - ahbintr(ahb); - DELAY(1000); - } - - ahb->num_ecbs = MIN(ahb->num_ecbs, - ahb->ha_inq_data->scsi_data.reserved[1]); - printf("ahb%ld: %.8s %s SCSI Adapter, FW Rev. %.4s, ID=%d, %d ECBs\n", - ahb->unit, ahb->ha_inq_data->scsi_data.product, - (ahb->ha_inq_data->scsi_data.flags & 0x4) ? "Differential" - : "Single Ended", - ahb->ha_inq_data->scsi_data.revision, - ahb->scsi_id, ahb->num_ecbs); - - /* Restore sense paddr for future CCB clients */ - ecb->hecb.sense_ptr = ahbsensepaddr(ahbecbvtop(ahb, ecb)); - - ahbecbfree(ahb, ecb); - - /* - * Create the device queue for our SIM. - */ - devq = cam_simq_alloc(ahb->num_ecbs); - if (devq == NULL) - return (ENOMEM); - - /* - * Construct our SIM entry - */ - ahb->sim = cam_sim_alloc(ahbaction, ahbpoll, "ahb", ahb, ahb->unit, - 2, ahb->num_ecbs, devq); - if (ahb->sim == NULL) { - cam_simq_free(devq); - return (ENOMEM); - } - - if (xpt_bus_register(ahb->sim, 0) != CAM_SUCCESS) { - cam_sim_free(ahb->sim, /*free_devq*/TRUE); - return (ENXIO); - } - - if (xpt_create_path(&ahb->path, /*periph*/NULL, - cam_sim_path(ahb->sim), CAM_TARGET_WILDCARD, - CAM_LUN_WILDCARD) != CAM_REQ_CMP) { - xpt_bus_deregister(cam_sim_path(ahb->sim)); - cam_sim_free(ahb->sim, /*free_devq*/TRUE); - return (ENXIO); - } - - /* - * Allow the board to generate interrupts. - */ - ahb_outb(ahb, INTDEF, ahb_inb(ahb, INTDEF) | INTEN); - - return (0); -} - -static void -ahbhandleimmed(struct ahb_softc *ahb, u_int32_t mbox, u_int intstat) -{ - struct ccb_hdr *ccb_h; - u_int target_id; - - if (ahb->immed_cmd == 0) { - printf("ahb%ld: Immediate Command complete with no " - " pending command\n", ahb->unit); - return; - } - - target_id = intstat & INTSTAT_TARGET_MASK; - - ccb_h = LIST_FIRST(&ahb->pending_ccbs); - while (ccb_h != NULL) { - struct ecb *pending_ecb; - union ccb *ccb; - - pending_ecb = (struct ecb *)ccb_h->ccb_ecb_ptr; - ccb = pending_ecb->ccb; - ccb_h = LIST_NEXT(ccb_h, sim_links.le); - if (ccb->ccb_h.target_id == target_id - || target_id == ahb->scsi_id) { - untimeout(ahbtimeout, pending_ecb, - ccb->ccb_h.timeout_ch); - LIST_REMOVE(&ccb->ccb_h, sim_links.le); - if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) - bus_dmamap_unload(ahb->buffer_dmat, - pending_ecb->dmamap); - if (pending_ecb == ahb->immed_ecb) - ccb->ccb_h.status = - CAM_CMD_TIMEOUT|CAM_RELEASE_SIMQ; - else if (target_id == ahb->scsi_id) - ccb->ccb_h.status = CAM_SCSI_BUS_RESET; - else - ccb->ccb_h.status = CAM_BDR_SENT; - ahbecbfree(ahb, pending_ecb); - xpt_done(ccb); - } else if (ahb->immed_ecb != NULL) { - /* Re-instate timeout */ - ccb->ccb_h.timeout_ch = - timeout(ahbtimeout, (caddr_t)pending_ecb, - (ccb->ccb_h.timeout * hz) / 1000); - } - } - - if (ahb->immed_ecb != NULL) { - ahb->immed_ecb = NULL; - printf("ahb%ld: No longer in timeout\n", ahb->unit); - } else if (target_id == ahb->scsi_id) - printf("ahb%ld: SCSI Bus Reset Delivered\n", ahb->unit); - else - printf("ahb%ld: Bus Device Reset Delibered to target %d\n", - ahb->unit, target_id); - - ahb->immed_cmd = 0; -} - -static void -ahbcalcresid(struct ahb_softc *ahb, struct ecb *ecb, union ccb *ccb) -{ - if (ecb->status.data_overrun != 0) { - /* - * Overrun Condition. The hardware doesn't - * provide a meaningful byte count in this case - * (the residual is always 0). Tell the XPT - * layer about the error. - */ - ccb->ccb_h.status = CAM_DATA_RUN_ERR; - } else { - ccb->csio.resid = ecb->status.resid_count; - - if ((ecb->hecb.flag_word1 & FW1_SG_ECB) != 0) { - /* - * For S/G transfers, the adapter provides a pointer - * to the address in the last S/G element used and a - * residual for that element. So, we need to sum up - * the elements that follow it in order to get a real - * residual number. If we have an overrun, the residual - * reported will be 0 and we already know that all S/G - * segments have been exhausted, so we can skip this - * step. - */ - ahb_sg_t *sg; - int num_sg; - - num_sg = ecb->hecb.data_len / sizeof(ahb_sg_t); - - /* Find the S/G the adapter was working on */ - for (sg = ecb->sg_list; - num_sg != 0 && sg->addr != ecb->status.resid_addr; - num_sg--, sg++) - ; - - /* Skip it */ - num_sg--; - sg++; - - /* Sum the rest */ - for (; num_sg != 0; num_sg--, sg++) - ccb->csio.resid += sg->len; - } - /* Underruns are not errors */ - ccb->ccb_h.status = CAM_REQ_CMP; - } -} - -static void -ahbprocesserror(struct ahb_softc *ahb, struct ecb *ecb, union ccb *ccb) -{ - struct hardware_ecb *hecb; - struct ecb_status *status; - - hecb = &ecb->hecb; - status = &ecb->status; - switch (status->ha_status) { - case HS_OK: - ccb->csio.scsi_status = status->scsi_status; - if (status->scsi_status != 0) { - ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; - if (status->sense_stored) { - ccb->ccb_h.status |= CAM_AUTOSNS_VALID; - ccb->csio.sense_resid = - ccb->csio.sense_len - status->sense_len; - bcopy(&ecb->sense, &ccb->csio.sense_data, - status->sense_len); - } - } - break; - case HS_TARGET_NOT_ASSIGNED: - ccb->ccb_h.status = CAM_PATH_INVALID; - break; - case HS_SEL_TIMEOUT: - ccb->ccb_h.status = CAM_SEL_TIMEOUT; - break; - case HS_DATA_RUN_ERR: - ahbcalcresid(ahb, ecb, ccb); - break; - case HS_UNEXPECTED_BUSFREE: - ccb->ccb_h.status = CAM_UNEXP_BUSFREE; - break; - case HS_INVALID_PHASE: - ccb->ccb_h.status = CAM_SEQUENCE_FAIL; - break; - case HS_REQUEST_SENSE_FAILED: - ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; - break; - case HS_TAG_MSG_REJECTED: - { - struct ccb_trans_settings neg; - - xpt_print_path(ccb->ccb_h.path); - printf("refuses tagged commands. Performing " - "non-tagged I/O\n"); - neg.flags = 0; - neg.valid = CCB_TRANS_TQ_VALID; - xpt_setup_ccb(&neg.ccb_h, ccb->ccb_h.path, /*priority*/1); - xpt_async(AC_TRANSFER_NEG, ccb->ccb_h.path, &neg); - ahb->tags_permitted &= ~(0x01 << ccb->ccb_h.target_id); - ccb->ccb_h.status = CAM_MSG_REJECT_REC; - break; - } - case HS_FIRMWARE_LOAD_REQ: - case HS_HARDWARE_ERR: - /* - * Tell the system that the Adapter - * is no longer functional. - */ - ccb->ccb_h.status = CAM_NO_HBA; - break; - case HS_CMD_ABORTED_HOST: - case HS_CMD_ABORTED_ADAPTER: - case HS_ATN_TARGET_FAILED: - case HS_SCSI_RESET_ADAPTER: - case HS_SCSI_RESET_INCOMING: - ccb->ccb_h.status = CAM_SCSI_BUS_RESET; - break; - case HS_DUP_TCB_RECEIVED: - case HS_INVALID_OPCODE: - case HS_INVALID_CMD_LINK: - case HS_INVALID_ECB_PARAM: - case HS_PROGRAM_CKSUM_ERROR: - panic("ahb%ld: Can't happen host status %x occurred", - ahb->unit, status->ha_status); - break; - } - if (ccb->ccb_h.status != CAM_REQ_CMP) { - xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); - ccb->ccb_h.status |= CAM_DEV_QFRZN; - } -} - -static void -ahbdone(struct ahb_softc *ahb, u_int32_t mbox, u_int intstat) -{ - struct ecb *ecb; - union ccb *ccb; - - ecb = ahbecbptov(ahb, mbox); - - if ((ecb->state & ECB_ACTIVE) == 0) - panic("ecb not active"); - - ccb = ecb->ccb; - - if (ccb != NULL) { - untimeout(ahbtimeout, ecb, ccb->ccb_h.timeout_ch); - LIST_REMOVE(&ccb->ccb_h, sim_links.le); - - if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { - bus_dmasync_op_t op; - - if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) - op = BUS_DMASYNC_POSTREAD; - else - op = BUS_DMASYNC_POSTWRITE; - bus_dmamap_sync(ahb->buffer_dmat, ecb->dmamap, op); - bus_dmamap_unload(ahb->buffer_dmat, ecb->dmamap); - } - - if ((intstat & INTSTAT_MASK) == INTSTAT_ECB_OK) { - ccb->ccb_h.status = CAM_REQ_CMP; - ccb->csio.resid = 0; - } else { - ahbprocesserror(ahb, ecb, ccb); - } - ahbecbfree(ahb, ecb); - xpt_done(ccb); - } else { - /* Non CCB Command */ - if ((intstat & INTSTAT_MASK) != INTSTAT_ECB_OK) { - printf("ahb%ld: Command 0%x Failed %x:%x:%x\n", - ahb->unit, ecb->hecb.opcode, - *((u_int16_t*)&ecb->status), - ecb->status.ha_status, ecb->status.resid_count); - } - /* Client owns this ECB and will release it. */ - } -} - -/* - * Catch an interrupt from the adaptor - */ -static void -ahbintr(void *arg) -{ - struct ahb_softc *ahb; - u_int intstat; - u_int32_t mbox; - - ahb = (struct ahb_softc *)arg; - - while (ahb_inb(ahb, HOSTSTAT) & HOSTSTAT_INTPEND) { - /* - * Fetch information about this interrupt. - */ - intstat = ahb_inb(ahb, INTSTAT); - mbox = ahb_inl(ahb, MBOXIN0); - - /* - * Reset interrupt latch. - */ - ahb_outb(ahb, CONTROL, CNTRL_CLRINT); - - /* - * Process the completed operation - */ - switch (intstat & INTSTAT_MASK) { - case INTSTAT_ECB_OK: - case INTSTAT_ECB_CMPWRETRY: - case INTSTAT_ECB_CMPWERR: - ahbdone(ahb, mbox, intstat); - break; - case INTSTAT_AEN_OCCURED: - if ((intstat & INTSTAT_TARGET_MASK) == ahb->scsi_id) { - /* Bus Reset */ - xpt_print_path(ahb->path); - switch (mbox) { - case HS_SCSI_RESET_ADAPTER: - printf("Host Adapter Initiated " - "Bus Reset occurred\n"); - break; - case HS_SCSI_RESET_INCOMING: - printf("Bus Reset Initiated " - "by another device occurred\n"); - break; - } - /* Notify the XPT */ - xpt_async(AC_BUS_RESET, ahb->path, NULL); - break; - } - printf("Unsupported initiator selection AEN occured\n"); - break; - case INTSTAT_IMMED_OK: - case INTSTAT_IMMED_ERR: - ahbhandleimmed(ahb, mbox, intstat); - break; - case INTSTAT_HW_ERR: - panic("Unrecoverable hardware Error Occurred\n"); - } - } -} - -static void -ahbexecuteecb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) -{ - struct ecb *ecb; - union ccb *ccb; - struct ahb_softc *ahb; - u_int32_t ecb_paddr; - int s; - - ecb = (struct ecb *)arg; - ccb = ecb->ccb; - ahb = (struct ahb_softc *)ccb->ccb_h.ccb_ahb_ptr; - - if (error != 0) { - if (error != EFBIG) - printf("ahb%ld: Unexepected error 0x%x returned from " - "bus_dmamap_load\n", ahb->unit, error); - if (ccb->ccb_h.status == CAM_REQ_INPROG) { - xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); - ccb->ccb_h.status = CAM_REQ_TOO_BIG|CAM_DEV_QFRZN; - } - ahbecbfree(ahb, ecb); - xpt_done(ccb); - return; - } - - ecb_paddr = ahbecbvtop(ahb, ecb); - - if (nseg != 0) { - ahb_sg_t *sg; - bus_dma_segment_t *end_seg; - bus_dmasync_op_t op; - - end_seg = dm_segs + nseg; - - /* Copy the segments into our SG list */ - sg = ecb->sg_list; - while (dm_segs < end_seg) { - sg->addr = dm_segs->ds_addr; - sg->len = dm_segs->ds_len; - sg++; - dm_segs++; - } - - if (nseg > 1) { - ecb->hecb.flag_word1 |= FW1_SG_ECB; - ecb->hecb.data_ptr = ahbsgpaddr(ecb_paddr); - ecb->hecb.data_len = sizeof(ahb_sg_t) * nseg; - } else { - ecb->hecb.data_ptr = ecb->sg_list->addr; - ecb->hecb.data_len = ecb->sg_list->len; - } - - if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { -/* ecb->hecb.flag_word2 |= FW2_DATA_DIR_IN; */ - op = BUS_DMASYNC_PREREAD; - } else { - op = BUS_DMASYNC_PREWRITE; - } - /* ecb->hecb.flag_word2 |= FW2_CHECK_DATA_DIR; */ - - bus_dmamap_sync(ahb->buffer_dmat, ecb->dmamap, op); - - } else { - ecb->hecb.data_ptr = 0; - ecb->hecb.data_len = 0; - } - - s = splcam(); - - /* - * Last time we need to check if this CCB needs to - * be aborted. - */ - if (ccb->ccb_h.status != CAM_REQ_INPROG) { - if (nseg != 0) - bus_dmamap_unload(ahb->buffer_dmat, ecb->dmamap); - ahbecbfree(ahb, ecb); - xpt_done(ccb); - splx(s); - return; - } - - ecb->state = ECB_ACTIVE; - ccb->ccb_h.status |= CAM_SIM_QUEUED; - LIST_INSERT_HEAD(&ahb->pending_ccbs, &ccb->ccb_h, sim_links.le); - - /* Tell the adapter about this command */ - ahbqueuembox(ahb, ecb_paddr, ATTN_STARTECB|ccb->ccb_h.target_id); - - ccb->ccb_h.timeout_ch = timeout(ahbtimeout, (caddr_t)ecb, - (ccb->ccb_h.timeout * hz) / 1000); - splx(s); -} - -static void -ahbaction(struct cam_sim *sim, union ccb *ccb) -{ - struct ahb_softc *ahb; - - CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ahbaction\n")); - - ahb = (struct ahb_softc *)cam_sim_softc(sim); - - switch (ccb->ccb_h.func_code) { - /* Common cases first */ - case XPT_SCSI_IO: /* Execute the requested I/O operation */ - { - struct ecb *ecb; - struct hardware_ecb *hecb; - - /* - * get an ecb to use. - */ - if ((ecb = ahbecbget(ahb)) == NULL) { - /* Should never occur */ - panic("Failed to get an ecb"); - } - - /* - * So we can find the ECB when an abort is requested - */ - ecb->ccb = ccb; - ccb->ccb_h.ccb_ecb_ptr = ecb; - ccb->ccb_h.ccb_ahb_ptr = ahb; - - /* - * Put all the arguments for the xfer in the ecb - */ - hecb = &ecb->hecb; - hecb->opcode = ECBOP_INITIATOR_SCSI_CMD; - hecb->flag_word1 = FW1_AUTO_REQUEST_SENSE - | FW1_ERR_STATUS_BLK_ONLY; - hecb->flag_word2 = ccb->ccb_h.target_lun - | FW2_NO_RETRY_ON_BUSY; - if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { - hecb->flag_word2 |= FW2_TAG_ENB - | ((ccb->csio.tag_action & 0x3) - << FW2_TAG_TYPE_SHIFT); - } - if ((ccb->ccb_h.flags & CAM_DIS_DISCONNECT) != 0) - hecb->flag_word2 |= FW2_DISABLE_DISC; - hecb->sense_len = ccb->csio.sense_len; - hecb->cdb_len = ccb->csio.cdb_len; - if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { - if ((ccb->ccb_h.flags & CAM_CDB_PHYS) == 0) { - bcopy(ccb->csio.cdb_io.cdb_ptr, - hecb->cdb, hecb->cdb_len); - } else { - /* I guess I could map it in... */ - ccb->ccb_h.status = CAM_REQ_INVALID; - ahbecbfree(ahb, ecb); - xpt_done(ccb); - return; - } - } else { - bcopy(ccb->csio.cdb_io.cdb_bytes, - hecb->cdb, hecb->cdb_len); - } - - /* - * If we have any data to send with this command, - * map it into bus space. - */ - if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { - if ((ccb->ccb_h.flags & CAM_SCATTER_VALID) == 0) { - /* - * We've been given a pointer - * to a single buffer. - */ - if ((ccb->ccb_h.flags & CAM_DATA_PHYS)==0) { - int s; - int error; - - s = splsoftvm(); - error = bus_dmamap_load( - ahb->buffer_dmat, - ecb->dmamap, - ccb->csio.data_ptr, - ccb->csio.dxfer_len, - ahbexecuteecb, - ecb, /*flags*/0); - if (error == EINPROGRESS) { - /* - * So as to maintain ordering, - * freeze the controller queue - * until our mapping is - * returned. - */ - xpt_freeze_simq(ahb->sim, 1); - ccb->ccb_h.status |= - CAM_RELEASE_SIMQ; - } - splx(s); - } else { - struct bus_dma_segment seg; - - /* Pointer to physical buffer */ - seg.ds_addr = - (bus_addr_t)ccb->csio.data_ptr; - seg.ds_len = ccb->csio.dxfer_len; - ahbexecuteecb(ecb, &seg, 1, 0); - } - } else { - struct bus_dma_segment *segs; - - if ((ccb->ccb_h.flags & CAM_DATA_PHYS) != 0) - panic("ahbaction - Physical segment " - "pointers unsupported"); - - if ((ccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0) - panic("btaction - Virtual segment " - "addresses unsupported"); - - /* Just use the segments provided */ - segs = (struct bus_dma_segment *) - ccb->csio.data_ptr; - ahbexecuteecb(ecb, segs, ccb->csio.sglist_cnt, - 0); - } - } else { - ahbexecuteecb(ecb, NULL, 0, 0); - } - break; - } - case XPT_EN_LUN: /* Enable LUN as a target */ - case XPT_TARGET_IO: /* Execute target I/O request */ - case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ - case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ - case XPT_ABORT: /* Abort the specified CCB */ - /* XXX Implement */ - ccb->ccb_h.status = CAM_REQ_INVALID; - xpt_done(ccb); - break; - case XPT_SET_TRAN_SETTINGS: - { - ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; - xpt_done(ccb); - break; - } - case XPT_GET_TRAN_SETTINGS: - /* Get default/user set transfer settings for the target */ - { - struct ccb_trans_settings *cts; - u_int target_mask; - - cts = &ccb->cts; - target_mask = 0x01 << ccb->ccb_h.target_id; - if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { - cts->flags = 0; - if ((ahb->disc_permitted & target_mask) != 0) - cts->flags |= CCB_TRANS_DISC_ENB; - if ((ahb->tags_permitted & target_mask) != 0) - cts->flags |= CCB_TRANS_TAG_ENB; - cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; - cts->sync_period = 25; /* 10MHz */ - - if (cts->sync_period != 0) - cts->sync_offset = 15; - - cts->valid = CCB_TRANS_SYNC_RATE_VALID - | CCB_TRANS_SYNC_OFFSET_VALID - | CCB_TRANS_BUS_WIDTH_VALID - | CCB_TRANS_DISC_VALID - | CCB_TRANS_TQ_VALID; - ccb->ccb_h.status = CAM_REQ_CMP; - } else { - ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; - } - xpt_done(ccb); - break; - } - case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ - { - int i; - - ahb->immed_cmd = IMMED_RESET; - ahbqueuembox(ahb, IMMED_RESET, ATTN_IMMED|ccb->ccb_h.target_id); - /* Poll for interrupt completion */ - for (i = 1000; ahb->immed_cmd != 0 && i != 0; i--) - DELAY(1000); - break; - } - case XPT_CALC_GEOMETRY: - { - struct ccb_calc_geometry *ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; - - ccg = &ccb->ccg; - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); - - if (size_mb > 1024 && (ahb->extended_trans != 0)) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; - xpt_done(ccb); - break; - } - case XPT_RESET_BUS: /* Reset the specified SCSI bus */ - { - int i; - - ahb->immed_cmd = IMMED_RESET; - ahbqueuembox(ahb, IMMED_RESET, ATTN_IMMED|ahb->scsi_id); - /* Poll for interrupt completion */ - for (i = 1000; ahb->immed_cmd != 0 && i != 0; i--) - DELAY(1000); - ccb->ccb_h.status = CAM_REQ_CMP; - xpt_done(ccb); - break; - } - case XPT_TERM_IO: /* Terminate the I/O process */ - /* XXX Implement */ - ccb->ccb_h.status = CAM_REQ_INVALID; - xpt_done(ccb); - break; - case XPT_PATH_INQ: /* Path routing inquiry */ - { - struct ccb_pathinq *cpi = &ccb->cpi; - - cpi->version_num = 1; /* XXX??? */ - cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE; - cpi->target_sprt = 0; - cpi->hba_misc = 0; - cpi->hba_eng_cnt = 0; - cpi->max_target = 7; - cpi->max_lun = 7; - cpi->initiator_id = ahb->scsi_id; - cpi->bus_id = cam_sim_bus(sim); - strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); - strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN); - strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); - cpi->unit_number = cam_sim_unit(sim); - cpi->ccb_h.status = CAM_REQ_CMP; - xpt_done(ccb); - break; - } -#if 0 - /* Need these??? */ - case XPT_IMMED_NOTIFY: /* Notify Host Target driver of event */ - case XPT_NOTIFY_ACK: /* Acknowledgement of event */ -#endif - default: - ccb->ccb_h.status = CAM_REQ_INVALID; - xpt_done(ccb); - break; - } -} - -static void -ahbpoll(struct cam_sim *sim) -{ - ahbintr(cam_sim_softc(sim)); -} - -void -ahbtimeout(void *arg) -{ - struct ecb *ecb; - union ccb *ccb; - struct ahb_softc *ahb; - int s; - - ecb = (struct ecb *)arg; - ccb = ecb->ccb; - ahb = (struct ahb_softc *)ccb->ccb_h.ccb_ahb_ptr; - xpt_print_path(ccb->ccb_h.path); - printf("ECB %p - timed out\n", (void *)ecb); - - s = splcam(); - - if ((ecb->state & ECB_ACTIVE) == 0) { - xpt_print_path(ccb->ccb_h.path); - printf("ECB %p - timed out ECB already completed\n", - (void *)ecb); - splx(s); - return; - } - /* - * In order to simplify the recovery process, we ask the XPT - * layer to halt the queue of new transactions and we traverse - * the list of pending CCBs and remove their timeouts. This - * means that the driver attempts to clear only one error - * condition at a time. In general, timeouts that occur - * close together are related anyway, so there is no benefit - * in attempting to handle errors in parrallel. Timeouts will - * be reinstated when the recovery process ends. - */ - if ((ecb->state & ECB_DEVICE_RESET) == 0) { - struct ccb_hdr *ccb_h; - - if ((ecb->state & ECB_RELEASE_SIMQ) == 0) { - xpt_freeze_simq(ahb->sim, /*count*/1); - ecb->state |= ECB_RELEASE_SIMQ; - } - - ccb_h = LIST_FIRST(&ahb->pending_ccbs); - while (ccb_h != NULL) { - struct ecb *pending_ecb; - - pending_ecb = (struct ecb *)ccb_h->ccb_ecb_ptr; - untimeout(ahbtimeout, pending_ecb, ccb_h->timeout_ch); - ccb_h = LIST_NEXT(ccb_h, sim_links.le); - } - - /* Store for our interrupt handler */ - ahb->immed_ecb = ecb; - - /* - * Send a Bus Device Reset message: - * The target that is holding up the bus may not - * be the same as the one that triggered this timeout - * (different commands have different timeout lengths), - * but we have no way of determining this from our - * timeout handler. Our strategy here is to queue a - * BDR message to the target of the timed out command. - * If this fails, we'll get another timeout 2 seconds - * later which will attempt a bus reset. - */ - xpt_print_path(ccb->ccb_h.path); - printf("Queuing BDR\n"); - ecb->state |= ECB_DEVICE_RESET; - ccb->ccb_h.timeout_ch = - timeout(ahbtimeout, (caddr_t)ecb, 2 * hz); - - ahb->immed_cmd = IMMED_RESET; - ahbqueuembox(ahb, IMMED_RESET, ATTN_IMMED|ccb->ccb_h.target_id); - } else if ((ecb->state & ECB_SCSIBUS_RESET) != 0) { - /* - * Try a SCSI bus reset. We do this only if we - * have already attempted to clear the condition with a BDR. - */ - xpt_print_path(ccb->ccb_h.path); - printf("Attempting SCSI Bus reset\n"); - ecb->state |= ECB_SCSIBUS_RESET; - ccb->ccb_h.timeout_ch = - timeout(ahbtimeout, (caddr_t)ecb, 2 * hz); - ahb->immed_cmd = IMMED_RESET; - ahbqueuembox(ahb, IMMED_RESET, ATTN_IMMED|ahb->scsi_id); - } else { - /* Bring out the hammer... */ - ahbreset(ahb); - - /* Simulate the reset complete interrupt */ - ahbhandleimmed(ahb, 0, ahb->scsi_id|INTSTAT_IMMED_OK); - } - - splx(s); -} - -#endif /* NEISA */ |