diff options
| author | cvs2svn <cvs2svn@FreeBSD.org> | 2000-09-22 22:19:56 +0000 |
|---|---|---|
| committer | cvs2svn <cvs2svn@FreeBSD.org> | 2000-09-22 22:19:56 +0000 |
| commit | 8460d7052a36f37e7d18b53c42642b36f38a4115 (patch) | |
| tree | fa8ddfecd89511368d84b5f4743048f75f0959e7 | |
| parent | 104ee923e2ad53596a834b173d1641de43564b05 (diff) | |
Notes
| -rw-r--r-- | sys/dev/aic7xxx/aic7xxx_93cx6.c | 213 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aic7xxx_93cx6.h | 92 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aic7xxx_freebsd.c | 1822 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aic7xxx_freebsd.h | 454 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aic7xxx_inline.h | 366 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aic7xxx_pci.c | 1927 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aicasm/aicasm.c | 770 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aicasm/aicasm.h | 78 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aicasm/aicasm_gram.y | 1455 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aicasm/aicasm_insformat.h | 129 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aicasm/aicasm_scan.l | 302 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aicasm/aicasm_symbol.c | 468 | ||||
| -rw-r--r-- | sys/dev/aic7xxx/aicasm/aicasm_symbol.h | 177 |
13 files changed, 8253 insertions, 0 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx_93cx6.c b/sys/dev/aic7xxx/aic7xxx_93cx6.c new file mode 100644 index 000000000000..a7dc77b2704d --- /dev/null +++ b/sys/dev/aic7xxx/aic7xxx_93cx6.c @@ -0,0 +1,213 @@ +/* + * Interface for the 93C66/56/46/26/06 serial eeprom parts. + * + * Copyright (c) 1995, 1996 Daniel M. Eischen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aic7xxx_93cx6.c#3 $ + * + * $FreeBSD$ + */ + +/* + * The instruction set of the 93C66/56/46/26/06 chips are as follows: + * + * Start OP * + * Function Bit Code Address** Data Description + * ------------------------------------------------------------------- + * READ 1 10 A5 - A0 Reads data stored in memory, + * starting at specified address + * EWEN 1 00 11XXXX Write enable must preceed + * all programming modes + * ERASE 1 11 A5 - A0 Erase register A5A4A3A2A1A0 + * WRITE 1 01 A5 - A0 D15 - D0 Writes register + * ERAL 1 00 10XXXX Erase all registers + * WRAL 1 00 01XXXX D15 - D0 Writes to all registers + * EWDS 1 00 00XXXX Disables all programming + * instructions + * *Note: A value of X for address is a don't care condition. + * **Note: There are 8 address bits for the 93C56/66 chips unlike + * the 93C46/26/06 chips which have 6 address bits. + * + * The 93C46 has a four wire interface: clock, chip select, data in, and + * data out. In order to perform one of the above functions, you need + * to enable the chip select for a clock period (typically a minimum of + * 1 usec, with the clock high and low a minimum of 750 and 250 nsec + * respectively). While the chip select remains high, you can clock in + * the instructions (above) starting with the start bit, followed by the + * OP code, Address, and Data (if needed). For the READ instruction, the + * requested 16-bit register contents is read from the data out line but + * is preceded by an initial zero (leading 0, followed by 16-bits, MSB + * first). The clock cycling from low to high initiates the next data + * bit to be sent from the chip. + * + */ + +#ifdef __linux__ +#include "aic7xxx_linux.h" +#include "aic7xxx_inline.h" +#include "aic7xxx_93cx6.h" +#endif + +#ifdef __FreeBSD__ +#include <dev/aic7xxx/aic7xxx_freebsd.h> +#include <dev/aic7xxx/aic7xxx_inline.h> +#include <dev/aic7xxx/aic7xxx_93cx6.h> +#endif + +/* + * Right now, we only have to read the SEEPROM. But we make it easier to + * add other 93Cx6 functions. + */ +static struct seeprom_cmd { + uint8_t len; + uint8_t bits[3]; +} seeprom_read = {3, {1, 1, 0}}; + +/* + * Wait for the SEERDY to go high; about 800 ns. + */ +#define CLOCK_PULSE(sd, rdy) \ + while ((SEEPROM_STATUS_INB(sd) & rdy) == 0) { \ + ; /* Do nothing */ \ + } \ + (void)SEEPROM_INB(sd); /* Clear clock */ + +/* + * Read the serial EEPROM and returns 1 if successful and 0 if + * not successful. + */ +int +read_seeprom(sd, buf, start_addr, count) + struct seeprom_descriptor *sd; + uint16_t *buf; + u_int start_addr; + u_int count; +{ + int i = 0; + u_int k = 0; + uint16_t v; + uint8_t temp; + + /* + * Read the requested registers of the seeprom. The loop + * will range from 0 to count-1. + */ + for (k = start_addr; k < count + start_addr; k++) { + /* Send chip select for one clock cycle. */ + temp = sd->sd_MS ^ sd->sd_CS; + SEEPROM_OUTB(sd, temp ^ sd->sd_CK); + CLOCK_PULSE(sd, sd->sd_RDY); + + /* + * Now we're ready to send the read command followed by the + * address of the 16-bit register we want to read. + */ + for (i = 0; i < seeprom_read.len; i++) { + if (seeprom_read.bits[i] != 0) + temp ^= sd->sd_DO; + SEEPROM_OUTB(sd, temp); + CLOCK_PULSE(sd, sd->sd_RDY); + SEEPROM_OUTB(sd, temp ^ sd->sd_CK); + CLOCK_PULSE(sd, sd->sd_RDY); + if (seeprom_read.bits[i] != 0) + temp ^= sd->sd_DO; + } + /* Send the 6 or 8 bit address (MSB first, LSB last). */ + for (i = (sd->sd_chip - 1); i >= 0; i--) { + if ((k & (1 << i)) != 0) + temp ^= sd->sd_DO; + SEEPROM_OUTB(sd, temp); + CLOCK_PULSE(sd, sd->sd_RDY); + SEEPROM_OUTB(sd, temp ^ sd->sd_CK); + CLOCK_PULSE(sd, sd->sd_RDY); + if ((k & (1 << i)) != 0) + temp ^= sd->sd_DO; + } + + /* + * Now read the 16 bit register. An initial 0 precedes the + * register contents which begins with bit 15 (MSB) and ends + * with bit 0 (LSB). The initial 0 will be shifted off the + * top of our word as we let the loop run from 0 to 16. + */ + v = 0; + for (i = 16; i >= 0; i--) { + SEEPROM_OUTB(sd, temp); + CLOCK_PULSE(sd, sd->sd_RDY); + v <<= 1; + if (SEEPROM_DATA_INB(sd) & sd->sd_DI) + v |= 1; + SEEPROM_OUTB(sd, temp ^ sd->sd_CK); + CLOCK_PULSE(sd, sd->sd_RDY); + } + + buf[k - start_addr] = v; + + /* Reset the chip select for the next command cycle. */ + temp = sd->sd_MS; + SEEPROM_OUTB(sd, temp); + CLOCK_PULSE(sd, sd->sd_RDY); + SEEPROM_OUTB(sd, temp ^ sd->sd_CK); + CLOCK_PULSE(sd, sd->sd_RDY); + SEEPROM_OUTB(sd, temp); + CLOCK_PULSE(sd, sd->sd_RDY); + } +#ifdef AHC_DUMP_EEPROM + printf("\nSerial EEPROM:\n\t"); + for (k = 0; k < count; k = k + 1) { + if (((k % 8) == 0) && (k != 0)) { + printf ("\n\t"); + } + printf (" 0x%x", buf[k]); + } + printf ("\n"); +#endif + return (1); +} + +int +verify_cksum(struct seeprom_config *sc) +{ + int i; + int maxaddr; + uint32_t checksum; + uint16_t *scarray; + + maxaddr = (sizeof(*sc)/2) - 1; + checksum = 0; + scarray = (uint16_t *)sc; + + for (i = 0; i < maxaddr; i++) + checksum = checksum + scarray[i]; + if (checksum == 0 + || (checksum & 0xFFFF) != sc->checksum) { + return (0); + } else { + return(1); + } +} diff --git a/sys/dev/aic7xxx/aic7xxx_93cx6.h b/sys/dev/aic7xxx/aic7xxx_93cx6.h new file mode 100644 index 000000000000..323bfe8fa05f --- /dev/null +++ b/sys/dev/aic7xxx/aic7xxx_93cx6.h @@ -0,0 +1,92 @@ +/* + * Interface to the 93C46/56 serial EEPROM that is used to store BIOS + * settings for the aic7xxx based adaptec SCSI controllers. It can + * also be used for 93C26 and 93C06 serial EEPROMS. + * + * Copyright (c) 1994, 1995, 2000 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, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aic7xxx_93cx6.h#3 $ + * + * $FreeBSD$ + */ +#ifndef _AIC7XXX_93CX6_H_ +#define _AIC7XXX_93CX6_H_ + +typedef enum { + C46 = 6, + C56_66 = 8 +} seeprom_chip_t; + +struct seeprom_descriptor { + struct ahc_softc *sd_ahc; + u_int sd_control_offset; + u_int sd_status_offset; + u_int sd_dataout_offset; + seeprom_chip_t sd_chip; + uint16_t sd_MS; + uint16_t sd_RDY; + uint16_t sd_CS; + uint16_t sd_CK; + uint16_t sd_DO; + uint16_t sd_DI; +}; + +/* + * This function will read count 16-bit words from the serial EEPROM and + * return their value in buf. The port address of the aic7xxx serial EEPROM + * control register is passed in as offset. The following parameters are + * also passed in: + * + * CS - Chip select + * CK - Clock + * DO - Data out + * DI - Data in + * RDY - SEEPROM ready + * MS - Memory port mode select + * + * A failed read attempt returns 0, and a successful read returns 1. + */ + +#define SEEPROM_INB(sd) \ + ahc_inb(sd->sd_ahc, sd->sd_control_offset) +#define SEEPROM_OUTB(sd, value) \ +do { \ + ahc_outb(sd->sd_ahc, sd->sd_control_offset, value); \ + ahc_flush_device_writes(sd->sd_ahc); \ +} while(0) + +#define SEEPROM_STATUS_INB(sd) \ + ahc_inb(sd->sd_ahc, sd->sd_status_offset) +#define SEEPROM_DATA_INB(sd) \ + ahc_inb(sd->sd_ahc, sd->sd_dataout_offset) + +int read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf, + u_int start_addr, u_int count); +int verify_cksum(struct seeprom_config *sc); + +#endif /* _AIC7XXX_93CX6_H_ */ diff --git a/sys/dev/aic7xxx/aic7xxx_freebsd.c b/sys/dev/aic7xxx/aic7xxx_freebsd.c new file mode 100644 index 000000000000..6f1b4d13cd08 --- /dev/null +++ b/sys/dev/aic7xxx/aic7xxx_freebsd.c @@ -0,0 +1,1822 @@ +/* + * Bus independent FreeBSD shim for the aic7xxx based adaptec SCSI controllers + * + * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 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, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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$ + * + * $FreeBSD$ + */ + +#include <dev/aic7xxx/aic7xxx_freebsd.h> +#include <dev/aic7xxx/aic7xxx_inline.h> + +#include <sys/eventhandler.h> + +#ifndef AHC_TMODE_ENABLE +#define AHC_TMODE_ENABLE 0 +#endif + +#define ccb_scb_ptr spriv_ptr0 +#define ccb_ahc_ptr spriv_ptr1 + +#ifdef AHC_DEBUG +static int ahc_debug = AHC_DEBUG; +#endif + +static void ahc_freebsd_intr(void *arg); + +#if UNUSED +static void ahc_dump_targcmd(struct target_cmd *cmd); +#endif +static void ahc_action(struct cam_sim *sim, union ccb *ccb); +static void ahc_get_tran_settings(struct ahc_softc *ahc, + int our_id, char channel, + struct ccb_trans_settings *cts); +static void ahc_async(void *callback_arg, uint32_t code, + struct cam_path *path, void *arg); +static void ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, + int nsegments, int error); +static void ahc_poll(struct cam_sim *sim); +static void ahc_setup_data(struct ahc_softc *ahc, struct cam_sim *sim, + struct ccb_scsiio *csio, struct scb *scb); +static void ahc_abort_ccb(struct ahc_softc *ahc, struct cam_sim *sim, + union ccb *ccb); +static int ahc_create_path(struct ahc_softc *ahc, + char channel, u_int target, u_int lun, + struct cam_path **path); + +static void ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb); + +static int +ahc_create_path(struct ahc_softc *ahc, char channel, u_int target, + u_int lun, struct cam_path **path) +{ + path_id_t path_id; + + if (channel == 'B') + path_id = cam_sim_path(ahc->platform_data->sim_b); + else + path_id = cam_sim_path(ahc->platform_data->sim); + + return (xpt_create_path(path, /*periph*/NULL, + path_id, target, lun)); +} + +/* + * Attach all the sub-devices we can find + */ +int +ahc_attach(struct ahc_softc *ahc) +{ + char ahc_info[256]; + struct ccb_setasync csa; + struct cam_devq *devq; + int bus_id; + int bus_id2; + struct cam_sim *sim; + struct cam_sim *sim2; + struct cam_path *path; + struct cam_path *path2; + long s; + int count; + int error; + + count = 0; + sim = NULL; + sim2 = NULL; + + ahc_controller_info(ahc, ahc_info); + printf("%s\n", ahc_info); + ahc_lock(ahc, &s); + /* Hook up our interrupt handler */ + if ((error = bus_setup_intr(ahc->dev_softc, ahc->platform_data->irq, + INTR_TYPE_CAM, ahc_freebsd_intr, ahc, + &ahc->platform_data->ih)) != 0) { + device_printf(ahc->dev_softc, "bus_setup_intr() failed: %d\n", + error); + goto fail; + } + + /* + * Attach secondary channel first if the user has + * declared it the primary channel. + */ + if ((ahc->flags & AHC_CHANNEL_B_PRIMARY) != 0) { + bus_id = 1; + bus_id2 = 0; + } else { + bus_id = 0; + bus_id2 = 1; + } + + /* + * Create the device queue for our SIM(s). + */ + devq = cam_simq_alloc(AHC_SCB_MAX); + if (devq == NULL) + goto fail; + + /* + * Construct our first channel SIM entry + */ + sim = cam_sim_alloc(ahc_action, ahc_poll, "ahc", ahc, + device_get_unit(ahc->dev_softc), + 1, AHC_SCB_MAX, devq); + if (sim == NULL) { + cam_simq_free(devq); + goto fail; + } + + if (xpt_bus_register(sim, bus_id) != CAM_SUCCESS) { + cam_sim_free(sim, /*free_devq*/TRUE); + sim = NULL; + goto fail; + } + + if (xpt_create_path(&path, /*periph*/NULL, + cam_sim_path(sim), CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(sim)); + cam_sim_free(sim, /*free_devq*/TRUE); + sim = NULL; + goto fail; + } + + xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = AC_LOST_DEVICE; + csa.callback = ahc_async; + csa.callback_arg = sim; + xpt_action((union ccb *)&csa); + count++; + + if (ahc->features & AHC_TWIN) { + sim2 = cam_sim_alloc(ahc_action, ahc_poll, "ahc", + ahc, device_get_unit(ahc->dev_softc), 1, + AHC_SCB_MAX, devq); + + if (sim2 == NULL) { + printf("ahc_attach: Unable to attach second " + "bus due to resource shortage"); + goto fail; + } + + if (xpt_bus_register(sim2, bus_id2) != CAM_SUCCESS) { + printf("ahc_attach: Unable to attach second " + "bus due to resource shortage"); + /* + * We do not want to destroy the device queue + * because the first bus is using it. + */ + cam_sim_free(sim2, /*free_devq*/FALSE); + goto fail; + } + + if (xpt_create_path(&path2, /*periph*/NULL, + cam_sim_path(sim2), + CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(sim2)); + cam_sim_free(sim2, /*free_devq*/FALSE); + sim2 = NULL; + goto fail; + } + xpt_setup_ccb(&csa.ccb_h, path2, /*priority*/5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = AC_LOST_DEVICE; + csa.callback = ahc_async; + csa.callback_arg = sim2; + xpt_action((union ccb *)&csa); + count++; + } + +fail: + if ((ahc->flags & AHC_CHANNEL_B_PRIMARY) != 0) { + ahc->platform_data->sim_b = sim; + ahc->platform_data->path_b = path; + ahc->platform_data->sim = sim2; + ahc->platform_data->path = path2; + } else { + ahc->platform_data->sim = sim; + ahc->platform_data->path = path; + ahc->platform_data->sim_b = sim2; + ahc->platform_data->path_b = path2; + } + ahc_unlock(ahc, &s); + + if (count != 0) + /* We have to wait until after any system dumps... */ + EVENTHANDLER_REGISTER(shutdown_final, ahc_shutdown, + ahc, SHUTDOWN_PRI_DEFAULT); + + return (count); +} + +/* + * Catch an interrupt from the adapter + */ +void +ahc_freebsd_intr(void *arg) +{ + struct ahc_softc *ahc; + + ahc = (struct ahc_softc *)arg; + ahc_intr(ahc); +} + +/* + * We have an scb which has been processed by the + * adaptor, now we look to see how the operation + * went. + */ +void +ahc_done(struct ahc_softc *ahc, struct scb *scb) +{ + union ccb *ccb; + + CAM_DEBUG(scb->io_ctx->ccb_h.path, CAM_DEBUG_TRACE, + ("ahc_done - scb %d\n", scb->hscb->tag)); + + ccb = scb->io_ctx; + LIST_REMOVE(scb, pending_links); + if (ccb->ccb_h.func_code == XPT_SCSI_IO + && ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) == 0 + || ccb->csio.tag_action == CAM_TAG_ACTION_NONE) + && (ahc->features & AHC_SCB_BTT) == 0) { + struct scb_tailq *untagged_q; + + untagged_q = &ahc->untagged_queues[ccb->ccb_h.target_id]; + TAILQ_REMOVE(untagged_q, scb, links.tqe); + ahc_run_untagged_queue(ahc, untagged_q); + } + + untimeout(ahc_timeout, (caddr_t)scb, ccb->ccb_h.timeout_ch); + + 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(ahc->buffer_dmat, scb->dmamap, op); + bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); + } + + if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { + if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) + ccb->ccb_h.status |= CAM_REQ_CMP; + ccb->ccb_h.status &= ~CAM_SIM_QUEUED; + ahc_free_scb(ahc, scb); + xpt_done(ccb); + return; + } + + /* + * If the recovery SCB completes, we have to be + * out of our timeout. + */ + if ((scb->flags & SCB_RECOVERY_SCB) != 0) { + struct scb *list_scb; + + /* + * We were able to complete the command successfully, + * so reinstate the timeouts for all other pending + * commands. + */ + LIST_FOREACH(list_scb, &ahc->pending_scbs, pending_links) { + union ccb *ccb; + + ccb = list_scb->io_ctx; + ccb->ccb_h.timeout_ch = + timeout(ahc_timeout, list_scb, + (ccb->ccb_h.timeout * hz)/1000); + } + + /* + * Ensure that we didn't put a second instance of this + * SCB into the QINFIFO. + */ + ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb), + SCB_GET_CHANNEL(ahc, scb), + SCB_GET_LUN(scb), scb->hscb->tag, + ROLE_INITIATOR, /*status*/0, + SEARCH_REMOVE); + if (ahc_get_transaction_status(scb) == CAM_BDR_SENT + || ahc_get_transaction_status(scb) == CAM_REQ_ABORTED) + ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT); + ahc_print_path(ahc, scb); + printf("no longer in timeout, status = %x\n", + ccb->ccb_h.status); + } + + /* Don't clobber any existing error state */ + if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) { + ccb->ccb_h.status |= CAM_REQ_CMP; + } else if ((scb->flags & SCB_SENSE) != 0) { + /* + * We performed autosense retrieval. + * + * Zero any sense not transferred by the + * device. The SCSI spec mandates that any + * untransfered data should be assumed to be + * zero. Complete the 'bounce' of sense information + * through buffers accessible via bus-space by + * copying it into the clients csio. + */ + memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); + memcpy(&ccb->csio.sense_data, + &ahc->scb_data->sense[scb->hscb->tag], + (scb->sg_list->len & AHC_SG_LEN_MASK) + - ccb->csio.sense_resid); + scb->io_ctx->ccb_h.status |= CAM_AUTOSNS_VALID; + } + ccb->ccb_h.status &= ~CAM_SIM_QUEUED; + ahc_free_scb(ahc, scb); + xpt_done(ccb); +} + +static void +ahc_action(struct cam_sim *sim, union ccb *ccb) +{ + struct ahc_softc *ahc; + struct tmode_lstate *lstate; + u_int target_id; + u_int our_id; + long s; + + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ahc_action\n")); + + ahc = (struct ahc_softc *)cam_sim_softc(sim); + + target_id = ccb->ccb_h.target_id; + our_id = SIM_SCSI_ID(ahc, sim); + + switch (ccb->ccb_h.func_code) { + /* Common cases first */ + case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ + case XPT_CONT_TARGET_IO:/* Continue Host Target I/O Connection*/ + { + struct tmode_tstate *tstate; + cam_status status; + + status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, + &lstate, TRUE); + + if (status != CAM_REQ_CMP) { + if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { + /* Response from the black hole device */ + tstate = NULL; + lstate = ahc->black_hole; + } else { + ccb->ccb_h.status = status; + xpt_done(ccb); + break; + } + } + if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { + + ahc_lock(ahc, &s); + SLIST_INSERT_HEAD(&lstate->accept_tios, &ccb->ccb_h, + sim_links.sle); + ccb->ccb_h.status = CAM_REQ_INPROG; + if ((ahc->flags & AHC_TQINFIFO_BLOCKED) != 0) + ahc_run_tqinfifo(ahc, /*paused*/FALSE); + ahc_unlock(ahc, &s); + break; + } + + /* + * The target_id represents the target we attempt to + * select. In target mode, this is the initiator of + * the original command. + */ + our_id = target_id; + target_id = ccb->csio.init_id; + /* FALLTHROUGH */ + } + case XPT_SCSI_IO: /* Execute the requested I/O operation */ + case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ + { + struct scb *scb; + struct hardware_scb *hscb; + struct ahc_initiator_tinfo *tinfo; + struct tmode_tstate *tstate; + uint16_t mask; + + /* + * get an scb to use. + */ + if ((scb = ahc_get_scb(ahc)) == NULL) { + + ahc_lock(ahc, &s); + ahc->flags |= AHC_RESOURCE_SHORTAGE; + ahc_unlock(ahc, &s); + xpt_freeze_simq(sim, /*count*/1); + ahc_set_transaction_status(scb, CAM_REQUEUE_REQ); + xpt_done(ccb); + return; + } + + hscb = scb->hscb; + + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, + ("start scb(%p)\n", scb)); + scb->io_ctx = ccb; + /* + * So we can find the SCB when an abort is requested + */ + ccb->ccb_h.ccb_scb_ptr = scb; + ccb->ccb_h.ccb_ahc_ptr = ahc; + + /* + * Put all the arguments for the xfer in the scb + */ + hscb->control = 0; + hscb->scsiid = BUILD_SCSIID(ahc, sim, target_id, our_id); + hscb->lun = ccb->ccb_h.target_lun; + mask = SCB_GET_TARGET_MASK(ahc, scb); + tinfo = ahc_fetch_transinfo(ahc, SIM_CHANNEL(ahc, sim), our_id, + target_id, &tstate); + + hscb->scsirate = tinfo->scsirate; + hscb->scsioffset = tinfo->current.offset; + if ((tstate->ultraenb & mask) != 0) + hscb->control |= ULTRAENB; + + if ((tstate->discenable & mask) != 0 + && (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0) + hscb->control |= DISCENB; + + if ((ccb->ccb_h.flags & CAM_NEGOTIATE) != 0 + && (tinfo->current.width != 0 || tinfo->current.period != 0)) { + scb->flags |= SCB_NEGOTIATE; + hscb->control |= MK_MESSAGE; + } + + if (ccb->ccb_h.func_code == XPT_RESET_DEV) { + hscb->cdb_len = 0; + scb->flags |= SCB_DEVICE_RESET; + hscb->control |= MK_MESSAGE; + ahc_execute_scb(scb, NULL, 0, 0); + } else { + if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { + struct target_data *tdata; + + tdata = &hscb->shared_data.tdata; + if (ahc->pending_device == lstate) { + scb->flags |= SCB_TARGET_IMMEDIATE; + ahc->pending_device = NULL; + } + hscb->control |= TARGET_SCB; + tdata->target_phases = IDENTIFY_SEEN; + if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { + tdata->target_phases |= SPHASE_PENDING; + tdata->scsi_status = + ccb->csio.scsi_status; + } + tdata->initiator_tag = ccb->csio.tag_id; + } + if (ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) + hscb->control |= ccb->csio.tag_action; + + ahc_setup_data(ahc, sim, &ccb->csio, scb); + } + break; + } + case XPT_NOTIFY_ACK: + case XPT_IMMED_NOTIFY: + { + struct tmode_tstate *tstate; + struct tmode_lstate *lstate; + cam_status status; + + status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, + &lstate, TRUE); + + if (status != CAM_REQ_CMP) { + ccb->ccb_h.status = status; + xpt_done(ccb); + break; + } + SLIST_INSERT_HEAD(&lstate->immed_notifies, &ccb->ccb_h, + sim_links.sle); + ccb->ccb_h.status = CAM_REQ_INPROG; + ahc_send_lstate_events(ahc, lstate); + break; + } + case XPT_EN_LUN: /* Enable LUN as a target */ + ahc_handle_en_lun(ahc, sim, ccb); + xpt_done(ccb); + break; + case XPT_ABORT: /* Abort the specified CCB */ + { + ahc_abort_ccb(ahc, sim, ccb); + break; + } + case XPT_SET_TRAN_SETTINGS: + { +#ifdef AHC_NEW_TRAN_SETTINGS + struct ahc_devinfo devinfo; + struct ccb_trans_settings *cts; + struct ccb_trans_settings_scsi *scsi; + struct ccb_trans_settings_spi *spi; + struct ahc_initiator_tinfo *tinfo; + struct tmode_tstate *tstate; + uint16_t *discenable; + uint16_t *tagenable; + u_int update_type; + + cts = &ccb->cts; + scsi = &cts->proto_specific.scsi; + spi = &cts->xport_specific.spi; + ahc_compile_devinfo(&devinfo, SIM_SCSI_ID(ahc, sim), + cts->ccb_h.target_id, + cts->ccb_h.target_lun, + SIM_CHANNEL(ahc, sim), + ROLE_UNKNOWN); + tinfo = ahc_fetch_transinfo(ahc, devinfo.channel, + devinfo.our_scsiid, + devinfo.target, &tstate); + update_type = 0; + if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { + update_type |= AHC_TRANS_GOAL; + discenable = &tstate->discenable; + tagenable = &tstate->tagenable; + tinfo->current.protocol_version = + cts->protocol_version; + tinfo->current.transport_version = + cts->transport_version; + tinfo->goal.protocol_version = + cts->protocol_version; + tinfo->goal.transport_version = + cts->transport_version; + } else if (cts->type == CTS_TYPE_USER_SETTINGS) { + update_type |= AHC_TRANS_USER; + discenable = &ahc->user_discenable; + tagenable = &ahc->user_tagenable; + tinfo->user.protocol_version = + cts->protocol_version; + tinfo->user.transport_version = + cts->transport_version; + } else { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } + + ahc_lock(ahc, &s); + + if ((spi->valid & CTS_SPI_VALID_DISC) != 0) { + if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0) + *discenable |= devinfo.target_mask; + else + *discenable &= ~devinfo.target_mask; + } + + if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) { + if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) + *tagenable |= devinfo.target_mask; + else + *tagenable &= ~devinfo.target_mask; + } + + if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) { + ahc_validate_width(ahc, &spi->bus_width); + ahc_set_width(ahc, &devinfo, spi->bus_width, + update_type, /*paused*/FALSE); + } + + if ((spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0) { + if (update_type == AHC_TRANS_USER) + spi->ppr_options = tinfo->user.ppr_options; + else + spi->ppr_options = tinfo->goal.ppr_options; + } + + if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0) { + if (update_type == AHC_TRANS_USER) + spi->sync_offset = tinfo->user.offset; + else + spi->sync_offset = tinfo->goal.offset; + } + + if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) { + if (update_type == AHC_TRANS_USER) + spi->sync_period = tinfo->user.period; + else + spi->sync_period = tinfo->goal.period; + } + + if (((spi->valid & CTS_SPI_VALID_SYNC_RATE) != 0) + || ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) != 0)) { + struct ahc_syncrate *syncrate; + u_int maxsync; + + if ((ahc->features & AHC_ULTRA2) != 0) + maxsync = AHC_SYNCRATE_DT; + else if ((ahc->features & AHC_ULTRA) != 0) + maxsync = AHC_SYNCRATE_ULTRA; + else + maxsync = AHC_SYNCRATE_FAST; + + syncrate = ahc_find_syncrate(ahc, &spi->sync_period, + &spi->ppr_options, + maxsync); + ahc_validate_offset(ahc, syncrate, &spi->sync_offset, + spi->bus_width); + + /* We use a period of 0 to represent async */ + if (spi->sync_offset == 0) { + spi->sync_period = 0; + spi->ppr_options = 0; + } + + ahc_set_syncrate(ahc, &devinfo, syncrate, + spi->sync_period, spi->sync_offset, + spi->ppr_options, update_type, + /*paused*/FALSE); + } + ahc_unlock(ahc, &s); + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); +#else + struct ahc_devinfo devinfo; + struct ccb_trans_settings *cts; + struct ahc_initiator_tinfo *tinfo; + struct tmode_tstate *tstate; + uint16_t *discenable; + uint16_t *tagenable; + u_int update_type; + long s; + + cts = &ccb->cts; + ahc_compile_devinfo(&devinfo, SIM_SCSI_ID(ahc, sim), + cts->ccb_h.target_id, + cts->ccb_h.target_lun, + SIM_CHANNEL(ahc, sim), + ROLE_UNKNOWN); + tinfo = ahc_fetch_transinfo(ahc, devinfo.channel, + devinfo.our_scsiid, + devinfo.target, &tstate); + update_type = 0; + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { + update_type |= AHC_TRANS_GOAL; + discenable = &tstate->discenable; + tagenable = &tstate->tagenable; + } else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { + update_type |= AHC_TRANS_USER; + discenable = &ahc->user_discenable; + tagenable = &ahc->user_tagenable; + } else { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } + + ahc_lock(ahc, &s); + + if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { + if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) + *discenable |= devinfo.target_mask; + else + *discenable &= ~devinfo.target_mask; + } + + if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) + *tagenable |= devinfo.target_mask; + else + *tagenable &= ~devinfo.target_mask; + } + + if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) { + ahc_validate_width(ahc, &cts->bus_width); + ahc_set_width(ahc, &devinfo, cts->bus_width, + update_type, /*paused*/FALSE); + } + + if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) { + if (update_type == AHC_TRANS_USER) + cts->sync_offset = tinfo->user.offset; + else + cts->sync_offset = tinfo->goal.offset; + } + + if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) { + if (update_type == AHC_TRANS_USER) + cts->sync_period = tinfo->user.period; + else + cts->sync_period = tinfo->goal.period; + } + + if (((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) + || ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)) { + struct ahc_syncrate *syncrate; + u_int ppr_options; + u_int maxsync; + + if ((ahc->features & AHC_ULTRA2) != 0) + maxsync = AHC_SYNCRATE_DT; + else if ((ahc->features & AHC_ULTRA) != 0) + maxsync = AHC_SYNCRATE_ULTRA; + else + maxsync = AHC_SYNCRATE_FAST; + + ppr_options = 0; + if (cts->sync_period <= 9) + ppr_options = MSG_EXT_PPR_DT_REQ; + + syncrate = ahc_find_syncrate(ahc, &cts->sync_period, + &ppr_options, + maxsync); + ahc_validate_offset(ahc, syncrate, &cts->sync_offset, + MSG_EXT_WDTR_BUS_8_BIT); + + /* We use a period of 0 to represent async */ + if (cts->sync_offset == 0) { + cts->sync_period = 0; + ppr_options = 0; + } + + if (ppr_options == MSG_EXT_PPR_DT_REQ + && tinfo->user.transport_version >= 3) { + tinfo->goal.transport_version = + tinfo->user.transport_version; + tinfo->current.transport_version = + tinfo->user.transport_version; + } + + ahc_set_syncrate(ahc, &devinfo, syncrate, + cts->sync_period, cts->sync_offset, + ppr_options, update_type, + /*paused*/FALSE); + } + ahc_unlock(ahc, &s); + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); +#endif + break; + } + case XPT_GET_TRAN_SETTINGS: + /* Get default/user set transfer settings for the target */ + { + + ahc_lock(ahc, &s); + ahc_get_tran_settings(ahc, SIM_SCSI_ID(ahc, sim), + SIM_CHANNEL(ahc, sim), &ccb->cts); + ahc_unlock(ahc, &s); + xpt_done(ccb); + break; + } + case XPT_CALC_GEOMETRY: + { + struct ccb_calc_geometry *ccg; + uint32_t size_mb; + uint32_t secs_per_cylinder; + int extended; + + ccg = &ccb->ccg; + size_mb = ccg->volume_size + / ((1024L * 1024L) / ccg->block_size); + extended = SIM_IS_SCSIBUS_B(ahc, sim) + ? ahc->flags & AHC_EXTENDED_TRANS_B + : ahc->flags & AHC_EXTENDED_TRANS_A; + + if (size_mb > 1024 && extended) { + 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 found; + + ahc_lock(ahc, &s); + found = ahc_reset_channel(ahc, SIM_CHANNEL(ahc, sim), + /*initiate reset*/TRUE); + ahc_unlock(ahc, &s); + if (bootverbose) { + xpt_print_path(SIM_PATH(ahc, sim)); + printf("SCSI bus reset delivered. " + "%d SCBs aborted.\n", found); + } + 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; + if ((ahc->features & AHC_WIDE) != 0) + cpi->hba_inquiry |= PI_WIDE_16; + if ((ahc->flags & AHC_TARGETMODE) != 0) { + cpi->target_sprt = PIT_PROCESSOR + | PIT_DISCONNECT + | PIT_TERM_IO; + } else { + cpi->target_sprt = 0; + } + cpi->hba_misc = (ahc->flags & AHC_INITIATORMODE) + ? 0 : PIM_NOINITIATOR; + cpi->hba_eng_cnt = 0; + cpi->max_target = (ahc->features & AHC_WIDE) ? 15 : 7; + cpi->max_lun = 64; + if (SIM_IS_SCSIBUS_B(ahc, sim)) { + cpi->initiator_id = ahc->our_id_b; + if ((ahc->flags & AHC_RESET_BUS_B) == 0) + cpi->hba_misc |= PIM_NOBUSRESET; + } else { + cpi->initiator_id = ahc->our_id; + if ((ahc->flags & AHC_RESET_BUS_A) == 0) + cpi->hba_misc |= PIM_NOBUSRESET; + } + cpi->bus_id = cam_sim_bus(sim); + cpi->base_transfer_speed = 3300; + 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); +#ifdef AHC_NEW_TRAN_SETTINGS + cpi->protocol = PROTO_SCSI; + cpi->protocol_version = SCSI_REV_2; + cpi->transport = XPORT_SPI; + cpi->transport_version = 2; + cpi->xport_specific.spi.ppr_options = SID_SPI_CLOCK_ST; + if ((ahc->features & AHC_DT) != 0) { + cpi->transport_version = 3; + cpi->xport_specific.spi.ppr_options = + SID_SPI_CLOCK_DT_ST; + } +#endif + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } +} + +static void +ahc_get_tran_settings(struct ahc_softc *ahc, int our_id, char channel, + struct ccb_trans_settings *cts) +{ +#ifdef AHC_NEW_TRAN_SETTINGS + struct ahc_devinfo devinfo; + struct ccb_trans_settings_scsi *scsi; + struct ccb_trans_settings_spi *spi; + struct ahc_initiator_tinfo *targ_info; + struct tmode_tstate *tstate; + struct ahc_transinfo *tinfo; + + scsi = &cts->proto_specific.scsi; + spi = &cts->xport_specific.spi; + ahc_compile_devinfo(&devinfo, our_id, + cts->ccb_h.target_id, + cts->ccb_h.target_lun, + channel, ROLE_UNKNOWN); + targ_info = ahc_fetch_transinfo(ahc, devinfo.channel, + devinfo.our_scsiid, + devinfo.target, &tstate); + + if (cts->type == CTS_TYPE_CURRENT_SETTINGS) + tinfo = &targ_info->current; + else + tinfo = &targ_info->user; + + scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; + spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; + if (cts->type == CTS_TYPE_USER_SETTINGS) { + if ((ahc->user_discenable & devinfo.target_mask) != 0) + spi->flags |= CTS_SPI_FLAGS_DISC_ENB; + + if ((ahc->user_tagenable & devinfo.target_mask) != 0) + scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; + } else { + if ((tstate->discenable & devinfo.target_mask) != 0) + spi->flags |= CTS_SPI_FLAGS_DISC_ENB; + + if ((tstate->tagenable & devinfo.target_mask) != 0) + scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; + } + cts->protocol_version = tinfo->protocol_version; + cts->transport_version = tinfo->transport_version; + + spi->sync_period = tinfo->period; + spi->sync_offset = tinfo->offset; + spi->bus_width = tinfo->width; + spi->ppr_options = tinfo->ppr_options; + + cts->protocol = PROTO_SCSI; + cts->transport = XPORT_SPI; + scsi->valid = CTS_SCSI_VALID_TQ; + spi->valid = CTS_SPI_VALID_SYNC_RATE + | CTS_SPI_VALID_SYNC_OFFSET + | CTS_SPI_VALID_BUS_WIDTH + | CTS_SPI_VALID_DISC + | CTS_SPI_VALID_PPR_OPTIONS; + + cts->ccb_h.status = CAM_REQ_CMP; +#else + struct ahc_devinfo devinfo; + struct ahc_initiator_tinfo *targ_info; + struct tmode_tstate *tstate; + struct ahc_transinfo *tinfo; + long s; + + ahc_compile_devinfo(&devinfo, our_id, + cts->ccb_h.target_id, + cts->ccb_h.target_lun, + channel, ROLE_UNKNOWN); + targ_info = ahc_fetch_transinfo(ahc, devinfo.channel, + devinfo.our_scsiid, + devinfo.target, &tstate); + + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) + tinfo = &targ_info->current; + else + tinfo = &targ_info->user; + + ahc_lock(ahc, &s); + + cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { + if ((ahc->user_discenable & devinfo.target_mask) != 0) + cts->flags |= CCB_TRANS_DISC_ENB; + + if ((ahc->user_tagenable & devinfo.target_mask) != 0) + cts->flags |= CCB_TRANS_TAG_ENB; + } else { + if ((tstate->discenable & devinfo.target_mask) != 0) + cts->flags |= CCB_TRANS_DISC_ENB; + + if ((tstate->tagenable & devinfo.target_mask) != 0) + cts->flags |= CCB_TRANS_TAG_ENB; + } + cts->sync_period = tinfo->period; + cts->sync_offset = tinfo->offset; + cts->bus_width = tinfo->width; + + ahc_unlock(ahc, &s); + + 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; + + cts->ccb_h.status = CAM_REQ_CMP; +#endif +} + +static void +ahc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) +{ + struct ahc_softc *ahc; + struct cam_sim *sim; + + sim = (struct cam_sim *)callback_arg; + ahc = (struct ahc_softc *)cam_sim_softc(sim); + switch (code) { + case AC_LOST_DEVICE: + { + struct ahc_devinfo devinfo; + long s; + + ahc_compile_devinfo(&devinfo, SIM_SCSI_ID(ahc, sim), + xpt_path_target_id(path), + xpt_path_lun_id(path), + SIM_CHANNEL(ahc, sim), + ROLE_UNKNOWN); + + /* + * Revert to async/narrow transfers + * for the next device. + */ + ahc_lock(ahc, &s); + ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT, + AHC_TRANS_GOAL|AHC_TRANS_CUR, /*paused*/FALSE); + ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL, + /*period*/0, /*offset*/0, /*ppr_options*/0, + AHC_TRANS_GOAL|AHC_TRANS_CUR, + /*paused*/FALSE); + ahc_unlock(ahc, &s); + break; + } + default: + break; + } +} + +static void +ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, + int error) +{ + struct scb *scb; + union ccb *ccb; + struct ahc_softc *ahc; + long s; + + scb = (struct scb *)arg; + ccb = scb->io_ctx; + ahc = (struct ahc_softc *)ccb->ccb_h.ccb_ahc_ptr; + + if (error != 0) { + if (error == EFBIG) + ahc_set_transaction_status(scb, CAM_REQ_TOO_BIG); + else + ahc_set_transaction_status(scb, CAM_REQ_CMP_ERR); + if (nsegments != 0) + bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap); + ahc_free_scb(ahc, scb); + xpt_done(ccb); + return; + } + if (nsegments != 0) { + struct ahc_dma_seg *sg; + bus_dma_segment_t *end_seg; + bus_dmasync_op_t op; + + end_seg = dm_segs + nsegments; + + /* Copy the segments into our SG list */ + sg = scb->sg_list; + while (dm_segs < end_seg) { + sg->addr = dm_segs->ds_addr; +/* XXX Add in the 5th byte of the address later. */ + sg->len = dm_segs->ds_len; + sg++; + dm_segs++; + } + + /* + * Note where to find the SG entries in bus space. + * We also set the full residual flag which the + * sequencer will clear as soon as a data transfer + * occurs. + */ + scb->hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID; + + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) + op = BUS_DMASYNC_PREREAD; + else + op = BUS_DMASYNC_PREWRITE; + + bus_dmamap_sync(ahc->buffer_dmat, scb->dmamap, op); + + if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { + struct target_data *tdata; + + tdata = &scb->hscb->shared_data.tdata; + tdata->target_phases |= DPHASE_PENDING; + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) + tdata->data_phase = P_DATAOUT; + else + tdata->data_phase = P_DATAIN; + + /* + * If the transfer is of an odd length and in the + * "in" direction (scsi->HostBus), then it may + * trigger a bug in the 'WideODD' feature of + * non-Ultra2 chips. Force the total data-length + * to be even by adding an extra, 1 byte, SG, + * element. We do this even if we are not currently + * negotiated wide as negotiation could occur before + * this command is executed. + */ + if ((ahc->bugs & AHC_TMODE_WIDEODD_BUG) != 0 + && (ccb->csio.dxfer_len & 0x1) != 0 + && (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + + nsegments++; + if (nsegments > AHC_NSEG) { + + ahc_set_transaction_status(scb, + CAM_REQ_TOO_BIG); + bus_dmamap_unload(ahc->buffer_dmat, + scb->dmamap); + ahc_free_scb(ahc, scb); + xpt_done(ccb); + return; + } + sg->addr = ahc->dma_bug_buf; + sg->len = 1; + sg++; + } + } + sg--; + sg->len |= AHC_DMA_LAST_SEG; + + /* Copy the first SG into the "current" data pointer area */ + scb->hscb->dataptr = scb->sg_list->addr; + scb->hscb->datacnt = scb->sg_list->len; + } else { + scb->hscb->sgptr = SG_LIST_NULL; + scb->hscb->dataptr = 0; + scb->hscb->datacnt = 0; + } + + scb->sg_count = nsegments; + + ahc_lock(ahc, &s); + + /* + * Last time we need to check if this SCB needs to + * be aborted. + */ + if (ahc_get_transaction_status(scb) != CAM_REQ_INPROG) { + if (nsegments != 0) + bus_dmamap_unload(ahc->buffer_dmat, + scb->dmamap); + ahc_free_scb(ahc, scb); + xpt_done(ccb); + ahc_unlock(ahc, &s); + return; + } + + LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links); + + ccb->ccb_h.status |= CAM_SIM_QUEUED; + + if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { + if (ccb->ccb_h.timeout == CAM_TIME_DEFAULT) + ccb->ccb_h.timeout = 5 * 1000; + ccb->ccb_h.timeout_ch = + timeout(ahc_timeout, (caddr_t)scb, + (ccb->ccb_h.timeout * hz) / 1000); + } + + /* + * We only allow one untagged transaction + * per target in the initiator role unless + * we are storing a full busy target *lun* + * table in SCB space. + */ + if ((scb->hscb->control & (TARGET_SCB|TAG_ENB)) == 0 + && (ahc->features & AHC_SCB_BTT) == 0) { + struct scb_tailq *untagged_q; + + untagged_q = &(ahc->untagged_queues[ccb->ccb_h.target_id]); + TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe); + if (TAILQ_FIRST(untagged_q) != scb) { + ahc_unlock(ahc, &s); + return; + } + } + scb->flags |= SCB_ACTIVE; + + if ((scb->flags & SCB_TARGET_IMMEDIATE) != 0) { + pause_sequencer(ahc); + if ((ahc->flags & AHC_PAGESCBS) == 0) + ahc_outb(ahc, SCBPTR, scb->hscb->tag); + ahc_outb(ahc, SCB_TAG, scb->hscb->tag); + ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP); + unpause_sequencer(ahc); + } else { + ahc_queue_scb(ahc, scb); + } + + ahc_unlock(ahc, &s); +} + +static void +ahc_poll(struct cam_sim *sim) +{ + ahc_intr(cam_sim_softc(sim)); +} + +static void +ahc_setup_data(struct ahc_softc *ahc, struct cam_sim *sim, + struct ccb_scsiio *csio, struct scb *scb) +{ + struct hardware_scb *hscb; + struct ccb_hdr *ccb_h; + + hscb = scb->hscb; + ccb_h = &csio->ccb_h; + + if (ccb_h->func_code == XPT_SCSI_IO) { + hscb->cdb_len = csio->cdb_len; + if ((ccb_h->flags & CAM_CDB_POINTER) != 0) { + + if (hscb->cdb_len > sizeof(hscb->cdb32) + || (ccb_h->flags & CAM_CDB_PHYS) != 0) { + ahc_set_transaction_status(scb, + CAM_REQ_INVALID); + xpt_done(scb->io_ctx); + ahc_free_scb(ahc, scb); + return; + } + if (hscb->cdb_len > 12) { + memcpy(hscb->cdb32, + csio->cdb_io.cdb_ptr, + hscb->cdb_len); + hscb->shared_data.cdb_ptr = scb->cdb32_busaddr; + } else { + memcpy(hscb->shared_data.cdb, + csio->cdb_io.cdb_ptr, + hscb->cdb_len); + } + } else { + if (hscb->cdb_len > 12) { + memcpy(hscb->cdb32, csio->cdb_io.cdb_bytes, + hscb->cdb_len); + hscb->shared_data.cdb_ptr = scb->cdb32_busaddr; + } else { + memcpy(hscb->shared_data.cdb, + csio->cdb_io.cdb_bytes, + hscb->cdb_len); + } + } + } + + /* Only use S/G if there is a transfer */ + if ((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { + if ((ccb_h->flags & CAM_SCATTER_VALID) == 0) { + /* We've been given a pointer to a single buffer */ + if ((ccb_h->flags & CAM_DATA_PHYS) == 0) { + int s; + int error; + + s = splsoftvm(); + error = bus_dmamap_load(ahc->buffer_dmat, + scb->dmamap, + csio->data_ptr, + csio->dxfer_len, + ahc_execute_scb, + scb, /*flags*/0); + if (error == EINPROGRESS) { + /* + * So as to maintain ordering, + * freeze the controller queue + * until our mapping is + * returned. + */ + xpt_freeze_simq(sim, + /*count*/1); + scb->io_ctx->ccb_h.status |= + CAM_RELEASE_SIMQ; + } + splx(s); + } else { + struct bus_dma_segment seg; + + /* Pointer to physical buffer */ + if (csio->dxfer_len > AHC_MAXTRANSFER_SIZE) + panic("ahc_setup_data - Transfer size " + "larger than can device max"); + + seg.ds_addr = (bus_addr_t)csio->data_ptr; + seg.ds_len = csio->dxfer_len; + ahc_execute_scb(scb, &seg, 1, 0); + } + } else { + struct bus_dma_segment *segs; + + if ((ccb_h->flags & CAM_DATA_PHYS) != 0) + panic("ahc_setup_data - Physical segment " + "pointers unsupported"); + + if ((ccb_h->flags & CAM_SG_LIST_PHYS) == 0) + panic("ahc_setup_data - Virtual segment " + "addresses unsupported"); + + /* Just use the segments provided */ + segs = (struct bus_dma_segment *)csio->data_ptr; + ahc_execute_scb(scb, segs, csio->sglist_cnt, 0); + } + } else { + ahc_execute_scb(scb, NULL, 0, 0); + } +} + +static void +ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb) { + + if ((scb->flags & SCB_RECOVERY_SCB) == 0) { + struct scb *list_scb; + + scb->flags |= SCB_RECOVERY_SCB; + + /* + * Take all queued, but not sent SCBs out of the equation. + * Also ensure that no new CCBs are queued to us while we + * try to fix this problem. + */ + if ((scb->io_ctx->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { + xpt_freeze_simq(SCB_GET_SIM(ahc, scb), /*count*/1); + scb->io_ctx->ccb_h.status |= CAM_RELEASE_SIMQ; + } + + /* + * Go through all of our pending SCBs and remove + * any scheduled timeouts for them. We will reschedule + * them after we've successfully fixed this problem. + */ + LIST_FOREACH(list_scb, &ahc->pending_scbs, pending_links) { + union ccb *ccb; + + ccb = list_scb->io_ctx; + untimeout(ahc_timeout, list_scb, ccb->ccb_h.timeout_ch); + } + } +} + +void +ahc_timeout(void *arg) +{ + struct scb *scb; + struct ahc_softc *ahc; + long s; + int found; + u_int last_phase; + int target; + int lun; + int i; + char channel; + + scb = (struct scb *)arg; + ahc = (struct ahc_softc *)scb->io_ctx->ccb_h.ccb_ahc_ptr; + + ahc_lock(ahc, &s); + + /* + * Ensure that the card doesn't do anything + * behind our back. Also make sure that we + * didn't "just" miss an interrupt that would + * affect this timeout. + */ + do { + ahc_intr(ahc); + pause_sequencer(ahc); + } while (ahc_inb(ahc, INTSTAT) & INT_PEND); + + ahc_print_path(ahc, scb); + if ((scb->flags & SCB_ACTIVE) == 0) { + /* Previous timeout took care of me already */ + printf("Timedout SCB %d handled by another timeout\n", + scb->hscb->tag); + unpause_sequencer(ahc); + ahc_unlock(ahc, &s); + return; + } + + target = SCB_GET_TARGET(ahc, scb); + channel = SCB_GET_CHANNEL(ahc, scb); + lun = SCB_GET_LUN(scb); + + printf("SCB 0x%x - timed out ", scb->hscb->tag); + /* + * Take a snapshot of the bus state and print out + * some information so we can track down driver bugs. + */ + last_phase = ahc_inb(ahc, LASTPHASE); + + for (i = 0; i < num_phases; i++) { + if (last_phase == phase_table[i].phase) + break; + } + printf("%s", phase_table[i].phasemsg); + + printf(", SEQADDR == 0x%x\n", + ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8)); + + if (scb->sg_count > 0) { + for (i = 0; i < scb->sg_count; i++) { + printf("sg[%d] - Addr 0x%x : Length %d\n", + i, + scb->sg_list[i].addr, + scb->sg_list[i].len); + } + } + if (scb->flags & (SCB_DEVICE_RESET|SCB_ABORT)) { + /* + * Been down this road before. + * Do a full bus reset. + */ +bus_reset: + ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT); + found = ahc_reset_channel(ahc, channel, /*Initiate Reset*/TRUE); + printf("%s: Issued Channel %c Bus Reset. " + "%d SCBs aborted\n", ahc_name(ahc), channel, found); + } else { + /* + * If we are a target, transition to bus free and report + * the timeout. + * + * The target/initiator that is holding up the bus may not + * be the same as the one that triggered this timeout + * (different commands have different timeout lengths). + * If the bus is idle and we are actiing as the initiator + * for this request, queue a BDR message to the timed out + * target. Otherwise, if the timed out transaction is + * active: + * Initiator transaction: + * Stuff the message buffer with a BDR message and assert + * ATN in the hopes that the target will let go of the bus + * and go to the mesgout phase. If this fails, we'll + * get another timeout 2 seconds later which will attempt + * a bus reset. + * + * Target transaction: + * Transition to BUS FREE and report the error. + * It's good to be the target! + */ + u_int active_scb_index; + + active_scb_index = ahc_inb(ahc, SCB_TAG); + + if (last_phase != P_BUSFREE + && (active_scb_index < ahc->scb_data->numscbs)) { + struct scb *active_scb; + + /* + * If the active SCB is not from our device, + * assume that another device is hogging the bus + * and wait for it's timeout to expire before + * taking additional action. + */ + active_scb = &ahc->scb_data->scbarray[active_scb_index]; + if (active_scb->hscb->scsiid != scb->hscb->scsiid + || active_scb->hscb->lun != scb->hscb->lun) { + struct ccb_hdr *ccbh; + u_int newtimeout; + + ahc_print_path(ahc, scb); + printf("Other SCB Timeout%s", + (scb->flags & SCB_OTHERTCL_TIMEOUT) != 0 + ? " again\n" : "\n"); + scb->flags |= SCB_OTHERTCL_TIMEOUT; + newtimeout = + MAX(active_scb->io_ctx->ccb_h.timeout, + scb->io_ctx->ccb_h.timeout); + ccbh = &scb->io_ctx->ccb_h; + scb->io_ctx->ccb_h.timeout_ch = + timeout(ahc_timeout, scb, + (newtimeout * hz) / 1000); + ahc_unlock(ahc, &s); + return; + } + + /* It's us */ + if ((scb->hscb->control & TARGET_SCB) != 0) { + + /* + * Send back any queued up transactions + * and properly record the error condition. + */ + ahc_freeze_devq(ahc, scb); + ahc_set_transaction_status(scb, + CAM_CMD_TIMEOUT); + ahc_freeze_scb(scb); + ahc_done(ahc, scb); + + /* Will clear us from the bus */ + restart_sequencer(ahc); + return; + } + + ahc_set_recoveryscb(ahc, active_scb); + ahc_outb(ahc, MSG_OUT, MSG_BUS_DEV_RESET); + ahc_outb(ahc, SCSISIGO, last_phase|ATNO); + ahc_print_path(ahc, active_scb); + printf("BDR message in message buffer\n"); + active_scb->flags |= SCB_DEVICE_RESET; + active_scb->io_ctx->ccb_h.timeout_ch = + timeout(ahc_timeout, (caddr_t)active_scb, 2 * hz); + unpause_sequencer(ahc); + } else { + int disconnected; + + /* XXX Shouldn't panic. Just punt instead */ + if ((scb->hscb->control & TARGET_SCB) != 0) + panic("Timed-out target SCB but bus idle"); + + if (last_phase != P_BUSFREE + && (ahc_inb(ahc, SSTAT0) & TARGET) != 0) { + /* XXX What happened to the SCB? */ + /* Hung target selection. Goto busfree */ + printf("%s: Hung target selection\n", + ahc_name(ahc)); + restart_sequencer(ahc); + return; + } + + if (ahc_search_qinfifo(ahc, target, channel, lun, + scb->hscb->tag, ROLE_INITIATOR, + /*status*/0, SEARCH_COUNT) > 0) { + disconnected = FALSE; + } else { + disconnected = TRUE; + } + + if (disconnected) { + u_int active_scb; + + ahc_set_recoveryscb(ahc, scb); + /* + * Simply set the MK_MESSAGE control bit. + */ + scb->hscb->control |= MK_MESSAGE; + scb->flags |= SCB_QUEUED_MSG + | SCB_DEVICE_RESET; + + /* + * Mark the cached copy of this SCB in the + * disconnected list too, so that a reconnect + * at this point causes a BDR or abort. + */ + active_scb = ahc_inb(ahc, SCBPTR); + if (ahc_search_disc_list(ahc, target, + channel, lun, + scb->hscb->tag, + /*stop_on_first*/TRUE, + /*remove*/FALSE, + /*save_state*/FALSE)) { + u_int scb_control; + + scb_control = ahc_inb(ahc, SCB_CONTROL); + scb_control |= MK_MESSAGE; + ahc_outb(ahc, SCB_CONTROL, scb_control); + } + ahc_outb(ahc, SCBPTR, active_scb); + + /* + * Actually re-queue this SCB in case we can + * select the device before it reconnects. + * Clear out any entries in the QINFIFO first + * so we are the next SCB for this target + * to run. + */ + ahc_search_qinfifo(ahc, + SCB_GET_TARGET(ahc, scb), + channel, SCB_GET_LUN(scb), + SCB_LIST_NULL, + ROLE_INITIATOR, + CAM_REQUEUE_REQ, + SEARCH_COMPLETE); + ahc_print_path(ahc, scb); + printf("Queuing a BDR SCB\n"); + ahc->qinfifo[ahc->qinfifonext++] = + scb->hscb->tag; + if ((ahc->features & AHC_QUEUE_REGS) != 0) { + ahc_outb(ahc, HNSCB_QOFF, + ahc->qinfifonext); + } else { + ahc_outb(ahc, KERNEL_QINPOS, + ahc->qinfifonext); + } + scb->io_ctx->ccb_h.timeout_ch = + timeout(ahc_timeout, (caddr_t)scb, 2 * hz); + unpause_sequencer(ahc); + } else { + /* Go "immediatly" to the bus reset */ + /* This shouldn't happen */ + ahc_set_recoveryscb(ahc, scb); + ahc_print_path(ahc, scb); + printf("SCB %d: Immediate reset. " + "Flags = 0x%x\n", scb->hscb->tag, + scb->flags); + goto bus_reset; + } + } + } + ahc_unlock(ahc, &s); +} + +static void +ahc_abort_ccb(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb) +{ + union ccb *abort_ccb; + + abort_ccb = ccb->cab.abort_ccb; + switch (abort_ccb->ccb_h.func_code) { + case XPT_ACCEPT_TARGET_IO: + case XPT_IMMED_NOTIFY: + case XPT_CONT_TARGET_IO: + { + struct tmode_tstate *tstate; + struct tmode_lstate *lstate; + struct ccb_hdr_slist *list; + cam_status status; + + status = ahc_find_tmode_devs(ahc, sim, abort_ccb, &tstate, + &lstate, TRUE); + + if (status != CAM_REQ_CMP) { + ccb->ccb_h.status = status; + break; + } + + if (abort_ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) + list = &lstate->accept_tios; + else if (abort_ccb->ccb_h.func_code == XPT_IMMED_NOTIFY) + list = &lstate->immed_notifies; + else + list = NULL; + + if (list != NULL) { + struct ccb_hdr *curelm; + int found; + + curelm = SLIST_FIRST(list); + found = 0; + if (curelm == &abort_ccb->ccb_h) { + found = 1; + SLIST_REMOVE_HEAD(list, sim_links.sle); + } else { + while(curelm != NULL) { + struct ccb_hdr *nextelm; + + nextelm = + SLIST_NEXT(curelm, sim_links.sle); + + if (nextelm == &abort_ccb->ccb_h) { + found = 1; + SLIST_NEXT(curelm, + sim_links.sle) = + SLIST_NEXT(nextelm, + sim_links.sle); + break; + } + curelm = nextelm; + } + } + + if (found) { + abort_ccb->ccb_h.status = CAM_REQ_ABORTED; + xpt_done(abort_ccb); + ccb->ccb_h.status = CAM_REQ_CMP; + } else { + printf("Not found\n"); + ccb->ccb_h.status = CAM_PATH_INVALID; + } + break; + } + /* FALLTHROUGH */ + } + case XPT_SCSI_IO: + /* XXX Fully implement the hard ones */ + ccb->ccb_h.status = CAM_UA_ABORT; + break; + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + } + xpt_done(ccb); +} + +void +ahc_send_async(struct ahc_softc *ahc, char channel, u_int target, + u_int lun, ac_code code) +{ + struct ccb_trans_settings cts; + struct cam_path *path; + void *arg; + int error; + + arg = NULL; + error = ahc_create_path(ahc, channel, target, lun, &path); + + if (error != CAM_REQ_CMP) + return; + + switch (code) { + case AC_TRANSFER_NEG: +#ifdef AHC_NEW_TRAN_SETTINGS + cts.type = CTS_TYPE_CURRENT_SETTINGS; +#else + cts.flags = CCB_TRANS_CURRENT_SETTINGS; +#endif + cts.ccb_h.path = path; + cts.ccb_h.target_id = target; + cts.ccb_h.target_lun = lun; + ahc_get_tran_settings(ahc, channel == 'A' ? ahc->our_id + : ahc->our_id_b, + channel, &cts); + arg = &cts; + break; + case AC_SENT_BDR: + case AC_BUS_RESET: + break; + default: + panic("ahc_send_async: Unexpected async event"); + } + xpt_async(code, path, arg); +} + +void +ahc_platform_set_tags(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo, int enable) +{ +} + +int +ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg) +{ + ahc->platform_data = + malloc(sizeof(struct ahc_platform_data), M_DEVBUF, M_NOWAIT); + if (ahc->platform_data == NULL) + return (ENOMEM); + memset(ahc->platform_data, 0, sizeof(struct ahc_platform_data)); + return (0); +} + +void +ahc_platform_free(struct ahc_softc *ahc) +{ + if (ahc->platform_data != NULL) { + if (ahc->platform_data->regs != NULL) + bus_release_resource(ahc->dev_softc, + ahc->platform_data->regs_res_type, + ahc->platform_data->regs_res_id, + ahc->platform_data->regs); + + if (ahc->platform_data->irq != NULL) + bus_release_resource(ahc->dev_softc, + ahc->platform_data->irq_res_type, + 0, ahc->platform_data->irq); + + free(ahc->platform_data, M_DEVBUF); + } +} + +int +ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc) +{ + /* We don't sort softcs under FreeBSD so report equal always */ + return (0); +} + +#if UNUSED +static void +ahc_dump_targcmd(struct target_cmd *cmd) +{ + uint8_t *byte; + uint8_t *last_byte; + int i; + + byte = &cmd->initiator_channel; + /* Debugging info for received commands */ + last_byte = &cmd[1].initiator_channel; + + i = 0; + while (byte < last_byte) { + if (i == 0) + printf("\t"); + printf("%#x", *byte++); + i++; + if (i == 8) { + printf("\n"); + i = 0; + } else { + printf(", "); + } + } +} +#endif diff --git a/sys/dev/aic7xxx/aic7xxx_freebsd.h b/sys/dev/aic7xxx/aic7xxx_freebsd.h new file mode 100644 index 000000000000..4acb1285337f --- /dev/null +++ b/sys/dev/aic7xxx/aic7xxx_freebsd.h @@ -0,0 +1,454 @@ +/* + * FreeBSD platform specific driver option settings, data structures, + * function declarations and includes. + * + * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 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, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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$ + * + * $FreeBSD$ + */ + +#ifndef _AIC7XXX_FREEBSD_H_ +#define _AIC7XXX_FREEBSD_H_ + +#include <opt_aic7xxx.h> /* for config options */ +#include <pci.h> /* for NPCI */ + +#include <stddef.h> /* For offsetof */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> /* For device_t */ +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/queue.h> + +#if NPCI > 0 +#define AHC_SUPPORT_PCI 1 +#ifdef AHC_ALLOW_MEMIO +#include <machine/bus_memio.h> +#endif +#endif +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/clock.h> +#include <machine/resource.h> + +#include <sys/rman.h> + +#if NPCI > 0 +#include <pci/pcireg.h> +#include <pci/pcivar.h> +#endif + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_debug.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> + +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> + +/****************************** Platform Macros *******************************/ +#define SIM_IS_SCSIBUS_B(ahc, sim) \ + ((sim) == ahc->platform_data->sim_b) +#define SIM_CHANNEL(ahc, sim) \ + (((sim) == ahc->platform_data->sim_b) ? 'B' : 'A') +#define SIM_SCSI_ID(ahc, sim) \ + (((sim) == ahc->platform_data->sim_b) ? ahc->our_id_b : ahc->our_id) +#define SIM_PATH(ahc, sim) \ + (((sim) == ahc->platform_data->sim_b) ? ahc->platform_data->path_b \ + : ahc->platform_data->path) +#define BUILD_SCSIID(ahc, sim, target_id, our_id) \ + ((((target_id) << TID_SHIFT) & TID) | (our_id) \ + | (SIM_IS_SCSIBUS_B(ahc, sim) ? TWIN_CHNLB : 0)) + +#define SCB_GET_SIM(ahc, scb) \ + (SCB_GET_CHANNEL(ahc, scb) == 'A' ? (ahc)->platform_data->sim \ + : (ahc)->platform_data->sim_b) + +/************************* Forward Declarations *******************************/ +typedef device_t ahc_dev_softc_t; +typedef union ccb *ahc_io_ctx_t; + +/***************************** Bus Space/DMA **********************************/ +#define ahc_dma_tag_create(ahc, parent_tag, alignment, boundary, \ + lowaddr, highaddr, filter, filterarg, \ + maxsize, nsegments, maxsegsz, flags, \ + dma_tagp) \ + bus_dma_tag_create(parent_tag, alignment, boundary, \ + lowaddr, highaddr, filter, filterarg, \ + maxsize, nsegments, maxsegsz, flags, \ + dma_tagp) + +#define ahc_dma_tag_destroy(ahc, tag) \ + bus_dma_tag_destroy(tag) + +#define ahc_dmamem_alloc(ahc, dmat, vaddr, flags, mapp) \ + bus_dmamem_alloc(dmat, vaddr, flags, mapp) + +#define ahc_dmamem_free(ahc, dmat, vaddr, map) \ + bus_dmamem_free(dmat, vaddr, map) + +#define ahc_dmamap_create(ahc, tag, flags, mapp) \ + bus_dmamap_create(tag, flags, mapp) + +#define ahc_dmamap_destroy(ahc, tag, map) \ + bus_dmamap_destroy(tag, map) + +#define ahc_dmamap_load(ahc, dmat, map, addr, buflen, callback, \ + callback_arg, flags) \ + bus_dmamap_load(dmat, map, addr, buflen, callback, callback_arg, flags) + +#define ahc_dmamap_unload(ahc, tag, map) \ + bus_dmamap_unload(tag, map) + +#define ahc_dmamap_sync(ahc, dma_tag, dmamap, op) \ + bus_dmamap_sync(dma_tag_dmamap, op) + +/************************ Tunable Driver Parameters **************************/ +/* + * The number of dma segments supported. The sequencer can handle any number + * of physically contiguous S/G entrys. To reduce the driver's memory + * consumption, we limit the number supported to be sufficient to handle + * the largest mapping supported by the kernel, MAXPHYS. Assuming the + * transfer is as fragmented as possible and unaligned, this turns out to + * be the number of paged sized transfers in MAXPHYS plus an extra element + * to handle any unaligned residual. The sequencer fetches SG elements + * in 128 byte chucks, so make the number per-transaction a nice multiple + * of 16 (8 byte S/G elements). + */ +/* XXX Worth the space??? */ +#define AHC_NSEG (roundup(btoc(MAXPHYS) + 1, 16)) + +/* This driver supports target mode */ +#define AHC_TARGET_MODE 1 + +/************************** Softc/SCB Platform Data ***************************/ +struct ahc_platform_data { + /* + * Hooks into the XPT. + */ + struct cam_sim *sim; + struct cam_sim *sim_b; + struct cam_path *path; + struct cam_path *path_b; + + int regs_res_type; + int regs_res_id; + int irq_res_type; + struct resource *regs; + struct resource *irq; + void *ih; +}; + +struct scb_platform_data { +}; + +/***************************** Core Includes **********************************/ +#include <dev/aic7xxx/aic7xxx.h> + +/*************************** Device Access ************************************/ +#define ahc_inb(ahc, port) \ + bus_space_read_1((ahc)->tag, (ahc)->bsh, port) + +#define ahc_outb(ahc, port, value) \ + bus_space_write_1((ahc)->tag, (ahc)->bsh, port, value) + +#define ahc_outsb(ahc, port, valp, count) \ + bus_space_write_multi_1((ahc)->tag, (ahc)->bsh, port, valp, count) + +#define ahc_insb(ahc, port, valp, count) \ + bus_space_read_multi_1((ahc)->tag, (ahc)->bsh, port, valp, count) + +static __inline void ahc_flush_device_writes(struct ahc_softc *); + +static __inline void +ahc_flush_device_writes(struct ahc_softc *ahc) +{ + /* XXX Is this sufficient for all architectures??? */ + ahc_inb(ahc, INTSTAT); +} + +/**************************** Locking Primitives ******************************/ +/* Lock protecting internal data structures */ +static __inline void ahc_lockinit(struct ahc_softc *); +static __inline void ahc_lock(struct ahc_softc *, unsigned long *flags); +static __inline void ahc_unlock(struct ahc_softc *, unsigned long *flags); + +/* Lock held during command compeletion to the upper layer */ +static __inline void ahc_done_lockinit(struct ahc_softc *); +static __inline void ahc_done_lock(struct ahc_softc *, unsigned long *flags); +static __inline void ahc_done_unlock(struct ahc_softc *, unsigned long *flags); + +static __inline void +ahc_lockinit(struct ahc_softc *ahc) +{ +} + +static __inline void +ahc_lock(struct ahc_softc *ahc, unsigned long *flags) +{ + *flags = splcam(); +} + +static __inline void +ahc_unlock(struct ahc_softc *ahc, unsigned long *flags) +{ + splx(*flags); +} + +/* Lock held during command compeletion to the upper layer */ +static __inline void +ahc_done_lockinit(struct ahc_softc *ahc) +{ +} + +static __inline void +ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags) +{ +} + +static __inline void +ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags) +{ +} + +/****************************** OS Primitives *********************************/ +#define ahc_delay DELAY + +/************************** Transaction Operations ****************************/ +static __inline void ahc_set_transaction_status(struct scb *, uint32_t); +static __inline void ahc_set_scsi_status(struct scb *, uint32_t); +static __inline uint32_t ahc_get_transaction_status(struct scb *); +static __inline uint32_t ahc_get_scsi_status(struct scb *); +static __inline void ahc_set_transaction_tag(struct scb *, int, u_int); +static __inline u_long ahc_get_transfer_length(struct scb *); +static __inline int ahc_get_transfer_dir(struct scb *); +static __inline void ahc_set_residual(struct scb *, u_long); +static __inline void ahc_set_sense_residual(struct scb *, u_long); +static __inline u_long ahc_get_residual(struct scb *); +static __inline int ahc_perform_autosense(struct scb *); +static __inline uint32_t ahc_get_sense_bufsize(struct ahc_softc*, struct scb*); +static __inline void ahc_freeze_ccb(union ccb *ccb); +static __inline void ahc_freeze_scb(struct scb *scb); +static __inline void ahc_platform_freeze_devq(struct ahc_softc *, struct scb *); +static __inline int ahc_platform_abort_scbs(struct ahc_softc *ahc, int target, + char channel, int lun, u_int tag, + role_t role, uint32_t status); + +static __inline +void ahc_set_transaction_status(struct scb *scb, uint32_t status) +{ + scb->io_ctx->ccb_h.status &= ~CAM_STATUS_MASK; + scb->io_ctx->ccb_h.status |= status; +} + +static __inline +void ahc_set_scsi_status(struct scb *scb, uint32_t status) +{ + scb->io_ctx->csio.scsi_status = status; +} + +static __inline +uint32_t ahc_get_transaction_status(struct scb *scb) +{ + return (scb->io_ctx->ccb_h.status & CAM_STATUS_MASK); +} + +static __inline +uint32_t ahc_get_scsi_status(struct scb *scb) +{ + return (scb->io_ctx->csio.scsi_status); +} + +static __inline +void ahc_set_transaction_tag(struct scb *scb, int enabled, u_int type) +{ + scb->io_ctx->csio.tag_action = type; + if (enabled) + scb->io_ctx->ccb_h.flags |= CAM_TAG_ACTION_VALID; + else + scb->io_ctx->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; +} + +static __inline +u_long ahc_get_transfer_length(struct scb *scb) +{ + return (scb->io_ctx->csio.dxfer_len); +} + +static __inline +int ahc_get_transfer_dir(struct scb *scb) +{ + return (scb->io_ctx->ccb_h.flags & CAM_DIR_MASK); +} + +static __inline +void ahc_set_residual(struct scb *scb, u_long resid) +{ + scb->io_ctx->csio.resid = resid; +} + +static __inline +void ahc_set_sense_residual(struct scb *scb, u_long resid) +{ + scb->io_ctx->csio.sense_resid = resid; +} + +static __inline +u_long ahc_get_residual(struct scb *scb) +{ + return (scb->io_ctx->csio.resid); +} + +static __inline +int ahc_perform_autosense(struct scb *scb) +{ + return (!(scb->io_ctx->ccb_h.flags & CAM_DIS_AUTOSENSE)); +} + +static __inline uint32_t +ahc_get_sense_bufsize(struct ahc_softc *ahc, struct scb *scb) +{ + return (sizeof(struct scsi_sense_data)); +} + +static __inline void +ahc_freeze_ccb(union ccb *ccb) +{ + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + ccb->ccb_h.status |= CAM_DEV_QFRZN; + xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); + } +} + +static __inline void +ahc_freeze_scb(struct scb *scb) +{ + ahc_freeze_ccb(scb->io_ctx); +} + +static __inline void +ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb) +{ + /* Nothing to do here for FreeBSD */ +} + +static __inline int +ahc_platform_abort_scbs(struct ahc_softc *ahc, int target, + char channel, int lun, u_int tag, + role_t role, uint32_t status) +{ + /* Nothing to do here for FreeBSD */ + return (0); +} + +/********************************** PCI ***************************************/ +#ifdef AHC_SUPPORT_PCI +static __inline uint32_t ahc_pci_read_config(ahc_dev_softc_t pci, + int reg, int width); +static __inline void ahc_pci_write_config(ahc_dev_softc_t pci, + int reg, uint32_t value, + int width); +static __inline int ahc_get_pci_function(ahc_dev_softc_t); +static __inline int ahc_get_pci_slot(ahc_dev_softc_t); +static __inline int ahc_get_pci_bus(ahc_dev_softc_t); + +int ahc_pci_map_registers(struct ahc_softc *ahc); +int ahc_pci_map_int(struct ahc_softc *ahc); + +static __inline uint32_t +ahc_pci_read_config(ahc_dev_softc_t pci, int reg, int width) +{ + return (pci_read_config(pci, reg, width)); +} + +static __inline void +ahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width) +{ + pci_write_config(pci, reg, value, width); +} + +static __inline int +ahc_get_pci_function(ahc_dev_softc_t pci) +{ + return (pci_get_function(pci)); +} + +static __inline int +ahc_get_pci_slot(ahc_dev_softc_t pci) +{ + return (pci_get_slot(pci)); +} + +static __inline int +ahc_get_pci_bus(ahc_dev_softc_t pci) +{ + return (pci_get_bus(pci)); +} +#endif +/******************************** VL/EISA *************************************/ +int aic7770_map_registers(struct ahc_softc *ahc); +int aic7770_map_int(struct ahc_softc *ahc); + +/********************************* Debug **************************************/ +static __inline void ahc_print_path(struct ahc_softc *, struct scb *); +static __inline void ahc_platform_dump_card_state(struct ahc_softc *ahc); + +static __inline void +ahc_print_path(struct ahc_softc *ahc, struct scb *scb) +{ + xpt_print_path(scb->io_ctx->ccb_h.path); +} + +static __inline void +ahc_platform_dump_card_state(struct ahc_softc *ahc) +{ + /* Nothing to do here for FreeBSD */ +} +/**************************** Transfer Settings *******************************/ +void ahc_notify_xfer_settings_change(struct ahc_softc *, + struct ahc_devinfo *); +void ahc_platform_set_tags(struct ahc_softc *, struct ahc_devinfo *, + int /*enable*/); + +/***************************** Initialization *********************************/ +int ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg); +void ahc_platform_free(struct ahc_softc *ahc); +int ahc_attach(struct ahc_softc *); +int ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc); + +/************************ Misc Function Declarations **************************/ +timeout_t ahc_timeout; +void ahc_done(struct ahc_softc *ahc, struct scb *scb); +void ahc_send_async(struct ahc_softc *, char /*channel*/, + u_int /*target*/, u_int /*lun*/, ac_code); +#endif /* _AIC7XXX_FREEBSD_H_ */ diff --git a/sys/dev/aic7xxx/aic7xxx_inline.h b/sys/dev/aic7xxx/aic7xxx_inline.h new file mode 100644 index 000000000000..7bf63dee9d33 --- /dev/null +++ b/sys/dev/aic7xxx/aic7xxx_inline.h @@ -0,0 +1,366 @@ +/* + * Inline routines shareable across OS platforms. + * + * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 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, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aic7xxx_inline.h#3 $ + * + * $FreeBSD$ + */ + +#ifndef _AIC7XXX_INLINE_H_ +#define _AIC7XXX_INLINE_H_ + +/************************* Sequencer Execution Control ************************/ +static __inline int sequencer_paused(struct ahc_softc *ahc); +static __inline void ahc_pause_bug_fix(struct ahc_softc *ahc); +static __inline void pause_sequencer(struct ahc_softc *ahc); +static __inline void unpause_sequencer(struct ahc_softc *ahc); + +/* + * Work around any chip bugs related to halting sequencer execution. + * On Ultra2 controllers, we must clear the CIOBUS stretch signal by + * reading a register that will set this signal and deassert it. + * Without this workaround, if the chip is paused, by an interrupt or + * manual pause while accessing scb ram, accesses to certain registers + * will hang the system (infinite pci retries). + */ +static __inline void +ahc_pause_bug_fix(struct ahc_softc *ahc) +{ + if ((ahc->features & AHC_ULTRA2) != 0) + (void)ahc_inb(ahc, CCSCBCTL); +} + +/* + * Determine whether the sequencer has halted code execution. + * Returns non-zero status if the sequencer is stopped. + */ +static __inline int +sequencer_paused(struct ahc_softc *ahc) +{ + return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0); +} + +/* + * Request that the sequencer stop and wait, indefinitely, for it + * to stop. The sequencer will only acknowledge that it is paused + * once it has reached an instruction boundary and PAUSEDIS is + * cleared in the SEQCTL register. The sequencer may use PAUSEDIS + * for critical sections. + */ +static __inline void +pause_sequencer(struct ahc_softc *ahc) +{ + ahc_outb(ahc, HCNTRL, ahc->pause); + + /* + * Since the sequencer can disable pausing in a critical section, we + * must loop until it actually stops. + */ + while (sequencer_paused(ahc) == 0) + ; + + ahc_pause_bug_fix(ahc); +} + +/* + * Allow the sequencer to continue program execution. + * We check here to ensure that no additional interrupt + * sources that would cause the sequencer to halt have been + * asserted. If, for example, a SCSI bus reset is detected + * while we are fielding a different, pausing, interrupt type, + * we don't want to release the sequencer before going back + * into our interrupt handler and dealing with this new + * condition. + */ +static __inline void +unpause_sequencer(struct ahc_softc *ahc) +{ + if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0) + ahc_outb(ahc, HCNTRL, ahc->unpause); +} + +/*********************** Untagged Transaction Routines ************************/ +u_int ahc_index_busy_tcl(struct ahc_softc *ahc, + u_int tcl, int unbusy); +static __inline void ahc_freeze_untagged_queues(struct ahc_softc *ahc); +static __inline void ahc_release_untagged_queues(struct ahc_softc *ahc); + +/* + * Block our completion routine from starting the next untagged + * transaction for this target or target lun. + */ +static __inline void +ahc_freeze_untagged_queues(struct ahc_softc *ahc) +{ + if ((ahc->features & AHC_SCB_BTT) == 0) + ahc->untagged_queue_lock++; +} + +/* + * Allow the next untagged transaction for this target or target lun + * to be executed. We use a counting semaphore to allow the lock + * to be acquired recursively. Once the count drops to zero, the + * transaction queues will be run. + */ +static __inline void +ahc_release_untagged_queues(struct ahc_softc *ahc) +{ + if ((ahc->features & AHC_SCB_BTT) == 0) { + ahc->untagged_queue_lock--; + if (ahc->untagged_queue_lock == 0) + ahc_run_untagged_queues(ahc); + } +} + +/************************** Memory mapping routines ***************************/ +static __inline struct ahc_dma_seg * + ahc_sg_bus_to_virt(struct scb *scb, + uint32_t sg_busaddr); +static __inline uint32_t + ahc_sg_virt_to_bus(struct scb *scb, + struct ahc_dma_seg *sg); +static __inline uint32_t + ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index); + +static __inline struct ahc_dma_seg * +ahc_sg_bus_to_virt(struct scb *scb, uint32_t sg_busaddr) +{ + int sg_index; + + sg_index = (sg_busaddr - scb->sg_list_phys)/sizeof(struct ahc_dma_seg); + /* sg_list_phys points to entry 1, not 0 */ + sg_index++; + + return (&scb->sg_list[sg_index]); +} + +static __inline uint32_t +ahc_sg_virt_to_bus(struct scb *scb, struct ahc_dma_seg *sg) +{ + int sg_index; + + /* sg_list_phys points to entry 1, not 0 */ + sg_index = sg - &scb->sg_list[1]; + + return (scb->sg_list_phys + (sg_index * sizeof(*scb->sg_list))); +} + +static __inline uint32_t +ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index) +{ + return (ahc->scb_data->hscb_busaddr + + (sizeof(struct hardware_scb) * index)); +} + +/******************************** Debugging ***********************************/ +static __inline char *ahc_name(struct ahc_softc *ahc); + +static __inline char * +ahc_name(struct ahc_softc *ahc) +{ + return (ahc->name); +} + +/*********************** Miscelaneous Support Functions ***********************/ + +static __inline int ahc_check_residual(struct scb *scb); +static __inline struct ahc_initiator_tinfo * + ahc_fetch_transinfo(struct ahc_softc *ahc, + char channel, u_int our_id, + u_int remote_id, + struct tmode_tstate **tstate); +static __inline struct scb* + ahc_get_scb(struct ahc_softc *ahc); +static __inline void ahc_free_scb(struct ahc_softc *ahc, struct scb *scb); +static __inline void ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb); + +/* + * Determine whether the sequencer reported a residual + * for this SCB/transaction. + */ +static __inline int +ahc_check_residual(struct scb *scb) +{ + struct status_pkt *sp; + + sp = &scb->hscb->shared_data.status; + if ((scb->hscb->sgptr & SG_RESID_VALID) != 0) + return (1); + return (0); +} + +/* + * Return pointers to the transfer negotiation information + * for the specified our_id/remote_id pair. + */ +static __inline struct ahc_initiator_tinfo * +ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id, + u_int remote_id, struct tmode_tstate **tstate) +{ + /* + * Transfer data structures are stored from the perspective + * of the target role. Since the parameters for a connection + * in the initiator role to a given target are the same as + * when the roles are reversed, we pretend we are the target. + */ + if (channel == 'B') + our_id += 8; + *tstate = ahc->enabled_targets[our_id]; + return (&(*tstate)->transinfo[remote_id]); +} + +/* + * Get a free scb. If there are none, see if we can allocate a new SCB. + */ +static __inline struct scb * +ahc_get_scb(struct ahc_softc *ahc) +{ + struct scb *scbp; + + if ((scbp = SLIST_FIRST(&ahc->scb_data->free_scbs))) { + SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle); + } else { + ahc_alloc_scbs(ahc); + scbp = SLIST_FIRST(&ahc->scb_data->free_scbs); + if (scbp != NULL) + SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle); + } + + return (scbp); +} + +/* + * Return an SCB resource to the free list. + */ +static __inline void +ahc_free_scb(struct ahc_softc *ahc, struct scb *scb) +{ + struct hardware_scb *hscb; + + hscb = scb->hscb; +#if 0 + /* What do we do to generically handle driver resource shortages??? */ + if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0 + && (scb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { + scb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ; + ahc->flags &= ~AHC_RESOURCE_SHORTAGE; + } +#endif + /* Clean up for the next user */ + scb->flags = SCB_FREE; + hscb->control = 0; + + SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links.sle); +} + +/* + * Tell the sequencer about a new transaction to execute. + */ +static __inline void +ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb) +{ + ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag; + if ((ahc->features & AHC_QUEUE_REGS) != 0) { + ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext); + } else { + if ((ahc->features & AHC_AUTOPAUSE) == 0) + pause_sequencer(ahc); + ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext); + if ((ahc->features & AHC_AUTOPAUSE) == 0) + unpause_sequencer(ahc); + } +} + +/************************** Interrupt Processing ******************************/ +static __inline void ahc_intr(struct ahc_softc *ahc); + +/* + * Catch an interrupt from the adapter + */ +static __inline void +ahc_intr(struct ahc_softc *ahc) +{ + u_int intstat; + + intstat = ahc_inb(ahc, INTSTAT); + + /* + * Any interrupts to process? + */ +#if AHC_PCI_CONFIG > 0 + if ((intstat & INT_PEND) == 0) { + if ((ahc->chip & AHC_PCI) != 0 + && (ahc->unsolicited_ints > 500)) { + if ((ahc_inb(ahc, ERROR) & PCIERRSTAT) != 0) + ahc_pci_intr(ahc); + ahc->unsolicited_ints = 0; + } else { + ahc->unsolicited_ints++; + } + return; + } else { + ahc->unsolicited_ints = 0; + } +#else + if ((intstat & INT_PEND) == 0) + return; +#endif + + if (intstat & CMDCMPLT) { + ahc_outb(ahc, CLRINT, CLRCMDINT); + /* + * Ensure that the chip sees that we've cleared + * this interrupt before we walk the output fifo. + * Otherwise, we may, due to posted bus writes, + * clear the interrupt after we finish the scan, + * and after the sequencer has added new entries + * and asserted the interrupt again. + */ + ahc_flush_device_writes(ahc); + ahc_run_qoutfifo(ahc); +#ifdef AHC_TARGET_MODE + if ((ahc->flags & AHC_TARGETMODE) != 0) + ahc_run_tqinfifo(ahc, /*paused*/FALSE); +#endif + } + if (intstat & BRKADRINT) + ahc_handle_brkadrint(ahc); + + if ((intstat & (SEQINT|SCSIINT)) != 0) + ahc_pause_bug_fix(ahc); + + if ((intstat & SEQINT) != 0) + ahc_handle_seqint(ahc, intstat); + + if ((intstat & SCSIINT) != 0) + ahc_handle_scsiint(ahc, intstat); +} + +#endif /* _AIC7XXX_INLINE_H_ */ diff --git a/sys/dev/aic7xxx/aic7xxx_pci.c b/sys/dev/aic7xxx/aic7xxx_pci.c new file mode 100644 index 000000000000..00a5213ed629 --- /dev/null +++ b/sys/dev/aic7xxx/aic7xxx_pci.c @@ -0,0 +1,1927 @@ +/* + * Product specific probe and attach routines for: + * 3940, 2940, aic7895, aic7890, aic7880, + * aic7870, aic7860 and aic7850 SCSI controllers + * + * Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000 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, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aic7xxx_pci.c#4 $ + * + * $FreeBSD$ + */ + +#ifdef __linux__ +#include "aic7xxx_linux.h" +#include "aic7xxx_inline.h" +#include "aic7xxx_93cx6.h" +#endif + +#ifdef __FreeBSD__ +#include <dev/aic7xxx/aic7xxx_freebsd.h> +#include <dev/aic7xxx/aic7xxx_inline.h> +#include <dev/aic7xxx/aic7xxx_93cx6.h> +#endif + +#define AHC_PCI_IOADDR PCIR_MAPS /* I/O Address */ +#define AHC_PCI_MEMADDR (PCIR_MAPS + 4) /* Mem I/O Address */ + +static __inline uint64_t +ahc_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor) +{ + uint64_t id; + + id = subvendor + | (subdevice << 16) + | ((uint64_t)vendor << 32) + | ((uint64_t)device << 48); + + return (id); +} + +#define ID_ALL_MASK 0xFFFFFFFFFFFFFFFFull +#define ID_DEV_VENDOR_MASK 0xFFFFFFFF00000000ull +#define ID_AIC7850 0x5078900400000000ull +#define ID_AHA_2910_15_20_30C 0x5078900478509004ull +#define ID_AIC7855 0x5578900400000000ull +#define ID_AIC7859 0x3860900400000000ull +#define ID_AHA_2930CU 0x3860900438699004ull +#define ID_AIC7860 0x6078900400000000ull +#define ID_AIC7860C 0x6078900478609004ull +#define ID_AHA_1480A 0x6075900400000000ull +#define ID_AHA_2940AU_0 0x6178900400000000ull +#define ID_AHA_2940AU_1 0x6178900478619004ull +#define ID_AHA_2940AU_CN 0x2178900478219004ull +#define ID_AHA_2930C_VAR 0x6038900438689004ull + +#define ID_AIC7870 0x7078900400000000ull +#define ID_AHA_2940 0x7178900400000000ull +#define ID_AHA_3940 0x7278900400000000ull +#define ID_AHA_398X 0x7378900400000000ull +#define ID_AHA_2944 0x7478900400000000ull +#define ID_AHA_3944 0x7578900400000000ull +#define ID_AHA_4944 0x7678900400000000ull + +#define ID_AIC7880 0x8078900400000000ull +#define ID_AIC7880_B 0x8078900478809004ull +#define ID_AHA_2940U 0x8178900400000000ull +#define ID_AHA_3940U 0x8278900400000000ull +#define ID_AHA_2944U 0x8478900400000000ull +#define ID_AHA_3944U 0x8578900400000000ull +#define ID_AHA_398XU 0x8378900400000000ull +#define ID_AHA_4944U 0x8678900400000000ull +#define ID_AHA_2940UB 0x8178900478819004ull +#define ID_AHA_2930U 0x8878900478889004ull +#define ID_AHA_2940U_PRO 0x8778900478879004ull +#define ID_AHA_2940U_CN 0x0078900478009004ull + +#define ID_AIC7895 0x7895900478959004ull +#define ID_AIC7895_RAID_PORT 0x7893900478939004ull +#define ID_AHA_2940U_DUAL 0x7895900478919004ull +#define ID_AHA_3940AU 0x7895900478929004ull +#define ID_AHA_3944AU 0x7895900478949004ull + +#define ID_AIC7890 0x001F9005000F9005ull +#define ID_AAA_131U2 0x0013900500039005ull +#define ID_AHA_2930U2 0x0011900501819005ull +#define ID_AHA_2940U2B 0x00109005A1009005ull +#define ID_AHA_2940U2_OEM 0x0010900521809005ull +#define ID_AHA_2940U2 0x00109005A1809005ull +#define ID_AHA_2950U2B 0x00109005E1009005ull + +#define ID_AIC7892 0x008F9005FFFF9005ull +#define ID_AHA_29160 0x00809005E2A09005ull +#define ID_AHA_29160_CPQ 0x00809005E2A00E11ull +#define ID_AHA_29160N 0x0080900562A09005ull +#define ID_AHA_29160B 0x00809005E2209005ull +#define ID_AHA_19160B 0x0081900562A19005ull + +#define ID_AIC7896 0x005F9005FFFF9005ull +#define ID_AHA_3950U2B_0 0x00509005FFFF9005ull +#define ID_AHA_3950U2B_1 0x00509005F5009005ull +#define ID_AHA_3950U2D_0 0x00519005FFFF9005ull +#define ID_AHA_3950U2D_1 0x00519005B5009005ull + +#define ID_AIC7899 0x00CF9005FFFF9005ull +#define ID_AHA_3960D 0x00C09005F6209005ull /* AKA AHA-39160 */ +#define ID_AHA_3960D_CPQ 0x00C09005F6200E11ull + +#define ID_AIC7810 0x1078900400000000ull +#define ID_AIC7815 0x7815900400000000ull + +static ahc_device_setup_t ahc_aic7850_setup; +static ahc_device_setup_t ahc_aic7855_setup; +static ahc_device_setup_t ahc_aic7860_setup; +static ahc_device_setup_t ahc_aic7870_setup; +static ahc_device_setup_t ahc_aha394X_setup; +static ahc_device_setup_t ahc_aha494X_setup; +static ahc_device_setup_t ahc_aha398X_setup; +static ahc_device_setup_t ahc_aic7880_setup; +static ahc_device_setup_t ahc_2940Pro_setup; +static ahc_device_setup_t ahc_aha394XU_setup; +static ahc_device_setup_t ahc_aha398XU_setup; +static ahc_device_setup_t ahc_aic7890_setup; +static ahc_device_setup_t ahc_aic7892_setup; +static ahc_device_setup_t ahc_aic7895_setup; +static ahc_device_setup_t ahc_aic7896_setup; +static ahc_device_setup_t ahc_aic7899_setup; +static ahc_device_setup_t ahc_raid_setup; +static ahc_device_setup_t ahc_aha394XX_setup; +static ahc_device_setup_t ahc_aha494XX_setup; +static ahc_device_setup_t ahc_aha398XX_setup; + +struct ahc_pci_identity ahc_pci_ident_table [] = +{ + /* aic7850 based controllers */ + { + ID_AHA_2910_15_20_30C, + ID_ALL_MASK, + "Adaptec 2910/15/20/30C SCSI adapter", + ahc_aic7850_setup + }, + /* aic7860 based controllers */ + { + ID_AHA_2930CU, + ID_ALL_MASK, + "Adaptec 2930CU SCSI adapter", + ahc_aic7860_setup + }, + { + ID_AHA_1480A & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 1480A Ultra SCSI adapter", + ahc_aic7860_setup + }, + { + ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 2940A Ultra SCSI adapter", + ahc_aic7860_setup + }, + { + ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 2940A/CN Ultra SCSI adapter", + ahc_aic7860_setup + }, + { + ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 2930C SCSI adapter (VAR)", + ahc_aic7860_setup + }, + /* aic7870 based controllers */ + { + ID_AHA_2940, + ID_ALL_MASK, + "Adaptec 2940 SCSI adapter", + ahc_aic7870_setup + }, + { + ID_AHA_3940, + ID_ALL_MASK, + "Adaptec 3940 SCSI adapter", + ahc_aha394X_setup + }, + { + ID_AHA_398X, + ID_ALL_MASK, + "Adaptec 398X SCSI RAID adapter", + ahc_aha398X_setup + }, + { + ID_AHA_2944, + ID_ALL_MASK, + "Adaptec 2944 SCSI adapter", + ahc_aic7870_setup + }, + { + ID_AHA_3944, + ID_ALL_MASK, + "Adaptec 3944 SCSI adapter", + ahc_aha394X_setup + }, + { + ID_AHA_4944, + ID_ALL_MASK, + "Adaptec 4944 SCSI adapter", + ahc_aha494X_setup + }, + /* aic7880 based controllers */ + { + ID_AHA_2940U & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 2940 Ultra SCSI adapter", + ahc_aic7880_setup + }, + { + ID_AHA_3940U & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 3940 Ultra SCSI adapter", + ahc_aha394XU_setup + }, + { + ID_AHA_2944U & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 2944 Ultra SCSI adapter", + ahc_aic7880_setup + }, + { + ID_AHA_3944U & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 3944 Ultra SCSI adapter", + ahc_aha394XU_setup + }, + { + ID_AHA_398XU & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 398X Ultra SCSI RAID adapter", + ahc_aha398XU_setup + }, + { + /* + * XXX Don't know the slot numbers + * so we can't identify channels + */ + ID_AHA_4944U & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 4944 Ultra SCSI adapter", + ahc_aic7880_setup + }, + { + ID_AHA_2930U & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 2930 Ultra SCSI adapter", + ahc_aic7880_setup + }, + { + ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 2940 Pro Ultra SCSI adapter", + ahc_2940Pro_setup + }, + { + ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec 2940/CN Ultra SCSI adapter", + ahc_aic7880_setup + }, + /* aic7890 based controllers */ + { + ID_AHA_2930U2, + ID_ALL_MASK, + "Adaptec 2930 Ultra2 SCSI adapter", + ahc_aic7890_setup + }, + { + ID_AHA_2940U2B, + ID_ALL_MASK, + "Adaptec 2940B Ultra2 SCSI adapter", + ahc_aic7890_setup + }, + { + ID_AHA_2940U2_OEM, + ID_ALL_MASK, + "Adaptec 2940 Ultra2 SCSI adapter (OEM)", + ahc_aic7890_setup + }, + { + ID_AHA_2940U2, + ID_ALL_MASK, + "Adaptec 2940 Ultra2 SCSI adapter", + ahc_aic7890_setup + }, + { + ID_AHA_2950U2B, + ID_ALL_MASK, + "Adaptec 2950 Ultra2 SCSI adapter", + ahc_aic7890_setup + }, + { + ID_AAA_131U2, + ID_ALL_MASK, + "Adaptec AAA-131 Ultra2 RAID adapter", + ahc_aic7890_setup + }, + /* aic7892 based controllers */ + { + ID_AHA_29160, + ID_ALL_MASK, + "Adaptec 29160 Ultra160 SCSI adapter", + ahc_aic7892_setup + }, + { + ID_AHA_29160_CPQ, + ID_ALL_MASK, + "Adaptec (Compaq OEM) 29160 Ultra160 SCSI adapter", + ahc_aic7892_setup + }, + { + ID_AHA_29160N, + ID_ALL_MASK, + "Adaptec 29160N Ultra160 SCSI adapter", + ahc_aic7892_setup + }, + { + ID_AHA_29160B, + ID_ALL_MASK, + "Adaptec 29160B Ultra160 SCSI adapter", + ahc_aic7892_setup + }, + { + ID_AHA_19160B, + ID_ALL_MASK, + "Adaptec 19160B Ultra160 SCSI adapter", + ahc_aic7892_setup + }, + /* aic7895 based controllers */ + { + ID_AHA_2940U_DUAL, + ID_ALL_MASK, + "Adaptec 2940/DUAL Ultra SCSI adapter", + ahc_aic7895_setup + }, + { + ID_AHA_3940AU, + ID_ALL_MASK, + "Adaptec 3940A Ultra SCSI adapter", + ahc_aic7895_setup + }, + { + ID_AHA_3944AU, + ID_ALL_MASK, + "Adaptec 3944A Ultra SCSI adapter", + ahc_aic7895_setup + }, + /* aic7896/97 based controllers */ + { + ID_AHA_3950U2B_0, + ID_ALL_MASK, + "Adaptec 3950B Ultra2 SCSI adapter", + ahc_aic7896_setup + }, + { + ID_AHA_3950U2B_1, + ID_ALL_MASK, + "Adaptec 3950B Ultra2 SCSI adapter", + ahc_aic7896_setup + }, + { + ID_AHA_3950U2D_0, + ID_ALL_MASK, + "Adaptec 3950D Ultra2 SCSI adapter", + ahc_aic7896_setup + }, + { + ID_AHA_3950U2D_1, + ID_ALL_MASK, + "Adaptec 3950D Ultra2 SCSI adapter", + ahc_aic7896_setup + }, + /* aic7899 based controllers */ + { + ID_AHA_3960D, + ID_ALL_MASK, + "Adaptec 3960D Ultra160 SCSI adapter", + ahc_aic7899_setup + }, + { + ID_AHA_3960D_CPQ, + ID_ALL_MASK, + "Adaptec (Compaq OEM) 3960D Ultra160 SCSI adapter", + ahc_aic7899_setup + }, + /* Generic chip probes for devices we don't know 'exactly' */ + { + ID_AIC7850 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7850 SCSI adapter", + ahc_aic7850_setup + }, + { + ID_AIC7855 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7855 SCSI adapter", + ahc_aic7855_setup + }, + { + ID_AIC7859 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7859 Ultra SCSI adapter", + ahc_aic7860_setup + }, + { + ID_AIC7860 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7860 SCSI adapter", + ahc_aic7860_setup + }, + { + ID_AIC7870 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7870 SCSI adapter", + ahc_aic7870_setup + }, + { + ID_AIC7880 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7880 Ultra SCSI adapter", + ahc_aic7880_setup + }, + { + ID_AIC7890 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7890/91 Ultra2 SCSI adapter", + ahc_aic7890_setup + }, + { + ID_AIC7892 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7892 Ultra160 SCSI adapter", + ahc_aic7892_setup + }, + { + ID_AIC7895 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7895 Ultra SCSI adapter", + ahc_aic7895_setup + }, + { + ID_AIC7895_RAID_PORT & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7895 Ultra SCSI adapter (RAID PORT)", + ahc_aic7895_setup + }, + { + ID_AIC7896 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7896/97 Ultra2 SCSI adapter", + ahc_aic7896_setup + }, + { + ID_AIC7899 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7899 Ultra160 SCSI adapter", + ahc_aic7899_setup + }, + { + ID_AIC7810 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7810 RAID memory controller", + ahc_raid_setup + }, + { + ID_AIC7815 & ID_DEV_VENDOR_MASK, + ID_DEV_VENDOR_MASK, + "Adaptec aic7815 RAID memory controller", + ahc_raid_setup + } +}; + +const int ahc_num_pci_devs = NUM_ELEMENTS(ahc_pci_ident_table); + +#define AHC_394X_SLOT_CHANNEL_A 4 +#define AHC_394X_SLOT_CHANNEL_B 5 + +#define AHC_398X_SLOT_CHANNEL_A 4 +#define AHC_398X_SLOT_CHANNEL_B 8 +#define AHC_398X_SLOT_CHANNEL_C 12 + +#define AHC_494X_SLOT_CHANNEL_A 4 +#define AHC_494X_SLOT_CHANNEL_B 5 +#define AHC_494X_SLOT_CHANNEL_C 6 +#define AHC_494X_SLOT_CHANNEL_D 7 + +#define DEVCONFIG 0x40 +#define SCBSIZE32 0x00010000ul /* aic789X only */ +#define MPORTMODE 0x00000400ul /* aic7870 only */ +#define RAMPSM 0x00000200ul /* aic7870 only */ +#define VOLSENSE 0x00000100ul +#define SCBRAMSEL 0x00000080ul +#define MRDCEN 0x00000040ul +#define EXTSCBTIME 0x00000020ul /* aic7870 only */ +#define EXTSCBPEN 0x00000010ul /* aic7870 only */ +#define BERREN 0x00000008ul +#define DACEN 0x00000004ul +#define STPWLEVEL 0x00000002ul +#define DIFACTNEGEN 0x00000001ul /* aic7870 only */ + +#define CSIZE_LATTIME 0x0c +#define CACHESIZE 0x0000003ful /* only 5 bits */ +#define LATTIME 0x0000ff00ul + +static int ahc_ext_scbram_present(struct ahc_softc *ahc); +static void ahc_scbram_config(struct ahc_softc *ahc, int enable, + int pcheck, int fast, int large); +static void ahc_probe_ext_scbram(struct ahc_softc *ahc); +static void check_extport(struct ahc_softc *ahc, u_int *sxfrctl1); +static void configure_termination(struct ahc_softc *ahc, + struct seeprom_descriptor *sd, + u_int adapter_control, + u_int *sxfrctl1); + +static void ahc_new_term_detect(struct ahc_softc *ahc, + int *enableSEC_low, + int *enableSEC_high, + int *enablePRI_low, + int *enablePRI_high, + int *eeprom_present); +static void aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present, + int *internal68_present, + int *externalcable_present, + int *eeprom_present); +static void aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present, + int *externalcable_present, + int *eeprom_present); +static int acquire_seeprom(struct ahc_softc *ahc, + struct seeprom_descriptor *sd); +static void release_seeprom(struct seeprom_descriptor *sd); +static void write_brdctl(struct ahc_softc *ahc, uint8_t value); +static uint8_t read_brdctl(struct ahc_softc *ahc); + +struct ahc_pci_identity * +ahc_find_pci_device(ahc_dev_softc_t pci) +{ + uint64_t full_id; + uint16_t device; + uint16_t vendor; + uint16_t subdevice; + uint16_t subvendor; + struct ahc_pci_identity *entry; + u_int i; + + vendor = ahc_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2); + device = ahc_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2); + subvendor = ahc_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2); + subdevice = ahc_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2); + full_id = ahc_compose_id(device, + vendor, + subdevice, + subvendor); + + for (i = 0; i < ahc_num_pci_devs; i++) { + entry = &ahc_pci_ident_table[i]; + if (entry->full_id == (full_id & entry->id_mask)) + return (entry); + } + return (NULL); +} + +int +ahc_pci_config(struct ahc_softc *ahc, struct ahc_pci_identity *entry) +{ + struct ahc_probe_config probe_config; + struct scb_data *shared_scb_data; + u_int command; + u_int our_id = 0; + u_int sxfrctl1; + u_int scsiseq; + u_int dscommand0; + int error; + uint8_t sblkctl; + + shared_scb_data = NULL; + ahc_init_probe_config(&probe_config); + error = entry->setup(ahc->dev_softc, &probe_config); + if (error != 0) + return (error); + probe_config.chip |= AHC_PCI; + probe_config.description = entry->name; + + error = ahc_pci_map_registers(ahc); + if (error != 0) + return (error); + + /* Ensure busmastering is enabled */ + command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/1); + command |= PCIM_CMD_BUSMASTEREN; + ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, /*bytes*/1); + + /* On all PCI adapters, we allow SCB paging */ + probe_config.flags |= AHC_PAGESCBS; + + error = ahc_softc_init(ahc, &probe_config); + if (error != 0) + return (error); + + /* Remeber how the card was setup in case there is no SEEPROM */ + pause_sequencer(ahc); + if ((ahc->features & AHC_ULTRA2) != 0) + our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID; + else + our_id = ahc_inb(ahc, SCSIID) & OID; + sxfrctl1 = ahc_inb(ahc, SXFRCTL1) & STPWEN; + scsiseq = ahc_inb(ahc, SCSISEQ); + + error = ahc_reset(ahc); + if (error != 0) + return (ENXIO); + + if ((ahc->features & AHC_DT) != 0) { + u_int sfunct; + + /* Perform ALT-Mode Setup */ + sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE; + ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE); + ahc_outb(ahc, OPTIONMODE, OPTIONMODE_DEFAULTS); + /* Send CRC info in target mode every 4K */ + ahc_outb(ahc, TARGCRCCNT, 0); + ahc_outb(ahc, TARGCRCCNT + 1, 0x10); + ahc_outb(ahc, SFUNCT, sfunct); + + /* Normal mode setup */ + ahc_outb(ahc, CRCCONTROL1, CRCVALCHKEN|CRCENDCHKEN|CRCREQCHKEN + |TARGCRCENDEN|TARGCRCCNTEN); + } + + error = ahc_pci_map_int(ahc); + if (error != 0) + return (error); + dscommand0 = ahc_inb(ahc, DSCOMMAND0); + dscommand0 |= MPARCKEN; + if ((ahc->features & AHC_ULTRA2) != 0) { + + /* + * DPARCKEN doesn't work correctly on + * some MBs so don't use it. + */ + dscommand0 &= ~DPARCKEN; + dscommand0 |= CACHETHEN; + } + + /* + * Handle chips that must have cache line + * streaming (dis/en)abled. + */ + if ((ahc->bugs & AHC_CACHETHEN_DIS_BUG) != 0) + dscommand0 |= CACHETHEN; + + if ((ahc->bugs & AHC_CACHETHEN_BUG) != 0) + dscommand0 &= ~CACHETHEN; + + ahc_outb(ahc, DSCOMMAND0, dscommand0); + + ahc->pci_cachesize = + ahc_pci_read_config(ahc->dev_softc, CSIZE_LATTIME, + /*bytes*/1) & CACHESIZE; + ahc->pci_cachesize *= 4; + + /* See if we have a SEEPROM and perform auto-term */ + check_extport(ahc, &sxfrctl1); + + /* + * Take the LED out of diagnostic mode + */ + sblkctl = ahc_inb(ahc, SBLKCTL); + ahc_outb(ahc, SBLKCTL, (sblkctl & ~(DIAGLEDEN|DIAGLEDON))); + + /* + * I don't know where this is set in the SEEPROM or by the + * BIOS, so we default to 100% on Ultra or slower controllers + * and 75% on ULTRA2 controllers. + */ + if ((ahc->features & AHC_ULTRA2) != 0) { + ahc_outb(ahc, DFF_THRSH, RD_DFTHRSH_75|WR_DFTHRSH_75); + } else { + ahc_outb(ahc, DSPCISTATUS, DFTHRSH_100); + } + + if (ahc->flags & AHC_USEDEFAULTS) { + /* + * PCI Adapter default setup + * Should only be used if the adapter does not have + * a SEEPROM. + */ + /* See if someone else set us up already */ + if (scsiseq != 0) { + printf("%s: Using left over BIOS settings\n", + ahc_name(ahc)); + ahc->flags &= ~AHC_USEDEFAULTS; + } else { + /* + * Assume only one connector and always turn + * on termination. + */ + our_id = 0x07; + sxfrctl1 = STPWEN; + } + ahc_outb(ahc, SCSICONF, our_id|ENSPCHK|RESET_SCSI); + + ahc->our_id = our_id; + } + + /* + * Take a look to see if we have external SRAM. + * We currently do not attempt to use SRAM that is + * shared among multiple controllers. + */ + ahc_probe_ext_scbram(ahc); + + /* + * Record our termination setting for the + * generic initialization routine. + */ + if ((sxfrctl1 & STPWEN) != 0) + ahc->flags |= AHC_TERM_ENB_A; + + /* Core initialization */ + error = ahc_init(ahc); + if (error != 0) + return (error); + + /* + * Link this softc in with all other ahc instances. + */ + ahc_softc_insert(ahc); + + return (0); +} + +/* + * Test for the presense of external sram in an + * "unshared" configuration. + */ +static int +ahc_ext_scbram_present(struct ahc_softc *ahc) +{ + int ramps; + int single_user; + uint32_t devconfig; + + devconfig = ahc_pci_read_config(ahc->dev_softc, + DEVCONFIG, /*bytes*/4); + single_user = (devconfig & MPORTMODE) != 0; + + if ((ahc->features & AHC_ULTRA2) != 0) + ramps = (ahc_inb(ahc, DSCOMMAND0) & RAMPS) != 0; + else if ((ahc->chip & AHC_CHIPID_MASK) >= AHC_AIC7870) + ramps = (devconfig & RAMPSM) != 0; + else + ramps = 0; + + if (ramps && single_user) + return (1); + return (0); +} + +/* + * Enable external scbram. + */ +static void +ahc_scbram_config(struct ahc_softc *ahc, int enable, int pcheck, + int fast, int large) +{ + uint32_t devconfig; + + if (ahc->features & AHC_MULTI_FUNC) { + /* + * Set the SCB Base addr (highest address bit) + * depending on which channel we are. + */ + ahc_outb(ahc, SCBBADDR, ahc_get_pci_function(ahc->dev_softc)); + } + + devconfig = ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4); + if ((ahc->features & AHC_ULTRA2) != 0) { + u_int dscommand0; + + dscommand0 = ahc_inb(ahc, DSCOMMAND0); + if (enable) + dscommand0 &= ~INTSCBRAMSEL; + else + dscommand0 |= INTSCBRAMSEL; + if (large) + dscommand0 &= ~USCBSIZE32; + else + dscommand0 |= USCBSIZE32; + ahc_outb(ahc, DSCOMMAND0, dscommand0); + } else { + if (fast) + devconfig &= ~EXTSCBTIME; + else + devconfig |= EXTSCBTIME; + if (enable) + devconfig &= ~SCBRAMSEL; + else + devconfig |= SCBRAMSEL; + if (large) + devconfig &= ~SCBSIZE32; + else + devconfig |= SCBSIZE32; + } + if (pcheck) + devconfig |= EXTSCBPEN; + else + devconfig &= ~EXTSCBPEN; + + ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, devconfig, /*bytes*/4); +} + +/* + * Take a look to see if we have external SRAM. + * We currently do not attempt to use SRAM that is + * shared among multiple controllers. + */ +static void +ahc_probe_ext_scbram(struct ahc_softc *ahc) +{ + int num_scbs; + int test_num_scbs; + int enable; + int pcheck; + int fast; + int large; + + enable = FALSE; + pcheck = FALSE; + fast = FALSE; + large = FALSE; + num_scbs = 0; + + if (ahc_ext_scbram_present(ahc) == 0) + goto done; + + /* + * Probe for the best parameters to use. + */ + ahc_scbram_config(ahc, /*enable*/TRUE, pcheck, fast, large); + num_scbs = ahc_probe_scbs(ahc); + if (num_scbs == 0) { + /* The SRAM wasn't really present. */ + goto done; + } + enable = TRUE; + + /* + * Clear any outstanding parity error + * and ensure that parity error reporting + * is enabled. + */ + ahc_outb(ahc, SEQCTL, 0); + ahc_outb(ahc, CLRINT, CLRPARERR); + ahc_outb(ahc, CLRINT, CLRBRKADRINT); + + /* Now see if we can do parity */ + ahc_scbram_config(ahc, enable, /*pcheck*/TRUE, fast, large); + num_scbs = ahc_probe_scbs(ahc); + if ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0 + || (ahc_inb(ahc, ERROR) & MPARERR) == 0) + pcheck = TRUE; + + /* Clear any resulting parity error */ + ahc_outb(ahc, CLRINT, CLRPARERR); + ahc_outb(ahc, CLRINT, CLRBRKADRINT); + + /* Now see if we can do fast timing */ + ahc_scbram_config(ahc, enable, pcheck, /*fast*/TRUE, large); + test_num_scbs = ahc_probe_scbs(ahc); + if (test_num_scbs == num_scbs + && ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0 + || (ahc_inb(ahc, ERROR) & MPARERR) == 0)) + fast = TRUE; + + /* + * See if we can use large SCBs and still maintain + * the same overall count of SCBs. + */ + if ((ahc->features & AHC_LARGE_SCBS) != 0) { + ahc_scbram_config(ahc, enable, pcheck, fast, /*large*/TRUE); + test_num_scbs = ahc_probe_scbs(ahc); + if (test_num_scbs >= num_scbs) { + large = TRUE; + num_scbs = test_num_scbs; + if (num_scbs >= 64) { + /* + * We have enough space to move the + * "busy targets table" into SCB space + * and make it qualify all the way to the + * lun level. + */ + ahc->flags |= AHC_SCB_BTT; + } + } + } +done: + /* + * Disable parity error reporting until we + * can load instruction ram. + */ + ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS); + /* Clear any latched parity error */ + ahc_outb(ahc, CLRINT, CLRPARERR); + ahc_outb(ahc, CLRINT, CLRBRKADRINT); + if (bootverbose && enable) { + printf("%s: External SRAM, %s access%s, %dbytes/SCB\n", + ahc_name(ahc), fast ? "fast" : "slow", + pcheck ? ", parity checking enabled" : "", + large ? 64 : 32); + } + ahc_scbram_config(ahc, enable, pcheck, fast, large); +} + +/* + * Check the external port logic for a serial eeprom + * and termination/cable detection contrls. + */ +static void +check_extport(struct ahc_softc *ahc, u_int *sxfrctl1) +{ + struct seeprom_descriptor sd; + struct seeprom_config sc; + u_int scsi_conf; + u_int adapter_control; + int have_seeprom; + int have_autoterm; + + sd.sd_ahc = ahc; + sd.sd_control_offset = SEECTL; + sd.sd_status_offset = SEECTL; + sd.sd_dataout_offset = SEECTL; + + /* + * For some multi-channel devices, the c46 is simply too + * small to work. For the other controller types, we can + * get our information from either SEEPROM type. Set the + * type to start our probe with accordingly. + */ + if (ahc->flags & AHC_LARGE_SEEPROM) + sd.sd_chip = C56_66; + else + sd.sd_chip = C46; + + sd.sd_MS = SEEMS; + sd.sd_RDY = SEERDY; + sd.sd_CS = SEECS; + sd.sd_CK = SEECK; + sd.sd_DO = SEEDO; + sd.sd_DI = SEEDI; + + have_seeprom = acquire_seeprom(ahc, &sd); + if (have_seeprom) { + + if (bootverbose) + printf("%s: Reading SEEPROM...", ahc_name(ahc)); + + for (;;) { + u_int start_addr; + + start_addr = 32 * (ahc->channel - 'A'); + + have_seeprom = read_seeprom(&sd, (uint16_t *)&sc, + start_addr, sizeof(sc)/2); + + if (have_seeprom) + have_seeprom = verify_cksum(&sc); + + if (have_seeprom != 0 || sd.sd_chip == C56_66) { + if (bootverbose) { + if (have_seeprom == 0) + printf ("checksum error\n"); + else + printf ("done.\n"); + } + break; + } + sd.sd_chip = C56_66; + } + } + +#if 0 + if (!have_seeprom) { + /* + * Pull scratch ram settings and treat them as + * if they are the contents of an seeprom if + * the 'ADPT' signature is found in SCB2. + */ + ahc_outb(ahc, SCBPTR, 2); + if (ahc_inb(ahc, SCB_BASE) == 'A' + && ahc_inb(ahc, SCB_BASE + 1) == 'D' + && ahc_inb(ahc, SCB_BASE + 2) == 'P' + && ahc_inb(ahc, SCB_BASE + 3) == 'T') { + uint8_t *sc_bytes; + int i; + + sc_bytes = (uint8_t *)≻ + for (i = 0; i < 64; i++) + sc_bytes[i] = ahc_inb(ahc, TARG_SCSIRATE + i); + /* Byte 0x1c is stored in byte 4 of SCB2 */ + sc_bytes[0x1c] = ahc_inb(ahc, SCB_BASE + 4); + have_seeprom = verify_cksum(&sc); + } + } +#endif + + if (!have_seeprom) { + if (bootverbose) + printf("%s: No SEEPROM available.\n", ahc_name(ahc)); + ahc->flags |= AHC_USEDEFAULTS; + } else { + /* + * Put the data we've collected down into SRAM + * where ahc_init will find it. + */ + int i; + int max_targ = sc.max_targets & CFMAXTARG; + uint16_t discenable; + uint16_t ultraenb; + + discenable = 0; + ultraenb = 0; + if ((sc.adapter_control & CFULTRAEN) != 0) { + /* + * Determine if this adapter has a "newstyle" + * SEEPROM format. + */ + for (i = 0; i < max_targ; i++) { + if ((sc.device_flags[i] & CFSYNCHISULTRA) != 0){ + ahc->flags |= AHC_NEWEEPROM_FMT; + break; + } + } + } + + for (i = 0; i < max_targ; i++) { + u_int scsirate; + uint16_t target_mask; + + target_mask = 0x01 << i; + if (sc.device_flags[i] & CFDISC) + discenable |= target_mask; + if ((ahc->flags & AHC_NEWEEPROM_FMT) != 0) { + if ((sc.device_flags[i] & CFSYNCHISULTRA) != 0) + ultraenb |= target_mask; + } else if ((sc.adapter_control & CFULTRAEN) != 0) { + ultraenb |= target_mask; + } + if ((sc.device_flags[i] & CFXFER) == 0x04 + && (ultraenb & target_mask) != 0) { + /* Treat 10MHz as a non-ultra speed */ + sc.device_flags[i] &= ~CFXFER; + ultraenb &= ~target_mask; + } + if ((ahc->features & AHC_ULTRA2) != 0) { + u_int offset; + + if (sc.device_flags[i] & CFSYNCH) + offset = MAX_OFFSET_ULTRA2; + else + offset = 0; + ahc_outb(ahc, TARG_OFFSET + i, offset); + + /* + * The ultra enable bits contain the + * high bit of the ultra2 sync rate + * field. + */ + scsirate = (sc.device_flags[i] & CFXFER) + | ((ultraenb & target_mask) + ? 0x8 : 0x0); + if (sc.device_flags[i] & CFWIDEB) + scsirate |= WIDEXFER; + } else { + scsirate = (sc.device_flags[i] & CFXFER) << 4; + if (sc.device_flags[i] & CFSYNCH) + scsirate |= SOFS; + if (sc.device_flags[i] & CFWIDEB) + scsirate |= WIDEXFER; + } + ahc_outb(ahc, TARG_SCSIRATE + i, scsirate); + } + ahc->our_id = sc.brtime_id & CFSCSIID; + + scsi_conf = (ahc->our_id & 0x7); + if (sc.adapter_control & CFSPARITY) + scsi_conf |= ENSPCHK; + if (sc.adapter_control & CFRESETB) + scsi_conf |= RESET_SCSI; + + if (sc.bios_control & CFEXTEND) + ahc->flags |= AHC_EXTENDED_TRANS_A; + if (ahc->features & AHC_ULTRA + && (ahc->flags & AHC_NEWEEPROM_FMT) == 0) { + /* Should we enable Ultra mode? */ + if (!(sc.adapter_control & CFULTRAEN)) + /* Treat us as a non-ultra card */ + ultraenb = 0; + } + + if (sc.signature == CFSIGNATURE) { + uint32_t devconfig; + + /* Honor the STPWLEVEL settings */ + devconfig = ahc_pci_read_config(ahc->dev_softc, + DEVCONFIG, /*bytes*/4); + devconfig &= ~STPWLEVEL; + if ((sc.bios_control & CFSTPWLEVEL) != 0) + devconfig |= STPWLEVEL; + ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, + devconfig, /*bytes*/4); + } + /* Set SCSICONF info */ + ahc_outb(ahc, SCSICONF, scsi_conf); + ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff)); + ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff)); + ahc_outb(ahc, ULTRA_ENB, ultraenb & 0xff); + ahc_outb(ahc, ULTRA_ENB + 1, (ultraenb >> 8) & 0xff); + } + + /* + * Cards that have the external logic necessary to talk to + * a SEEPROM, are almost certain to have the remaining logic + * necessary for auto-termination control. This assumption + * hasn't failed yet... + */ + have_autoterm = have_seeprom; + if (have_seeprom) + adapter_control = sc.adapter_control; + else + adapter_control = CFAUTOTERM; + + /* + * Some low-cost chips have SEEPROM and auto-term control built + * in, instead of using a GAL. They can tell us directly + * if the termination logic is enabled. + */ + if ((ahc->features & AHC_SPIOCAP) != 0) { + if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) != 0) + have_autoterm = TRUE; + else + have_autoterm = FALSE; + } + + if (have_autoterm) + configure_termination(ahc, &sd, adapter_control, sxfrctl1); + + release_seeprom(&sd); +} + +static void +configure_termination(struct ahc_softc *ahc, + struct seeprom_descriptor *sd, + u_int adapter_control, + u_int *sxfrctl1) +{ + uint8_t brddat; + + brddat = 0; + + /* + * Update the settings in sxfrctl1 to match the + * termination settings + */ + *sxfrctl1 = 0; + + /* + * SEECS must be on for the GALS to latch + * the data properly. Be sure to leave MS + * on or we will release the seeprom. + */ + SEEPROM_OUTB(sd, sd->sd_MS | sd->sd_CS); + if ((adapter_control & CFAUTOTERM) != 0 + || (ahc->features & AHC_NEW_TERMCTL) != 0) { + int internal50_present; + int internal68_present; + int externalcable_present; + int eeprom_present; + int enableSEC_low; + int enableSEC_high; + int enablePRI_low; + int enablePRI_high; + + enableSEC_low = 0; + enableSEC_high = 0; + enablePRI_low = 0; + enablePRI_high = 0; + if ((ahc->features & AHC_NEW_TERMCTL) != 0) { + ahc_new_term_detect(ahc, &enableSEC_low, + &enableSEC_high, + &enablePRI_low, + &enablePRI_high, + &eeprom_present); + if ((adapter_control & CFSEAUTOTERM) == 0) { + if (bootverbose) + printf("%s: Manual SE Termination\n", + ahc_name(ahc)); + enableSEC_low = (adapter_control & CFSELOWTERM); + enableSEC_high = + (adapter_control & CFSEHIGHTERM); + } + if ((adapter_control & CFAUTOTERM) == 0) { + if (bootverbose) + printf("%s: Manual LVD Termination\n", + ahc_name(ahc)); + enablePRI_low = (adapter_control & CFSTERM); + enablePRI_high = (adapter_control & CFWSTERM); + } + /* Make the table calculations below happy */ + internal50_present = 0; + internal68_present = 1; + externalcable_present = 1; + } else if ((ahc->features & AHC_SPIOCAP) != 0) { + aic785X_cable_detect(ahc, &internal50_present, + &externalcable_present, + &eeprom_present); + } else { + aic787X_cable_detect(ahc, &internal50_present, + &internal68_present, + &externalcable_present, + &eeprom_present); + } + + if ((ahc->features & AHC_WIDE) == 0) + internal68_present = 0; + + if (bootverbose) { + if ((ahc->features & AHC_ULTRA2) == 0) { + printf("%s: internal 50 cable %s present, " + "internal 68 cable %s present\n", + ahc_name(ahc), + internal50_present ? "is":"not", + internal68_present ? "is":"not"); + + printf("%s: external cable %s present\n", + ahc_name(ahc), + externalcable_present ? "is":"not"); + } + printf("%s: BIOS eeprom %s present\n", + ahc_name(ahc), eeprom_present ? "is" : "not"); + } + + if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) { + /* + * The 50 pin connector is a separate bus, + * so force it to always be terminated. + * In the future, perform current sensing + * to determine if we are in the middle of + * a properly terminated bus. + */ + internal50_present = 0; + } + + /* + * Now set the termination based on what + * we found. + * Flash Enable = BRDDAT7 + * Secondary High Term Enable = BRDDAT6 + * Secondary Low Term Enable = BRDDAT5 (7890) + * Primary High Term Enable = BRDDAT4 (7890) + */ + if ((ahc->features & AHC_ULTRA2) == 0 + && (internal50_present != 0) + && (internal68_present != 0) + && (externalcable_present != 0)) { + printf("%s: Illegal cable configuration!!. " + "Only two connectors on the " + "adapter may be used at a " + "time!\n", ahc_name(ahc)); + } + + if ((ahc->features & AHC_WIDE) != 0 + && ((externalcable_present == 0) + || (internal68_present == 0) + || (enableSEC_high != 0))) { + brddat |= BRDDAT6; + if (bootverbose) { + if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) + printf("%s: 68 pin termination " + "Enabled\n", ahc_name(ahc)); + else + printf("%s: %sHigh byte termination " + "Enabled\n", ahc_name(ahc), + enableSEC_high ? "Secondary " + : ""); + } + } + + if (((internal50_present ? 1 : 0) + + (internal68_present ? 1 : 0) + + (externalcable_present ? 1 : 0)) <= 1 + || (enableSEC_low != 0)) { + if ((ahc->features & AHC_ULTRA2) != 0) + brddat |= BRDDAT5; + else + *sxfrctl1 |= STPWEN; + if (bootverbose) { + if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) + printf("%s: 50 pin termination " + "Enabled\n", ahc_name(ahc)); + else + printf("%s: %sLow byte termination " + "Enabled\n", ahc_name(ahc), + enableSEC_low ? "Secondary " + : ""); + } + } + + if (enablePRI_low != 0) { + *sxfrctl1 |= STPWEN; + if (bootverbose) + printf("%s: Primary Low Byte termination " + "Enabled\n", ahc_name(ahc)); + } + + /* + * Setup STPWEN before setting up the rest of + * the termination per the tech note on the U160 cards. + */ + ahc_outb(ahc, SXFRCTL1, *sxfrctl1); + + if (enablePRI_high != 0) { + brddat |= BRDDAT4; + if (bootverbose) + printf("%s: Primary High Byte " + "termination Enabled\n", + ahc_name(ahc)); + } + + write_brdctl(ahc, brddat); + + } else { + if ((adapter_control & CFSTERM) != 0) { + *sxfrctl1 |= STPWEN; + + if (bootverbose) + printf("%s: %sLow byte termination Enabled\n", + ahc_name(ahc), + (ahc->features & AHC_ULTRA2) ? "Primary " + : ""); + } + + if ((adapter_control & CFWSTERM) != 0) { + brddat |= BRDDAT6; + if (bootverbose) + printf("%s: %sHigh byte termination Enabled\n", + ahc_name(ahc), + (ahc->features & AHC_ULTRA2) + ? "Secondary " : ""); + } + + /* + * Setup STPWEN before setting up the rest of + * the termination per the tech note on the U160 cards. + */ + ahc_outb(ahc, SXFRCTL1, *sxfrctl1); + + write_brdctl(ahc, brddat); + } + SEEPROM_OUTB(sd, sd->sd_MS); /* Clear CS */ +} + +static void +ahc_new_term_detect(struct ahc_softc *ahc, int *enableSEC_low, + int *enableSEC_high, int *enablePRI_low, + int *enablePRI_high, int *eeprom_present) +{ + uint8_t brdctl; + + /* + * BRDDAT7 = Eeprom + * BRDDAT6 = Enable Secondary High Byte termination + * BRDDAT5 = Enable Secondary Low Byte termination + * BRDDAT4 = Enable Primary high byte termination + * BRDDAT3 = Enable Primary low byte termination + */ + brdctl = read_brdctl(ahc); + *eeprom_present = brdctl & BRDDAT7; + *enableSEC_high = (brdctl & BRDDAT6); + *enableSEC_low = (brdctl & BRDDAT5); + *enablePRI_high = (brdctl & BRDDAT4); + *enablePRI_low = (brdctl & BRDDAT3); +} + +static void +aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present, + int *internal68_present, int *externalcable_present, + int *eeprom_present) +{ + uint8_t brdctl; + + /* + * First read the status of our cables. + * Set the rom bank to 0 since the + * bank setting serves as a multiplexor + * for the cable detection logic. + * BRDDAT5 controls the bank switch. + */ + write_brdctl(ahc, 0); + + /* + * Now read the state of the internal + * connectors. BRDDAT6 is INT50 and + * BRDDAT7 is INT68. + */ + brdctl = read_brdctl(ahc); + *internal50_present = !(brdctl & BRDDAT6); + *internal68_present = !(brdctl & BRDDAT7); + + /* + * Set the rom bank to 1 and determine + * the other signals. + */ + write_brdctl(ahc, BRDDAT5); + + /* + * Now read the state of the external + * connectors. BRDDAT6 is EXT68 and + * BRDDAT7 is EPROMPS. + */ + brdctl = read_brdctl(ahc); + *externalcable_present = !(brdctl & BRDDAT6); + *eeprom_present = brdctl & BRDDAT7; +} + +static void +aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present, + int *externalcable_present, int *eeprom_present) +{ + uint8_t brdctl; + + ahc_outb(ahc, BRDCTL, BRDRW|BRDCS); + ahc_outb(ahc, BRDCTL, 0); + brdctl = ahc_inb(ahc, BRDCTL); + *internal50_present = !(brdctl & BRDDAT5); + *externalcable_present = !(brdctl & BRDDAT6); + + *eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) != 0; +} + +static int +acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd) +{ + int wait; + + if ((ahc->features & AHC_SPIOCAP) != 0 + && (ahc_inb(ahc, SPIOCAP) & SEEPROM) == 0) + return (0); + + /* + * Request access of the memory port. When access is + * granted, SEERDY will go high. We use a 1 second + * timeout which should be near 1 second more than + * is needed. Reason: after the chip reset, there + * should be no contention. + */ + SEEPROM_OUTB(sd, sd->sd_MS); + wait = 1000; /* 1 second timeout in msec */ + while (--wait && ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0)) { + ahc_delay(1000); /* delay 1 msec */ + } + if ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0) { + SEEPROM_OUTB(sd, 0); + return (0); + } + return(1); +} + +static void +release_seeprom(struct seeprom_descriptor *sd) +{ + /* Release access to the memory port and the serial EEPROM. */ + SEEPROM_OUTB(sd, 0); +} + +static void +write_brdctl(struct ahc_softc *ahc, uint8_t value) +{ + uint8_t brdctl; + + if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { + brdctl = BRDSTB; + if (ahc->channel == 'B') + brdctl |= BRDCS; + } else if ((ahc->features & AHC_ULTRA2) != 0) { + brdctl = 0; + } else { + brdctl = BRDSTB|BRDCS; + } + ahc_outb(ahc, BRDCTL, brdctl); + ahc_delay(20); + brdctl |= value; + ahc_outb(ahc, BRDCTL, brdctl); + ahc_delay(20); + if ((ahc->features & AHC_ULTRA2) != 0) + brdctl |= BRDSTB_ULTRA2; + else + brdctl &= ~BRDSTB; + ahc_outb(ahc, BRDCTL, brdctl); + ahc_delay(20); + if ((ahc->features & AHC_ULTRA2) != 0) + brdctl = 0; + else + brdctl &= ~BRDCS; + ahc_outb(ahc, BRDCTL, brdctl); +} + +static uint8_t +read_brdctl(ahc) + struct ahc_softc *ahc; +{ + uint8_t brdctl; + uint8_t value; + + if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { + brdctl = BRDRW; + if (ahc->channel == 'B') + brdctl |= BRDCS; + } else if ((ahc->features & AHC_ULTRA2) != 0) { + brdctl = BRDRW_ULTRA2; + } else { + brdctl = BRDRW|BRDCS; + } + ahc_outb(ahc, BRDCTL, brdctl); + ahc_delay(20); + value = ahc_inb(ahc, BRDCTL); + ahc_outb(ahc, BRDCTL, 0); + return (value); +} + +#define DPE 0x80 +#define SSE 0x40 +#define RMA 0x20 +#define RTA 0x10 +#define STA 0x08 +#define DPR 0x01 + +void +ahc_pci_intr(struct ahc_softc *ahc) +{ + u_int error; + u_int status1; + + error = ahc_inb(ahc, ERROR); + if ((error & PCIERRSTAT) == 0) + return; + + status1 = ahc_pci_read_config(ahc->dev_softc, + PCIR_STATUS + 1, /*bytes*/1); + + printf("%s: PCI error Interrupt at seqaddr = 0x%x\n", + ahc_name(ahc), + ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8)); + + if (status1 & DPE) { + printf("%s: Data Parity Error Detected during address " + "or write data phase\n", ahc_name(ahc)); + } + if (status1 & SSE) { + printf("%s: Signaled System Error Detected\n", ahc_name(ahc)); + } + if (status1 & RMA) { + printf("%s: Signaled a Master Abort\n", ahc_name(ahc)); + } + if (status1 & RTA) { + printf("%s: Received a Target Abort\n", ahc_name(ahc)); + } + if (status1 & STA) { + printf("%s: Signaled a Target Abort\n", ahc_name(ahc)); + } + if (status1 & DPR) { + printf("%s: Data Parity Error has been reported via PERR#\n", + ahc_name(ahc)); + } + if ((status1 & (DPE|SSE|RMA|RTA|STA|DPR)) == 0) { + printf("%s: Latched PCIERR interrupt with " + "no status bits set\n", ahc_name(ahc)); + } + ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1, + status1, /*bytes*/1); + + if (status1 & (DPR|RMA|RTA)) { + ahc_outb(ahc, CLRINT, CLRPARERR); + } + + unpause_sequencer(ahc); +} + +static int +ahc_aic7850_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + probe_config->channel = 'A'; + probe_config->chip = AHC_AIC7850; + probe_config->features = AHC_AIC7850_FE; + probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG + | AHC_PCI_MWI_BUG; + return (0); +} + +static int +ahc_aic7855_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + probe_config->channel = 'A'; + probe_config->chip = AHC_AIC7855; + probe_config->features = AHC_AIC7855_FE; + probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG + | AHC_PCI_MWI_BUG; + return (0); +} + +static int +ahc_aic7860_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + uint8_t rev; + + probe_config->channel = 'A'; + probe_config->chip = AHC_AIC7860; + probe_config->features = AHC_AIC7860_FE; + probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG + | AHC_PCI_MWI_BUG; + rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1); + if (rev >= 1) + probe_config->bugs |= AHC_PCI_2_1_RETRY_BUG; + return (0); +} + +static int +ahc_aic7870_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + probe_config->channel = 'A'; + probe_config->chip = AHC_AIC7870; + probe_config->features = AHC_AIC7870_FE; + probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG + | AHC_PCI_MWI_BUG; + return (0); +} + +static int +ahc_aha394X_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + int error; + + error = ahc_aic7870_setup(pci, probe_config); + if (error == 0) + error = ahc_aha394XX_setup(pci, probe_config); + return (error); +} + +static int +ahc_aha398X_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + int error; + + error = ahc_aic7870_setup(pci, probe_config); + if (error == 0) + error = ahc_aha398XX_setup(pci, probe_config); + return (error); +} + +static int +ahc_aha494X_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + int error; + + error = ahc_aic7870_setup(pci, probe_config); + if (error == 0) + error = ahc_aha494XX_setup(pci, probe_config); + return (error); +} + +static int +ahc_aic7880_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + uint8_t rev; + + probe_config->channel = 'A'; + probe_config->chip = AHC_AIC7880; + probe_config->features = AHC_AIC7880_FE; + probe_config->bugs |= AHC_TMODE_WIDEODD_BUG; + rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1); + if (rev >= 1) { + probe_config->bugs |= AHC_PCI_2_1_RETRY_BUG; + } else { + probe_config->bugs |= AHC_CACHETHEN_BUG; + } + return (0); +} + +static int +ahc_2940Pro_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + int error; + + probe_config->flags |= AHC_INT50_SPEEDFLEX; + error = ahc_aic7880_setup(pci, probe_config); + return (0); +} + +static int +ahc_aha394XU_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + int error; + + error = ahc_aic7880_setup(pci, probe_config); + if (error == 0) + error = ahc_aha394XX_setup(pci, probe_config); + return (error); +} + +static int +ahc_aha398XU_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + int error; + + error = ahc_aic7880_setup(pci, probe_config); + if (error == 0) + error = ahc_aha398XX_setup(pci, probe_config); + return (error); +} + +static int +ahc_aic7890_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + uint8_t rev; + + probe_config->channel = 'A'; + probe_config->chip = AHC_AIC7890; + probe_config->features = AHC_AIC7890_FE; + probe_config->flags |= AHC_NEWEEPROM_FMT; + rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1); + if (rev == 0) + probe_config->bugs |= AHC_AUTOFLUSH_BUG|AHC_CACHETHEN_BUG; + return (0); +} + +static int +ahc_aic7892_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + probe_config->channel = 'A'; + probe_config->chip = AHC_AIC7892; + probe_config->features = AHC_AIC7892_FE; + probe_config->flags |= AHC_NEWEEPROM_FMT; + probe_config->bugs |= AHC_SCBCHAN_UPLOAD_BUG; + return (0); +} + +static int +ahc_aic7895_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + uint8_t rev; + + probe_config->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A'; + /* + * The 'C' revision of the aic7895 has a few additional features. + */ + rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1); + if (rev >= 4) { + probe_config->chip = AHC_AIC7895C; + probe_config->features = AHC_AIC7895C_FE; + } else { + u_int command; + + probe_config->chip = AHC_AIC7895; + probe_config->features = AHC_AIC7895_FE; + + /* + * The BIOS disables the use of MWI transactions + * since it does not have the MWI bug work around + * we have. Disabling MWI reduces performance, so + * turn it on again. + */ + command = ahc_pci_read_config(pci, PCIR_COMMAND, /*bytes*/1); + command |= PCIM_CMD_MWRICEN; + ahc_pci_write_config(pci, PCIR_COMMAND, command, /*bytes*/1); + probe_config->bugs |= AHC_PCI_MWI_BUG; + } + /* + * XXX Does CACHETHEN really not work??? What about PCI retry? + * on C level chips. Need to test, but for now, play it safe. + */ + probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_PCI_2_1_RETRY_BUG + | AHC_CACHETHEN_BUG; + +#if 0 + uint32_t devconfig; + + /* + * Cachesize must also be zero due to stray DAC + * problem when sitting behind some bridges. + */ + ahc_pci_write_config(pci, CSIZE_LATTIME, 0, /*bytes*/1); + devconfig = ahc_pci_read_config(pci, DEVCONFIG, /*bytes*/1); + devconfig |= MRDCEN; + ahc_pci_write_config(pci, DEVCONFIG, devconfig, /*bytes*/1); +#endif + probe_config->flags |= AHC_NEWEEPROM_FMT; + return (0); +} + +static int +ahc_aic7896_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + probe_config->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A'; + probe_config->chip = AHC_AIC7896; + probe_config->features = AHC_AIC7896_FE; + probe_config->flags |= AHC_NEWEEPROM_FMT; + probe_config->bugs |= AHC_CACHETHEN_DIS_BUG; + return (0); +} + +static int +ahc_aic7899_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + probe_config->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A'; + probe_config->chip = AHC_AIC7899; + probe_config->features = AHC_AIC7899_FE; + probe_config->flags |= AHC_NEWEEPROM_FMT; + probe_config->bugs |= AHC_SCBCHAN_UPLOAD_BUG; + return (0); +} + +static int +ahc_raid_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + printf("RAID functionality unsupported\n"); + return (ENXIO); +} + +static int +ahc_aha394XX_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + switch (ahc_get_pci_slot(pci)) { + case AHC_394X_SLOT_CHANNEL_A: + probe_config->channel = 'A'; + break; + case AHC_394X_SLOT_CHANNEL_B: + probe_config->channel = 'B'; + break; + default: + printf("adapter at unexpected slot %d\n" + "unable to map to a channel\n", + ahc_get_pci_slot(pci)); + probe_config->channel = 'A'; + } + return (0); +} + +static int +ahc_aha398XX_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + switch (ahc_get_pci_slot(pci)) { + case AHC_398X_SLOT_CHANNEL_A: + probe_config->channel = 'A'; + break; + case AHC_398X_SLOT_CHANNEL_B: + probe_config->channel = 'B'; + break; + case AHC_398X_SLOT_CHANNEL_C: + probe_config->channel = 'C'; + break; + default: + printf("adapter at unexpected slot %d\n" + "unable to map to a channel\n", + ahc_get_pci_slot(pci)); + probe_config->channel = 'A'; + break; + } + probe_config->flags |= AHC_LARGE_SEEPROM; + return (0); +} + +static int +ahc_aha494XX_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config) +{ + switch (ahc_get_pci_slot(pci)) { + case AHC_494X_SLOT_CHANNEL_A: + probe_config->channel = 'A'; + break; + case AHC_494X_SLOT_CHANNEL_B: + probe_config->channel = 'B'; + break; + case AHC_494X_SLOT_CHANNEL_C: + probe_config->channel = 'C'; + break; + case AHC_494X_SLOT_CHANNEL_D: + probe_config->channel = 'D'; + break; + default: + printf("adapter at unexpected slot %d\n" + "unable to map to a channel\n", + ahc_get_pci_slot(pci)); + probe_config->channel = 'A'; + } + probe_config->flags |= AHC_LARGE_SEEPROM; + return (0); +} diff --git a/sys/dev/aic7xxx/aicasm/aicasm.c b/sys/dev/aic7xxx/aicasm/aicasm.c new file mode 100644 index 000000000000..361d7e833010 --- /dev/null +++ b/sys/dev/aic7xxx/aicasm/aicasm.c @@ -0,0 +1,770 @@ +/* + * Aic7xxx SCSI host adapter firmware asssembler + * + * Copyright (c) 1997, 1998, 2000 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, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aicasm/aicasm.c#4 $ + * + * $FreeBSD$ + */ +#include <sys/types.h> +#include <sys/mman.h> + +#include <ctype.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include "aicasm.h" +#include "aicasm_symbol.h" +#include "aicasm_insformat.h" + +typedef struct patch { + STAILQ_ENTRY(patch) links; + int patch_func; + u_int begin; + u_int skip_instr; + u_int skip_patch; +} patch_t; + +STAILQ_HEAD(patch_list, patch) patches; + +static void usage(void); +static void back_patch(void); +static void output_code(void); +static void output_listing(char *ifilename); +static void dump_scope(scope_t *scope); +static void emit_patch(scope_t *scope, int patch); +static int check_patch(patch_t **start_patch, int start_instr, + int *skip_addr, int *func_vals); + +struct path_list search_path; +int includes_search_curdir; +char *appname; +FILE *ofile; +char *ofilename; +char *regfilename; +FILE *regfile; +char *listfilename; +FILE *listfile; + +static STAILQ_HEAD(,instruction) seq_program; +struct cs_tailq cs_tailq; +struct scope_list scope_stack; +symlist_t patch_functions; + +#if DEBUG +extern int yy_flex_debug; +extern int yydebug; +#endif +extern FILE *yyin; +extern int yyparse(void); + +int main(int argc, char *argv[]); + +int +main(int argc, char *argv[]) +{ + extern char *optarg; + extern int optind; + int ch; + int retval; + char *inputfilename; + scope_t *sentinal; + + STAILQ_INIT(&patches); + SLIST_INIT(&search_path); + STAILQ_INIT(&seq_program); + TAILQ_INIT(&cs_tailq); + SLIST_INIT(&scope_stack); + + /* Set Sentinal scope node */ + sentinal = scope_alloc(); + sentinal->type = SCOPE_ROOT; + + includes_search_curdir = 1; + appname = *argv; + regfile = NULL; + listfile = NULL; +#if DEBUG + yy_flex_debug = 0; + yydebug = 0; +#endif + while ((ch = getopt(argc, argv, "d:l:n:o:r:I:O:")) != -1) { + switch(ch) { + case 'd': +#if DEBUG + if (strcmp(optarg, "s") == 0) { + yy_flex_debug = 1; + } else if (strcmp(optarg, "p") == 0) { + yydebug = 1; + } else { + fprintf(stderr, "%s: -d Requires either an " + "'s' or 'p' argument\n", appname); + usage(); + } +#else + stop("-d: Assembler not built with debugging " + "information", EX_SOFTWARE); +#endif + break; + case 'l': + /* Create a program listing */ + if ((listfile = fopen(optarg, "w")) == NULL) { + perror(optarg); + stop(NULL, EX_CANTCREAT); + } + listfilename = optarg; + break; + case 'n': + /* Don't complain about the -nostdinc directrive */ + if (strcmp(optarg, "ostdinc")) { + fprintf(stderr, "%s: Unknown option -%c%s\n", + appname, ch, optarg); + usage(); + /* NOTREACHED */ + } + break; + case 'o': + if ((ofile = fopen(optarg, "w")) == NULL) { + perror(optarg); + stop(NULL, EX_CANTCREAT); + } + ofilename = optarg; + break; + case 'r': + if ((regfile = fopen(optarg, "w")) == NULL) { + perror(optarg); + stop(NULL, EX_CANTCREAT); + } + regfilename = optarg; + break; + case 'I': + { + path_entry_t include_dir; + + if (strcmp(optarg, "-") == 0) { + if (includes_search_curdir == 0) { + fprintf(stderr, "%s: Warning - '-I-' " + "specified multiple " + "times\n", appname); + } + includes_search_curdir = 0; + for (include_dir = search_path.slh_first; + include_dir != NULL; + include_dir = include_dir->links.sle_next) + /* + * All entries before a '-I-' only + * apply to includes specified with + * quotes instead of "<>". + */ + include_dir->quoted_includes_only = 1; + } else { + include_dir = + (path_entry_t)malloc(sizeof(*include_dir)); + if (include_dir == NULL) { + perror(optarg); + stop(NULL, EX_OSERR); + } + include_dir->directory = strdup(optarg); + if (include_dir->directory == NULL) { + perror(optarg); + stop(NULL, EX_OSERR); + } + include_dir->quoted_includes_only = 0; + SLIST_INSERT_HEAD(&search_path, include_dir, + links); + } + break; + } + case '?': + default: + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + if (argc != 1) { + fprintf(stderr, "%s: No input file specifiled\n", appname); + usage(); + /* NOTREACHED */ + } + + symtable_open(); + inputfilename = *argv; + include_file(*argv, SOURCE_FILE); + retval = yyparse(); + if (retval == 0) { + if (SLIST_FIRST(&scope_stack) == NULL + || SLIST_FIRST(&scope_stack)->type != SCOPE_ROOT) { + stop("Unterminated conditional expression", + EX_DATAERR); + /* NOTREACHED */ + } + + /* Process outmost scope */ + process_scope(SLIST_FIRST(&scope_stack)); + /* + * Decend the tree of scopes and insert/emit + * patches as appropriate. We perform a depth first + * tranversal, recursively handling each scope. + */ + /* start at the root scope */ + dump_scope(SLIST_FIRST(&scope_stack)); + + /* Patch up forward jump addresses */ + back_patch(); + + if (ofile != NULL) + output_code(); + if (regfile != NULL) { + symtable_dump(regfile); + } + if (listfile != NULL) + output_listing(inputfilename); + } + + stop(NULL, 0); + /* NOTREACHED */ + return (0); +} + +static void +usage() +{ + + (void)fprintf(stderr, +"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file] + [-r register_output_file] [-l program_list_file] + input_file\n", + appname); + exit(EX_USAGE); +} + +static void +back_patch() +{ + struct instruction *cur_instr; + + for(cur_instr = seq_program.stqh_first; + cur_instr != NULL; + cur_instr = cur_instr->links.stqe_next) { + if (cur_instr->patch_label != NULL) { + struct ins_format3 *f3_instr; + u_int address; + + if (cur_instr->patch_label->type != LABEL) { + char buf[255]; + + snprintf(buf, sizeof(buf), + "Undefined label %s", + cur_instr->patch_label->name); + stop(buf, EX_DATAERR); + /* NOTREACHED */ + } + f3_instr = &cur_instr->format.format3; + address = f3_instr->address; + address += cur_instr->patch_label->info.linfo->address; + f3_instr->address = address; + } + } +} + +static void +output_code() +{ + struct instruction *cur_instr; + patch_t *cur_patch; + critical_section_t *cs; + symbol_node_t *cur_node; + int instrcount; + + instrcount = 0; + fprintf(ofile, +"/* + * DO NOT EDIT - This file is automatically generated. + */\n"); + + fprintf(ofile, "static uint8_t seqprog[] = {\n"); + for(cur_instr = seq_program.stqh_first; + cur_instr != NULL; + cur_instr = cur_instr->links.stqe_next) { + + fprintf(ofile, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n", +#if BYTE_ORDER == LITTLE_ENDIAN + cur_instr->format.bytes[0], + cur_instr->format.bytes[1], + cur_instr->format.bytes[2], + cur_instr->format.bytes[3]); +#else + cur_instr->format.bytes[3], + cur_instr->format.bytes[2], + cur_instr->format.bytes[1], + cur_instr->format.bytes[0]); +#endif + instrcount++; + } + fprintf(ofile, "};\n\n"); + + /* + * Output patch information. Patch functions first. + */ + for(cur_node = SLIST_FIRST(&patch_functions); + cur_node != NULL; + cur_node = SLIST_NEXT(cur_node,links)) { + fprintf(ofile, +"static int ahc_patch%d_func(struct ahc_softc *ahc); + +static int +ahc_patch%d_func(struct ahc_softc *ahc) +{ + return (%s); +}\n\n", + cur_node->symbol->info.condinfo->func_num, + cur_node->symbol->info.condinfo->func_num, + cur_node->symbol->name); + } + + fprintf(ofile, +"typedef int patch_func_t (struct ahc_softc *); +struct patch { + patch_func_t *patch_func; + uint32_t begin :10, + skip_instr :10, + skip_patch :12; +} patches[] = {\n"); + + for(cur_patch = STAILQ_FIRST(&patches); + cur_patch != NULL; + cur_patch = STAILQ_NEXT(cur_patch,links)) { + fprintf(ofile, "\t{ ahc_patch%d_func, %d, %d, %d },\n", + cur_patch->patch_func, cur_patch->begin, + cur_patch->skip_instr, cur_patch->skip_patch); + } + + fprintf(ofile, "\n};\n"); + + fprintf(ofile, +"struct cs { + u_int16_t begin; + u_int16_t end; +} critical_sections[] = {\n"); + + for(cs = TAILQ_FIRST(&cs_tailq); + cs != NULL; + cs = TAILQ_NEXT(cs, links)) { + fprintf(ofile, "\t{ %d, %d },\n", + cs->begin_addr, cs->end_addr); + } + + fprintf(ofile, "\n};\n"); + + fprintf(stderr, "%s: %d instructions used\n", appname, instrcount); +} + +static void +dump_scope(scope_t *scope) +{ + scope_t *cur_scope; + + /* + * Emit the first patch for this scope + */ + emit_patch(scope, 0); + + /* + * Dump each scope within this one. + */ + cur_scope = TAILQ_FIRST(&scope->inner_scope); + + while (cur_scope != NULL) { + + dump_scope(cur_scope); + + cur_scope = TAILQ_NEXT(cur_scope, scope_links); + } + + /* + * Emit the second, closing, patch for this scope + */ + emit_patch(scope, 1); +} + +void +emit_patch(scope_t *scope, int patch) +{ + patch_info_t *pinfo; + patch_t *new_patch; + + pinfo = &scope->patches[patch]; + + if (pinfo->skip_instr == 0) + /* No-Op patch */ + return; + + new_patch = (patch_t *)malloc(sizeof(*new_patch)); + + if (new_patch == NULL) + stop("Could not malloc patch structure", EX_OSERR); + + memset(new_patch, 0, sizeof(*new_patch)); + + if (patch == 0) { + new_patch->patch_func = scope->func_num; + new_patch->begin = scope->begin_addr; + } else { + new_patch->patch_func = 0; + new_patch->begin = scope->end_addr; + } + new_patch->skip_instr = pinfo->skip_instr; + new_patch->skip_patch = pinfo->skip_patch; + STAILQ_INSERT_TAIL(&patches, new_patch, links); +} + +void +output_listing(char *ifilename) +{ + char buf[1024]; + FILE *ifile; + struct instruction *cur_instr; + patch_t *cur_patch; + symbol_node_t *cur_func; + int *func_values; + int instrcount; + int instrptr; + int line; + int func_count; + int skip_addr; + + instrcount = 0; + instrptr = 0; + line = 1; + skip_addr = 0; + if ((ifile = fopen(ifilename, "r")) == NULL) { + perror(ifilename); + stop(NULL, EX_DATAERR); + } + + /* + * Determine which options to apply to this listing. + */ + for (func_count = 0, cur_func = SLIST_FIRST(&patch_functions); + cur_func != NULL; + cur_func = SLIST_NEXT(cur_func, links)) + func_count++; + + func_values = NULL; + if (func_count != 0) { + func_values = (int *)malloc(func_count * sizeof(int)); + + if (func_values == NULL) + stop("Could not malloc", EX_OSERR); + + func_values[0] = 0; /* FALSE func */ + func_count--; + + /* + * Ask the user to fill in the return values for + * the rest of the functions. + */ + + + for (cur_func = SLIST_FIRST(&patch_functions); + cur_func != NULL && SLIST_NEXT(cur_func, links) != NULL; + cur_func = SLIST_NEXT(cur_func, links), func_count--) { + int input; + + fprintf(stdout, "\n(%s)\n", cur_func->symbol->name); + fprintf(stdout, + "Enter the return value for " + "this expression[T/F]:"); + + while (1) { + + input = getchar(); + input = toupper(input); + + if (input == 'T') { + func_values[func_count] = 1; + break; + } else if (input == 'F') { + func_values[func_count] = 0; + break; + } + } + if (isatty(fileno(stdin)) == 0) + putchar(input); + } + fprintf(stdout, "\nThanks!\n"); + } + + /* Now output the listing */ + cur_patch = STAILQ_FIRST(&patches); + for(cur_instr = STAILQ_FIRST(&seq_program); + cur_instr != NULL; + cur_instr = STAILQ_NEXT(cur_instr, links), instrcount++) { + + if (check_patch(&cur_patch, instrcount, + &skip_addr, func_values) == 0) { + /* Don't count this instruction as it is in a patch + * that was removed. + */ + continue; + } + + while (line < cur_instr->srcline) { + fgets(buf, sizeof(buf), ifile); + fprintf(listfile, "\t\t%s", buf); + line++; + } + fprintf(listfile, "%03x %02x%02x%02x%02x", instrptr, +#if BYTE_ORDER == LITTLE_ENDIAN + cur_instr->format.bytes[0], + cur_instr->format.bytes[1], + cur_instr->format.bytes[2], + cur_instr->format.bytes[3]); +#else + cur_instr->format.bytes[3], + cur_instr->format.bytes[2], + cur_instr->format.bytes[1], + cur_instr->format.bytes[0]); +#endif + fgets(buf, sizeof(buf), ifile); + fprintf(listfile, "\t%s", buf); + line++; + instrptr++; + } + /* Dump the remainder of the file */ + while(fgets(buf, sizeof(buf), ifile) != NULL) + fprintf(listfile, "\t\t%s", buf); + + fclose(ifile); +} + +static int +check_patch(patch_t **start_patch, int start_instr, + int *skip_addr, int *func_vals) +{ + patch_t *cur_patch; + + cur_patch = *start_patch; + + while (cur_patch != NULL && start_instr == cur_patch->begin) { + if (func_vals[cur_patch->patch_func] == 0) { + int skip; + + /* Start rejecting code */ + *skip_addr = start_instr + cur_patch->skip_instr; + for (skip = cur_patch->skip_patch; + skip > 0 && cur_patch != NULL; + skip--) + cur_patch = STAILQ_NEXT(cur_patch, links); + } else { + /* Accepted this patch. Advance to the next + * one and wait for our intruction pointer to + * hit this point. + */ + cur_patch = STAILQ_NEXT(cur_patch, links); + } + } + + *start_patch = cur_patch; + if (start_instr < *skip_addr) + /* Still skipping */ + return (0); + + return (1); +} + +/* + * Print out error information if appropriate, and clean up before + * terminating the program. + */ +void +stop(const char *string, int err_code) +{ + if (string != NULL) { + fprintf(stderr, "%s: ", appname); + if (yyfilename != NULL) { + fprintf(stderr, "Stopped at file %s, line %d - ", + yyfilename, yylineno); + } + fprintf(stderr, "%s\n", string); + } + + if (ofile != NULL) { + fclose(ofile); + if (err_code != 0) { + fprintf(stderr, "%s: Removing %s due to error\n", + appname, ofilename); + unlink(ofilename); + } + } + + if (regfile != NULL) { + fclose(regfile); + if (err_code != 0) { + fprintf(stderr, "%s: Removing %s due to error\n", + appname, regfilename); + unlink(regfilename); + } + } + + if (listfile != NULL) { + fclose(listfile); + if (err_code != 0) { + fprintf(stderr, "%s: Removing %s due to error\n", + appname, listfilename); + unlink(listfilename); + } + } + + symlist_free(&patch_functions); + symtable_close(); + + exit(err_code); +} + +struct instruction * +seq_alloc() +{ + struct instruction *new_instr; + + new_instr = (struct instruction *)malloc(sizeof(struct instruction)); + if (new_instr == NULL) + stop("Unable to malloc instruction object", EX_SOFTWARE); + memset(new_instr, 0, sizeof(*new_instr)); + STAILQ_INSERT_TAIL(&seq_program, new_instr, links); + new_instr->srcline = yylineno; + return new_instr; +} + +critical_section_t * +cs_alloc() +{ + critical_section_t *new_cs; + + new_cs= (critical_section_t *)malloc(sizeof(critical_section_t)); + if (new_cs == NULL) + stop("Unable to malloc critical_section object", EX_SOFTWARE); + memset(new_cs, 0, sizeof(*new_cs)); + + TAILQ_INSERT_TAIL(&cs_tailq, new_cs, links); + return new_cs; +} + +scope_t * +scope_alloc() +{ + scope_t *new_scope; + + new_scope = (scope_t *)malloc(sizeof(scope_t)); + if (new_scope == NULL) + stop("Unable to malloc scope object", EX_SOFTWARE); + memset(new_scope, 0, sizeof(*new_scope)); + TAILQ_INIT(&new_scope->inner_scope); + + if (SLIST_FIRST(&scope_stack) != NULL) { + TAILQ_INSERT_TAIL(&SLIST_FIRST(&scope_stack)->inner_scope, + new_scope, scope_links); + } + /* This patch is now the current scope */ + SLIST_INSERT_HEAD(&scope_stack, new_scope, scope_stack_links); + return new_scope; +} + +void +process_scope(scope_t *scope) +{ + /* + * We are "leaving" this scope. We should now have + * enough information to process the lists of scopes + * we encapsulate. + */ + scope_t *cur_scope; + u_int skip_patch_count; + u_int skip_instr_count; + + cur_scope = TAILQ_LAST(&scope->inner_scope, scope_tailq); + skip_patch_count = 0; + skip_instr_count = 0; + while (cur_scope != NULL) { + u_int patch0_patch_skip; + + patch0_patch_skip = 0; + switch (cur_scope->type) { + case SCOPE_IF: + case SCOPE_ELSE_IF: + if (skip_instr_count != 0) { + /* Create a tail patch */ + patch0_patch_skip++; + cur_scope->patches[1].skip_patch = + skip_patch_count + 1; + cur_scope->patches[1].skip_instr = + skip_instr_count; + } + + /* Count Head patch */ + patch0_patch_skip++; + + /* Count any patches contained in our inner scope */ + patch0_patch_skip += cur_scope->inner_scope_patches; + + cur_scope->patches[0].skip_patch = patch0_patch_skip; + cur_scope->patches[0].skip_instr = + cur_scope->end_addr - cur_scope->begin_addr; + + skip_instr_count += cur_scope->patches[0].skip_instr; + + skip_patch_count += patch0_patch_skip; + if (cur_scope->type == SCOPE_IF) { + scope->inner_scope_patches += skip_patch_count; + skip_patch_count = 0; + skip_instr_count = 0; + } + break; + case SCOPE_ELSE: + /* Count any patches contained in our innter scope */ + skip_patch_count += cur_scope->inner_scope_patches; + + skip_instr_count += cur_scope->end_addr + - cur_scope->begin_addr; + break; + case SCOPE_ROOT: + stop("Unexpected scope type encountered", EX_SOFTWARE); + /* NOTREACHED */ + } + + cur_scope = TAILQ_PREV(cur_scope, scope_tailq, scope_links); + } +} diff --git a/sys/dev/aic7xxx/aicasm/aicasm.h b/sys/dev/aic7xxx/aicasm/aicasm.h new file mode 100644 index 000000000000..a3b30fe0e5b8 --- /dev/null +++ b/sys/dev/aic7xxx/aicasm/aicasm.h @@ -0,0 +1,78 @@ +/* + * Assembler for the sequencer program downloaded to Aic7xxx SCSI host adapters + * + * Copyright (c) 1997 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, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aicasm/aicasm.h#3 $ + * + * $FreeBSD$ + */ + +#ifdef __linux__ +#include "../queue.h" +#else +#include <sys/queue.h> +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +typedef struct path_entry { + char *directory; + int quoted_includes_only; + SLIST_ENTRY(path_entry) links; +} *path_entry_t; + +typedef enum { + QUOTED_INCLUDE, + BRACKETED_INCLUDE, + SOURCE_FILE +} include_type; + +SLIST_HEAD(path_list, path_entry); + +extern struct path_list search_path; +extern struct cs_tailq cs_tailq; +extern struct scope_list scope_stack; +extern struct symlist patch_functions; +extern int includes_search_curdir; /* False if we've seen -I- */ +extern char *appname; +extern int yylineno; +extern char *yyfilename; + +void stop(const char *errstring, int err_code); +void include_file(char *file_name, include_type type); +struct instruction *seq_alloc(void); +struct critical_section *cs_alloc(void); +struct scope *scope_alloc(void); +void process_scope(struct scope *); diff --git a/sys/dev/aic7xxx/aicasm/aicasm_gram.y b/sys/dev/aic7xxx/aicasm/aicasm_gram.y new file mode 100644 index 000000000000..8ef77111d91a --- /dev/null +++ b/sys/dev/aic7xxx/aicasm/aicasm_gram.y @@ -0,0 +1,1455 @@ +%{ +/* + * Parser for the Aic7xxx SCSI Host adapter sequencer assembler. + * + * Copyright (c) 1997, 1998, 2000 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, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aicasm/aicasm_gram.y#4 $ + * + * $FreeBSD$ + */ + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#include <sys/types.h> +#ifdef __linux__ +#include "../queue.h" +#else +#include <sys/queue.h> +#endif + +#include "aicasm.h" +#include "aicasm_symbol.h" +#include "aicasm_insformat.h" + +int yylineno; +char *yyfilename; +static symbol_t *cur_symbol; +static symtype cur_symtype; +static symbol_t *accumulator; +static symbol_ref_t allones; +static symbol_ref_t allzeros; +static symbol_ref_t none; +static symbol_ref_t sindex; +static int instruction_ptr; +static int sram_or_scb_offset; +static int download_constant_count; +static int in_critical_section; + +static void process_bitmask(int mask_type, symbol_t *sym, int mask); +static void initialize_symbol(symbol_t *symbol); +static void process_register(symbol_t **p_symbol); +static void format_1_instr(int opcode, symbol_ref_t *dest, + expression_t *immed, symbol_ref_t *src, int ret); +static void format_2_instr(int opcode, symbol_ref_t *dest, + expression_t *places, symbol_ref_t *src, int ret); +static void format_3_instr(int opcode, symbol_ref_t *src, + expression_t *immed, symbol_ref_t *address); +static void test_readable_symbol(symbol_t *symbol); +static void test_writable_symbol(symbol_t *symbol); +static void type_check(symbol_t *symbol, expression_t *expression, int and_op); +static void make_expression(expression_t *immed, int value); +static void add_conditional(symbol_t *symbol); +static int is_download_const(expression_t *immed); + +#define YYDEBUG 1 +#define SRAM_SYMNAME "SRAM_BASE" +#define SCB_SYMNAME "SCB_BASE" +%} + +%union { + int value; + char *str; + symbol_t *sym; + symbol_ref_t sym_ref; + expression_t expression; +} + +%token T_REGISTER + +%token <value> T_CONST + +%token T_DOWNLOAD + +%token T_SCB + +%token T_SRAM + +%token T_ALIAS + +%token T_SIZE + +%token <value> T_ADDRESS + +%token T_ACCESS_MODE + +%token <value> T_MODE + +%token T_BEGIN_CS + +%token T_END_CS + +%token T_BIT + +%token T_MASK + +%token <value> T_NUMBER + +%token <str> T_PATH + +%token <sym> T_CEXPR + +%token T_EOF T_INCLUDE + +%token <value> T_SHR T_SHL T_ROR T_ROL + +%token <value> T_MVI T_MOV T_CLR T_BMOV + +%token <value> T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL + +%token <value> T_ADD T_ADC + +%token <value> T_INC T_DEC + +%token <value> T_STC T_CLC + +%token <value> T_CMP T_NOT T_XOR + +%token <value> T_TEST T_AND + +%token <value> T_OR + +%token T_RET + +%token T_NOP + +%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX + +%token T_A + +%token <sym> T_SYMBOL + +%token T_NL + +%token T_IF T_ELSE T_ELSE_IF T_ENDIF + +%type <sym_ref> reg_symbol address destination source opt_source + +%type <expression> expression immediate immediate_or_a + +%type <value> ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne + +%type <value> numerical_value + +%left '|' +%left '&' +%left '+' '-' +%right '~' +%nonassoc UMINUS +%% + +program: + include +| program include +| register +| program register +| constant +| program constant +| scratch_ram +| program scratch_ram +| scb +| program scb +| label +| program label +| critical_section_start +| program critical_section_start +| critical_section_end +| program critical_section_end +| conditional +| program conditional +| code +| program code +; + +include: + T_INCLUDE '<' T_PATH '>' + { include_file($3, BRACKETED_INCLUDE); } +| T_INCLUDE '"' T_PATH '"' + { include_file($3, QUOTED_INCLUDE); } +; + +register: + T_REGISTER { cur_symtype = REGISTER; } reg_definition +; + +reg_definition: + T_SYMBOL '{' + { + if ($1->type != UNINITIALIZED) { + stop("Register multiply defined", EX_DATAERR); + /* NOTREACHED */ + } + cur_symbol = $1; + cur_symbol->type = cur_symtype; + initialize_symbol(cur_symbol); + } + reg_attribute_list + '}' + { + /* + * Default to allowing everything in for registers + * with no bit or mask definitions. + */ + if (cur_symbol->info.rinfo->valid_bitmask == 0) + cur_symbol->info.rinfo->valid_bitmask = 0xFF; + + if (cur_symbol->info.rinfo->size == 0) + cur_symbol->info.rinfo->size = 1; + + /* + * This might be useful for registers too. + */ + if (cur_symbol->type != REGISTER) { + if (cur_symbol->info.rinfo->address == 0) + cur_symbol->info.rinfo->address = + sram_or_scb_offset; + sram_or_scb_offset += + cur_symbol->info.rinfo->size; + } + cur_symbol = NULL; + } +; + +reg_attribute_list: + reg_attribute +| reg_attribute_list reg_attribute +; + +reg_attribute: + reg_address +| size +| access_mode +| bit_defn +| mask_defn +| alias +| accumulator +| allones +| allzeros +| none +| sindex +; + +reg_address: + T_ADDRESS T_NUMBER + { + cur_symbol->info.rinfo->address = $2; + } +; + +size: + T_SIZE T_NUMBER + { + cur_symbol->info.rinfo->size = $2; + } +; + +access_mode: + T_ACCESS_MODE T_MODE + { + cur_symbol->info.rinfo->mode = $2; + } +; + +bit_defn: + T_BIT T_SYMBOL T_NUMBER + { + process_bitmask(BIT, $2, $3); + } +; + +mask_defn: + T_MASK T_SYMBOL expression + { + process_bitmask(MASK, $2, $3.value); + } +; + +alias: + T_ALIAS T_SYMBOL + { + if ($2->type != UNINITIALIZED) { + stop("Re-definition of register alias", + EX_DATAERR); + /* NOTREACHED */ + } + $2->type = ALIAS; + initialize_symbol($2); + $2->info.ainfo->parent = cur_symbol; + } +; + +accumulator: + T_ACCUM + { + if (accumulator != NULL) { + stop("Only one accumulator definition allowed", + EX_DATAERR); + /* NOTREACHED */ + } + accumulator = cur_symbol; + } +; + +allones: + T_ALLONES + { + if (allones.symbol != NULL) { + stop("Only one definition of allones allowed", + EX_DATAERR); + /* NOTREACHED */ + } + allones.symbol = cur_symbol; + } +; + +allzeros: + T_ALLZEROS + { + if (allzeros.symbol != NULL) { + stop("Only one definition of allzeros allowed", + EX_DATAERR); + /* NOTREACHED */ + } + allzeros.symbol = cur_symbol; + } +; + +none: + T_NONE + { + if (none.symbol != NULL) { + stop("Only one definition of none allowed", + EX_DATAERR); + /* NOTREACHED */ + } + none.symbol = cur_symbol; + } +; + +sindex: + T_SINDEX + { + if (sindex.symbol != NULL) { + stop("Only one definition of sindex allowed", + EX_DATAERR); + /* NOTREACHED */ + } + sindex.symbol = cur_symbol; + } +; + +expression: + expression '|' expression + { + $$.value = $1.value | $3.value; + symlist_merge(&$$.referenced_syms, + &$1.referenced_syms, + &$3.referenced_syms); + } +| expression '&' expression + { + $$.value = $1.value & $3.value; + symlist_merge(&$$.referenced_syms, + &$1.referenced_syms, + &$3.referenced_syms); + } +| expression '+' expression + { + $$.value = $1.value + $3.value; + symlist_merge(&$$.referenced_syms, + &$1.referenced_syms, + &$3.referenced_syms); + } +| expression '-' expression + { + $$.value = $1.value - $3.value; + symlist_merge(&($$.referenced_syms), + &($1.referenced_syms), + &($3.referenced_syms)); + } +| '(' expression ')' + { + $$ = $2; + } +| '~' expression + { + $$ = $2; + $$.value = (~$$.value) & 0xFF; + } +| '-' expression %prec UMINUS + { + $$ = $2; + $$.value = -$$.value; + } +| T_NUMBER + { + $$.value = $1; + SLIST_INIT(&$$.referenced_syms); + } +| T_SYMBOL + { + symbol_t *symbol; + + symbol = $1; + switch (symbol->type) { + case ALIAS: + symbol = $1->info.ainfo->parent; + case REGISTER: + case SCBLOC: + case SRAMLOC: + $$.value = symbol->info.rinfo->address; + break; + case MASK: + case BIT: + $$.value = symbol->info.minfo->mask; + break; + case DOWNLOAD_CONST: + case CONST: + $$.value = symbol->info.cinfo->value; + break; + case UNINITIALIZED: + default: + { + char buf[255]; + + snprintf(buf, sizeof(buf), + "Undefined symbol %s referenced", + symbol->name); + stop(buf, EX_DATAERR); + /* NOTREACHED */ + break; + } + } + SLIST_INIT(&$$.referenced_syms); + symlist_add(&$$.referenced_syms, symbol, SYMLIST_INSERT_HEAD); + } +; + +constant: + T_CONST T_SYMBOL numerical_value + { + if ($2->type != UNINITIALIZED) { + stop("Re-definition of symbol as a constant", + EX_DATAERR); + /* NOTREACHED */ + } + $2->type = CONST; + initialize_symbol($2); + $2->info.cinfo->value = $3; + $2->info.cinfo->define = $1; + } +| T_CONST T_SYMBOL T_DOWNLOAD + { + if ($1) { + stop("Invalid downloaded constant declaration", + EX_DATAERR); + /* NOTREACHED */ + } + if ($2->type != UNINITIALIZED) { + stop("Re-definition of symbol as a downloaded constant", + EX_DATAERR); + /* NOTREACHED */ + } + $2->type = DOWNLOAD_CONST; + initialize_symbol($2); + $2->info.cinfo->value = download_constant_count++; + $2->info.cinfo->define = FALSE; + } +; + +numerical_value: + T_NUMBER + { + $$ = $1; + } +| '-' T_NUMBER + { + $$ = -$2; + } +; + +scratch_ram: + T_SRAM '{' + { + cur_symbol = symtable_get(SRAM_SYMNAME); + cur_symtype = SRAMLOC; + if (cur_symbol->type != UNINITIALIZED) { + stop("Only one SRAM definition allowed", + EX_DATAERR); + /* NOTREACHED */ + } + cur_symbol->type = SRAMLOC; + initialize_symbol(cur_symbol); + } + reg_address + { + sram_or_scb_offset = cur_symbol->info.rinfo->address; + } + scb_or_sram_reg_list + '}' + { + cur_symbol = NULL; + } +; + +scb: + T_SCB '{' + { + cur_symbol = symtable_get(SCB_SYMNAME); + cur_symtype = SCBLOC; + if (cur_symbol->type != UNINITIALIZED) { + stop("Only one SRAM definition allowed", + EX_SOFTWARE); + /* NOTREACHED */ + } + cur_symbol->type = SCBLOC; + initialize_symbol(cur_symbol); + /* 64 bytes of SCB space */ + cur_symbol->info.rinfo->size = 64; + } + reg_address + { + sram_or_scb_offset = cur_symbol->info.rinfo->address; + } + scb_or_sram_reg_list + '}' + { + cur_symbol = NULL; + } +; + +scb_or_sram_reg_list: + reg_definition +| scb_or_sram_reg_list reg_definition +; + +reg_symbol: + T_SYMBOL + { + process_register(&$1); + $$.symbol = $1; + $$.offset = 0; + } +| T_SYMBOL '[' T_SYMBOL ']' + { + process_register(&$1); + if ($3->type != CONST) { + stop("register offset must be a constant", EX_DATAERR); + /* NOTREACHED */ + } + if (($3->info.cinfo->value + 1) > $1->info.rinfo->size) { + stop("Accessing offset beyond range of register", + EX_DATAERR); + /* NOTREACHED */ + } + $$.symbol = $1; + $$.offset = $3->info.cinfo->value; + } +| T_SYMBOL '[' T_NUMBER ']' + { + process_register(&$1); + if (($3 + 1) > $1->info.rinfo->size) { + stop("Accessing offset beyond range of register", + EX_DATAERR); + /* NOTREACHED */ + } + $$.symbol = $1; + $$.offset = $3; + } +| T_A + { + if (accumulator == NULL) { + stop("No accumulator has been defined", EX_DATAERR); + /* NOTREACHED */ + } + $$.symbol = accumulator; + $$.offset = 0; + } +; + +destination: + reg_symbol + { + test_writable_symbol($1.symbol); + $$ = $1; + } +; + +immediate: + expression + { $$ = $1; } +; + +immediate_or_a: + expression + { + $$ = $1; + } +| T_A + { + SLIST_INIT(&$$.referenced_syms); + $$.value = 0; + } +; + +source: + reg_symbol + { + test_readable_symbol($1.symbol); + $$ = $1; + } +; + +opt_source: + { + $$.symbol = NULL; + $$.offset = 0; + } +| ',' source + { $$ = $2; } +; + +ret: + { $$ = 0; } +| T_RET + { $$ = 1; } +; + +critical_section_start: + T_BEGIN_CS + { + critical_section_t *cs; + + if (in_critical_section != FALSE) { + stop("Critical Section within Critical Section", + EX_DATAERR); + /* NOTREACHED */ + } + cs = cs_alloc(); + cs->begin_addr = instruction_ptr; + in_critical_section = TRUE; + } + +critical_section_end: + T_END_CS + { + critical_section_t *cs; + + if (in_critical_section == FALSE) { + stop("Unballanced 'end_cs'", EX_DATAERR); + /* NOTREACHED */ + } + cs = TAILQ_LAST(&cs_tailq, cs_tailq); + cs->end_addr = instruction_ptr; + in_critical_section = FALSE; + } + +label: + T_SYMBOL ':' + { + if ($1->type != UNINITIALIZED) { + stop("Program label multiply defined", EX_DATAERR); + /* NOTREACHED */ + } + $1->type = LABEL; + initialize_symbol($1); + $1->info.linfo->address = instruction_ptr; + } +; + +address: + T_SYMBOL + { + $$.symbol = $1; + $$.offset = 0; + } +| T_SYMBOL '+' T_NUMBER + { + $$.symbol = $1; + $$.offset = $3; + } +| T_SYMBOL '-' T_NUMBER + { + $$.symbol = $1; + $$.offset = -$3; + } +| '.' + { + $$.symbol = NULL; + $$.offset = 0; + } +| '.' '+' T_NUMBER + { + $$.symbol = NULL; + $$.offset = $3; + } +| '.' '-' T_NUMBER + { + $$.symbol = NULL; + $$.offset = -$3; + } +; + +conditional: + T_IF T_CEXPR '{' + { + scope_t *new_scope; + + add_conditional($2); + new_scope = scope_alloc(); + new_scope->type = SCOPE_IF; + new_scope->begin_addr = instruction_ptr; + new_scope->func_num = $2->info.condinfo->func_num; + } +| T_ELSE T_IF T_CEXPR '{' + { + scope_t *new_scope; + scope_t *scope_context; + scope_t *last_scope; + + /* + * Ensure that the previous scope is either an + * if or and else if. + */ + scope_context = SLIST_FIRST(&scope_stack); + last_scope = TAILQ_LAST(&scope_context->inner_scope, + scope_tailq); + if (last_scope == NULL + || last_scope->type == T_ELSE) { + + stop("'else if' without leading 'if'", EX_DATAERR); + /* NOTREACHED */ + } + add_conditional($3); + new_scope = scope_alloc(); + new_scope->type = SCOPE_ELSE_IF; + new_scope->begin_addr = instruction_ptr; + new_scope->func_num = $3->info.condinfo->func_num; + } +| T_ELSE '{' + { + scope_t *new_scope; + scope_t *scope_context; + scope_t *last_scope; + + /* + * Ensure that the previous scope is either an + * if or and else if. + */ + scope_context = SLIST_FIRST(&scope_stack); + last_scope = TAILQ_LAST(&scope_context->inner_scope, + scope_tailq); + if (last_scope == NULL + || last_scope->type == SCOPE_ELSE) { + + stop("'else' without leading 'if'", EX_DATAERR); + /* NOTREACHED */ + } + new_scope = scope_alloc(); + new_scope->type = SCOPE_ELSE; + new_scope->begin_addr = instruction_ptr; + } +; + +conditional: + '}' + { + scope_t *scope_context; + + scope_context = SLIST_FIRST(&scope_stack); + if (scope_context->type == SCOPE_ROOT) { + stop("Unexpected '}' encountered", EX_DATAERR); + /* NOTREACHED */ + } + + scope_context->end_addr = instruction_ptr; + + /* Pop the scope */ + SLIST_REMOVE_HEAD(&scope_stack, scope_stack_links); + + process_scope(scope_context); + + if (SLIST_FIRST(&scope_stack) == NULL) { + stop("Unexpected '}' encountered", EX_DATAERR); + /* NOTREACHED */ + } + } +; + +f1_opcode: + T_AND { $$ = AIC_OP_AND; } +| T_XOR { $$ = AIC_OP_XOR; } +| T_ADD { $$ = AIC_OP_ADD; } +| T_ADC { $$ = AIC_OP_ADC; } +; + +code: + f1_opcode destination ',' immediate_or_a opt_source ret ';' + { + format_1_instr($1, &$2, &$4, &$5, $6); + } +; + +code: + T_OR reg_symbol ',' immediate_or_a opt_source ret ';' + { + format_1_instr(AIC_OP_OR, &$2, &$4, &$5, $6); + } +; + +code: + T_INC destination opt_source ret ';' + { + expression_t immed; + + make_expression(&immed, 1); + format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4); + } +; + +code: + T_DEC destination opt_source ret ';' + { + expression_t immed; + + make_expression(&immed, -1); + format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4); + } +; + +code: + T_CLC ret ';' + { + expression_t immed; + + make_expression(&immed, -1); + format_1_instr(AIC_OP_ADD, &none, &immed, &allzeros, $2); + } +| T_CLC T_MVI destination ',' immediate_or_a ret ';' + { + format_1_instr(AIC_OP_ADD, &$3, &$5, &allzeros, $6); + } +; + +code: + T_STC ret ';' + { + expression_t immed; + + make_expression(&immed, 1); + format_1_instr(AIC_OP_ADD, &none, &immed, &allones, $2); + } +| T_STC destination ret ';' + { + expression_t immed; + + make_expression(&immed, 1); + format_1_instr(AIC_OP_ADD, &$2, &immed, &allones, $3); + } +; + +code: + T_BMOV destination ',' source ',' immediate_or_a ret ';' + { + format_1_instr(AIC_OP_BMOV, &$2, &$6, &$4, $7); + } +; + +code: + T_MOV destination ',' source ret ';' + { + expression_t immed; + + make_expression(&immed, 0xff); + format_1_instr(AIC_OP_AND, &$2, &immed, &$4, $5); + } +; + +code: + T_MVI destination ',' immediate_or_a ret ';' + { + format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5); + } +; + +code: + T_NOT destination opt_source ret ';' + { + expression_t immed; + + make_expression(&immed, 0xff); + format_1_instr(AIC_OP_XOR, &$2, &immed, &$3, $4); + } +; + +code: + T_CLR destination ret ';' + { + expression_t immed; + + make_expression(&immed, 0xff); + format_1_instr(AIC_OP_AND, &$2, &immed, &allzeros, $3); + } +; + +code: + T_NOP ret ';' + { + expression_t immed; + + make_expression(&immed, 0xff); + format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, $2); + } +; + +code: + T_RET ';' + { + expression_t immed; + + make_expression(&immed, 0xff); + format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, TRUE); + } +; + + /* + * This grammer differs from the one in the aic7xxx + * reference manual since the grammer listed there is + * ambiguous and causes a shift/reduce conflict. + * It also seems more logical as the "immediate" + * argument is listed as the second arg like the + * other formats. + */ + +f2_opcode: + T_SHL { $$ = AIC_OP_SHL; } +| T_SHR { $$ = AIC_OP_SHR; } +| T_ROL { $$ = AIC_OP_ROL; } +| T_ROR { $$ = AIC_OP_ROR; } +; + +code: + f2_opcode destination ',' expression opt_source ret ';' + { + format_2_instr($1, &$2, &$4, &$5, $6); + } +; + +jmp_jc_jnc_call: + T_JMP { $$ = AIC_OP_JMP; } +| T_JC { $$ = AIC_OP_JC; } +| T_JNC { $$ = AIC_OP_JNC; } +| T_CALL { $$ = AIC_OP_CALL; } +; + +jz_jnz: + T_JZ { $$ = AIC_OP_JZ; } +| T_JNZ { $$ = AIC_OP_JNZ; } +; + +je_jne: + T_JE { $$ = AIC_OP_JE; } +| T_JNE { $$ = AIC_OP_JNE; } +; + +code: + jmp_jc_jnc_call address ';' + { + expression_t immed; + + make_expression(&immed, 0); + format_3_instr($1, &sindex, &immed, &$2); + } +; + +code: + T_OR reg_symbol ',' immediate jmp_jc_jnc_call address ';' + { + format_3_instr($5, &$2, &$4, &$6); + } +; + +code: + T_TEST source ',' immediate_or_a jz_jnz address ';' + { + format_3_instr($5, &$2, &$4, &$6); + } +; + +code: + T_CMP source ',' immediate_or_a je_jne address ';' + { + format_3_instr($5, &$2, &$4, &$6); + } +; + +code: + T_MOV source jmp_jc_jnc_call address ';' + { + expression_t immed; + + make_expression(&immed, 0); + format_3_instr($3, &$2, &immed, &$4); + } +; + +code: + T_MVI immediate jmp_jc_jnc_call address ';' + { + format_3_instr($3, &allzeros, &$2, &$4); + } +; + +%% + +static void +process_bitmask(int mask_type, symbol_t *sym, int mask) +{ + /* + * Add the current register to its + * symbol list, if it already exists, + * warn if we are setting it to a + * different value, or in the bit to + * the "allowed bits" of this register. + */ + if (sym->type == UNINITIALIZED) { + sym->type = mask_type; + initialize_symbol(sym); + if (mask_type == BIT) { + if (mask == 0) { + stop("Bitmask with no bits set", EX_DATAERR); + /* NOTREACHED */ + } + if ((mask & ~(0x01 << (ffs(mask) - 1))) != 0) { + stop("Bitmask with more than one bit set", + EX_DATAERR); + /* NOTREACHED */ + } + } + sym->info.minfo->mask = mask; + } else if (sym->type != mask_type) { + stop("Bit definition mirrors a definition of the same " + " name, but a different type", EX_DATAERR); + /* NOTREACHED */ + } else if (mask != sym->info.minfo->mask) { + stop("Bitmask redefined with a conflicting value", EX_DATAERR); + /* NOTREACHED */ + } + /* Fail if this symbol is already listed */ + if (symlist_search(&(sym->info.minfo->symrefs), + cur_symbol->name) != NULL) { + stop("Bitmask defined multiple times for register", EX_DATAERR); + /* NOTREACHED */ + } + symlist_add(&(sym->info.minfo->symrefs), cur_symbol, + SYMLIST_INSERT_HEAD); + cur_symbol->info.rinfo->valid_bitmask |= mask; + cur_symbol->info.rinfo->typecheck_masks = TRUE; +} + +static void +initialize_symbol(symbol_t *symbol) +{ + switch (symbol->type) { + case UNINITIALIZED: + stop("Call to initialize_symbol with type field unset", + EX_SOFTWARE); + /* NOTREACHED */ + break; + case REGISTER: + case SRAMLOC: + case SCBLOC: + symbol->info.rinfo = + (struct reg_info *)malloc(sizeof(struct reg_info)); + if (symbol->info.rinfo == NULL) { + stop("Can't create register info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.rinfo, 0, + sizeof(struct reg_info)); + break; + case ALIAS: + symbol->info.ainfo = + (struct alias_info *)malloc(sizeof(struct alias_info)); + if (symbol->info.ainfo == NULL) { + stop("Can't create alias info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.ainfo, 0, + sizeof(struct alias_info)); + break; + case MASK: + case BIT: + symbol->info.minfo = + (struct mask_info *)malloc(sizeof(struct mask_info)); + if (symbol->info.minfo == NULL) { + stop("Can't create bitmask info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.minfo, 0, sizeof(struct mask_info)); + SLIST_INIT(&(symbol->info.minfo->symrefs)); + break; + case CONST: + case DOWNLOAD_CONST: + symbol->info.cinfo = + (struct const_info *)malloc(sizeof(struct const_info)); + if (symbol->info.cinfo == NULL) { + stop("Can't create alias info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.cinfo, 0, + sizeof(struct const_info)); + break; + case LABEL: + symbol->info.linfo = + (struct label_info *)malloc(sizeof(struct label_info)); + if (symbol->info.linfo == NULL) { + stop("Can't create label info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.linfo, 0, + sizeof(struct label_info)); + break; + case CONDITIONAL: + symbol->info.condinfo = + (struct cond_info *)malloc(sizeof(struct cond_info)); + if (symbol->info.condinfo == NULL) { + stop("Can't create conditional info", EX_SOFTWARE); + /* NOTREACHED */ + } + memset(symbol->info.condinfo, 0, + sizeof(struct cond_info)); + break; + default: + stop("Call to initialize_symbol with invalid symbol type", + EX_SOFTWARE); + /* NOTREACHED */ + break; + } +} + +static void +process_register(symbol_t **p_symbol) +{ + char buf[255]; + symbol_t *symbol = *p_symbol; + + if (symbol->type == UNINITIALIZED) { + snprintf(buf, sizeof(buf), "Undefined register %s", + symbol->name); + stop(buf, EX_DATAERR); + /* NOTREACHED */ + } else if (symbol->type == ALIAS) { + *p_symbol = symbol->info.ainfo->parent; + } else if ((symbol->type != REGISTER) + && (symbol->type != SCBLOC) + && (symbol->type != SRAMLOC)) { + snprintf(buf, sizeof(buf), + "Specified symbol %s is not a register", + symbol->name); + stop(buf, EX_DATAERR); + } +} + +static void +format_1_instr(int opcode, symbol_ref_t *dest, expression_t *immed, + symbol_ref_t *src, int ret) +{ + struct instruction *instr; + struct ins_format1 *f1_instr; + + if (src->symbol == NULL) + src = dest; + + /* Test register permissions */ + test_writable_symbol(dest->symbol); + test_readable_symbol(src->symbol); + + /* Ensure that immediate makes sense for this destination */ + type_check(dest->symbol, immed, opcode); + + /* Allocate sequencer space for the instruction and fill it out */ + instr = seq_alloc(); + f1_instr = &instr->format.format1; + f1_instr->ret = ret ? 1 : 0; + f1_instr->opcode = opcode; + f1_instr->destination = dest->symbol->info.rinfo->address + + dest->offset; + f1_instr->source = src->symbol->info.rinfo->address + + src->offset; + f1_instr->immediate = immed->value; + + if (is_download_const(immed)) + f1_instr->parity = 1; + + symlist_free(&immed->referenced_syms); + instruction_ptr++; +} + +static void +format_2_instr(int opcode, symbol_ref_t *dest, expression_t *places, + symbol_ref_t *src, int ret) +{ + struct instruction *instr; + struct ins_format2 *f2_instr; + uint8_t shift_control; + + if (src->symbol == NULL) + src = dest; + + /* Test register permissions */ + test_writable_symbol(dest->symbol); + test_readable_symbol(src->symbol); + + /* Allocate sequencer space for the instruction and fill it out */ + instr = seq_alloc(); + f2_instr = &instr->format.format2; + f2_instr->ret = ret ? 1 : 0; + f2_instr->opcode = AIC_OP_ROL; + f2_instr->destination = dest->symbol->info.rinfo->address + + dest->offset; + f2_instr->source = src->symbol->info.rinfo->address + + src->offset; + if (places->value > 8 || places->value <= 0) { + stop("illegal shift value", EX_DATAERR); + /* NOTREACHED */ + } + switch (opcode) { + case AIC_OP_SHL: + if (places->value == 8) + shift_control = 0xf0; + else + shift_control = (places->value << 4) | places->value; + break; + case AIC_OP_SHR: + if (places->value == 8) { + shift_control = 0xf8; + } else { + shift_control = (places->value << 4) + | (8 - places->value) + | 0x08; + } + break; + case AIC_OP_ROL: + shift_control = places->value & 0x7; + break; + case AIC_OP_ROR: + shift_control = (8 - places->value) | 0x08; + break; + default: + shift_control = 0; /* Quiet Compiler */ + stop("Invalid shift operation specified", EX_SOFTWARE); + /* NOTREACHED */ + break; + }; + f2_instr->shift_control = shift_control; + symlist_free(&places->referenced_syms); + instruction_ptr++; +} + +static void +format_3_instr(int opcode, symbol_ref_t *src, + expression_t *immed, symbol_ref_t *address) +{ + struct instruction *instr; + struct ins_format3 *f3_instr; + int addr; + + /* Test register permissions */ + test_readable_symbol(src->symbol); + + /* Ensure that immediate makes sense for this source */ + type_check(src->symbol, immed, opcode); + + /* Allocate sequencer space for the instruction and fill it out */ + instr = seq_alloc(); + f3_instr = &instr->format.format3; + if (address->symbol == NULL) { + /* 'dot' referrence. Use the current instruction pointer */ + addr = instruction_ptr + address->offset; + } else if (address->symbol->type == UNINITIALIZED) { + /* forward reference */ + addr = address->offset; + instr->patch_label = address->symbol; + } else + addr = address->symbol->info.linfo->address + address->offset; + f3_instr->opcode = opcode; + f3_instr->address = addr; + f3_instr->source = src->symbol->info.rinfo->address + + src->offset; + f3_instr->immediate = immed->value; + + if (is_download_const(immed)) + f3_instr->parity = 1; + + symlist_free(&immed->referenced_syms); + instruction_ptr++; +} + +static void +test_readable_symbol(symbol_t *symbol) +{ + if (symbol->info.rinfo->mode == WO) { + stop("Write Only register specified as source", + EX_DATAERR); + /* NOTREACHED */ + } +} + +static void +test_writable_symbol(symbol_t *symbol) +{ + if (symbol->info.rinfo->mode == RO) { + stop("Read Only register specified as destination", + EX_DATAERR); + /* NOTREACHED */ + } +} + +static void +type_check(symbol_t *symbol, expression_t *expression, int opcode) +{ + symbol_node_t *node; + int and_op; + char buf[255]; + + and_op = FALSE; + if (opcode == AIC_OP_AND || opcode == AIC_OP_JNZ || AIC_OP_JZ) + and_op = TRUE; + + /* + * Make sure that we aren't attempting to write something + * that hasn't been defined. If this is an and operation, + * this is a mask, so "undefined" bits are okay. + */ + if (and_op == FALSE + && (expression->value & ~symbol->info.rinfo->valid_bitmask) != 0) { + snprintf(buf, sizeof(buf), + "Invalid bit(s) 0x%x in immediate written to %s", + expression->value & ~symbol->info.rinfo->valid_bitmask, + symbol->name); + stop(buf, EX_DATAERR); + /* NOTREACHED */ + } + + /* + * Now make sure that all of the symbols referenced by the + * expression are defined for this register. + */ + if(symbol->info.rinfo->typecheck_masks != FALSE) { + for(node = expression->referenced_syms.slh_first; + node != NULL; + node = node->links.sle_next) { + if ((node->symbol->type == MASK + || node->symbol->type == BIT) + && symlist_search(&node->symbol->info.minfo->symrefs, + symbol->name) == NULL) { + snprintf(buf, sizeof(buf), + "Invalid bit or mask %s " + "for register %s", + node->symbol->name, symbol->name); + stop(buf, EX_DATAERR); + /* NOTREACHED */ + } + } + } +} + +static void +make_expression(expression_t *immed, int value) +{ + SLIST_INIT(&immed->referenced_syms); + immed->value = value & 0xff; +} + +static void +add_conditional(symbol_t *symbol) +{ + static int numfuncs; + + if (numfuncs == 0) { + /* add a special conditional, "0" */ + symbol_t *false_func; + + false_func = symtable_get("0"); + if (false_func->type != UNINITIALIZED) { + stop("Conditional expression '0' " + "conflicts with a symbol", EX_DATAERR); + /* NOTREACHED */ + } + false_func->type = CONDITIONAL; + initialize_symbol(false_func); + false_func->info.condinfo->func_num = numfuncs++; + symlist_add(&patch_functions, false_func, SYMLIST_INSERT_HEAD); + } + + /* This condition has occurred before */ + if (symbol->type == CONDITIONAL) + return; + + if (symbol->type != UNINITIALIZED) { + stop("Conditional expression conflicts with a symbol", + EX_DATAERR); + /* NOTREACHED */ + } + + symbol->type = CONDITIONAL; + initialize_symbol(symbol); + symbol->info.condinfo->func_num = numfuncs++; + symlist_add(&patch_functions, symbol, SYMLIST_INSERT_HEAD); +} + +void +yyerror(const char *string) +{ + stop(string, EX_DATAERR); +} + +static int +is_download_const(expression_t *immed) +{ + if ((immed->referenced_syms.slh_first != NULL) + && (immed->referenced_syms.slh_first->symbol->type == DOWNLOAD_CONST)) + return (TRUE); + + return (FALSE); +} diff --git a/sys/dev/aic7xxx/aicasm/aicasm_insformat.h b/sys/dev/aic7xxx/aicasm/aicasm_insformat.h new file mode 100644 index 000000000000..7c6026090a94 --- /dev/null +++ b/sys/dev/aic7xxx/aicasm/aicasm_insformat.h @@ -0,0 +1,129 @@ +/* + * Instruction formats for the sequencer program downloaded to + * Aic7xxx SCSI host adapters + * + * Copyright (c) 1997, 1998, 2000 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, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aicasm/aicasm_insformat.h#3 $ + * + * $FreeBSD$ + */ + +#if linux +#include <endian.h> +#else +#include <machine/endian.h> +#endif + +struct ins_format1 { +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t immediate : 8, + source : 9, + destination : 9, + ret : 1, + opcode : 4, + parity : 1; +#else + uint32_t parity : 1, + opcode : 4, + ret : 1, + destination : 9, + source : 9, + immediate : 8; +#endif +}; + +struct ins_format2 { +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t shift_control : 8, + source : 9, + destination : 9, + ret : 1, + opcode : 4, + parity : 1; +#else + uint32_t parity : 1, + opcode : 4, + ret : 1, + destination : 9, + source : 9, + shift_control : 8; +#endif +}; + +struct ins_format3 { +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t immediate : 8, + source : 9, + address : 10, + opcode : 4, + parity : 1; +#else + uint32_t parity : 1, + opcode : 4, + address : 10, + source : 9, + immediate : 8; +#endif +}; + +union ins_formats { + struct ins_format1 format1; + struct ins_format2 format2; + struct ins_format3 format3; + uint8_t bytes[4]; + uint32_t integer; +}; +struct instruction { + union ins_formats format; + u_int srcline; + struct symbol *patch_label; + STAILQ_ENTRY(instruction) links; +}; + +#define AIC_OP_OR 0x0 +#define AIC_OP_AND 0x1 +#define AIC_OP_XOR 0x2 +#define AIC_OP_ADD 0x3 +#define AIC_OP_ADC 0x4 +#define AIC_OP_ROL 0x5 +#define AIC_OP_BMOV 0x6 + +#define AIC_OP_JMP 0x8 +#define AIC_OP_JC 0x9 +#define AIC_OP_JNC 0xa +#define AIC_OP_CALL 0xb +#define AIC_OP_JNE 0xc +#define AIC_OP_JNZ 0xd +#define AIC_OP_JE 0xe +#define AIC_OP_JZ 0xf + +/* Pseudo Ops */ +#define AIC_OP_SHL 0x10 +#define AIC_OP_SHR 0x20 +#define AIC_OP_ROR 0x30 diff --git a/sys/dev/aic7xxx/aicasm/aicasm_scan.l b/sys/dev/aic7xxx/aicasm/aicasm_scan.l new file mode 100644 index 000000000000..5709d6ff68d6 --- /dev/null +++ b/sys/dev/aic7xxx/aicasm/aicasm_scan.l @@ -0,0 +1,302 @@ +%{ +/* + * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler. + * + * Copyright (c) 1997, 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, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aicasm/aicasm_scan.l#3 $ + * + * $FreeBSD$ + */ + +#include <sys/types.h> + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <sysexits.h> +#ifdef __linux__ +#include "../queue.h" +#else +#include <sys/queue.h> +#endif + +#include "aicasm.h" +#include "aicasm_symbol.h" +#include "y.tab.h" + +#define MAX_STR_CONST 256 +char string_buf[MAX_STR_CONST]; +char *string_buf_ptr; +int parren_count; +%} + +PATH [-/A-Za-z0-9_.]*[./][-/A-Za-z0-9_.]* +WORD [A-Za-z_][-A-Za-z_0-9]* +SPACE [ \t]+ + +%x COMMENT +%x CEXPR +%x INCLUDE + +%% +\n { ++yylineno; } +"/*" { BEGIN COMMENT; /* Enter comment eating state */ } +<COMMENT>"/*" { fprintf(stderr, "Warning! Comment within comment."); } +<COMMENT>\n { ++yylineno; } +<COMMENT>[^*/\n]* ; +<COMMENT>"*"+[^*/\n]* ; +<COMMENT>"/"+[^*/\n]* ; +<COMMENT>"*"+"/" { BEGIN INITIAL; } +if[ \t]*\( { + string_buf_ptr = string_buf; + parren_count = 1; + BEGIN CEXPR; + return T_IF; + } +<CEXPR>\( { *string_buf_ptr++ = '('; parren_count++; } +<CEXPR>\) { + parren_count--; + if (parren_count == 0) { + /* All done */ + BEGIN INITIAL; + *string_buf_ptr = '\0'; + yylval.sym = symtable_get(string_buf); + return T_CEXPR; + } else { + *string_buf_ptr++ = ')'; + } + } +<CEXPR>\n { ++yylineno; } +<CEXPR>[^()\n]+ { + char *yptr = yytext; + + while (*yptr != '\0') { + /* Remove duplicate spaces */ + if (*yptr == '\t') + *yptr = ' '; + if (*yptr == ' ' + && string_buf_ptr != string_buf + && string_buf_ptr[-1] == ' ') + yptr++; + else + *string_buf_ptr++ = *yptr++; + } + } + +{SPACE} ; + + /* Register/SCB/SRAM definition keywords */ +register { return T_REGISTER; } +const { yylval.value = FALSE; return T_CONST; } +download { return T_DOWNLOAD; } +address { return T_ADDRESS; } +access_mode { return T_ACCESS_MODE; } +RW|RO|WO { + if (strcmp(yytext, "RW") == 0) + yylval.value = RW; + else if (strcmp(yytext, "RO") == 0) + yylval.value = RO; + else + yylval.value = WO; + return T_MODE; + } +BEGIN_CRITICAL { return T_BEGIN_CS; } +END_CRITICAL { return T_END_CS; } +bit { return T_BIT; } +mask { return T_MASK; } +alias { return T_ALIAS; } +size { return T_SIZE; } +scb { return T_SCB; } +scratch_ram { return T_SRAM; } +accumulator { return T_ACCUM; } +allones { return T_ALLONES; } +allzeros { return T_ALLZEROS; } +none { return T_NONE; } +sindex { return T_SINDEX; } +A { return T_A; } + + /* Opcodes */ +shl { return T_SHL; } +shr { return T_SHR; } +ror { return T_ROR; } +rol { return T_ROL; } +mvi { return T_MVI; } +mov { return T_MOV; } +clr { return T_CLR; } +jmp { return T_JMP; } +jc { return T_JC; } +jnc { return T_JNC; } +je { return T_JE; } +jne { return T_JNE; } +jz { return T_JZ; } +jnz { return T_JNZ; } +call { return T_CALL; } +add { return T_ADD; } +adc { return T_ADC; } +bmov { return T_BMOV; } +inc { return T_INC; } +dec { return T_DEC; } +stc { return T_STC; } +clc { return T_CLC; } +cmp { return T_CMP; } +not { return T_NOT; } +xor { return T_XOR; } +test { return T_TEST;} +and { return T_AND; } +or { return T_OR; } +ret { return T_RET; } +nop { return T_NOP; } +else { return T_ELSE; } + + /* Allowed Symbols */ +[-+,:()~|&."{};<>[\]!] { return yytext[0]; } + + /* Number processing */ +0[0-7]* { + yylval.value = strtol(yytext, NULL, 8); + return T_NUMBER; + } + +0[xX][0-9a-fA-F]+ { + yylval.value = strtoul(yytext + 2, NULL, 16); + return T_NUMBER; + } + +[1-9][0-9]* { + yylval.value = strtol(yytext, NULL, 10); + return T_NUMBER; + } + + /* Include Files */ +#include { return T_INCLUDE; BEGIN INCLUDE;} +<INCLUDE>[<>\"] { return yytext[0]; } +<INCLUDE>{PATH} { yylval.str = strdup(yytext); return T_PATH; } +<INCLUDE>; { BEGIN INITIAL; return yytext[0]; } +<INCLUDE>. { stop("Invalid include line", EX_DATAERR); } + + /* For parsing C include files with #define foo */ +#define { yylval.value = TRUE; return T_CONST; } + /* Throw away macros */ +#define[^\n]*[()]+[^\n]* ; +{PATH} { yylval.str = strdup(yytext); return T_PATH; } + +{WORD} { yylval.sym = symtable_get(yytext); return T_SYMBOL; } + +. { + char buf[255]; + + snprintf(buf, sizeof(buf), "Invalid character " + "'%c'", yytext[0]); + stop(buf, EX_DATAERR); + } +%% + +typedef struct include { + YY_BUFFER_STATE buffer; + int lineno; + char *filename; + SLIST_ENTRY(include) links; +}include_t; + +SLIST_HEAD(, include) include_stack; + +void +include_file(char *file_name, include_type type) +{ + FILE *newfile; + include_t *include; + + newfile = NULL; + /* Try the current directory first */ + if (includes_search_curdir != 0 || type == SOURCE_FILE) + newfile = fopen(file_name, "r"); + + if (newfile == NULL && type != SOURCE_FILE) { + path_entry_t include_dir; + for (include_dir = search_path.slh_first; + include_dir != NULL; + include_dir = include_dir->links.sle_next) { + char fullname[PATH_MAX]; + + if ((include_dir->quoted_includes_only == TRUE) + && (type != QUOTED_INCLUDE)) + continue; + + snprintf(fullname, sizeof(fullname), + "%s/%s", include_dir->directory, file_name); + + if ((newfile = fopen(fullname, "r")) != NULL) + break; + } + } + + if (newfile == NULL) { + perror(file_name); + stop("Unable to open input file", EX_SOFTWARE); + /* NOTREACHED */ + } + + if (type != SOURCE_FILE) { + include = (include_t *)malloc(sizeof(include_t)); + if (include == NULL) { + stop("Unable to allocate include stack entry", + EX_SOFTWARE); + /* NOTREACHED */ + } + include->buffer = YY_CURRENT_BUFFER; + include->lineno = yylineno; + include->filename = yyfilename; + SLIST_INSERT_HEAD(&include_stack, include, links); + } + yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE)); + yylineno = 1; + yyfilename = strdup(file_name); +} + +int +yywrap() +{ + include_t *include; + + yy_delete_buffer(YY_CURRENT_BUFFER); + (void)fclose(yyin); + if (yyfilename != NULL) + free(yyfilename); + yyfilename = NULL; + include = include_stack.slh_first; + if (include != NULL) { + yy_switch_to_buffer(include->buffer); + yylineno = include->lineno; + yyfilename = include->filename; + SLIST_REMOVE_HEAD(&include_stack, links); + free(include); + return (0); + } + return (1); +} diff --git a/sys/dev/aic7xxx/aicasm/aicasm_symbol.c b/sys/dev/aic7xxx/aicasm/aicasm_symbol.c new file mode 100644 index 000000000000..1a238507ba7a --- /dev/null +++ b/sys/dev/aic7xxx/aicasm/aicasm_symbol.c @@ -0,0 +1,468 @@ +/* + * Aic7xxx SCSI host adapter firmware asssembler symbol table implementation + * + * Copyright (c) 1997 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, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aicasm/aicasm_symbol.c#3 $ + * + * $FreeBSD$ + */ + +#include <sys/types.h> + +#ifdef __linux__ +#include <db1/db.h> +#else +#include <db.h> +#endif +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#include "aicasm_symbol.h" +#include "aicasm.h" + +static DB *symtable; + +symbol_t * +symbol_create(char *name) +{ + symbol_t *new_symbol; + + new_symbol = (symbol_t *)malloc(sizeof(symbol_t)); + if (new_symbol == NULL) { + perror("Unable to create new symbol"); + exit(EX_SOFTWARE); + } + memset(new_symbol, 0, sizeof(*new_symbol)); + new_symbol->name = strdup(name); + new_symbol->type = UNINITIALIZED; + return (new_symbol); +} + +void +symbol_delete(symbol_t *symbol) +{ + if (symtable != NULL) { + DBT key; + + key.data = symbol->name; + key.size = strlen(symbol->name); + symtable->del(symtable, &key, /*flags*/0); + } + switch(symbol->type) { + case SCBLOC: + case SRAMLOC: + case REGISTER: + if (symbol->info.rinfo != NULL) + free(symbol->info.rinfo); + break; + case ALIAS: + if (symbol->info.ainfo != NULL) + free(symbol->info.ainfo); + break; + case MASK: + case BIT: + if (symbol->info.minfo != NULL) { + symlist_free(&symbol->info.minfo->symrefs); + free(symbol->info.minfo); + } + break; + case DOWNLOAD_CONST: + case CONST: + if (symbol->info.cinfo != NULL) + free(symbol->info.cinfo); + break; + case LABEL: + if (symbol->info.linfo != NULL) + free(symbol->info.linfo); + break; + case UNINITIALIZED: + default: + break; + } + free(symbol->name); + free(symbol); +} + +void +symtable_open() +{ + symtable = dbopen(/*filename*/NULL, + O_CREAT | O_NONBLOCK | O_RDWR, /*mode*/0, DB_HASH, + /*openinfo*/NULL); + + if (symtable == NULL) { + perror("Symbol table creation failed"); + exit(EX_SOFTWARE); + /* NOTREACHED */ + } +} + +void +symtable_close() +{ + if (symtable != NULL) { + DBT key; + DBT data; + + while (symtable->seq(symtable, &key, &data, R_FIRST) == 0) { + symbol_t *stored_ptr; + + memcpy(&stored_ptr, data.data, sizeof(stored_ptr)); + symbol_delete(stored_ptr); + } + symtable->close(symtable); + } +} + +/* + * The semantics of get is to return an uninitialized symbol entry + * if a lookup fails. + */ +symbol_t * +symtable_get(char *name) +{ + symbol_t *stored_ptr; + DBT key; + DBT data; + int retval; + + key.data = (void *)name; + key.size = strlen(name); + + if ((retval = symtable->get(symtable, &key, &data, /*flags*/0)) != 0) { + if (retval == -1) { + perror("Symbol table get operation failed"); + exit(EX_SOFTWARE); + /* NOTREACHED */ + } else if (retval == 1) { + /* Symbol wasn't found, so create a new one */ + symbol_t *new_symbol; + + new_symbol = symbol_create(name); + data.data = &new_symbol; + data.size = sizeof(new_symbol); + if (symtable->put(symtable, &key, &data, + /*flags*/0) !=0) { + perror("Symtable put failed"); + exit(EX_SOFTWARE); + } + return (new_symbol); + } else { + perror("Unexpected return value from db get routine"); + exit(EX_SOFTWARE); + /* NOTREACHED */ + } + } + memcpy(&stored_ptr, data.data, sizeof(stored_ptr)); + return (stored_ptr); +} + +symbol_node_t * +symlist_search(symlist_t *symlist, char *symname) +{ + symbol_node_t *curnode; + + curnode = symlist->slh_first; + while(curnode != NULL) { + if (strcmp(symname, curnode->symbol->name) == 0) + break; + curnode = curnode->links.sle_next; + } + return (curnode); +} + +void +symlist_add(symlist_t *symlist, symbol_t *symbol, int how) +{ + symbol_node_t *newnode; + + newnode = (symbol_node_t *)malloc(sizeof(symbol_node_t)); + if (newnode == NULL) { + stop("symlist_add: Unable to malloc symbol_node", EX_SOFTWARE); + /* NOTREACHED */ + } + newnode->symbol = symbol; + if (how == SYMLIST_SORT) { + symbol_node_t *curnode; + int mask; + + mask = FALSE; + switch(symbol->type) { + case REGISTER: + case SCBLOC: + case SRAMLOC: + break; + case BIT: + case MASK: + mask = TRUE; + break; + default: + stop("symlist_add: Invalid symbol type for sorting", + EX_SOFTWARE); + /* NOTREACHED */ + } + + curnode = symlist->slh_first; + if (curnode == NULL + || (mask && (curnode->symbol->info.minfo->mask > + newnode->symbol->info.minfo->mask)) + || (!mask && (curnode->symbol->info.rinfo->address > + newnode->symbol->info.rinfo->address))) { + SLIST_INSERT_HEAD(symlist, newnode, links); + return; + } + + while (1) { + if (curnode->links.sle_next == NULL) { + SLIST_INSERT_AFTER(curnode, newnode, + links); + break; + } else { + symbol_t *cursymbol; + + cursymbol = curnode->links.sle_next->symbol; + if ((mask && (cursymbol->info.minfo->mask > + symbol->info.minfo->mask)) + || (!mask &&(cursymbol->info.rinfo->address > + symbol->info.rinfo->address))){ + SLIST_INSERT_AFTER(curnode, newnode, + links); + break; + } + } + curnode = curnode->links.sle_next; + } + } else { + SLIST_INSERT_HEAD(symlist, newnode, links); + } +} + +void +symlist_free(symlist_t *symlist) +{ + symbol_node_t *node1, *node2; + + node1 = symlist->slh_first; + while (node1 != NULL) { + node2 = node1->links.sle_next; + free(node1); + node1 = node2; + } + SLIST_INIT(symlist); +} + +void +symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1, + symlist_t *symlist_src2) +{ + symbol_node_t *node; + + *symlist_dest = *symlist_src1; + while((node = symlist_src2->slh_first) != NULL) { + SLIST_REMOVE_HEAD(symlist_src2, links); + SLIST_INSERT_HEAD(symlist_dest, node, links); + } + + /* These are now empty */ + SLIST_INIT(symlist_src1); + SLIST_INIT(symlist_src2); +} + +void +symtable_dump(FILE *ofile) +{ + /* + * Sort the registers by address with a simple insertion sort. + * Put bitmasks next to the first register that defines them. + * Put constants at the end. + */ + symlist_t registers; + symlist_t masks; + symlist_t constants; + symlist_t download_constants; + symlist_t aliases; + + SLIST_INIT(®isters); + SLIST_INIT(&masks); + SLIST_INIT(&constants); + SLIST_INIT(&download_constants); + SLIST_INIT(&aliases); + + if (symtable != NULL) { + DBT key; + DBT data; + int flag = R_FIRST; + + while (symtable->seq(symtable, &key, &data, flag) == 0) { + symbol_t *cursym; + + memcpy(&cursym, data.data, sizeof(cursym)); + switch(cursym->type) { + case REGISTER: + case SCBLOC: + case SRAMLOC: + symlist_add(®isters, cursym, SYMLIST_SORT); + break; + case MASK: + case BIT: + symlist_add(&masks, cursym, SYMLIST_SORT); + break; + case CONST: + if (cursym->info.cinfo->define == FALSE) { + symlist_add(&constants, cursym, + SYMLIST_INSERT_HEAD); + } + break; + case DOWNLOAD_CONST: + symlist_add(&download_constants, cursym, + SYMLIST_INSERT_HEAD); + break; + case ALIAS: + symlist_add(&aliases, cursym, + SYMLIST_INSERT_HEAD); + break; + default: + break; + } + flag = R_NEXT; + } + + /* Put in the masks and bits */ + while (masks.slh_first != NULL) { + symbol_node_t *curnode; + symbol_node_t *regnode; + char *regname; + + curnode = masks.slh_first; + SLIST_REMOVE_HEAD(&masks, links); + + regnode = + curnode->symbol->info.minfo->symrefs.slh_first; + regname = regnode->symbol->name; + regnode = symlist_search(®isters, regname); + SLIST_INSERT_AFTER(regnode, curnode, links); + } + + /* Add the aliases */ + while (aliases.slh_first != NULL) { + symbol_node_t *curnode; + symbol_node_t *regnode; + char *regname; + + curnode = aliases.slh_first; + SLIST_REMOVE_HEAD(&aliases, links); + + regname = curnode->symbol->info.ainfo->parent->name; + regnode = symlist_search(®isters, regname); + SLIST_INSERT_AFTER(regnode, curnode, links); + } + + /* Output what we have */ + fprintf(ofile, +"/* + * DO NOT EDIT - This file is automatically generated. + */\n"); + while (registers.slh_first != NULL) { + symbol_node_t *curnode; + u_int8_t value; + char *tab_str; + char *tab_str2; + + curnode = registers.slh_first; + SLIST_REMOVE_HEAD(®isters, links); + switch(curnode->symbol->type) { + case REGISTER: + case SCBLOC: + case SRAMLOC: + fprintf(ofile, "\n"); + value = curnode->symbol->info.rinfo->address; + tab_str = "\t"; + tab_str2 = "\t\t"; + break; + case ALIAS: + { + symbol_t *parent; + + parent = curnode->symbol->info.ainfo->parent; + value = parent->info.rinfo->address; + tab_str = "\t"; + tab_str2 = "\t\t"; + break; + } + case MASK: + case BIT: + value = curnode->symbol->info.minfo->mask; + tab_str = "\t\t"; + tab_str2 = "\t"; + break; + default: + value = 0; /* Quiet compiler */ + tab_str = NULL; + tab_str2 = NULL; + stop("symtable_dump: Invalid symbol type " + "encountered", EX_SOFTWARE); + break; + } + fprintf(ofile, "#define%s%-16s%s0x%02x\n", + tab_str, curnode->symbol->name, tab_str2, + value); + free(curnode); + } + fprintf(ofile, "\n\n"); + + while (constants.slh_first != NULL) { + symbol_node_t *curnode; + + curnode = constants.slh_first; + SLIST_REMOVE_HEAD(&constants, links); + fprintf(ofile, "#define\t%-8s\t0x%02x\n", + curnode->symbol->name, + curnode->symbol->info.cinfo->value); + free(curnode); + } + + + fprintf(ofile, "\n\n/* Downloaded Constant Definitions */\n"); + + while (download_constants.slh_first != NULL) { + symbol_node_t *curnode; + + curnode = download_constants.slh_first; + SLIST_REMOVE_HEAD(&download_constants, links); + fprintf(ofile, "#define\t%-8s\t0x%02x\n", + curnode->symbol->name, + curnode->symbol->info.cinfo->value); + free(curnode); + } + } +} + diff --git a/sys/dev/aic7xxx/aicasm/aicasm_symbol.h b/sys/dev/aic7xxx/aicasm/aicasm_symbol.h new file mode 100644 index 000000000000..ec070c898819 --- /dev/null +++ b/sys/dev/aic7xxx/aicasm/aicasm_symbol.h @@ -0,0 +1,177 @@ +/* + * Aic7xxx SCSI host adapter firmware asssembler symbol table definitions + * + * Copyright (c) 1997 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, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * 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: //depot/src/aic7xxx/aicasm/aicasm_symbol.h#3 $ + * + * $FreeBSD$ + */ + +#ifdef __linux__ +#include "../queue.h" +#else +#include <sys/queue.h> +#endif + +typedef enum { + UNINITIALIZED, + REGISTER, + ALIAS, + SCBLOC, + SRAMLOC, + MASK, + BIT, + CONST, + DOWNLOAD_CONST, + LABEL, + CONDITIONAL +}symtype; + +typedef enum { + RO = 0x01, + WO = 0x02, + RW = 0x03 +}amode_t; + +struct reg_info { + u_int8_t address; + int size; + amode_t mode; + u_int8_t valid_bitmask; + int typecheck_masks; +}; + +typedef SLIST_HEAD(symlist, symbol_node) symlist_t; + +struct mask_info { + symlist_t symrefs; + u_int8_t mask; +}; + +struct const_info { + u_int8_t value; + int define; +}; + +struct alias_info { + struct symbol *parent; +}; + +struct label_info { + int address; +}; + +struct cond_info { + int func_num; +}; + +typedef struct expression_info { + symlist_t referenced_syms; + int value; +} expression_t; + +typedef struct symbol { + char *name; + symtype type; + union { + struct reg_info *rinfo; + struct mask_info *minfo; + struct const_info *cinfo; + struct alias_info *ainfo; + struct label_info *linfo; + struct cond_info *condinfo; + }info; +} symbol_t; + +typedef struct symbol_ref { + symbol_t *symbol; + int offset; +} symbol_ref_t; + +typedef struct symbol_node { + SLIST_ENTRY(symbol_node) links; + symbol_t *symbol; +} symbol_node_t; + +typedef struct critical_section { + TAILQ_ENTRY(critical_section) links; + int begin_addr; + int end_addr; +} critical_section_t; + +typedef enum { + SCOPE_ROOT, + SCOPE_IF, + SCOPE_ELSE_IF, + SCOPE_ELSE +} scope_type; + +typedef struct patch_info { + int skip_patch; + int skip_instr; +} patch_info_t; + +typedef struct scope { + SLIST_ENTRY(scope) scope_stack_links; + TAILQ_ENTRY(scope) scope_links; + TAILQ_HEAD(, scope) inner_scope; + scope_type type; + int inner_scope_patches; + int begin_addr; + int end_addr; + patch_info_t patches[2]; + int func_num; +} scope_t; + +TAILQ_HEAD(cs_tailq, critical_section); +SLIST_HEAD(scope_list, scope); +TAILQ_HEAD(scope_tailq, scope); + +void symbol_delete __P((symbol_t *symbol)); + +void symtable_open __P((void)); + +void symtable_close __P((void)); + +symbol_t * + symtable_get __P((char *name)); + +symbol_node_t * + symlist_search __P((symlist_t *symlist, char *symname)); + +void + symlist_add __P((symlist_t *symlist, symbol_t *symbol, int how)); +#define SYMLIST_INSERT_HEAD 0x00 +#define SYMLIST_SORT 0x01 + +void symlist_free __P((symlist_t *symlist)); + +void symlist_merge __P((symlist_t *symlist_dest, symlist_t *symlist_src1, + symlist_t *symlist_src2)); +void symtable_dump __P((FILE *ofile)); |
