diff options
author | Scott Long <scottl@FreeBSD.org> | 2005-12-14 03:26:49 +0000 |
---|---|---|
committer | Scott Long <scottl@FreeBSD.org> | 2005-12-14 03:26:49 +0000 |
commit | 5351742ea2dbd32e73ac42a60d98c272adef77d3 (patch) | |
tree | 831a51fc62191543452a8379ed6e4bd857df82d6 | |
parent | 91f6764e93835c23803d5d8ade18afc466823ff4 (diff) |
Notes
-rw-r--r-- | sys/dev/amr/amr.c | 942 | ||||
-rw-r--r-- | sys/dev/amr/amr_cam.c | 15 | ||||
-rw-r--r-- | sys/dev/amr/amr_disk.c | 4 | ||||
-rw-r--r-- | sys/dev/amr/amr_pci.c | 182 | ||||
-rw-r--r-- | sys/dev/amr/amrio.h | 2 | ||||
-rw-r--r-- | sys/dev/amr/amrreg.h | 56 | ||||
-rw-r--r-- | sys/dev/amr/amrvar.h | 35 |
7 files changed, 950 insertions, 286 deletions
diff --git a/sys/dev/amr/amr.c b/sys/dev/amr/amr.c index 8963e3803bf8..3531fc4fed84 100644 --- a/sys/dev/amr/amr.c +++ b/sys/dev/amr/amr.c @@ -66,6 +66,8 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/malloc.h> #include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/sysctl.h> #include <sys/bio.h> #include <sys/bus.h> @@ -73,6 +75,7 @@ __FBSDID("$FreeBSD$"); #include <sys/stat.h> #include <machine/bus.h> +#include <machine/cpu.h> #include <machine/resource.h> #include <sys/rman.h> @@ -92,6 +95,8 @@ __FBSDID("$FreeBSD$"); #define AMR_ENABLE_CAM 0 #endif +SYSCTL_NODE(_hw, OID_AUTO, amr, CTLFLAG_RD, 0, "AMR driver parameters"); + static d_open_t amr_open; static d_close_t amr_close; static d_ioctl_t amr_ioctl; @@ -115,7 +120,7 @@ static void amr_startup(void *arg); */ static int amr_query_controller(struct amr_softc *sc); static void *amr_enquiry(struct amr_softc *sc, size_t bufsize, - u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual); + u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual, int *status); static void amr_completeio(struct amr_command *ac); static int amr_support_ext_cdb(struct amr_softc *sc); @@ -130,13 +135,13 @@ static void amr_freecmd_cluster(struct amr_command_cluster *acc); */ static int amr_bio_command(struct amr_softc *sc, struct amr_command **acp); static int amr_wait_command(struct amr_command *ac) __unused; -static int amr_getslot(struct amr_command *ac); static int amr_mapcmd(struct amr_command *ac); static void amr_unmapcmd(struct amr_command *ac); static int amr_start(struct amr_command *ac); static int amr_start1(struct amr_softc *sc, struct amr_command *ac); static void amr_complete(void *context, int pending); static void amr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error); +static void amr_setup_dma64map(void *arg, bus_dma_segment_t *segs, int nsegments, int error); static void amr_setup_data_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error); /* @@ -172,6 +177,8 @@ static void amr_printcommand(struct amr_command *ac); #endif #endif +static void amr_init_sysctl(struct amr_softc *sc); + /******************************************************************************** ******************************************************************************** Inline Glue @@ -232,6 +239,11 @@ amr_attach(struct amr_softc *sc) debug(2, "controller query complete"); + /* + * Setup sysctls. + */ + amr_init_sysctl(sc); + #if AMR_ENABLE_CAM != 0 /* * Attach our 'real' SCSI channels to CAM. @@ -282,7 +294,9 @@ amr_startup(void *arg) debug_called(1); /* pull ourselves off the intrhook chain */ - config_intrhook_disestablish(&sc->amr_ich); + if (sc->amr_ich.ich_func) + config_intrhook_disestablish(&sc->amr_ich); + sc->amr_ich.ich_func = NULL; /* get up-to-date drive information */ if (amr_query_controller(sc)) { @@ -328,6 +342,17 @@ amr_startup(void *arg) return; } +static void +amr_init_sysctl(struct amr_softc *sc) +{ + + SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->amr_dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->amr_dev)), + OID_AUTO, "allow_volume_configure", CTLFLAG_RW, &sc->amr_allow_vol_config, 0, + ""); +} + + /******************************************************************************* * Free resources associated with a controller instance */ @@ -354,8 +379,11 @@ amr_free(struct amr_softc *sc) if( sc->amr_dev_t != (struct cdev *)NULL) destroy_dev(sc->amr_dev_t); - if (mtx_initialized(&sc->amr_io_lock)) - mtx_destroy(&sc->amr_io_lock); + if (mtx_initialized(&sc->amr_hw_lock)) + mtx_destroy(&sc->amr_hw_lock); + + if (mtx_initialized(&sc->amr_list_lock)) + mtx_destroy(&sc->amr_list_lock); } /******************************************************************************* @@ -367,10 +395,10 @@ amr_submit_bio(struct amr_softc *sc, struct bio *bio) { debug_called(2); - mtx_lock(&sc->amr_io_lock); + mtx_lock(&sc->amr_list_lock); amr_enqueue_bio(sc, bio); amr_startio(sc); - mtx_unlock(&sc->amr_io_lock); + mtx_unlock(&sc->amr_list_lock); return(0); } @@ -389,6 +417,47 @@ amr_open(struct cdev *dev, int flags, int fmt, d_thread_t *td) return(0); } +#ifdef LSI +static int +amr_del_ld(struct amr_softc *sc, int drv_no, int status) +{ + + debug_called(1); + + sc->amr_state &= ~AMR_STATE_QUEUE_FRZN; + sc->amr_state &= ~AMR_STATE_LD_DELETE; + sc->amr_state |= AMR_STATE_REMAP_LD; + debug(1, "State Set"); + + if (!status) { + debug(1, "disk begin destroyed %d",drv_no); + if (--amr_disks_registered == 0) + cdevsw_remove(&amrddisk_cdevsw); + debug(1, "disk begin destroyed success"); + } + return 0; +} + +static int +amr_prepare_ld_delete(struct amr_softc *sc) +{ + + debug_called(1); + if (sc->ld_del_supported == 0) + return(ENOIOCTL); + + sc->amr_state |= AMR_STATE_QUEUE_FRZN; + sc->amr_state |= AMR_STATE_LD_DELETE; + + /* 5 minutes for the all the commands to be flushed.*/ + tsleep((void *)&sc->ld_del_supported, PCATCH | PRIBIO,"delete_logical_drv",hz * 60 * 1); + if ( sc->amr_busyslots ) + return(ENOIOCTL); + + return 0; +} +#endif + /******************************************************************************** * Accept the last close on the control device. */ @@ -407,6 +476,233 @@ amr_close(struct cdev *dev, int flags, int fmt, d_thread_t *td) /******************************************************************************** * Handle controller-specific control operations. */ +static void +amr_rescan_drives(struct cdev *dev) +{ + struct amr_softc *sc = (struct amr_softc *)dev->si_drv1; + int i, error = 0; + + sc->amr_state |= AMR_STATE_REMAP_LD; + while (sc->amr_busyslots) { + device_printf(sc->amr_dev, "idle controller\n"); + amr_done(sc); + } + + /* mark ourselves as in-shutdown */ + sc->amr_state |= AMR_STATE_SHUTDOWN; + + /* flush controller */ + device_printf(sc->amr_dev, "flushing cache..."); + printf("%s\n", amr_flush(sc) ? "failed" : "done"); + + /* delete all our child devices */ + for(i = 0 ; i < AMR_MAXLD; i++) { + if(sc->amr_drive[i].al_disk != 0) { + if((error = device_delete_child(sc->amr_dev, + sc->amr_drive[i].al_disk)) != 0) + goto shutdown_out; + + sc->amr_drive[i].al_disk = 0; + } + } + +shutdown_out: + amr_startup(sc); +} + +int +amr_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, + d_thread_t *td) +{ + struct amr_softc *sc = (struct amr_softc *)dev->si_drv1; + struct amr_command *ac; + struct amr_mailbox *mb; + struct amr_linux_ioctl ali; + void *dp, *temp; + int error; + int adapter, len, ac_flags = 0; + int logical_drives_changed = 0; + u_int32_t linux_version = 0x02100000; + u_int8_t status; + struct amr_passthrough *ap; /* 60 bytes */ + + error = 0; + dp = NULL; + ac = NULL; + ap = NULL; + + copyin(addr, &ali, sizeof(ali)); + switch (ali.ui.fcs.opcode) { + case 0x82: + switch(ali.ui.fcs.subopcode) { + case 'e': + copyout(&linux_version, (void *)(uintptr_t)ali.data, + sizeof(linux_version)); + error = 0; + break; + + case 'm': + copyout(&sc->amr_linux_no_adapters, (void *)(uintptr_t)ali.data, + sizeof(sc->amr_linux_no_adapters)); + td->td_retval[0] = sc->amr_linux_no_adapters; + error = 0; + break; + + default: + printf("Unknown subopcode\n"); + error = ENOIOCTL; + break; + } + break; + + case 0x80: + case 0x81: + if (ali.ui.fcs.opcode == 0x80) + len = max(ali.outlen, ali.inlen); + else + len = ali.ui.fcs.length; + + adapter = (ali.ui.fcs.adapno) ^ 'm' << 8; + + ap = malloc(sizeof(struct amr_passthrough), + M_DEVBUF, M_WAITOK | M_ZERO); + + mb = (void *)&ali.mbox[0]; + + if ((ali.mbox[0] == FC_DEL_LOGDRV && ali.mbox[2] == OP_DEL_LOGDRV) || /* delete */ + (ali.mbox[0] == AMR_CMD_CONFIG && ali.mbox[2] == 0x0d)) { /* create */ + if (sc->amr_allow_vol_config == 0) { + error = EPERM; + break; + } + logical_drives_changed = 1; + } + + if (ali.mbox[0] == AMR_CMD_PASS) { + error = copyin((void *)(uintptr_t)mb->mb_physaddr, ap, + sizeof(struct amr_passthrough)); + if (error) + break; + + if (ap->ap_data_transfer_length) + dp = malloc(ap->ap_data_transfer_length, M_DEVBUF, + M_WAITOK | M_ZERO); + + if (ali.inlen) { + error = copyin((void *)(uintptr_t)ap->ap_data_transfer_address, + dp, ap->ap_data_transfer_length); + if (error) + break; + } + + mtx_lock(&sc->amr_list_lock); + while ((ac = amr_alloccmd(sc)) == NULL) + msleep(sc, &sc->amr_list_lock, PPAUSE, "amrioc", hz); + mtx_unlock(&sc->amr_list_lock); + + ac_flags = AMR_CMD_DATAIN|AMR_CMD_DATAOUT|AMR_CMD_CCB_DATAIN|AMR_CMD_CCB_DATAOUT; + bzero(&ac->ac_mailbox, sizeof(ac->ac_mailbox)); + ac->ac_mailbox.mb_command = AMR_CMD_PASS; + ac->ac_flags = ac_flags; + + ac->ac_data = ap; + ac->ac_length = sizeof(struct amr_passthrough); + ac->ac_ccb_data = dp; + ac->ac_ccb_length = ap->ap_data_transfer_length; + temp = (void *)(uintptr_t)ap->ap_data_transfer_address; + + error = amr_wait_command(ac); + if (error) + break; + + status = ac->ac_status; + error = copyout(&status, &((struct amr_passthrough *)(uintptr_t)mb->mb_physaddr)->ap_scsi_status, sizeof(status)); + if (error) + break; + + if (ali.outlen) { + error = copyout(dp, temp, ap->ap_data_transfer_length); + if (error) + break; + } + error = copyout(ap->ap_request_sense_area, ((struct amr_passthrough *)(uintptr_t)mb->mb_physaddr)->ap_request_sense_area, ap->ap_request_sense_length); + if (error) + break; + + error = 0; + break; + } else if (ali.mbox[0] == AMR_CMD_PASS_64) { + printf("No AMR_CMD_PASS_64\n"); + error = ENOIOCTL; + break; + } else if (ali.mbox[0] == AMR_CMD_EXTPASS) { + printf("No AMR_CMD_EXTPASS\n"); + error = ENOIOCTL; + break; + } else { + if (len) + dp = malloc(len, M_DEVBUF, M_WAITOK | M_ZERO); + + if (ali.inlen) { + error = copyin((void *)(uintptr_t)mb->mb_physaddr, dp, len); + if (error) + break; + } + + mtx_lock(&sc->amr_list_lock); + while ((ac = amr_alloccmd(sc)) == NULL) + msleep(sc, &sc->amr_list_lock, PPAUSE, "amrioc", hz); + mtx_unlock(&sc->amr_list_lock); + + ac_flags = AMR_CMD_DATAIN|AMR_CMD_DATAOUT; + bzero(&ac->ac_mailbox, sizeof(ac->ac_mailbox)); + bcopy(&ali.mbox[0], &ac->ac_mailbox, sizeof(ali.mbox)); + + ac->ac_length = len; + ac->ac_data = dp; + ac->ac_flags = ac_flags; + + error = amr_wait_command(ac); + if (error) + break; + + status = ac->ac_status; + error = copyout(&status, &((struct amr_mailbox *)&((struct amr_linux_ioctl *)addr)->mbox[0])->mb_status, sizeof(status)); + if (ali.outlen) { + error = copyout(dp, (void *)(uintptr_t)mb->mb_physaddr, len); + if (error) + break; + } + + error = 0; + if (logical_drives_changed) + amr_rescan_drives(dev); + break; + } + break; + + default: + debug(1, "unknown linux ioctl 0x%lx", cmd); + printf("unknown linux ioctl 0x%lx\n", cmd); + error = ENOIOCTL; + break; + } + + /* + * At this point, we know that there is a lock held and that these + * objects have been allocated. + */ + mtx_lock(&sc->amr_list_lock); + if (ac != NULL) + amr_releasecmd(ac); + mtx_unlock(&sc->amr_list_lock); + if (dp != NULL) + free(dp, M_DEVBUF); + if (ap != NULL) + free(ap, M_DEVBUF); + return(error); +} + static int amr_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, d_thread_t *td) { @@ -425,13 +721,19 @@ amr_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, d_thread_t * unsigned long au_length; unsigned char *au_cmd; int *au_statusp, au_direction; - int error; + int error, ac_flags = 0; struct amr_passthrough *ap; /* 60 bytes */ + int logical_drives_changed = 0; debug_called(1); arg._p = (void *)addr; + error = 0; + dp = NULL; + ac = NULL; + ap = NULL; + switch(cmd) { case AMR_IO_VERSION: @@ -464,24 +766,35 @@ amr_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, d_thread_t * au_statusp = &arg.au->au_status; break; + case 0xc0046d00: + case 0xc06e6d00: /* Linux emulation */ + return amr_linux_ioctl_int(dev, cmd, addr, flag, td); + break; + default: debug(1, "unknown ioctl 0x%lx", cmd); return(ENOIOCTL); } - error = 0; - dp = NULL; - ac = NULL; - ap = NULL; - - /* Logical Drive not supported by the driver */ - if (au_cmd[0] == 0xa4 && au_cmd[1] == 0x1c) - return (ENOIOCTL); + if ((au_cmd[0] == FC_DEL_LOGDRV && au_cmd[1] == OP_DEL_LOGDRV) || /* delete */ + (au_cmd[0] == AMR_CMD_CONFIG && au_cmd[1] == 0x0d)) { /* create */ + if (sc->amr_allow_vol_config == 0) { + error = EPERM; + goto out; + } + logical_drives_changed = 1; +#ifdef LSI + if ((error = amr_prepare_ld_delete(sc)) != 0) + return (error); +#endif + } /* handle inbound data buffer */ if (au_length != 0 && au_cmd[0] != 0x06) { - dp = malloc(au_length, M_DEVBUF, M_WAITOK|M_ZERO); - + if ((dp = malloc(au_length, M_DEVBUF, M_WAITOK|M_ZERO)) == NULL) { + error = ENOMEM; + goto out; + } if ((error = copyin(au_buffer, dp, au_length)) != 0) { free(dp, M_DEVBUF); return (error); @@ -493,11 +806,10 @@ amr_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, d_thread_t * if (au_cmd[0] == AMR_CMD_PASS) ap = malloc(sizeof(struct amr_passthrough), M_DEVBUF, M_WAITOK|M_ZERO); - mtx_lock(&sc->amr_io_lock); - if ((ac = amr_alloccmd(sc)) == NULL) { - error = ENOMEM; - goto out; - } + mtx_lock(&sc->amr_list_lock); + while ((ac = amr_alloccmd(sc)) == NULL) + msleep(sc, &sc->amr_list_lock, PPAUSE, "amrioc", hz); + mtx_unlock(&sc->amr_list_lock); /* handle SCSI passthrough command */ if (au_cmd[0] == AMR_CMD_PASS) { @@ -522,15 +834,11 @@ amr_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, d_thread_t * /* build command */ ac->ac_data = ap; ac->ac_length = sizeof(struct amr_passthrough); - ac->ac_flags |= AMR_CMD_DATAOUT; ac->ac_ccb_data = dp; ac->ac_ccb_length = au_length; - if (au_direction & AMR_IO_READ) - ac->ac_flags |= AMR_CMD_CCB_DATAIN; - if (au_direction & AMR_IO_WRITE) - ac->ac_flags |= AMR_CMD_CCB_DATAOUT; ac->ac_mailbox.mb_command = AMR_CMD_PASS; + ac_flags = AMR_CMD_DATAIN|AMR_CMD_DATAOUT|AMR_CMD_CCB_DATAIN|AMR_CMD_CCB_DATAOUT; } else { /* direct command to controller */ @@ -546,21 +854,18 @@ amr_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, d_thread_t * /* build the command */ ac->ac_data = dp; ac->ac_length = au_length; - if (au_direction & AMR_IO_READ) - ac->ac_flags |= AMR_CMD_DATAIN; - if (au_direction & AMR_IO_WRITE) - ac->ac_flags |= AMR_CMD_DATAOUT; + ac_flags = AMR_CMD_DATAIN|AMR_CMD_DATAOUT; } + ac->ac_flags = ac_flags; + /* run the command */ if ((error = amr_wait_command(ac)) != 0) goto out; /* copy out data and set status */ if (au_length != 0) { - mtx_unlock(&sc->amr_io_lock); error = copyout(dp, au_buffer, au_length); - mtx_lock(&sc->amr_io_lock); } debug(2, "copyout %ld bytes from %p -> %p", au_length, dp, au_buffer); if (dp != NULL) @@ -572,13 +877,20 @@ out: * At this point, we know that there is a lock held and that these * objects have been allocated. */ + mtx_lock(&sc->amr_list_lock); if (ac != NULL) amr_releasecmd(ac); - mtx_unlock(&sc->amr_io_lock); + mtx_unlock(&sc->amr_list_lock); if (dp != NULL) free(dp, M_DEVBUF); if (ap != NULL) free(ap, M_DEVBUF); + +#ifndef LSI + if (logical_drives_changed) + amr_rescan_drives(dev); +#endif + return(error); } @@ -623,8 +935,7 @@ amr_query_controller(struct amr_softc *sc) struct amr_prodinfo *ap; struct amr_enquiry *ae; int ldrv; - - mtx_lock(&sc->amr_io_lock); + int status; /* * If we haven't found the real limit yet, let us have a couple of commands in @@ -646,7 +957,7 @@ amr_query_controller(struct amr_softc *sc) * Try to issue an ENQUIRY3 command */ if ((aex = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3, - AMR_CONFIG_ENQ3_SOLICITED_FULL)) != NULL) { + AMR_CONFIG_ENQ3_SOLICITED_FULL, &status)) != NULL) { /* * Fetch current state of logical drives. @@ -663,9 +974,8 @@ amr_query_controller(struct amr_softc *sc) /* * Get product info for channel count. */ - if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) == NULL) { + if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0, &status)) == NULL) { device_printf(sc->amr_dev, "can't obtain product data from controller\n"); - mtx_unlock(&sc->amr_io_lock); return(1); } sc->amr_maxdrives = 40; @@ -674,13 +984,18 @@ amr_query_controller(struct amr_softc *sc) sc->amr_type |= AMR_TYPE_40LD; free(ap, M_DEVBUF); + ap = amr_enquiry(sc, 0, FC_DEL_LOGDRV, OP_SUP_DEL_LOGDRV, 0, &status); + free(ap, M_DEVBUF); + if (!status) { + sc->amr_ld_del_supported = 1; + device_printf(sc->amr_dev, "delete logical drives supported by controller\n"); + } } else { /* failed, try the 8LD ENQUIRY commands */ - if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) == NULL) { - if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) { + if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0, &status)) == NULL) { + if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0, &status)) == NULL) { device_printf(sc->amr_dev, "can't obtain configuration data from controller\n"); - mtx_unlock(&sc->amr_io_lock); return(1); } ae->ae_signature = 0; @@ -715,7 +1030,6 @@ amr_query_controller(struct amr_softc *sc) */ sc->amr_maxio = imin(sc->amr_maxio, AMR_LIMITCMD); - mtx_unlock(&sc->amr_io_lock); return(0); } @@ -723,7 +1037,7 @@ amr_query_controller(struct amr_softc *sc) * Run a generic enquiry-style command. */ static void * -amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual) +amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual, int *status) { struct amr_command *ac; void *result; @@ -736,7 +1050,10 @@ amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, result = NULL; /* get ourselves a command buffer */ - if ((ac = amr_alloccmd(sc)) == NULL) + mtx_lock(&sc->amr_list_lock); + ac = amr_alloccmd(sc); + mtx_unlock(&sc->amr_list_lock); + if (ac == NULL) goto out; /* allocate the response structure */ if ((result = malloc(bufsize, M_DEVBUF, M_ZERO|M_NOWAIT)) == NULL) @@ -754,15 +1071,19 @@ amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, mbox[0] = cmd; mbox[2] = cmdsub; mbox[3] = cmdqual; + *status = 0; /* can't assume that interrupts are going to work here, so play it safe */ if (sc->amr_poll_command(ac)) goto out; error = ac->ac_status; + *status = ac->ac_status; out: + mtx_lock(&sc->amr_list_lock); if (ac != NULL) amr_releasecmd(ac); + mtx_unlock(&sc->amr_list_lock); if ((error != 0) && (result != NULL)) { free(result, M_DEVBUF); result = NULL; @@ -781,8 +1102,10 @@ amr_flush(struct amr_softc *sc) /* get ourselves a command buffer */ error = 1; - mtx_lock(&sc->amr_io_lock); - if ((ac = amr_alloccmd(sc)) == NULL) + mtx_lock(&sc->amr_list_lock); + ac = amr_alloccmd(sc); + mtx_unlock(&sc->amr_list_lock); + if (ac == NULL) goto out; /* set command flags */ ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT; @@ -796,9 +1119,10 @@ amr_flush(struct amr_softc *sc) error = ac->ac_status; out: + mtx_lock(&sc->amr_list_lock); if (ac != NULL) amr_releasecmd(ac); - mtx_unlock(&sc->amr_io_lock); + mtx_unlock(&sc->amr_list_lock); return(error); } @@ -816,7 +1140,10 @@ amr_support_ext_cdb(struct amr_softc *sc) /* get ourselves a command buffer */ error = 0; - if ((ac = amr_alloccmd(sc)) == NULL) + mtx_lock(&sc->amr_list_lock); + ac = amr_alloccmd(sc); + mtx_unlock(&sc->amr_list_lock); + if (ac == NULL) goto out; /* set command flags */ ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT; @@ -835,8 +1162,10 @@ amr_support_ext_cdb(struct amr_softc *sc) } out: + mtx_lock(&sc->amr_list_lock); if (ac != NULL) amr_releasecmd(ac); + mtx_unlock(&sc->amr_list_lock); return(error); } @@ -891,17 +1220,22 @@ amr_startio(struct amr_softc *sc) static void amr_completeio(struct amr_command *ac) { - struct amrd_softc *sc = ac->ac_bio->bio_disk->d_drv1; + struct amrd_softc *sc = ac->ac_bio->bio_disk->d_drv1; + static struct timeval lastfail; + static int curfail; if (ac->ac_status != AMR_STATUS_SUCCESS) { /* could be more verbose here? */ ac->ac_bio->bio_error = EIO; ac->ac_bio->bio_flags |= BIO_ERROR; - device_printf(sc->amrd_dev, "I/O error - 0x%x\n", ac->ac_status); + if (ppsratecheck(&lastfail, &curfail, 1)) + device_printf(sc->amrd_dev, "I/O error - 0x%x\n", ac->ac_status); /* amr_printcommand(ac);*/ } amrd_intr(ac->ac_bio); + mtx_lock(&ac->ac_sc->amr_list_lock); amr_releasecmd(ac); + mtx_unlock(&ac->ac_sc->amr_list_lock); } /******************************************************************************** @@ -944,10 +1278,18 @@ amr_bio_command(struct amr_softc *sc, struct amr_command **acp) ac->ac_length = bio->bio_bcount; if (bio->bio_cmd == BIO_READ) { ac->ac_flags |= AMR_CMD_DATAIN; - cmd = AMR_CMD_LREAD; + if (AMR_IS_SG64(sc)) { + cmd = AMR_CMD_LREAD64; + ac->ac_flags |= AMR_CMD_SG64; + } else + cmd = AMR_CMD_LREAD; } else { ac->ac_flags |= AMR_CMD_DATAOUT; - cmd = AMR_CMD_LWRITE; + if (AMR_IS_SG64(sc)) { + cmd = AMR_CMD_LWRITE64; + ac->ac_flags |= AMR_CMD_SG64; + } else + cmd = AMR_CMD_LWRITE; } amrd = (struct amrd_softc *)bio->bio_disk->d_drv1; driveno = amrd->amrd_drive - sc->amr_drive; @@ -957,6 +1299,9 @@ amr_bio_command(struct amr_softc *sc, struct amr_command **acp) ac->ac_mailbox.mb_blkcount = blkcount; ac->ac_mailbox.mb_lba = bio->bio_pblkno; ac->ac_mailbox.mb_drive = driveno; + if (sc->amr_state & AMR_STATE_REMAP_LD) + ac->ac_mailbox.mb_drive |= 0x80; + /* we fill in the s/g related data when the command is mapped */ if ((bio->bio_pblkno + blkcount) > sc->amr_drive[driveno].al_size) @@ -981,11 +1326,12 @@ amr_wait_command(struct amr_command *ac) ac->ac_complete = NULL; ac->ac_flags |= AMR_CMD_SLEEP; - if ((error = amr_start(ac)) != 0) + if ((error = amr_start(ac)) != 0) { return(error); + } while ((ac->ac_flags & AMR_CMD_BUSY) && (error != EWOULDBLOCK)) { - error = msleep(ac, &ac->ac_sc->amr_io_lock, PRIBIO, "amrwcmd", 0); + error = tsleep(ac, PRIBIO, "amrwcmd", 0); } return(error); } @@ -1030,15 +1376,20 @@ amr_setup_polled_dmamap(void *arg, bus_dma_segment_t *segs, int nsegs, int err) { struct amr_command *ac = arg; struct amr_softc *sc = ac->ac_sc; + int flags; - amr_setup_dmamap(arg, segs, nsegs, err); - if (ac->ac_flags & AMR_CMD_DATAIN) { - bus_dmamap_sync(sc->amr_buffer_dmat,ac->ac_dmamap, - BUS_DMASYNC_PREREAD); - } - if (ac->ac_flags & AMR_CMD_DATAOUT) { - bus_dmamap_sync(sc->amr_buffer_dmat,ac->ac_dmamap, - BUS_DMASYNC_PREWRITE); + flags = 0; + if (ac->ac_flags & AMR_CMD_DATAIN) + flags |= BUS_DMASYNC_PREREAD; + if (ac->ac_flags & AMR_CMD_DATAOUT) + flags |= BUS_DMASYNC_PREWRITE; + + if (AC_IS_SG64(ac)) { + amr_setup_dma64map(arg, segs, nsegs, err); + bus_dmamap_sync(sc->amr_buffer64_dmat,ac->ac_dma64map, flags); + } else { + amr_setup_dmamap(arg, segs, nsegs, err); + bus_dmamap_sync(sc->amr_buffer_dmat,ac->ac_dmamap, flags); } sc->amr_poll_command1(sc, ac); } @@ -1050,6 +1401,8 @@ amr_setup_polled_dmamap(void *arg, bus_dma_segment_t *segs, int nsegs, int err) static int amr_quartz_poll_command(struct amr_command *ac) { + bus_dma_tag_t tag; + bus_dmamap_t datamap; struct amr_softc *sc = ac->ac_sc; int error; @@ -1057,10 +1410,18 @@ amr_quartz_poll_command(struct amr_command *ac) error = 0; + if (AC_IS_SG64(ac)) { + tag = sc->amr_buffer64_dmat; + datamap = ac->ac_dma64map; + } else { + tag = sc->amr_buffer_dmat; + datamap = ac->ac_dmamap; + } + /* now we have a slot, we can map the command (unmapped in amr_complete) */ if (ac->ac_data != 0) { - if (bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, - ac->ac_length, amr_setup_polled_dmamap, ac, BUS_DMA_NOWAIT) != 0) { + if (bus_dmamap_load(tag, datamap, ac->ac_data, ac->ac_length, + amr_setup_polled_dmamap, ac, BUS_DMA_NOWAIT) != 0) { error = 1; } } else { @@ -1075,10 +1436,11 @@ amr_quartz_poll_command1(struct amr_softc *sc, struct amr_command *ac) { int count, error; + mtx_lock(&sc->amr_hw_lock); if ((sc->amr_state & AMR_STATE_INTEN) == 0) { count=0; while (sc->amr_busyslots) { - msleep(sc, &sc->amr_io_lock, PRIBIO | PCATCH, "amrpoll", hz); + msleep(sc, &sc->amr_hw_lock, PRIBIO | PCATCH, "amrpoll", hz); if(count++>10) { break; } @@ -1086,8 +1448,13 @@ amr_quartz_poll_command1(struct amr_softc *sc, struct amr_command *ac) if(sc->amr_busyslots) { device_printf(sc->amr_dev, "adapter is busy\n"); - if (ac->ac_data != NULL) - bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap); + mtx_unlock(&sc->amr_hw_lock); + if (ac->ac_data != NULL) { + if (AC_IS_SG64(ac)) + bus_dmamap_unload(sc->amr_buffer64_dmat, ac->ac_dma64map); + else + bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap); + } ac->ac_status=0; return(1); } @@ -1116,6 +1483,7 @@ amr_quartz_poll_command1(struct amr_softc *sc, struct amr_command *ac) /* acknowledge that we have the commands */ AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK); while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK); + mtx_unlock(&sc->amr_hw_lock); /* unmap the command's data buffer */ if (ac->ac_flags & AMR_CMD_DATAIN) { @@ -1126,30 +1494,28 @@ amr_quartz_poll_command1(struct amr_softc *sc, struct amr_command *ac) bus_dmamap_sync(sc->amr_buffer_dmat,ac->ac_dmamap, BUS_DMASYNC_POSTWRITE); } - bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap); + if (AC_IS_SG64(ac)) + bus_dmamap_unload(sc->amr_buffer64_dmat, ac->ac_dma64map); + else + bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap); return(error); } -/******************************************************************************** - * Get a free command slot for a command if it doesn't already have one. - * - * May be safely called multiple times for a given command. - */ -static int -amr_getslot(struct amr_command *ac) +static __inline int +amr_freeslot(struct amr_command *ac) { - struct amr_softc *sc = ac->ac_sc; - int slot; + struct amr_softc *sc = ac->ac_sc; + int slot; debug_called(3); slot = ac->ac_slot; - if (sc->amr_busycmd[slot] != NULL) - panic("amr: slot %d busy?\n", slot); + if (sc->amr_busycmd[slot] == NULL) + panic("amr: slot %d not busy?\n", slot); - sc->amr_busycmd[slot] = ac; - sc->amr_busyslots++; + sc->amr_busycmd[slot] = NULL; + atomic_subtract_int(&sc->amr_busyslots, 1); return (0); } @@ -1163,7 +1529,6 @@ static void amr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) { struct amr_command *ac = (struct amr_command *)arg; - struct amr_softc *sc = ac->ac_sc; struct amr_sgentry *sg; int i; u_int8_t *sgc; @@ -1171,13 +1536,14 @@ amr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) debug_called(3); /* get base address of s/g table */ - sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG); + sg = ac->ac_sg.sg32; /* save data physical address */ - ac->ac_dataphys = segs[0].ds_addr; - /* for AMR_CMD_CONFIG the s/g count goes elsewhere */ - if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG) { + /* for AMR_CMD_CONFIG Read/Write the s/g count goes elsewhere */ + if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG && ( + ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_channel == AMR_CONFIG_READ_NVRAM_CONFIG || + ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_channel == AMR_CONFIG_WRITE_NVRAM_CONFIG)) { sgc = &(((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param); } else { sgc = &ac->ac_mailbox.mb_nsgelem; @@ -1187,12 +1553,14 @@ amr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) if (nsegments < 2) { *sgc = 0; ac->ac_mailbox.mb_nsgelem = 0; - ac->ac_mailbox.mb_physaddr = ac->ac_dataphys; + ac->ac_mailbox.mb_physaddr = segs[0].ds_addr; } else { ac->ac_mailbox.mb_nsgelem = nsegments; *sgc = nsegments; - ac->ac_mailbox.mb_physaddr = sc->amr_sgbusaddr + - (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry)); + /* XXX Setting these to 0 might not be needed. */ + ac->ac_sg64_lo = 0; + ac->ac_sg64_hi = 0; + ac->ac_mailbox.mb_physaddr = ac->ac_sgbusaddr; for (i = 0; i < nsegments; i++, sg++) { sg->sg_addr = segs[i].ds_addr; sg->sg_count = segs[i].ds_len; @@ -1202,6 +1570,41 @@ amr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) } static void +amr_setup_dma64map(void *arg, bus_dma_segment_t *segs, int nsegments, int error) +{ + struct amr_command *ac = (struct amr_command *)arg; + struct amr_sg64entry *sg; + int i; + u_int8_t *sgc; + + debug_called(3); + + /* get base address of s/g table */ + sg = ac->ac_sg.sg64; + + /* save data physical address */ + + /* for AMR_CMD_CONFIG Read/Write the s/g count goes elsewhere */ + if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG && ( + ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_channel == AMR_CONFIG_READ_NVRAM_CONFIG || + ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_channel == AMR_CONFIG_WRITE_NVRAM_CONFIG)) { + sgc = &(((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param); + } else { + sgc = &ac->ac_mailbox.mb_nsgelem; + } + + ac->ac_mailbox.mb_nsgelem = nsegments; + *sgc = nsegments; + ac->ac_sg64_hi = 0; + ac->ac_sg64_lo = ac->ac_sgbusaddr; + ac->ac_mailbox.mb_physaddr = 0xffffffff; + for (i = 0; i < nsegments; i++, sg++) { + sg->sg_addr = segs[i].ds_addr; + sg->sg_count = segs[i].ds_len; + } +} + +static void amr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) { struct amr_command *ac = (struct amr_command *)arg; @@ -1212,7 +1615,7 @@ amr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) int i; /* get base address of s/g table */ - sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG); + sg = ac->ac_sg.sg32; /* decide whether we need to populate the s/g table */ if( ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS ) { @@ -1222,8 +1625,7 @@ amr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) } else { /* save s/g table information in passthrough */ aep->ap_no_sg_elements = nsegments; - aep->ap_data_transfer_address = sc->amr_sgbusaddr + - (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry)); + aep->ap_data_transfer_address = ac->ac_sgbusaddr; /* * populate s/g table (overwrites previous call which mapped the * passthrough) @@ -1234,9 +1636,8 @@ amr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count); } } - debug(3, "slot %d %d segments at 0x%x, passthrough at 0x%x\n", - ac->ac_slot, aep->ap_no_sg_elements, aep->ap_data_transfer_address, - ac->ac_dataphys); + debug(3, "slot %d %d segments at 0x%x\n", ac->ac_slot, + aep->ap_no_sg_elements, aep->ap_data_transfer_address); } else { if (nsegments < 2) { ap->ap_no_sg_elements = 0; @@ -1244,8 +1645,7 @@ amr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) } else { /* save s/g table information in passthrough */ ap->ap_no_sg_elements = nsegments; - ap->ap_data_transfer_address = sc->amr_sgbusaddr + - (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry)); + ap->ap_data_transfer_address = ac->ac_sgbusaddr; /* * populate s/g table (overwrites previous call which mapped the * passthrough) @@ -1256,9 +1656,8 @@ amr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count); } } - debug(3, "slot %d %d segments at 0x%x, passthrough at 0x%x", - ac->ac_slot, ap->ap_no_sg_elements, ap->ap_data_transfer_address, - ac->ac_dataphys); + debug(3, "slot %d %d segments at 0x%x\n", ac->ac_slot, + ap->ap_no_sg_elements, ap->ap_data_transfer_address); } if (ac->ac_flags & AMR_CMD_CCB_DATAIN) bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, @@ -1276,37 +1675,129 @@ amr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) ac->ac_flags |= AMR_CMD_MAPPED; - amr_start1(sc, ac); + if (amr_start1(sc, ac) == EBUSY) { + amr_freeslot(ac); + amr_requeue_ready(ac); + } +} + +static void +amr_setup_ccb64map(void *arg, bus_dma_segment_t *segs, int nsegments, int error) +{ + struct amr_command *ac = (struct amr_command *)arg; + struct amr_softc *sc = ac->ac_sc; + struct amr_sg64entry *sg; + struct amr_passthrough *ap = (struct amr_passthrough *)ac->ac_data; + struct amr_ext_passthrough *aep = (struct amr_ext_passthrough *)ac->ac_data; + int i; + + /* get base address of s/g table */ + sg = ac->ac_sg.sg64; + + /* decide whether we need to populate the s/g table */ + if( ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS ) { + /* save s/g table information in passthrough */ + aep->ap_no_sg_elements = nsegments; + aep->ap_data_transfer_address = ac->ac_sgbusaddr; + /* + * populate s/g table (overwrites previous call which mapped the + * passthrough) + */ + for (i = 0; i < nsegments; i++, sg++) { + sg->sg_addr = segs[i].ds_addr; + sg->sg_count = segs[i].ds_len; + debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count); + } + debug(3, "slot %d %d segments at 0x%x\n", ac->ac_slot, + aep->ap_no_sg_elements, aep->ap_data_transfer_address); + } else { + /* save s/g table information in passthrough */ + ap->ap_no_sg_elements = nsegments; + ap->ap_data_transfer_address = ac->ac_sgbusaddr; + /* + * populate s/g table (overwrites previous call which mapped the + * passthrough) + */ + for (i = 0; i < nsegments; i++, sg++) { + sg->sg_addr = segs[i].ds_addr; + sg->sg_count = segs[i].ds_len; + debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count); + } + debug(3, "slot %d %d segments at 0x%x\n", ac->ac_slot, + ap->ap_no_sg_elements, ap->ap_data_transfer_address); + } + if (ac->ac_flags & AMR_CMD_CCB_DATAIN) + bus_dmamap_sync(sc->amr_buffer64_dmat, ac->ac_ccb_dma64map, + BUS_DMASYNC_PREREAD); + if (ac->ac_flags & AMR_CMD_CCB_DATAOUT) + bus_dmamap_sync(sc->amr_buffer64_dmat, ac->ac_ccb_dma64map, + BUS_DMASYNC_PREWRITE); + if ((ac->ac_flags & (AMR_CMD_CCB_DATAIN | AMR_CMD_CCB_DATAOUT)) == 0) + panic("no direction for ccb?\n"); + + if (ac->ac_flags & AMR_CMD_DATAIN) + bus_dmamap_sync(sc->amr_buffer64_dmat, ac->ac_dma64map, + BUS_DMASYNC_PREREAD); + if (ac->ac_flags & AMR_CMD_DATAOUT) + bus_dmamap_sync(sc->amr_buffer64_dmat, ac->ac_dma64map, + BUS_DMASYNC_PREWRITE); + + ac->ac_flags |= AMR_CMD_MAPPED; + + if (amr_start1(sc, ac) == EBUSY) { + amr_freeslot(ac); + amr_requeue_ready(ac); + } } static int amr_mapcmd(struct amr_command *ac) { + bus_dma_tag_t tag; + bus_dmamap_t datamap, ccbmap; + bus_dmamap_callback_t *cb; + bus_dmamap_callback_t *ccb_cb; struct amr_softc *sc = ac->ac_sc; debug_called(3); + if (AC_IS_SG64(ac)) { + tag = sc->amr_buffer64_dmat; + datamap = ac->ac_dma64map; + ccbmap = ac->ac_ccb_dma64map; + cb = amr_setup_dma64map; + ccb_cb = amr_setup_ccb64map; + } else { + tag = sc->amr_buffer_dmat; + datamap = ac->ac_dmamap; + ccbmap = ac->ac_ccb_dmamap; + cb = amr_setup_dmamap; + ccb_cb = amr_setup_ccbmap; + } + /* if the command involves data at all, and hasn't been mapped */ if ((ac->ac_flags & AMR_CMD_MAPPED) == 0 && (ac->ac_data != NULL)) { if (ac->ac_ccb_data == NULL) { /* map the data buffers into bus space and build the s/g list */ - if (bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, - ac->ac_length, amr_setup_data_dmamap, ac, 0) == EINPROGRESS) { + if (bus_dmamap_load(tag, datamap, ac->ac_data, ac->ac_length, + amr_setup_data_dmamap, ac, 0) == EINPROGRESS) { sc->amr_state |= AMR_STATE_QUEUE_FRZN; } } else { - if (bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, - ac->ac_length, amr_setup_dmamap, ac, BUS_DMA_NOWAIT) != 0){ + if (bus_dmamap_load(tag, datamap, ac->ac_data, ac->ac_length, + cb, ac, BUS_DMA_NOWAIT) != 0) { return (ENOMEM); } - if (bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, - ac->ac_ccb_data, ac->ac_ccb_length, amr_setup_ccbmap, ac, - 0) == EINPROGRESS) { + if (bus_dmamap_load(tag, ccbmap, ac->ac_ccb_data, + ac->ac_ccb_length, ccb_cb, ac, 0) == EINPROGRESS) { sc->amr_state |= AMR_STATE_QUEUE_FRZN; } } - } else if ((ac->ac_flags & AMR_CMD_MAPPED) == 0) { - amr_start1(sc, ac); + } else { + if (amr_start1(sc, ac) == EBUSY) { + amr_freeslot(ac); + amr_requeue_ready(ac); + } } return (0); @@ -1316,6 +1807,7 @@ static void amr_unmapcmd(struct amr_command *ac) { struct amr_softc *sc = ac->ac_sc; + int flag; debug_called(3); @@ -1323,23 +1815,37 @@ amr_unmapcmd(struct amr_command *ac) if (ac->ac_flags & AMR_CMD_MAPPED) { if (ac->ac_data != NULL) { + + flag = 0; if (ac->ac_flags & AMR_CMD_DATAIN) - bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, - BUS_DMASYNC_POSTREAD); + flag |= BUS_DMASYNC_POSTREAD; if (ac->ac_flags & AMR_CMD_DATAOUT) - bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap); + flag |= BUS_DMASYNC_POSTWRITE; + + if (AC_IS_SG64(ac)) { + bus_dmamap_sync(sc->amr_buffer64_dmat, ac->ac_dma64map, flag); + bus_dmamap_unload(sc->amr_buffer64_dmat, ac->ac_dma64map); + } else { + bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, flag); + bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap); + } } if (ac->ac_ccb_data != NULL) { + + flag = 0; if (ac->ac_flags & AMR_CMD_CCB_DATAIN) - bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, - BUS_DMASYNC_POSTREAD); + flag |= BUS_DMASYNC_POSTREAD; if (ac->ac_flags & AMR_CMD_CCB_DATAOUT) - bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_ccb_dmamap); + flag |= BUS_DMASYNC_POSTWRITE; + + if (AC_IS_SG64(ac)) { + bus_dmamap_sync(sc->amr_buffer64_dmat,ac->ac_ccb_dma64map,flag); + bus_dmamap_unload(sc->amr_buffer64_dmat, ac->ac_ccb_dma64map); + } else { + bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, flag); + bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_ccb_dmamap); + } } ac->ac_flags &= ~AMR_CMD_MAPPED; } @@ -1350,16 +1856,27 @@ amr_setup_data_dmamap(void *arg, bus_dma_segment_t *segs, int nsegs, int err) { struct amr_command *ac = arg; struct amr_softc *sc = ac->ac_sc; + int flags; - amr_setup_dmamap(arg, segs, nsegs, err); - + flags = 0; if (ac->ac_flags & AMR_CMD_DATAIN) - bus_dmamap_sync(sc->amr_buffer_dmat,ac->ac_dmamap,BUS_DMASYNC_PREREAD); + flags |= BUS_DMASYNC_PREREAD; if (ac->ac_flags & AMR_CMD_DATAOUT) - bus_dmamap_sync(sc->amr_buffer_dmat,ac->ac_dmamap,BUS_DMASYNC_PREWRITE); + flags |= BUS_DMASYNC_PREWRITE; + + if (AC_IS_SG64(ac)) { + amr_setup_dma64map(arg, segs, nsegs, err); + bus_dmamap_sync(sc->amr_buffer64_dmat,ac->ac_dma64map, flags); + } else { + amr_setup_dmamap(arg, segs, nsegs, err); + bus_dmamap_sync(sc->amr_buffer_dmat,ac->ac_dmamap, flags); + } ac->ac_flags |= AMR_CMD_MAPPED; - amr_start1(sc, ac); + if (amr_start1(sc, ac) == EBUSY) { + amr_freeslot(ac); + amr_requeue_ready(ac); + } } /******************************************************************************** @@ -1371,6 +1888,7 @@ amr_start(struct amr_command *ac) { struct amr_softc *sc; int error = 0; + int slot; debug_called(3); @@ -1379,9 +1897,11 @@ amr_start(struct amr_command *ac) ac->ac_flags |= AMR_CMD_BUSY; /* get a command slot (freed in amr_done) */ - if (amr_getslot(ac)) { - return(EBUSY); - } + slot = ac->ac_slot; + if (sc->amr_busycmd[slot] != NULL) + panic("amr: slot %d busy?\n", slot); + sc->amr_busycmd[slot] = ac; + atomic_add_int(&sc->amr_busyslots, 1); /* Now we have a slot, we can map the command (unmapped in amr_complete). */ if ((error = amr_mapcmd(ac)) == ENOMEM) { @@ -1389,8 +1909,7 @@ amr_start(struct amr_command *ac) * Memroy resources are short, so free the slot and let this be tried * later. */ - sc->amr_busycmd[ac->ac_slot] = NULL; - sc->amr_busyslots--; + amr_freeslot(ac); } return (error); @@ -1400,64 +1919,37 @@ amr_start(struct amr_command *ac) static int amr_start1(struct amr_softc *sc, struct amr_command *ac) { - int done, i; - - /* mark the new mailbox we are going to copy in as busy */ - ac->ac_mailbox.mb_busy = 1; - - /* clear the poll/ack fields in the mailbox */ - sc->amr_mailbox->mb_poll = 0; - sc->amr_mailbox->mb_ack = 0; + int i = 0; + + mtx_lock(&sc->amr_hw_lock); + while (sc->amr_mailbox->mb_busy && (i++ < 10)) + DELAY(1); + if (sc->amr_mailbox->mb_busy) { + mtx_unlock(&sc->amr_hw_lock); + return (EBUSY); + } /* * Save the slot number so that we can locate this command when complete. * Note that ident = 0 seems to be special, so we don't use it. */ - ac->ac_mailbox.mb_ident = ac->ac_slot + 1; - - /* - * Spin waiting for the mailbox, give up after ~1 second. We expect the - * controller to be able to handle our I/O. - * - * XXX perhaps we should wait for less time, and count on the deferred command - * handling to deal with retries? - */ - debug(4, "wait for mailbox"); - for (i = 10000, done = 0; (i > 0) && !done; i--) { - - /* is the mailbox free? */ - if (sc->amr_mailbox->mb_busy == 0) { - debug(4, "got mailbox"); - sc->amr_mailbox64->mb64_segment = 0; - bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE); - done = 1; - - /* not free, spin waiting */ - } else { - debug(4, "busy flag %x\n", sc->amr_mailbox->mb_busy); - /* this is somewhat ugly */ - DELAY(100); - } + ac->ac_mailbox.mb_ident = ac->ac_slot + 1; /* will be coppied into mbox */ + bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, 14); + sc->amr_mailbox->mb_busy = 1; + sc->amr_mailbox->mb_poll = 0; + sc->amr_mailbox->mb_ack = 0; + sc->amr_mailbox64->sg64_hi = ac->ac_sg64_hi; + sc->amr_mailbox64->sg64_lo = ac->ac_sg64_lo; + + if (sc->amr_submit_command(sc)) { + /* the controller wasn't ready to take the command, forget that we tried to post it */ + sc->amr_mailbox->mb_busy = 0; + mtx_unlock(&sc->amr_hw_lock); + return (EBUSY); } - /* - * Now give the command to the controller - */ - if (done) { - if (sc->amr_submit_command(sc)) { - /* the controller wasn't ready to take the command, forget that we tried to post it */ - sc->amr_mailbox->mb_busy = 0; - return(EBUSY); - } - debug(3, "posted command"); - return(0); - } - - /* - * The controller wouldn't take the command. Return the command as busy - * so that it is retried later. - */ - return(EBUSY); + mtx_unlock(&sc->amr_hw_lock); + return(0); } /******************************************************************************** @@ -1494,8 +1986,7 @@ amr_done(struct amr_softc *sc) if (ac != NULL) { /* pull the command from the busy index */ - sc->amr_busycmd[idx] = NULL; - sc->amr_busyslots--; + amr_freeslot(ac); /* save status for later use */ ac->ac_status = mbox.mb_status; @@ -1505,9 +1996,8 @@ amr_done(struct amr_softc *sc) device_printf(sc->amr_dev, "bad slot %d completed\n", idx); } } - } else { + } else break; /* no work */ - } } /* handle completion and timeouts */ @@ -1558,8 +2048,10 @@ amr_complete(void *context, int pending) } } + mtx_lock(&sc->amr_list_lock); sc->amr_state &= ~AMR_STATE_QUEUE_FRZN; amr_startio(sc); + mtx_unlock(&sc->amr_list_lock); } /******************************************************************************** @@ -1634,9 +2126,30 @@ amr_alloccmd_cluster(struct amr_softc *sc) ac = &acc->acc_command[i]; ac->ac_sc = sc; ac->ac_slot = nextslot; - if (!bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap) && - !bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_ccb_dmamap)) - amr_releasecmd(ac); + + /* + * The SG table for each slot is a fixed size and is assumed to + * to hold 64-bit s/g objects when the driver is configured to do + * 64-bit DMA. 32-bit DMA commands still use the same table, but + * cast down to 32-bit objects. + */ + if (AMR_IS_SG64(sc)) { + ac->ac_sgbusaddr = sc->amr_sgbusaddr + + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sg64entry)); + ac->ac_sg.sg64 = sc->amr_sg64table + (ac->ac_slot * AMR_NSEG); + } else { + ac->ac_sgbusaddr = sc->amr_sgbusaddr + + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry)); + ac->ac_sg.sg32 = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG); + } + + if (bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap) || + bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_ccb_dmamap) || + (AMR_IS_SG64(sc) && + (bus_dmamap_create(sc->amr_buffer64_dmat, 0,&ac->ac_dma64map) || + bus_dmamap_create(sc->amr_buffer64_dmat, 0, &ac->ac_ccb_dma64map)))) + break; + amr_releasecmd(ac); if (++nextslot > sc->amr_maxio) break; } @@ -1653,8 +2166,13 @@ amr_freecmd_cluster(struct amr_command_cluster *acc) struct amr_softc *sc = acc->acc_command[0].ac_sc; int i; - for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) + for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) { bus_dmamap_destroy(sc->amr_buffer_dmat, acc->acc_command[i].ac_dmamap); + bus_dmamap_destroy(sc->amr_buffer_dmat, acc->acc_command[i].ac_ccb_dmamap); + if (AMR_IS_SG64(sc)) + bus_dmamap_destroy(sc->amr_buffer64_dmat, acc->acc_command[i].ac_dma64map); + bus_dmamap_destroy(sc->amr_buffer64_dmat, acc->acc_command[i].ac_ccb_dma64map); + } free(acc, M_DEVBUF); } @@ -1672,8 +2190,10 @@ amr_quartz_submit_command(struct amr_softc *sc) { debug_called(3); +#if 0 if (AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT) return(EBUSY); +#endif AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT); return(0); } @@ -1696,9 +2216,10 @@ amr_std_submit_command(struct amr_softc *sc) static int amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave) { - int worked; + int worked, i; u_int32_t outd; - u_int8_t nstatus; + u_int8_t nstatus ,status; + u_int8_t completed[46]; debug_called(3); @@ -1711,16 +2232,33 @@ amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave) AMR_QPUT_ODB(sc, AMR_QODB_READY); while ((nstatus = sc->amr_mailbox->mb_nstatus) == 0xff) - ; + cpu_spinwait(); sc->amr_mailbox->mb_nstatus = 0xff; - /* save mailbox, which contains a list of completed commands */ - bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave)); + /* wait until fw wrote out all completions */ + for (i = 0; i < nstatus; i++) { + while ((completed[i] = sc->amr_mailbox->mb_completed[i]) == 0xff) + cpu_spinwait(); + sc->amr_mailbox->mb_completed[i] = 0xff; + } + + /* this should never happen, someone screwed up the completion status */ + if ((status = sc->amr_mailbox->mb_status) == 0xff) { + device_printf(sc->amr_dev, "status 0xff from the firmware\n"); + return (worked); + } + sc->amr_mailbox->mb_status = 0xff; + + /* Save information for later processing */ mbsave->mb_nstatus = nstatus; + mbsave->mb_status = status; + for (i = 0; i < nstatus; i++) + mbsave->mb_completed[i] = completed[i]; /* acknowledge that we have the commands */ AMR_QPUT_IDB(sc, AMR_QIDB_ACK); +#if 0 #ifndef AMR_QUARTZ_GOFASTER /* * This waits for the controller to notice that we've taken the @@ -1734,6 +2272,7 @@ amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave) while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK) ; /* XXX aiee! what if it dies? */ #endif +#endif worked = 1; /* got some work */ } @@ -1859,28 +2398,27 @@ amr_describe_controller(struct amr_softc *sc) struct amr_prodinfo *ap; struct amr_enquiry *ae; char *prod; + int status; - mtx_lock(&sc->amr_io_lock); /* * Try to get 40LD product info, which tells us what the card is labelled as. */ - if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) != NULL) { + if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0, &status)) != NULL) { device_printf(sc->amr_dev, "<LSILogic %.80s> Firmware %.16s, BIOS %.16s, %dMB RAM\n", ap->ap_product, ap->ap_firmware, ap->ap_bios, ap->ap_memsize); free(ap, M_DEVBUF); - mtx_unlock(&sc->amr_io_lock); return; } /* * Try 8LD extended ENQUIRY to get controller signature, and use lookup table. */ - if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) != NULL) { + if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0, &status)) != NULL) { prod = amr_describe_code(amr_table_adaptertype, ae->ae_signature); - } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) != NULL) { + } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0, &status)) != NULL) { /* * Try to work it out based on the PCI signatures. @@ -1898,7 +2436,6 @@ amr_describe_controller(struct amr_softc *sc) } } else { device_printf(sc->amr_dev, "<unsupported controller>\n"); - mtx_unlock(&sc->amr_io_lock); return; } @@ -1939,7 +2476,6 @@ amr_describe_controller(struct amr_softc *sc) ae->ae_adapter.aa_memorysize); } free(ae, M_DEVBUF); - mtx_unlock(&sc->amr_io_lock); } int diff --git a/sys/dev/amr/amr_cam.c b/sys/dev/amr/amr_cam.c index 51d5c6fe7735..251ec6b693ec 100644 --- a/sys/dev/amr/amr_cam.c +++ b/sys/dev/amr/amr_cam.c @@ -253,10 +253,10 @@ amr_cam_action(struct cam_sim *sim, union ccb *ccb) /* save the channel number in the ccb */ csio->ccb_h.sim_priv.entries[0].field = cam_sim_bus(sim); - mtx_lock(&sc->amr_io_lock); + mtx_lock(&sc->amr_list_lock); amr_enqueue_ccb(sc, ccb); amr_startio(sc); - mtx_unlock(&sc->amr_io_lock); + mtx_unlock(&sc->amr_list_lock); return; } break; @@ -456,11 +456,17 @@ amr_cam_command(struct amr_softc *sc, struct amr_command **acp) ac->ac_length = sizeof(*aep); ac->ac_complete = amr_cam_complete_extcdb; ac->ac_mailbox.mb_command = AMR_CMD_EXTPASS; + if (AMR_IS_SG64(sc)) + ac->ac_flags |= AMR_CMD_SG64; } else { ac->ac_data = ap; ac->ac_length = sizeof(*ap); ac->ac_complete = amr_cam_complete; - ac->ac_mailbox.mb_command = AMR_CMD_PASS; + if (AMR_IS_SG64(sc)) { + ac->ac_mailbox.mb_command = AMR_CMD_PASS_64; + ac->ac_flags |= AMR_CMD_SG64; + } else + ac->ac_mailbox.mb_command = AMR_CMD_PASS; } out: @@ -484,11 +490,8 @@ out: static void amr_cam_poll(struct cam_sim *sim) { - struct amr_softc *sc = cam_sim_softc(sim); - mtx_lock(&sc->amr_io_lock); amr_done(cam_sim_softc(sim)); - mtx_unlock(&sc->amr_io_lock); } /******************************************************************************** diff --git a/sys/dev/amr/amr_disk.c b/sys/dev/amr/amr_disk.c index a394deeb1441..91b43c8f6a05 100644 --- a/sys/dev/amr/amr_disk.c +++ b/sys/dev/amr/amr_disk.c @@ -89,7 +89,7 @@ static disk_strategy_t amrd_strategy; static devclass_t amrd_devclass; #ifdef FREEBSD_4 -static int disks_registered = 0; +int amr_disks_registered = 0; #endif static device_method_t amrd_methods[] = { @@ -257,7 +257,7 @@ amrd_detach(device_t dev) return(EBUSY); #ifdef FREEBSD_4 - if (--disks_registered == 0) + if (--amr_disks_registered == 0) cdevsw_remove(&amrddisk_cdevsw); #else disk_destroy(sc->amrd_disk); diff --git a/sys/dev/amr/amr_pci.c b/sys/dev/amr/amr_pci.c index 5a3152b01ea0..c724ea23a0c1 100644 --- a/sys/dev/amr/amr_pci.c +++ b/sys/dev/amr/amr_pci.c @@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/kernel.h> #include <sys/module.h> +#include <sys/sysctl.h> #include <sys/bio.h> #include <sys/bus.h> @@ -90,6 +91,12 @@ static int amr_sglist_map(struct amr_softc *sc); static void amr_setup_mbox_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int amr_setup_mbox(struct amr_softc *sc); +static u_int amr_force_sg32 = 0; +TUNABLE_INT("hw.amr.force_sg32", &amr_force_sg32); +SYSCTL_DECL(_hw_amr); +SYSCTL_UINT(_hw_amr, OID_AUTO, force_sg32, CTLFLAG_RDTUN, &amr_force_sg32, 0, + "Force the AMR driver to use 32bit scatter gather"); + static device_method_t amr_methods[] = { /* Device interface */ DEVMETHOD(device_probe, amr_pci_probe), @@ -113,48 +120,61 @@ static driver_t amr_pci_driver = { static devclass_t amr_devclass; DRIVER_MODULE(amr, pci, amr_pci_driver, amr_devclass, 0, 0); -static struct +static struct amr_ident { int vendor; int device; - int flag; -#define PROBE_SIGNATURE (1<<0) + int flags; +#define AMR_ID_PROBE_SIG (1<<0) /* generic i960RD, check signature */ +#define AMR_ID_DO_SG64 (1<<1) +#define AMR_ID_QUARTZ (1<<2) } amr_device_ids[] = { {0x101e, 0x9010, 0}, {0x101e, 0x9060, 0}, - {0x8086, 0x1960, PROBE_SIGNATURE},/* generic i960RD, check for signature */ - {0x101e, 0x1960, 0}, - {0x1000, 0x1960, PROBE_SIGNATURE}, - {0x1000, 0x0407, 0}, - {0x1000, 0x0408, 0}, - {0x1000, 0x0409, 0}, - {0x1028, 0x000e, PROBE_SIGNATURE}, /* perc4/di i960 */ - {0x1028, 0x000f, 0}, /* perc4/di Verde*/ - {0x1028, 0x0013, 0}, /* perc4/di */ + {0x8086, 0x1960, AMR_ID_QUARTZ | AMR_ID_PROBE_SIG}, + {0x101e, 0x1960, AMR_ID_QUARTZ}, + {0x1000, 0x1960, AMR_ID_QUARTZ | AMR_ID_PROBE_SIG}, + {0x1000, 0x0407, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, + {0x1000, 0x0408, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, + {0x1000, 0x0409, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, + {0x1028, 0x000e, AMR_ID_QUARTZ | AMR_ID_DO_SG64 | AMR_ID_PROBE_SIG}, /* perc4/di i960 */ + {0x1028, 0x000f, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, /* perc4/di Verde*/ + {0x1028, 0x0013, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, /* perc4/di */ {0, 0, 0} }; -static int -amr_pci_probe(device_t dev) +static struct amr_ident * +amr_find_ident(device_t dev) { - int i, sig; - - debug_called(1); + struct amr_ident *id; + int sig; - for (i = 0; amr_device_ids[i].vendor != 0; i++) { - if ((pci_get_vendor(dev) == amr_device_ids[i].vendor) && - (pci_get_device(dev) == amr_device_ids[i].device)) { + for (id = amr_device_ids; id->vendor != 0; id++) { + if ((pci_get_vendor(dev) == id->vendor) && + (pci_get_device(dev) == id->device)) { /* do we need to test for a signature? */ - if (amr_device_ids[i].flag & PROBE_SIGNATURE) { + if (id->flags & AMR_ID_PROBE_SIG) { sig = pci_read_config(dev, AMR_CFG_SIG, 2); if ((sig != AMR_SIGNATURE_1) && (sig != AMR_SIGNATURE_2)) continue; } - device_set_desc(dev, LSI_DESC_PCI); - return(BUS_PROBE_DEFAULT); + return (id); } } + return (NULL); +} + +static int +amr_pci_probe(device_t dev) +{ + + debug_called(1); + + if (amr_find_ident(dev) != NULL) { + device_set_desc(dev, LSI_DESC_PCI); + return(BUS_PROBE_DEFAULT); + } return(ENXIO); } @@ -162,6 +182,7 @@ static int amr_pci_attach(device_t dev) { struct amr_softc *sc; + struct amr_ident *id; int rid, rtype, error; u_int32_t command; @@ -173,7 +194,6 @@ amr_pci_attach(device_t dev) sc = device_get_softc(dev); bzero(sc, sizeof(*sc)); sc->amr_dev = dev; - mtx_init(&sc->amr_io_lock, "AMR IO Lock", NULL, MTX_DEF); /* assume failure is 'not configured' */ error = ENXIO; @@ -181,30 +201,35 @@ amr_pci_attach(device_t dev) /* * Determine board type. */ + if ((id = amr_find_ident(dev)) == NULL) + return (ENXIO); + command = pci_read_config(dev, PCIR_COMMAND, 1); - if ((pci_get_device(dev) == 0x1960) || (pci_get_device(dev) == 0x0407) || - (pci_get_device(dev) == 0x0408) || (pci_get_device(dev) == 0x0409) || - (pci_get_device(dev) == 0x000e) || (pci_get_device(dev) == 0x000f) || - (pci_get_device(dev) == 0x0013)) { + if (id->flags & AMR_ID_QUARTZ) { /* * Make sure we are going to be able to talk to this board. */ if ((command & PCIM_CMD_MEMEN) == 0) { device_printf(dev, "memory window not available\n"); - goto out; + return (ENXIO); } sc->amr_type |= AMR_TYPE_QUARTZ; - } else { /* * Make sure we are going to be able to talk to this board. */ if ((command & PCIM_CMD_PORTEN) == 0) { device_printf(dev, "I/O window not available\n"); - goto out; + return (ENXIO); } } + if ((amr_force_sg32 == 0) && (id->flags & AMR_ID_DO_SG64) && + (sizeof(vm_paddr_t) > 4)) { + device_printf(dev, "Using 64-bit DMA\n"); + sc->amr_type |= AMR_TYPE_SG64; + } + /* force the busmaster enable bit on */ if (!(command & PCIM_CMD_BUSMASTEREN)) { device_printf(dev, "busmaster bit not set, enabling\n"); @@ -235,7 +260,9 @@ amr_pci_attach(device_t dev) device_printf(sc->amr_dev, "can't allocate interrupt\n"); goto out; } - if (bus_setup_intr(sc->amr_dev, sc->amr_irq, INTR_TYPE_BIO | INTR_ENTROPY | INTR_MPSAFE, amr_pci_intr, sc, &sc->amr_intr)) { + if (bus_setup_intr(sc->amr_dev, sc->amr_irq, + INTR_TYPE_BIO | INTR_ENTROPY | INTR_MPSAFE, amr_pci_intr, + sc, &sc->amr_intr)) { device_printf(sc->amr_dev, "can't set up interrupt\n"); goto out; } @@ -249,7 +276,9 @@ amr_pci_attach(device_t dev) * Allocate the parent bus DMA tag appropriate for PCI. */ if (bus_dma_tag_create(NULL, /* parent */ - 1, 0, /* alignment, boundary */ + 1, 0, /* alignment,boundary */ + AMR_IS_SG64(sc) ? + BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ @@ -266,24 +295,42 @@ amr_pci_attach(device_t dev) * Create DMA tag for mapping buffers into controller-addressable space. */ if (bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ - 1, 0, /* alignment, boundary */ + 1, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ MAXBSIZE, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ - busdma_lock_mutex, &sc->amr_io_lock, /* lockfunc, lockarg */ + busdma_lock_mutex, /* lockfunc */ + &sc->amr_list_lock, /* lockarg */ &sc->amr_buffer_dmat)) { device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n"); goto out; } + if (bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ + 1, 0, /* alignment,boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ + MAXBSIZE, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + busdma_lock_mutex, /* lockfunc */ + &sc->amr_list_lock, /* lockarg */ + &sc->amr_buffer64_dmat)) { + device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n"); + goto out; + } + debug(2, "dma tag done"); /* * Allocate and set up mailbox in a bus-visible fashion. */ + mtx_init(&sc->amr_list_lock, "AMR List Lock", NULL, MTX_DEF); + mtx_init(&sc->amr_hw_lock, "AMR HW Lock", NULL, MTX_DEF); if ((error = amr_setup_mbox(sc)) != 0) goto out; @@ -423,9 +470,7 @@ amr_pci_intr(void *arg) debug_called(2); /* collect finished commands, queue anything waiting */ - mtx_lock(&sc->amr_io_lock); amr_done(sc); - mtx_unlock(&sc->amr_io_lock); } /******************************************************************************** @@ -436,8 +481,8 @@ amr_pci_intr(void *arg) static void amr_pci_free(struct amr_softc *sc) { - u_int8_t *p; - + u_int8_t *p + debug_called(1); amr_free(sc); @@ -445,6 +490,8 @@ amr_pci_free(struct amr_softc *sc) /* destroy data-transfer DMA tag */ if (sc->amr_buffer_dmat) bus_dma_tag_destroy(sc->amr_buffer_dmat); + if (sc->amr_buffer64_dmat) + bus_dma_tag_destroy(sc->amr_buffer64_dmat); /* free and destroy DMA memory and tag for s/g lists */ if (sc->amr_sgtable) @@ -453,9 +500,10 @@ amr_pci_free(struct amr_softc *sc) bus_dma_tag_destroy(sc->amr_sg_dmat); /* free and destroy DMA memory and tag for mailbox */ + /* XXX Brain damaged GCC Alert! */ + p = (u_int8_t *)(uintptr_t)(volatile void *)sc->amr_mailbox64; if (sc->amr_mailbox) { - p = (u_int8_t *)(uintptr_t)(volatile void *)sc->amr_mailbox; - bus_dmamem_free(sc->amr_mailbox_dmat, p - 16, sc->amr_mailbox_dmamap); + bus_dmamem_free(sc->amr_mailbox_dmat, p, sc->amr_mailbox_dmamap); } if (sc->amr_mailbox_dmat) bus_dma_tag_destroy(sc->amr_mailbox_dmat); @@ -495,6 +543,7 @@ static int amr_sglist_map(struct amr_softc *sc) { size_t segsize; + u_int8_t *p; int error; debug_called(1); @@ -503,19 +552,23 @@ amr_sglist_map(struct amr_softc *sc) * Create a single tag describing a region large enough to hold all of * the s/g lists we will need. * - * Note that we could probably use AMR_LIMITCMD here, but that may become tunable. + * Note that we could probably use AMR_LIMITCMD here, but that may become + * tunable. */ - segsize = sizeof(struct amr_sgentry) * AMR_NSEG * AMR_MAXCMD; + if (AMR_IS_SG64(sc)) + segsize = sizeof(struct amr_sg64entry) * AMR_NSEG * AMR_MAXCMD; + else + segsize = sizeof(struct amr_sgentry) * AMR_NSEG * AMR_MAXCMD; + error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ - 1, 0, /* alignment, boundary */ + 1, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ segsize, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ - busdma_lock_mutex, /* lockfunc */ - &Giant, /* lockarg */ + NULL, NULL, /* lockfunc, lockarg */ &sc->amr_sg_dmat); if (error != 0) { device_printf(sc->amr_dev, "can't allocate scatter/gather DMA tag\n"); @@ -527,25 +580,32 @@ amr_sglist_map(struct amr_softc *sc) * controller-visible space. * * XXX this assumes we can get enough space for all the s/g maps in one - * contiguous slab. We may need to switch to a more complex arrangement where - * we allocate in smaller chunks and keep a lookup table from slot to bus address. + * contiguous slab. We may need to switch to a more complex arrangement + * where we allocate in smaller chunks and keep a lookup table from slot + * to bus address. * - * XXX HACK ALERT: at least some controllers don't like the s/g memory being - * allocated below 0x2000. We leak some memory if we get some - * below this mark and allocate again. We should be able to - * avoid this with the tag setup, but that does't seem to work. + * XXX HACK ALERT: at least some controllers don't like the s/g memory + * being allocated below 0x2000. We leak some memory if + * we get some below this mark and allocate again. We + * should be able to avoid this with the tag setup, but + * that does't seem to work. */ retry: - error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&sc->amr_sgtable, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap); + error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&p, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap); if (error) { device_printf(sc->amr_dev, "can't allocate s/g table\n"); return(ENOMEM); } - bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, sc->amr_sgtable, segsize, amr_sglist_map_helper, sc, 0); + bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, p, segsize, amr_sglist_map_helper, sc, 0); if (sc->amr_sgbusaddr < 0x2000) { debug(1, "s/g table too low (0x%x), reallocating\n", sc->amr_sgbusaddr); goto retry; } + + if (AMR_IS_SG64(sc)) + sc->amr_sg64table = (struct amr_sg64entry *)p; + sc->amr_sgtable = (struct amr_sgentry *)p; + return(0); } @@ -563,7 +623,7 @@ amr_setup_mbox_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) debug_called(1); /* save phsyical base of the basic mailbox structure */ - sc->amr_mailboxphys = segs->ds_addr + 16; + sc->amr_mailboxphys = segs->ds_addr + offsetof(struct amr_mailbox64, mb); } static int @@ -579,15 +639,15 @@ amr_setup_mbox(struct amr_softc *sc) * mailbox. */ error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ - 16, 0, /* alignment, boundary */ + 16, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ - sizeof(struct amr_mailbox) + 16, 1, /* maxsize, nsegments */ + sizeof(struct amr_mailbox64), /* maxsize */ + 1, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ - busdma_lock_mutex, /* lockfunc */ - &Giant, /* lockarg */ + NULL, NULL, /* lockfunc, lockarg */ &sc->amr_mailbox_dmat); if (error != 0) { device_printf(sc->amr_dev, "can't allocate mailbox tag\n"); @@ -610,8 +670,8 @@ amr_setup_mbox(struct amr_softc *sc) * Conventional mailbox is inside the mailbox64 region. */ bzero(p, sizeof(struct amr_mailbox64)); - sc->amr_mailbox64 = (struct amr_mailbox64 *)(p + 12); - sc->amr_mailbox = (struct amr_mailbox *)(p + 16); + sc->amr_mailbox64 = (struct amr_mailbox64 *)p; + sc->amr_mailbox = &sc->amr_mailbox64->mb; return(0); } diff --git a/sys/dev/amr/amrio.h b/sys/dev/amr/amrio.h index e14ade467cd0..bc06a863268d 100644 --- a/sys/dev/amr/amrio.h +++ b/sys/dev/amr/amrio.h @@ -65,7 +65,7 @@ /* * Fetch the driver's interface version. */ -#define AMR_IO_VERSION_NUMBER 0x01 +#define AMR_IO_VERSION_NUMBER 153 #define AMR_IO_VERSION _IOR('A', 0x200, int) /* diff --git a/sys/dev/amr/amrreg.h b/sys/dev/amr/amrreg.h index f08b113b7343..8056ded6536c 100644 --- a/sys/dev/amr/amrreg.h +++ b/sys/dev/amr/amrreg.h @@ -119,12 +119,27 @@ #define AMR_CMD_GET_MACHINEID 0x36 #define AMR_CMD_GET_INITIATOR 0x7d /* returns one byte */ #define AMR_CMD_CONFIG 0xa1 +#define AMR_CMD_LREAD64 0xa7 +#define AMR_CMD_LWRITE64 0xa8 +#define AMR_CMD_PASS_64 0xc3 +#define AMR_CMD_EXTPASS 0xe3 + +#define AMR_CONFIG_READ_NVRAM_CONFIG 0x04 +#define AMR_CONFIG_WRITE_NVRAM_CONFIG 0x0d +#define AMR_CONFIG_ENQ3_SOLICITED_NOTIFY 0x01 #define AMR_CONFIG_PRODUCT_INFO 0x0e #define AMR_CONFIG_ENQ3 0x0f #define AMR_CONFIG_ENQ3_SOLICITED_NOTIFY 0x01 #define AMR_CONFIG_ENQ3_SOLICITED_FULL 0x02 #define AMR_CONFIG_ENQ3_UNSOLICITED 0x03 -#define AMR_CMD_EXTPASS 0xe3 + +/* + * Command for random deletion of logical drives + */ +#define FC_DEL_LOGDRV 0xA4 +#define OP_SUP_DEL_LOGDRV 0x2A +#define OP_GET_LDID_MAP 0x18 +#define OP_DEL_LOGDRV 0x1C /* * Command results @@ -397,13 +412,14 @@ struct amr_mailbox { u_int8_t mb_command; u_int8_t mb_ident; - u_int16_t mb_blkcount; + u_int16_t mb_blkcount; /* u_int8_t opcode */ + /* u_int8_t subopcode */ u_int32_t mb_lba; u_int32_t mb_physaddr; u_int8_t mb_drive; - u_int8_t mb_nsgelem; - u_int8_t res1; - u_int8_t mb_busy; + u_int8_t mb_nsgelem; /* u_int8_t rserv[0] */ + u_int8_t res1; /* u_int8_t rserv[1] */ + u_int8_t mb_busy; /* u_int8_t rserv[2] */ u_int8_t mb_nstatus; u_int8_t mb_status; u_int8_t mb_completed[46]; @@ -414,7 +430,9 @@ struct amr_mailbox struct amr_mailbox64 { - u_int32_t mb64_segment; /* for 64-bit controllers */ + u_int8_t pad[8]; /* Needed for alignment */ + u_int32_t sg64_lo; /* S/G pointer for 64-bit commands */ + u_int32_t sg64_hi; /* S/G pointer for 64-bit commands */ struct amr_mailbox mb; } __packed; @@ -443,6 +461,12 @@ struct amr_sgentry u_int32_t sg_count; } __packed; +struct amr_sg64entry +{ + u_int64_t sg_addr; + u_int32_t sg_count; +} __packed; + struct amr_passthrough { u_int8_t ap_timeout:3; @@ -489,6 +513,26 @@ struct amr_ext_passthrough u_int32_t ap_data_transfer_length; } __packed; +struct amr_linux_ioctl { + u_int32_t inlen; + u_int32_t outlen; + union { + u_int8_t fca[16]; + struct { + u_int8_t opcode; + u_int8_t subopcode; + u_int16_t adapno; + u_int32_t buffer; + u_int8_t pad[4]; + u_int32_t length; + } __packed fcs; + } __packed ui; + u_int8_t mbox[18]; + struct amr_passthrough pthru; + u_int32_t data; + u_int8_t pad[4]; +} __packed; + #ifdef _KERNEL /******************************************************************************** ******************************************************************************** diff --git a/sys/dev/amr/amrvar.h b/sys/dev/amr/amrvar.h index 61ed563acf96..84b3c61af07b 100644 --- a/sys/dev/amr/amrvar.h +++ b/sys/dev/amr/amrvar.h @@ -60,7 +60,7 @@ #include <sys/lock.h> #include <sys/mutex.h> -#define LSI_DESC_PCI "LSILogic MegaRAID 1.51" +#define LSI_DESC_PCI "LSILogic MegaRAID 1.53" #ifdef AMR_DEBUG # define debug(level, fmt, args...) do {if (level <= AMR_DEBUG) printf("%s: " fmt "\n", __func__ , ##args);} while(0) @@ -110,6 +110,13 @@ struct amr_command struct amr_softc *ac_sc; u_int8_t ac_slot; int ac_status; /* command completion status */ + union { + struct amr_sgentry *sg32; + struct amr_sg64entry *sg64; + } ac_sg; + u_int32_t ac_sgbusaddr; + u_int32_t ac_sg64_lo; + u_int32_t ac_sg64_hi; struct amr_mailbox ac_mailbox; int ac_flags; #define AMR_CMD_DATAIN (1<<0) @@ -120,21 +127,23 @@ struct amr_command #define AMR_CMD_MAPPED (1<<5) #define AMR_CMD_SLEEP (1<<6) #define AMR_CMD_BUSY (1<<7) +#define AMR_CMD_SG64 (1<<8) +#define AC_IS_SG64(ac) ((ac)->ac_flags & AMR_CMD_SG64) struct bio *ac_bio; + void (* ac_complete)(struct amr_command *ac); + void *ac_private; void *ac_data; size_t ac_length; bus_dmamap_t ac_dmamap; - u_int32_t ac_dataphys; + bus_dmamap_t ac_dma64map; void *ac_ccb_data; size_t ac_ccb_length; bus_dmamap_t ac_ccb_dmamap; - u_int32_t ac_ccb_dataphys; + bus_dmamap_t ac_ccb_dma64map; - void (* ac_complete)(struct amr_command *ac); - void *ac_private; }; struct amr_command_cluster @@ -158,6 +167,7 @@ struct amr_softc bus_space_tag_t amr_btag; bus_dma_tag_t amr_parent_dmat; /* parent DMA tag */ bus_dma_tag_t amr_buffer_dmat; /* data buffer DMA tag */ + bus_dma_tag_t amr_buffer64_dmat; struct resource *amr_irq; /* interrupt */ void *amr_intr; @@ -170,6 +180,7 @@ struct amr_softc /* scatter/gather lists and their controller-visible mappings */ struct amr_sgentry *amr_sgtable; /* s/g lists */ + struct amr_sg64entry *amr_sg64table; /* 64bit s/g lists */ u_int32_t amr_sgbusaddr; /* s/g table base address in bus space */ bus_dma_tag_t amr_sg_dmat; /* s/g buffer DMA tag */ bus_dmamap_t amr_sg_dmamap; /* map for s/g buffers */ @@ -191,6 +202,8 @@ struct amr_softc #define AMR_STATE_SHUTDOWN (1<<3) #define AMR_STATE_CRASHDUMP (1<<4) #define AMR_STATE_QUEUE_FRZN (1<<5) +#define AMR_STATE_LD_DELETE (1<<6) +#define AMR_STATE_REMAP_LD (1<<7) /* per-controller queues */ struct bio_queue_head amr_bioq; /* pending I/O with no commands */ @@ -207,7 +220,8 @@ struct amr_softc struct cam_devq *amr_cam_devq; /* control device */ - struct cdev *amr_dev_t; + struct cdev *amr_dev_t; + struct mtx amr_list_lock; /* controller type-specific support */ int amr_type; @@ -215,6 +229,8 @@ struct amr_softc #define AMR_IS_QUARTZ(sc) ((sc)->amr_type & AMR_TYPE_QUARTZ) #define AMR_TYPE_40LD (1<<1) #define AMR_IS_40LD(sc) ((sc)->amr_type & AMR_TYPE_40LD) +#define AMR_TYPE_SG64 (1<<2) +#define AMR_IS_SG64(sc) ((sc)->amr_type & AMR_TYPE_SG64) int (* amr_submit_command)(struct amr_softc *sc); int (* amr_get_work)(struct amr_softc *sc, struct amr_mailbox *mbsave); int (*amr_poll_command)(struct amr_command *ac); @@ -224,7 +240,10 @@ struct amr_softc /* misc glue */ struct intr_config_hook amr_ich; /* wait-for-interrupts probe hook */ struct callout_handle amr_timeout; /* periodic status check */ - struct mtx amr_io_lock; + int amr_allow_vol_config; + int amr_linux_no_adapters; + int amr_ld_del_supported; + struct mtx amr_hw_lock; }; /* @@ -235,6 +254,8 @@ extern void amr_free(struct amr_softc *sc); extern int amr_flush(struct amr_softc *sc); extern int amr_done(struct amr_softc *sc); extern void amr_startio(struct amr_softc *sc); +extern int amr_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t addr, + int32_t flag, d_thread_t *td); /* * Command buffer allocation. |