151973Smsmith/*- 251973Smsmith * Copyright (c) 1999 Michael Smith 351973Smsmith * All rights reserved. 451973Smsmith * 551973Smsmith * Redistribution and use in source and binary forms, with or without 651973Smsmith * modification, are permitted provided that the following conditions 751973Smsmith * are met: 851973Smsmith * 1. Redistributions of source code must retain the above copyright 951973Smsmith * notice, this list of conditions and the following disclaimer. 1051973Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1151973Smsmith * notice, this list of conditions and the following disclaimer in the 1251973Smsmith * documentation and/or other materials provided with the distribution. 1351973Smsmith * 1451973Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1551973Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1651973Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1751973Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1851973Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1951973Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2051973Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2151973Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2251973Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2351973Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2451973Smsmith * SUCH DAMAGE. 2551973Smsmith * 2651973Smsmith * $FreeBSD$ 2751973Smsmith */ 2851973Smsmith 2951973Smsmith/* 3051973Smsmith * Driver for the Mylex DAC960 family of RAID controllers. 3151973Smsmith */ 3251973Smsmith 3351973Smsmith#include <sys/param.h> 3451973Smsmith#include <sys/systm.h> 3551973Smsmith#include <sys/malloc.h> 3651973Smsmith#include <sys/kernel.h> 3751973Smsmith 3851973Smsmith#include <sys/bus.h> 3951973Smsmith#include <sys/conf.h> 4058188Smsmith#include <sys/stat.h> 4151973Smsmith 4251973Smsmith#include <machine/resource.h> 4351973Smsmith#include <machine/bus.h> 4451973Smsmith#include <machine/clock.h> 4551973Smsmith#include <sys/rman.h> 4651973Smsmith 47112946Sphk#include <geom/geom_disk.h> 48112946Sphk 4978752Smsmith#include <dev/mlx/mlx_compat.h> 5051973Smsmith#include <dev/mlx/mlxio.h> 5151973Smsmith#include <dev/mlx/mlxvar.h> 5251973Smsmith#include <dev/mlx/mlxreg.h> 5351973Smsmith 5451973Smsmithstatic struct cdevsw mlx_cdevsw = { 55126080Sphk .d_version = D_VERSION, 56126080Sphk .d_flags = D_NEEDGIANT, 57111815Sphk .d_open = mlx_open, 58111815Sphk .d_close = mlx_close, 59111815Sphk .d_ioctl = mlx_ioctl, 60111815Sphk .d_name = "mlx", 6151973Smsmith}; 6251973Smsmith 6351973Smsmithdevclass_t mlx_devclass; 6451973Smsmith 6551973Smsmith/* 6651973Smsmith * Per-interface accessor methods 6751973Smsmith */ 6851973Smsmithstatic int mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 6951973Smsmithstatic int mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 7051973Smsmithstatic void mlx_v3_intaction(struct mlx_softc *sc, int action); 7158188Smsmithstatic int mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 7251973Smsmith 7352225Smsmithstatic int mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 7452225Smsmithstatic int mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 7552225Smsmithstatic void mlx_v4_intaction(struct mlx_softc *sc, int action); 7658188Smsmithstatic int mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 7752225Smsmith 7854419Smsmithstatic int mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 7954419Smsmithstatic int mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 8054419Smsmithstatic void mlx_v5_intaction(struct mlx_softc *sc, int action); 8158188Smsmithstatic int mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 8254419Smsmith 8351973Smsmith/* 8451973Smsmith * Status monitoring 8551973Smsmith */ 8651973Smsmithstatic void mlx_periodic(void *data); 8751973Smsmithstatic void mlx_periodic_enquiry(struct mlx_command *mc); 8851973Smsmithstatic void mlx_periodic_eventlog_poll(struct mlx_softc *sc); 8951973Smsmithstatic void mlx_periodic_eventlog_respond(struct mlx_command *mc); 9051973Smsmithstatic void mlx_periodic_rebuild(struct mlx_command *mc); 9151973Smsmith 9251973Smsmith/* 9351973Smsmith * Channel Pause 9451973Smsmith */ 9551973Smsmithstatic void mlx_pause_action(struct mlx_softc *sc); 9651973Smsmithstatic void mlx_pause_done(struct mlx_command *mc); 9751973Smsmith 9851973Smsmith/* 9951973Smsmith * Command submission. 10051973Smsmith */ 10151973Smsmithstatic void *mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, 10251973Smsmith void (*complete)(struct mlx_command *mc)); 10351973Smsmithstatic int mlx_flush(struct mlx_softc *sc); 10459136Smsmithstatic int mlx_check(struct mlx_softc *sc, int drive); 10551973Smsmithstatic int mlx_rebuild(struct mlx_softc *sc, int channel, int target); 10651973Smsmithstatic int mlx_wait_command(struct mlx_command *mc); 10751973Smsmithstatic int mlx_poll_command(struct mlx_command *mc); 108119665Sscottlvoid mlx_startio_cb(void *arg, 109119665Sscottl bus_dma_segment_t *segs, 110119665Sscottl int nsegments, int error); 11151973Smsmithstatic void mlx_startio(struct mlx_softc *sc); 11251973Smsmithstatic void mlx_completeio(struct mlx_command *mc); 113119665Sscottlstatic int mlx_user_command(struct mlx_softc *sc, 114119665Sscottl struct mlx_usercommand *mu); 115119665Sscottlvoid mlx_user_cb(void *arg, bus_dma_segment_t *segs, 116119665Sscottl int nsegments, int error); 11751973Smsmith 11851973Smsmith/* 11951973Smsmith * Command buffer allocation. 12051973Smsmith */ 12151973Smsmithstatic struct mlx_command *mlx_alloccmd(struct mlx_softc *sc); 12251973Smsmithstatic void mlx_releasecmd(struct mlx_command *mc); 12351973Smsmithstatic void mlx_freecmd(struct mlx_command *mc); 12451973Smsmith 12551973Smsmith/* 12651973Smsmith * Command management. 12751973Smsmith */ 12851973Smsmithstatic int mlx_getslot(struct mlx_command *mc); 129119665Sscottlstatic void mlx_setup_dmamap(struct mlx_command *mc, 130119665Sscottl bus_dma_segment_t *segs, 131119665Sscottl int nsegments, int error); 13251973Smsmithstatic void mlx_unmapcmd(struct mlx_command *mc); 13351973Smsmithstatic int mlx_start(struct mlx_command *mc); 13451973Smsmithstatic int mlx_done(struct mlx_softc *sc); 13551973Smsmithstatic void mlx_complete(struct mlx_softc *sc); 13651973Smsmith 13751973Smsmith/* 13851973Smsmith * Debugging. 13951973Smsmith */ 14051973Smsmithstatic char *mlx_diagnose_command(struct mlx_command *mc); 14154979Smsmithstatic void mlx_describe_controller(struct mlx_softc *sc); 14258188Smsmithstatic int mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2); 14351973Smsmith 14451973Smsmith/* 14551973Smsmith * Utility functions. 14651973Smsmith */ 14751973Smsmithstatic struct mlx_sysdrive *mlx_findunit(struct mlx_softc *sc, int unit); 14851973Smsmith 14951973Smsmith/******************************************************************************** 15051973Smsmith ******************************************************************************** 15151973Smsmith Public Interfaces 15251973Smsmith ******************************************************************************** 15351973Smsmith ********************************************************************************/ 15451973Smsmith 15551973Smsmith/******************************************************************************** 15651973Smsmith * Free all of the resources associated with (sc) 15751973Smsmith * 15851973Smsmith * Should not be called if the controller is active. 15951973Smsmith */ 16051973Smsmithvoid 16151973Smsmithmlx_free(struct mlx_softc *sc) 16251973Smsmith{ 16351973Smsmith struct mlx_command *mc; 16451973Smsmith 16558188Smsmith debug_called(1); 16651973Smsmith 16751973Smsmith /* cancel status timeout */ 16851973Smsmith untimeout(mlx_periodic, sc, sc->mlx_timeout); 16951973Smsmith 17051973Smsmith /* throw away any command buffers */ 17151973Smsmith while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) { 17251973Smsmith TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); 17351973Smsmith mlx_freecmd(mc); 17451973Smsmith } 17551973Smsmith 17651973Smsmith /* destroy data-transfer DMA tag */ 17751973Smsmith if (sc->mlx_buffer_dmat) 17851973Smsmith bus_dma_tag_destroy(sc->mlx_buffer_dmat); 17951973Smsmith 18051973Smsmith /* free and destroy DMA memory and tag for s/g lists */ 18151973Smsmith if (sc->mlx_sgtable) 18251973Smsmith bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); 18351973Smsmith if (sc->mlx_sg_dmat) 18451973Smsmith bus_dma_tag_destroy(sc->mlx_sg_dmat); 18551973Smsmith 18651973Smsmith /* disconnect the interrupt handler */ 18751973Smsmith if (sc->mlx_intr) 18851973Smsmith bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr); 18951973Smsmith if (sc->mlx_irq != NULL) 19051973Smsmith bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq); 19151973Smsmith 19251973Smsmith /* destroy the parent DMA tag */ 19351973Smsmith if (sc->mlx_parent_dmat) 19451973Smsmith bus_dma_tag_destroy(sc->mlx_parent_dmat); 19551973Smsmith 19651973Smsmith /* release the register window mapping */ 19751973Smsmith if (sc->mlx_mem != NULL) 19869292Smdodd bus_release_resource(sc->mlx_dev, sc->mlx_mem_type, sc->mlx_mem_rid, sc->mlx_mem); 19954979Smsmith 20054979Smsmith /* free controller enquiry data */ 20154979Smsmith if (sc->mlx_enq2 != NULL) 20254979Smsmith free(sc->mlx_enq2, M_DEVBUF); 20358188Smsmith 20458188Smsmith /* destroy control device */ 205130585Sphk if (sc->mlx_dev_t != (struct cdev *)NULL) 20658188Smsmith destroy_dev(sc->mlx_dev_t); 20751973Smsmith} 20851973Smsmith 20951973Smsmith/******************************************************************************** 21051973Smsmith * Map the scatter/gather table into bus space 21151973Smsmith */ 21251973Smsmithstatic void 21351973Smsmithmlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error) 21451973Smsmith{ 21551973Smsmith struct mlx_softc *sc = (struct mlx_softc *)arg; 21651973Smsmith 21758188Smsmith debug_called(1); 21851973Smsmith 21951973Smsmith /* save base of s/g table's address in bus space */ 22051973Smsmith sc->mlx_sgbusaddr = segs->ds_addr; 22151973Smsmith} 22251973Smsmith 22351973Smsmithstatic int 22451973Smsmithmlx_sglist_map(struct mlx_softc *sc) 22551973Smsmith{ 22651973Smsmith size_t segsize; 22760074Smsmith int error, ncmd; 22851973Smsmith 22958188Smsmith debug_called(1); 23051973Smsmith 23151973Smsmith /* destroy any existing mappings */ 23251973Smsmith if (sc->mlx_sgtable) 23351973Smsmith bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); 23451973Smsmith if (sc->mlx_sg_dmat) 23551973Smsmith bus_dma_tag_destroy(sc->mlx_sg_dmat); 23651973Smsmith 23751973Smsmith /* 23851973Smsmith * Create a single tag describing a region large enough to hold all of 23960074Smsmith * the s/g lists we will need. If we're called early on, we don't know how 24060074Smsmith * many commands we're going to be asked to support, so only allocate enough 24160074Smsmith * for a couple. 24251973Smsmith */ 24360074Smsmith if (sc->mlx_enq2 == NULL) { 24460074Smsmith ncmd = 2; 24560074Smsmith } else { 24660074Smsmith ncmd = sc->mlx_enq2->me_max_commands; 24760074Smsmith } 24860074Smsmith segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * ncmd; 24951973Smsmith error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 250118707Sscottl 1, 0, /* alignment,boundary */ 25151973Smsmith BUS_SPACE_MAXADDR, /* lowaddr */ 25251973Smsmith BUS_SPACE_MAXADDR, /* highaddr */ 25351973Smsmith NULL, NULL, /* filter, filterarg */ 25451973Smsmith segsize, 1, /* maxsize, nsegments */ 25551973Smsmith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 25651973Smsmith 0, /* flags */ 257118707Sscottl NULL, NULL, /* lockfunc, lockarg */ 25851973Smsmith &sc->mlx_sg_dmat); 25951973Smsmith if (error != 0) { 26051973Smsmith device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n"); 26151973Smsmith return(ENOMEM); 26251973Smsmith } 26351973Smsmith 26451973Smsmith /* 26551973Smsmith * Allocate enough s/g maps for all commands and permanently map them into 26651973Smsmith * controller-visible space. 26751973Smsmith * 26851973Smsmith * XXX this assumes we can get enough space for all the s/g maps in one 269118707Sscottl * contiguous slab. We may need to switch to a more complex arrangement 270118707Sscottl * where we allocate in smaller chunks and keep a lookup table from slot 271118707Sscottl * to bus address. 27251973Smsmith */ 273118707Sscottl error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable, 274118707Sscottl BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap); 27551973Smsmith if (error) { 27651973Smsmith device_printf(sc->mlx_dev, "can't allocate s/g table\n"); 27751973Smsmith return(ENOMEM); 27851973Smsmith } 279119665Sscottl (void)bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable, 280119665Sscottl segsize, mlx_dma_map_sg, sc, 0); 28151973Smsmith return(0); 28251973Smsmith} 28351973Smsmith 28451973Smsmith/******************************************************************************** 28551973Smsmith * Initialise the controller and softc 28651973Smsmith */ 28751973Smsmithint 28851973Smsmithmlx_attach(struct mlx_softc *sc) 28951973Smsmith{ 29058188Smsmith struct mlx_enquiry_old *meo; 29158188Smsmith int rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg; 29251973Smsmith 29358188Smsmith debug_called(1); 29451973Smsmith 29551973Smsmith /* 29651973Smsmith * Initialise per-controller queues. 29751973Smsmith */ 29852544Smsmith TAILQ_INIT(&sc->mlx_work); 29951973Smsmith TAILQ_INIT(&sc->mlx_freecmds); 30078752Smsmith MLX_BIO_QINIT(sc->mlx_bioq); 30151973Smsmith 30251973Smsmith /* 30351973Smsmith * Select accessor methods based on controller interface type. 30451973Smsmith */ 30551973Smsmith switch(sc->mlx_iftype) { 30658188Smsmith case MLX_IFTYPE_2: 30751973Smsmith case MLX_IFTYPE_3: 30851973Smsmith sc->mlx_tryqueue = mlx_v3_tryqueue; 30951973Smsmith sc->mlx_findcomplete = mlx_v3_findcomplete; 31051973Smsmith sc->mlx_intaction = mlx_v3_intaction; 31158188Smsmith sc->mlx_fw_handshake = mlx_v3_fw_handshake; 31251973Smsmith break; 31352225Smsmith case MLX_IFTYPE_4: 31452225Smsmith sc->mlx_tryqueue = mlx_v4_tryqueue; 31552225Smsmith sc->mlx_findcomplete = mlx_v4_findcomplete; 31652225Smsmith sc->mlx_intaction = mlx_v4_intaction; 31758188Smsmith sc->mlx_fw_handshake = mlx_v4_fw_handshake; 31852225Smsmith break; 31954419Smsmith case MLX_IFTYPE_5: 32054419Smsmith sc->mlx_tryqueue = mlx_v5_tryqueue; 32154419Smsmith sc->mlx_findcomplete = mlx_v5_findcomplete; 32254419Smsmith sc->mlx_intaction = mlx_v5_intaction; 32358188Smsmith sc->mlx_fw_handshake = mlx_v5_fw_handshake; 32454419Smsmith break; 32551973Smsmith default: 32651973Smsmith return(ENXIO); /* should never happen */ 32751973Smsmith } 32851973Smsmith 32951973Smsmith /* disable interrupts before we start talking to the controller */ 33051973Smsmith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 33151973Smsmith 33251973Smsmith /* 33358188Smsmith * Wait for the controller to come ready, handshake with the firmware if required. 33458188Smsmith * This is typically only necessary on platforms where the controller BIOS does not 33558188Smsmith * run. 33658188Smsmith */ 33758188Smsmith hsmsg = 0; 33858188Smsmith DELAY(1000); 33958188Smsmith while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) { 34058188Smsmith /* report first time around... */ 34158188Smsmith if (hsmsg == 0) { 34258188Smsmith device_printf(sc->mlx_dev, "controller initialisation in progress...\n"); 34358188Smsmith hsmsg = 1; 34458188Smsmith } 34558188Smsmith /* did we get a real message? */ 34658188Smsmith if (hscode == 2) { 34758188Smsmith hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2); 34858188Smsmith /* fatal initialisation error? */ 34958188Smsmith if (hscode != 0) { 35058188Smsmith return(ENXIO); 35158188Smsmith } 35258188Smsmith } 35358188Smsmith } 35458188Smsmith if (hsmsg == 1) 35558188Smsmith device_printf(sc->mlx_dev, "initialisation complete.\n"); 35658188Smsmith 35758188Smsmith /* 35851973Smsmith * Allocate and connect our interrupt. 35951973Smsmith */ 36051973Smsmith rid = 0; 361127135Snjl sc->mlx_irq = bus_alloc_resource_any(sc->mlx_dev, SYS_RES_IRQ, &rid, 362127135Snjl RF_SHAREABLE | RF_ACTIVE); 36351973Smsmith if (sc->mlx_irq == NULL) { 36459136Smsmith device_printf(sc->mlx_dev, "can't allocate interrupt\n"); 36551973Smsmith return(ENXIO); 36651973Smsmith } 367166901Spiso error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO | INTR_ENTROPY, NULL, mlx_intr, sc, &sc->mlx_intr); 36851973Smsmith if (error) { 36959136Smsmith device_printf(sc->mlx_dev, "can't set up interrupt\n"); 37051973Smsmith return(ENXIO); 37151973Smsmith } 37251973Smsmith 37351973Smsmith /* 37451973Smsmith * Create DMA tag for mapping buffers into controller-addressable space. 37551973Smsmith */ 376119665Sscottl error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 377119665Sscottl 1, 0, /* align, boundary */ 378119665Sscottl BUS_SPACE_MAXADDR, /* lowaddr */ 379119665Sscottl BUS_SPACE_MAXADDR, /* highaddr */ 380119665Sscottl NULL, NULL, /* filter, filterarg */ 381119665Sscottl MAXBSIZE, MLX_NSEG, /* maxsize, nsegments */ 382119665Sscottl BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 383119665Sscottl 0, /* flags */ 384117126Sscottl busdma_lock_mutex, /* lockfunc */ 385117126Sscottl &Giant, /* lockarg */ 38651973Smsmith &sc->mlx_buffer_dmat); 38751973Smsmith if (error != 0) { 38851973Smsmith device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n"); 38951973Smsmith return(ENOMEM); 39051973Smsmith } 39151973Smsmith 39251973Smsmith /* 393119665Sscottl * Create some initial scatter/gather mappings so we can run the probe 394119665Sscottl * commands. 39551973Smsmith */ 39651973Smsmith error = mlx_sglist_map(sc); 39751973Smsmith if (error != 0) { 39859136Smsmith device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n"); 39951973Smsmith return(error); 40051973Smsmith } 40151973Smsmith 40260074Smsmith /* 40360074Smsmith * We don't (yet) know where the event log is up to. 40460074Smsmith */ 40560074Smsmith sc->mlx_currevent = -1; 40660074Smsmith 40760074Smsmith /* 40860074Smsmith * Obtain controller feature information 40960074Smsmith */ 41054979Smsmith if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) { 41151973Smsmith device_printf(sc->mlx_dev, "ENQUIRY2 failed\n"); 41251973Smsmith return(ENXIO); 41351973Smsmith } 41451973Smsmith 41554979Smsmith /* 41651973Smsmith * Do quirk/feature related things. 41751973Smsmith */ 41854979Smsmith fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff; 41951973Smsmith switch(sc->mlx_iftype) { 42058188Smsmith case MLX_IFTYPE_2: 42158188Smsmith /* These controllers don't report the firmware version in the ENQUIRY2 response */ 42258188Smsmith if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) { 42358188Smsmith device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n"); 42458188Smsmith return(ENXIO); 42558188Smsmith } 42658188Smsmith sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor; 42758188Smsmith 42858188Smsmith /* XXX require 2.42 or better (PCI) or 2.14 or better (EISA) */ 42958188Smsmith if (meo->me_fwminor < 42) { 43058188Smsmith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 43158188Smsmith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n"); 43258188Smsmith } 433126113Scperciva free(meo, M_DEVBUF); 43458188Smsmith break; 43551973Smsmith case MLX_IFTYPE_3: 43652225Smsmith /* XXX certify 3.52? */ 43754979Smsmith if (fwminor < 51) { 43852544Smsmith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 43952544Smsmith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n"); 44051973Smsmith } 44151973Smsmith break; 44252225Smsmith case MLX_IFTYPE_4: 44352225Smsmith /* XXX certify firmware versions? */ 44454979Smsmith if (fwminor < 6) { 44552544Smsmith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 44652544Smsmith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n"); 44752225Smsmith } 44852225Smsmith break; 44954419Smsmith case MLX_IFTYPE_5: 45054979Smsmith if (fwminor < 7) { 45154419Smsmith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 45254419Smsmith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n"); 45354419Smsmith } 45454419Smsmith break; 45551973Smsmith default: 45651973Smsmith return(ENXIO); /* should never happen */ 45751973Smsmith } 45851973Smsmith 45951973Smsmith /* 46060074Smsmith * Create the final scatter/gather mappings now that we have characterised the controller. 46151973Smsmith */ 46251973Smsmith error = mlx_sglist_map(sc); 46351973Smsmith if (error != 0) { 46460074Smsmith device_printf(sc->mlx_dev, "can't make final s/g list mapping\n"); 46551973Smsmith return(error); 46651973Smsmith } 46751973Smsmith 46851973Smsmith /* 46959136Smsmith * No user-requested background operation is in progress. 47051973Smsmith */ 47159136Smsmith sc->mlx_background = 0; 47259136Smsmith sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE; 47351973Smsmith 47451973Smsmith /* 47558188Smsmith * Create the control device. 47651973Smsmith */ 477191242Sed sc->mlx_dev_t = make_dev(&mlx_cdevsw, 0, UID_ROOT, GID_OPERATOR, 47858188Smsmith S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev)); 479191242Sed sc->mlx_dev_t->si_drv1 = sc; 48051973Smsmith 48151973Smsmith /* 48251973Smsmith * Start the timeout routine. 48351973Smsmith */ 48451973Smsmith sc->mlx_timeout = timeout(mlx_periodic, sc, hz); 48551973Smsmith 48658188Smsmith /* print a little information about the controller */ 48758188Smsmith mlx_describe_controller(sc); 48858188Smsmith 48951973Smsmith return(0); 49051973Smsmith} 49151973Smsmith 49251973Smsmith/******************************************************************************** 49351973Smsmith * Locate disk resources and attach children to them. 49451973Smsmith */ 49551973Smsmithvoid 49651973Smsmithmlx_startup(struct mlx_softc *sc) 49751973Smsmith{ 49851973Smsmith struct mlx_enq_sys_drive *mes; 49951973Smsmith struct mlx_sysdrive *dr; 50051973Smsmith int i, error; 50151973Smsmith 50258188Smsmith debug_called(1); 50351973Smsmith 50451973Smsmith /* 50551973Smsmith * Scan all the system drives and attach children for those that 50651973Smsmith * don't currently have them. 50751973Smsmith */ 50851973Smsmith mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL); 50951973Smsmith if (mes == NULL) { 51054979Smsmith device_printf(sc->mlx_dev, "error fetching drive status\n"); 51151973Smsmith return; 51251973Smsmith } 51351973Smsmith 51451973Smsmith /* iterate over drives returned */ 51551973Smsmith for (i = 0, dr = &sc->mlx_sysdrive[0]; 51651973Smsmith (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); 51751973Smsmith i++, dr++) { 51851973Smsmith /* are we already attached to this drive? */ 51951973Smsmith if (dr->ms_disk == 0) { 52051973Smsmith /* pick up drive information */ 52151973Smsmith dr->ms_size = mes[i].sd_size; 52252225Smsmith dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf; 52351973Smsmith dr->ms_state = mes[i].sd_state; 52451973Smsmith 52551973Smsmith /* generate geometry information */ 52651973Smsmith if (sc->mlx_geom == MLX_GEOM_128_32) { 52751973Smsmith dr->ms_heads = 128; 52851973Smsmith dr->ms_sectors = 32; 52951973Smsmith dr->ms_cylinders = dr->ms_size / (128 * 32); 53051973Smsmith } else { /* MLX_GEOM_255/63 */ 53151973Smsmith dr->ms_heads = 255; 53251973Smsmith dr->ms_sectors = 63; 53351973Smsmith dr->ms_cylinders = dr->ms_size / (255 * 63); 53451973Smsmith } 53554073Smdodd dr->ms_disk = device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1); 53651973Smsmith if (dr->ms_disk == 0) 53751973Smsmith device_printf(sc->mlx_dev, "device_add_child failed\n"); 53854073Smdodd device_set_ivars(dr->ms_disk, dr); 53951973Smsmith } 54051973Smsmith } 54151973Smsmith free(mes, M_DEVBUF); 54251973Smsmith if ((error = bus_generic_attach(sc->mlx_dev)) != 0) 54351973Smsmith device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error); 54451973Smsmith 54551973Smsmith /* mark controller back up */ 54651973Smsmith sc->mlx_state &= ~MLX_STATE_SHUTDOWN; 54751973Smsmith 54851973Smsmith /* enable interrupts */ 54951973Smsmith sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); 55051973Smsmith} 55151973Smsmith 55251973Smsmith/******************************************************************************** 55351973Smsmith * Disconnect from the controller completely, in preparation for unload. 55451973Smsmith */ 55551973Smsmithint 55651973Smsmithmlx_detach(device_t dev) 55751973Smsmith{ 55851973Smsmith struct mlx_softc *sc = device_get_softc(dev); 55954419Smsmith struct mlxd_softc *mlxd; 56054419Smsmith int i, s, error; 56151973Smsmith 56258188Smsmith debug_called(1); 56351973Smsmith 56454419Smsmith error = EBUSY; 56554419Smsmith s = splbio(); 56651973Smsmith if (sc->mlx_state & MLX_STATE_OPEN) 56754419Smsmith goto out; 56851973Smsmith 56954419Smsmith for (i = 0; i < MLX_MAXDRIVES; i++) { 57054419Smsmith if (sc->mlx_sysdrive[i].ms_disk != 0) { 57154419Smsmith mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk); 57254419Smsmith if (mlxd->mlxd_flags & MLXD_OPEN) { /* drive is mounted, abort detach */ 57354419Smsmith device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n"); 57454419Smsmith goto out; 57554419Smsmith } 57654419Smsmith } 57754419Smsmith } 57851973Smsmith if ((error = mlx_shutdown(dev))) 57954419Smsmith goto out; 58051973Smsmith 58151973Smsmith mlx_free(sc); 58251973Smsmith 58354419Smsmith error = 0; 58454419Smsmith out: 58554419Smsmith splx(s); 58654419Smsmith return(error); 58751973Smsmith} 58851973Smsmith 58951973Smsmith/******************************************************************************** 59051973Smsmith * Bring the controller down to a dormant state and detach all child devices. 59151973Smsmith * 59251973Smsmith * This function is called before detach, system shutdown, or before performing 59351973Smsmith * an operation which may add or delete system disks. (Call mlx_startup to 59451973Smsmith * resume normal operation.) 59551973Smsmith * 59659249Sphk * Note that we can assume that the bioq on the controller is empty, as we won't 59751973Smsmith * allow shutdown if any device is open. 59851973Smsmith */ 59951973Smsmithint 60051973Smsmithmlx_shutdown(device_t dev) 60151973Smsmith{ 60251973Smsmith struct mlx_softc *sc = device_get_softc(dev); 60351973Smsmith int i, s, error; 60451973Smsmith 60558188Smsmith debug_called(1); 60651973Smsmith 60751973Smsmith s = splbio(); 60851973Smsmith error = 0; 60951973Smsmith 61051973Smsmith sc->mlx_state |= MLX_STATE_SHUTDOWN; 61154419Smsmith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 61251973Smsmith 61351973Smsmith /* flush controller */ 61451973Smsmith device_printf(sc->mlx_dev, "flushing cache..."); 61551973Smsmith if (mlx_flush(sc)) { 61651973Smsmith printf("failed\n"); 61751973Smsmith } else { 61851973Smsmith printf("done\n"); 61951973Smsmith } 62051973Smsmith 62151973Smsmith /* delete all our child devices */ 62251973Smsmith for (i = 0; i < MLX_MAXDRIVES; i++) { 62351973Smsmith if (sc->mlx_sysdrive[i].ms_disk != 0) { 62451973Smsmith if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0) 62551973Smsmith goto out; 62651973Smsmith sc->mlx_sysdrive[i].ms_disk = 0; 62751973Smsmith } 62851973Smsmith } 62951973Smsmith 63051973Smsmith out: 63151973Smsmith splx(s); 63251973Smsmith return(error); 63351973Smsmith} 63451973Smsmith 63551973Smsmith/******************************************************************************** 63651973Smsmith * Bring the controller to a quiescent state, ready for system suspend. 63751973Smsmith */ 63851973Smsmithint 63951973Smsmithmlx_suspend(device_t dev) 64051973Smsmith{ 64151973Smsmith struct mlx_softc *sc = device_get_softc(dev); 64251973Smsmith int s; 64351973Smsmith 64458188Smsmith debug_called(1); 64551973Smsmith 64651973Smsmith s = splbio(); 64751973Smsmith sc->mlx_state |= MLX_STATE_SUSPEND; 64851973Smsmith 64951973Smsmith /* flush controller */ 65051973Smsmith device_printf(sc->mlx_dev, "flushing cache..."); 65151973Smsmith printf("%s\n", mlx_flush(sc) ? "failed" : "done"); 65251973Smsmith 65351973Smsmith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 65451973Smsmith splx(s); 65551973Smsmith 65651973Smsmith return(0); 65751973Smsmith} 65851973Smsmith 65951973Smsmith/******************************************************************************** 66051973Smsmith * Bring the controller back to a state ready for operation. 66151973Smsmith */ 66251973Smsmithint 66351973Smsmithmlx_resume(device_t dev) 66451973Smsmith{ 66551973Smsmith struct mlx_softc *sc = device_get_softc(dev); 66651973Smsmith 66758188Smsmith debug_called(1); 66851973Smsmith 66951973Smsmith sc->mlx_state &= ~MLX_STATE_SUSPEND; 67051973Smsmith sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); 67151973Smsmith 67251973Smsmith return(0); 67351973Smsmith} 67451973Smsmith 67551973Smsmith/******************************************************************************* 67651973Smsmith * Take an interrupt, or be poked by other code to look for interrupt-worthy 67751973Smsmith * status. 67851973Smsmith */ 67951973Smsmithvoid 68051973Smsmithmlx_intr(void *arg) 68151973Smsmith{ 68251973Smsmith struct mlx_softc *sc = (struct mlx_softc *)arg; 68351973Smsmith 68458188Smsmith debug_called(1); 68551973Smsmith 68654419Smsmith /* collect finished commands, queue anything waiting */ 68754419Smsmith mlx_done(sc); 68851973Smsmith}; 68951973Smsmith 69051973Smsmith/******************************************************************************* 69151973Smsmith * Receive a buf structure from a child device and queue it on a particular 69251973Smsmith * disk resource, then poke the disk resource to start as much work as it can. 69351973Smsmith */ 69451973Smsmithint 69578752Smsmithmlx_submit_buf(struct mlx_softc *sc, mlx_bio *bp) 69651973Smsmith{ 69752544Smsmith int s; 69852544Smsmith 69958188Smsmith debug_called(1); 70051973Smsmith 70152544Smsmith s = splbio(); 70278752Smsmith MLX_BIO_QINSERT(sc->mlx_bioq, bp); 70351973Smsmith sc->mlx_waitbufs++; 70452544Smsmith splx(s); 70551973Smsmith mlx_startio(sc); 70651973Smsmith return(0); 70751973Smsmith} 70851973Smsmith 70951973Smsmith/******************************************************************************** 71051973Smsmith * Accept an open operation on the control device. 71151973Smsmith */ 71251973Smsmithint 713130585Sphkmlx_open(struct cdev *dev, int flags, int fmt, struct thread *td) 71451973Smsmith{ 715191242Sed struct mlx_softc *sc = dev->si_drv1; 71651973Smsmith 71751973Smsmith sc->mlx_state |= MLX_STATE_OPEN; 71851973Smsmith return(0); 71951973Smsmith} 72051973Smsmith 72151973Smsmith/******************************************************************************** 72251973Smsmith * Accept the last close on the control device. 72351973Smsmith */ 72451973Smsmithint 725130585Sphkmlx_close(struct cdev *dev, int flags, int fmt, struct thread *td) 72651973Smsmith{ 727191242Sed struct mlx_softc *sc = dev->si_drv1; 72851973Smsmith 72951973Smsmith sc->mlx_state &= ~MLX_STATE_OPEN; 73051973Smsmith return (0); 73151973Smsmith} 73251973Smsmith 73351973Smsmith/******************************************************************************** 73451973Smsmith * Handle controller-specific control operations. 73551973Smsmith */ 73651973Smsmithint 737130585Sphkmlx_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td) 73851973Smsmith{ 739191242Sed struct mlx_softc *sc = dev->si_drv1; 74059136Smsmith struct mlx_rebuild_request *rb = (struct mlx_rebuild_request *)addr; 74159136Smsmith struct mlx_rebuild_status *rs = (struct mlx_rebuild_status *)addr; 74259136Smsmith int *arg = (int *)addr; 74359136Smsmith struct mlx_pause *mp; 74459136Smsmith struct mlx_sysdrive *dr; 74559136Smsmith struct mlxd_softc *mlxd; 74659136Smsmith int i, error; 74751973Smsmith 74851973Smsmith switch(cmd) { 74951973Smsmith /* 75051973Smsmith * Enumerate connected system drives; returns the first system drive's 75151973Smsmith * unit number if *arg is -1, or the next unit after *arg if it's 75251973Smsmith * a valid unit on this controller. 75351973Smsmith */ 75451973Smsmith case MLX_NEXT_CHILD: 75551973Smsmith /* search system drives */ 75651973Smsmith for (i = 0; i < MLX_MAXDRIVES; i++) { 75751973Smsmith /* is this one attached? */ 75851973Smsmith if (sc->mlx_sysdrive[i].ms_disk != 0) { 75951973Smsmith /* looking for the next one we come across? */ 76051973Smsmith if (*arg == -1) { 761182965Ssepotvin *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk); 76251973Smsmith return(0); 76351973Smsmith } 76451973Smsmith /* we want the one after this one */ 76551973Smsmith if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) 76651973Smsmith *arg = -1; 76751973Smsmith } 76851973Smsmith } 76951973Smsmith return(ENOENT); 77051973Smsmith 77151973Smsmith /* 77251973Smsmith * Scan the controller to see whether new drives have appeared. 77351973Smsmith */ 77451973Smsmith case MLX_RESCAN_DRIVES: 77551973Smsmith mlx_startup(sc); 77651973Smsmith return(0); 77751973Smsmith 77851973Smsmith /* 77951973Smsmith * Disconnect from the specified drive; it may be about to go 78051973Smsmith * away. 78151973Smsmith */ 78251973Smsmith case MLX_DETACH_DRIVE: /* detach one drive */ 78351973Smsmith 78451973Smsmith if (((dr = mlx_findunit(sc, *arg)) == NULL) || 78551973Smsmith ((mlxd = device_get_softc(dr->ms_disk)) == NULL)) 78651973Smsmith return(ENOENT); 78751973Smsmith 78851973Smsmith device_printf(dr->ms_disk, "detaching..."); 78951973Smsmith error = 0; 79051973Smsmith if (mlxd->mlxd_flags & MLXD_OPEN) { 79151973Smsmith error = EBUSY; 79251973Smsmith goto detach_out; 79351973Smsmith } 79451973Smsmith 79551973Smsmith /* flush controller */ 79651973Smsmith if (mlx_flush(sc)) { 79751973Smsmith error = EBUSY; 79851973Smsmith goto detach_out; 79951973Smsmith } 80051973Smsmith 80151973Smsmith /* nuke drive */ 80251973Smsmith if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0) 80351973Smsmith goto detach_out; 80451973Smsmith dr->ms_disk = 0; 80551973Smsmith 80651973Smsmith detach_out: 80751973Smsmith if (error) { 80851973Smsmith printf("failed\n"); 80951973Smsmith } else { 81051973Smsmith printf("done\n"); 81151973Smsmith } 81251973Smsmith return(error); 81351973Smsmith 81451973Smsmith /* 81551973Smsmith * Pause one or more SCSI channels for a period of time, to assist 81651973Smsmith * in the process of hot-swapping devices. 81751973Smsmith * 81851973Smsmith * Note that at least the 3.51 firmware on the DAC960PL doesn't seem 81951973Smsmith * to do this right. 82051973Smsmith */ 82151973Smsmith case MLX_PAUSE_CHANNEL: /* schedule a channel pause */ 82251973Smsmith /* Does this command work on this firmware? */ 82351973Smsmith if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS)) 82451973Smsmith return(EOPNOTSUPP); 82551973Smsmith 82651973Smsmith mp = (struct mlx_pause *)addr; 82751973Smsmith if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) { 82851973Smsmith /* cancel a pending pause operation */ 82951973Smsmith sc->mlx_pause.mp_which = 0; 83051973Smsmith } else { 83151973Smsmith /* fix for legal channels */ 83254979Smsmith mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1); 83351973Smsmith /* check time values */ 83451973Smsmith if ((mp->mp_when < 0) || (mp->mp_when > 3600)) 83551973Smsmith return(EINVAL); 83651973Smsmith if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30))) 83751973Smsmith return(EINVAL); 83851973Smsmith 83951973Smsmith /* check for a pause currently running */ 84051973Smsmith if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) 84151973Smsmith return(EBUSY); 84251973Smsmith 84351973Smsmith /* looks ok, go with it */ 84451973Smsmith sc->mlx_pause.mp_which = mp->mp_which; 84551973Smsmith sc->mlx_pause.mp_when = time_second + mp->mp_when; 84651973Smsmith sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong; 84751973Smsmith } 84851973Smsmith return(0); 84951973Smsmith 85051973Smsmith /* 85151973Smsmith * Accept a command passthrough-style. 85251973Smsmith */ 85351973Smsmith case MLX_COMMAND: 85451973Smsmith return(mlx_user_command(sc, (struct mlx_usercommand *)addr)); 85551973Smsmith 85659136Smsmith /* 85759136Smsmith * Start a rebuild on a given SCSI disk 85859136Smsmith */ 85959136Smsmith case MLX_REBUILDASYNC: 86059136Smsmith if (sc->mlx_background != 0) { 86159136Smsmith rb->rr_status = 0x0106; 86259136Smsmith return(EBUSY); 86359136Smsmith } 86459136Smsmith rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target); 86559136Smsmith switch (rb->rr_status) { 86659136Smsmith case 0: 86759136Smsmith error = 0; 86859136Smsmith break; 86959136Smsmith case 0x10000: 87059136Smsmith error = ENOMEM; /* couldn't set up the command */ 87159136Smsmith break; 87259136Smsmith case 0x0002: 87359136Smsmith error = EBUSY; 87459136Smsmith break; 87559136Smsmith case 0x0104: 87659136Smsmith error = EIO; 87759136Smsmith break; 87859136Smsmith case 0x0105: 87959136Smsmith error = ERANGE; 88059136Smsmith break; 88159136Smsmith case 0x0106: 88259136Smsmith error = EBUSY; 88359136Smsmith break; 88459136Smsmith default: 88559136Smsmith error = EINVAL; 88659136Smsmith break; 88759136Smsmith } 88859136Smsmith if (error == 0) 88959136Smsmith sc->mlx_background = MLX_BACKGROUND_REBUILD; 89059136Smsmith return(error); 89159136Smsmith 89259136Smsmith /* 89359136Smsmith * Get the status of the current rebuild or consistency check. 89459136Smsmith */ 89559136Smsmith case MLX_REBUILDSTAT: 89659136Smsmith *rs = sc->mlx_rebuildstat; 89759136Smsmith return(0); 89859136Smsmith 89959136Smsmith /* 90059136Smsmith * Return the per-controller system drive number matching the 90159136Smsmith * disk device number in (arg), if it happens to belong to us. 90259136Smsmith */ 90359136Smsmith case MLX_GET_SYSDRIVE: 90459136Smsmith error = ENOENT; 90559136Smsmith mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg); 90659136Smsmith if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) && 90759136Smsmith (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) { 90859136Smsmith error = 0; 90959136Smsmith *arg = mlxd->mlxd_drive - sc->mlx_sysdrive; 91059136Smsmith } 91159136Smsmith return(error); 91259136Smsmith 91351973Smsmith default: 91451973Smsmith return(ENOTTY); 91551973Smsmith } 91651973Smsmith} 91751973Smsmith 91851973Smsmith/******************************************************************************** 91951973Smsmith * Handle operations requested by a System Drive connected to this controller. 92051973Smsmith */ 92151973Smsmithint 92251973Smsmithmlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd, 92383366Sjulian caddr_t addr, int32_t flag, struct thread *td) 92451973Smsmith{ 92551973Smsmith int *arg = (int *)addr; 92659136Smsmith int error, result; 92751973Smsmith 92851973Smsmith switch(cmd) { 92951973Smsmith /* 93051973Smsmith * Return the current status of this drive. 93151973Smsmith */ 93251973Smsmith case MLXD_STATUS: 93351973Smsmith *arg = drive->ms_state; 93451973Smsmith return(0); 93551973Smsmith 93651973Smsmith /* 93759136Smsmith * Start a background consistency check on this drive. 93851973Smsmith */ 93959136Smsmith case MLXD_CHECKASYNC: /* start a background consistency check */ 94059136Smsmith if (sc->mlx_background != 0) { 94159136Smsmith *arg = 0x0106; 94251973Smsmith return(EBUSY); 94359136Smsmith } 94459136Smsmith result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]); 94559136Smsmith switch (result) { 94651973Smsmith case 0: 94751973Smsmith error = 0; 94851973Smsmith break; 94951973Smsmith case 0x10000: 95051973Smsmith error = ENOMEM; /* couldn't set up the command */ 95151973Smsmith break; 95259136Smsmith case 0x0002: 95351973Smsmith error = EIO; 95451973Smsmith break; 95551973Smsmith case 0x0105: 95651973Smsmith error = ERANGE; 95751973Smsmith break; 95859136Smsmith case 0x0106: 95959136Smsmith error = EBUSY; 96059136Smsmith break; 96151973Smsmith default: 96251973Smsmith error = EINVAL; 96351973Smsmith break; 96451973Smsmith } 96559136Smsmith if (error == 0) 96659136Smsmith sc->mlx_background = MLX_BACKGROUND_CHECK; 96759136Smsmith *arg = result; 96851973Smsmith return(error); 96951973Smsmith 97051973Smsmith } 97151973Smsmith return(ENOIOCTL); 97251973Smsmith} 97351973Smsmith 97451973Smsmith 97551973Smsmith/******************************************************************************** 97651973Smsmith ******************************************************************************** 97751973Smsmith Status Monitoring 97851973Smsmith ******************************************************************************** 97951973Smsmith ********************************************************************************/ 98051973Smsmith 98151973Smsmith/******************************************************************************** 98251973Smsmith * Fire off commands to periodically check the status of connected drives. 98351973Smsmith */ 98451973Smsmithstatic void 98551973Smsmithmlx_periodic(void *data) 98651973Smsmith{ 98751973Smsmith struct mlx_softc *sc = (struct mlx_softc *)data; 98851973Smsmith 98958188Smsmith debug_called(1); 99051973Smsmith 99151973Smsmith /* 99251973Smsmith * Run a bus pause? 99351973Smsmith */ 99451973Smsmith if ((sc->mlx_pause.mp_which != 0) && 99551973Smsmith (sc->mlx_pause.mp_when > 0) && 99651973Smsmith (time_second >= sc->mlx_pause.mp_when)){ 99751973Smsmith 99851973Smsmith mlx_pause_action(sc); /* pause is running */ 99951973Smsmith sc->mlx_pause.mp_when = 0; 100051973Smsmith sysbeep(500, hz); 100151973Smsmith 100251973Smsmith /* 100351973Smsmith * Bus pause still running? 100451973Smsmith */ 100551973Smsmith } else if ((sc->mlx_pause.mp_which != 0) && 100651973Smsmith (sc->mlx_pause.mp_when == 0)) { 100751973Smsmith 100851973Smsmith /* time to stop bus pause? */ 100951973Smsmith if (time_second >= sc->mlx_pause.mp_howlong) { 101051973Smsmith mlx_pause_action(sc); 101151973Smsmith sc->mlx_pause.mp_which = 0; /* pause is complete */ 101251973Smsmith sysbeep(500, hz); 101351973Smsmith } else { 101451973Smsmith sysbeep((time_second % 5) * 100 + 500, hz/8); 101551973Smsmith } 101651973Smsmith 101751973Smsmith /* 101851973Smsmith * Run normal periodic activities? 101951973Smsmith */ 102054419Smsmith } else if (time_second > (sc->mlx_lastpoll + 10)) { 102154419Smsmith sc->mlx_lastpoll = time_second; 102251973Smsmith 102354419Smsmith /* 102454419Smsmith * Check controller status. 102554419Smsmith * 102654419Smsmith * XXX Note that this may not actually launch a command in situations of high load. 102754419Smsmith */ 102858188Smsmith mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY, 102958188Smsmith imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry); 103051973Smsmith 103154419Smsmith /* 103254419Smsmith * Check system drive status. 103354419Smsmith * 103454419Smsmith * XXX This might be better left to event-driven detection, eg. I/O to an offline 103554419Smsmith * drive will detect it's offline, rebuilds etc. should detect the drive is back 103654419Smsmith * online. 103754419Smsmith */ 103854419Smsmith mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES, 103951973Smsmith mlx_periodic_enquiry); 104054419Smsmith 104151973Smsmith } 104251973Smsmith 104359136Smsmith /* get drive rebuild/check status */ 104459136Smsmith /* XXX should check sc->mlx_background if this is only valid while in progress */ 104559136Smsmith mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild); 104659136Smsmith 104754419Smsmith /* deal with possibly-missed interrupts and timed-out commands */ 104854419Smsmith mlx_done(sc); 104951973Smsmith 105051973Smsmith /* reschedule another poll next second or so */ 105151973Smsmith sc->mlx_timeout = timeout(mlx_periodic, sc, hz); 105251973Smsmith} 105351973Smsmith 105451973Smsmith/******************************************************************************** 105551973Smsmith * Handle the result of an ENQUIRY command instigated by periodic status polling. 105651973Smsmith */ 105751973Smsmithstatic void 105851973Smsmithmlx_periodic_enquiry(struct mlx_command *mc) 105951973Smsmith{ 106051973Smsmith struct mlx_softc *sc = mc->mc_sc; 106151973Smsmith 106258188Smsmith debug_called(1); 106351973Smsmith 106451973Smsmith /* Command completed OK? */ 106551973Smsmith if (mc->mc_status != 0) { 106658188Smsmith device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc)); 106751973Smsmith goto out; 106851973Smsmith } 106951973Smsmith 107051973Smsmith /* respond to command */ 107151973Smsmith switch(mc->mc_mailbox[0]) { 107251973Smsmith /* 107358188Smsmith * This is currently a bit fruitless, as we don't know how to extract the eventlog 107458188Smsmith * pointer yet. 107558188Smsmith */ 107658188Smsmith case MLX_CMD_ENQUIRY_OLD: 107758188Smsmith { 107858188Smsmith struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data; 107958188Smsmith struct mlx_enquiry_old *meo = (struct mlx_enquiry_old *)mc->mc_data; 108058188Smsmith int i; 108158188Smsmith 108258188Smsmith /* convert data in-place to new format */ 108358188Smsmith for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) { 108458188Smsmith me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan; 108558188Smsmith me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ; 108658188Smsmith } 108758188Smsmith me->me_misc_flags = 0; 108858188Smsmith me->me_rebuild_count = meo->me_rebuild_count; 108958188Smsmith me->me_dead_count = meo->me_dead_count; 109058188Smsmith me->me_critical_sd_count = meo->me_critical_sd_count; 109158188Smsmith me->me_event_log_seq_num = 0; 109258188Smsmith me->me_offline_sd_count = meo->me_offline_sd_count; 109358188Smsmith me->me_max_commands = meo->me_max_commands; 109458188Smsmith me->me_rebuild_flag = meo->me_rebuild_flag; 109558188Smsmith me->me_fwmajor = meo->me_fwmajor; 109658188Smsmith me->me_fwminor = meo->me_fwminor; 109758188Smsmith me->me_status_flags = meo->me_status_flags; 109858188Smsmith me->me_flash_age = meo->me_flash_age; 109958188Smsmith for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) { 110058188Smsmith if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) { 110158188Smsmith me->me_drvsize[i] = 0; /* drive beyond supported range */ 110258188Smsmith } else { 110358188Smsmith me->me_drvsize[i] = meo->me_drvsize[i]; 110458188Smsmith } 110558188Smsmith } 110658188Smsmith me->me_num_sys_drvs = meo->me_num_sys_drvs; 110758188Smsmith } 110858188Smsmith /* FALLTHROUGH */ 110958188Smsmith 111058188Smsmith /* 111151973Smsmith * Generic controller status update. We could do more with this than just 111251973Smsmith * checking the event log. 111351973Smsmith */ 111451973Smsmith case MLX_CMD_ENQUIRY: 111551973Smsmith { 111651973Smsmith struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data; 111751973Smsmith 111859136Smsmith if (sc->mlx_currevent == -1) { 111954979Smsmith /* initialise our view of the event log */ 112054979Smsmith sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num; 112159447Smsmith } else if ((me->me_event_log_seq_num != sc->mlx_lastevent) && !(sc->mlx_flags & MLX_EVENTLOG_BUSY)) { 112251973Smsmith /* record where current events are up to */ 112351973Smsmith sc->mlx_currevent = me->me_event_log_seq_num; 112458188Smsmith debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent); 112551973Smsmith 112659447Smsmith /* mark the event log as busy */ 112759447Smsmith atomic_set_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY); 112859447Smsmith 112954979Smsmith /* drain new eventlog entries */ 113051973Smsmith mlx_periodic_eventlog_poll(sc); 113151973Smsmith } 113251973Smsmith break; 113351973Smsmith } 113451973Smsmith case MLX_CMD_ENQSYSDRIVE: 113551973Smsmith { 113651973Smsmith struct mlx_enq_sys_drive *mes = (struct mlx_enq_sys_drive *)mc->mc_data; 113751973Smsmith struct mlx_sysdrive *dr; 113851973Smsmith int i; 113951973Smsmith 114051973Smsmith for (i = 0, dr = &sc->mlx_sysdrive[0]; 114151973Smsmith (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); 114251973Smsmith i++) { 114351973Smsmith 114459136Smsmith /* has state been changed by controller? */ 114559136Smsmith if (dr->ms_state != mes[i].sd_state) { 114659136Smsmith switch(mes[i].sd_state) { 114759136Smsmith case MLX_SYSD_OFFLINE: 114859136Smsmith device_printf(dr->ms_disk, "drive offline\n"); 114959136Smsmith break; 115059136Smsmith case MLX_SYSD_ONLINE: 115159136Smsmith device_printf(dr->ms_disk, "drive online\n"); 115259136Smsmith break; 115359136Smsmith case MLX_SYSD_CRITICAL: 115459136Smsmith device_printf(dr->ms_disk, "drive critical\n"); 115559136Smsmith break; 115651973Smsmith } 115759136Smsmith /* save new state */ 115859136Smsmith dr->ms_state = mes[i].sd_state; 115951973Smsmith } 116051973Smsmith } 116151973Smsmith break; 116251973Smsmith } 116351973Smsmith default: 116487599Sobrien device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __func__, mc->mc_mailbox[0]); 116551973Smsmith break; 116651973Smsmith } 116751973Smsmith 116851973Smsmith out: 116951973Smsmith free(mc->mc_data, M_DEVBUF); 117051973Smsmith mlx_releasecmd(mc); 117151973Smsmith} 117251973Smsmith 1173119665Sscottlstatic void 1174119665Sscottlmlx_eventlog_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 1175119665Sscottl{ 1176119665Sscottl struct mlx_command *mc; 1177119665Sscottl 1178119665Sscottl mc = (struct mlx_command *)arg; 1179119665Sscottl mlx_setup_dmamap(mc, segs, nsegments, error); 1180119665Sscottl 1181119665Sscottl /* build the command to get one entry */ 1182119665Sscottl mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, 1183119665Sscottl mc->mc_sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0); 1184119665Sscottl mc->mc_complete = mlx_periodic_eventlog_respond; 1185119665Sscottl mc->mc_private = mc; 1186119665Sscottl 1187119665Sscottl /* start the command */ 1188119665Sscottl if (mlx_start(mc) != 0) { 1189119665Sscottl mlx_releasecmd(mc); 1190119665Sscottl free(mc->mc_data, M_DEVBUF); 1191119665Sscottl mc->mc_data = NULL; 1192119665Sscottl } 1193119665Sscottl 1194119665Sscottl} 1195119665Sscottl 119651973Smsmith/******************************************************************************** 119751973Smsmith * Instigate a poll for one event log message on (sc). 119851973Smsmith * We only poll for one message at a time, to keep our command usage down. 119951973Smsmith */ 120051973Smsmithstatic void 120151973Smsmithmlx_periodic_eventlog_poll(struct mlx_softc *sc) 120251973Smsmith{ 120351973Smsmith struct mlx_command *mc; 120451973Smsmith void *result = NULL; 1205119665Sscottl int error = 0; 120651973Smsmith 120758188Smsmith debug_called(1); 120851973Smsmith 120951973Smsmith /* get ourselves a command buffer */ 121051973Smsmith error = 1; 121151973Smsmith if ((mc = mlx_alloccmd(sc)) == NULL) 121251973Smsmith goto out; 1213119665Sscottl 121451973Smsmith /* allocate the response structure */ 1215119665Sscottl if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF, 1216119665Sscottl M_NOWAIT)) == NULL) 121751973Smsmith goto out; 1218119665Sscottl 121951973Smsmith /* get a command slot */ 122051973Smsmith if (mlx_getslot(mc)) 122151973Smsmith goto out; 122251973Smsmith 122351973Smsmith /* map the command so the controller can see it */ 122451973Smsmith mc->mc_data = result; 122552276Smsmith mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024; 1226119665Sscottl error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, 1227119665Sscottl mc->mc_length, mlx_eventlog_cb, mc, BUS_DMA_NOWAIT); 122851973Smsmith 122951973Smsmith out: 123052276Smsmith if (error != 0) { 123152276Smsmith if (mc != NULL) 123252276Smsmith mlx_releasecmd(mc); 1233119665Sscottl if ((result != NULL) && (mc->mc_data != NULL)) 123452276Smsmith free(result, M_DEVBUF); 123551973Smsmith } 123651973Smsmith} 123751973Smsmith 123851973Smsmith/******************************************************************************** 123951973Smsmith * Handle the result of polling for a log message, generate diagnostic output. 124051973Smsmith * If this wasn't the last message waiting for us, we'll go collect another. 124151973Smsmith */ 124251973Smsmithstatic char *mlx_sense_messages[] = { 124351973Smsmith "because write recovery failed", 124451973Smsmith "because of SCSI bus reset failure", 124551973Smsmith "because of double check condition", 124651973Smsmith "because it was removed", 124751973Smsmith "because of gross error on SCSI chip", 124851973Smsmith "because of bad tag returned from drive", 124951973Smsmith "because of timeout on SCSI command", 125051973Smsmith "because of reset SCSI command issued from system", 125151973Smsmith "because busy or parity error count exceeded limit", 125251973Smsmith "because of 'kill drive' command from system", 125351973Smsmith "because of selection timeout", 125451973Smsmith "due to SCSI phase sequence error", 125551973Smsmith "due to unknown status" 125651973Smsmith}; 125751973Smsmith 125851973Smsmithstatic void 125951973Smsmithmlx_periodic_eventlog_respond(struct mlx_command *mc) 126051973Smsmith{ 126151973Smsmith struct mlx_softc *sc = mc->mc_sc; 126251973Smsmith struct mlx_eventlog_entry *el = (struct mlx_eventlog_entry *)mc->mc_data; 126351973Smsmith char *reason; 126451973Smsmith 126558188Smsmith debug_called(1); 126651973Smsmith 126754419Smsmith sc->mlx_lastevent++; /* next message... */ 126851973Smsmith if (mc->mc_status == 0) { 126951973Smsmith 127051973Smsmith /* handle event log message */ 127151973Smsmith switch(el->el_type) { 127251973Smsmith /* 127351973Smsmith * This is the only sort of message we understand at the moment. 127451973Smsmith * The tests here are probably incomplete. 127551973Smsmith */ 127651973Smsmith case MLX_LOGMSG_SENSE: /* sense data */ 127751973Smsmith /* Mylex vendor-specific message indicating a drive was killed? */ 127851973Smsmith if ((el->el_sensekey == 9) && 127951973Smsmith (el->el_asc == 0x80)) { 128051973Smsmith if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) { 128151973Smsmith reason = mlx_sense_messages[el->el_asq]; 128251973Smsmith } else { 128351973Smsmith reason = "for unknown reason"; 128451973Smsmith } 128551973Smsmith device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n", 128651973Smsmith el->el_channel, el->el_target, reason); 128751973Smsmith } 128851973Smsmith /* SCSI drive was reset? */ 128951973Smsmith if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) { 129051973Smsmith device_printf(sc->mlx_dev, "physical drive %d:%d reset\n", 129151973Smsmith el->el_channel, el->el_target); 129251973Smsmith } 129351973Smsmith /* SCSI drive error? */ 129451973Smsmith if (!((el->el_sensekey == 0) || 129551973Smsmith ((el->el_sensekey == 2) && 129651973Smsmith (el->el_asc == 0x04) && 129751973Smsmith ((el->el_asq == 0x01) || 129851973Smsmith (el->el_asq == 0x02))))) { 129951973Smsmith device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n", 130051973Smsmith el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq); 130151973Smsmith device_printf(sc->mlx_dev, " info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":"); 130251973Smsmith } 130351973Smsmith break; 130451973Smsmith 130551973Smsmith default: 130651973Smsmith device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type); 130751973Smsmith break; 130851973Smsmith } 130951973Smsmith } else { 131051973Smsmith device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc)); 131159447Smsmith /* give up on all the outstanding messages, as we may have come unsynched */ 131259447Smsmith sc->mlx_lastevent = sc->mlx_currevent; 131351973Smsmith } 131451973Smsmith 131551973Smsmith /* dispose of command and data */ 131651973Smsmith free(mc->mc_data, M_DEVBUF); 131751973Smsmith mlx_releasecmd(mc); 131851973Smsmith 131951973Smsmith /* is there another message to obtain? */ 132059447Smsmith if (sc->mlx_lastevent != sc->mlx_currevent) { 132151973Smsmith mlx_periodic_eventlog_poll(sc); 132259447Smsmith } else { 132359447Smsmith /* clear log-busy status */ 132459447Smsmith atomic_clear_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY); 132559447Smsmith } 132651973Smsmith} 132751973Smsmith 132851973Smsmith/******************************************************************************** 132959136Smsmith * Handle check/rebuild operations in progress. 133051973Smsmith */ 133151973Smsmithstatic void 133251973Smsmithmlx_periodic_rebuild(struct mlx_command *mc) 133351973Smsmith{ 133451973Smsmith struct mlx_softc *sc = mc->mc_sc; 133559136Smsmith struct mlx_rebuild_status *mr = (struct mlx_rebuild_status *)mc->mc_data; 133651973Smsmith 133751973Smsmith switch(mc->mc_status) { 133859136Smsmith case 0: /* operation running, update stats */ 133959136Smsmith sc->mlx_rebuildstat = *mr; 134059136Smsmith 134159136Smsmith /* spontaneous rebuild/check? */ 134259136Smsmith if (sc->mlx_background == 0) { 134359136Smsmith sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS; 134459136Smsmith device_printf(sc->mlx_dev, "background check/rebuild operation started\n"); 134559136Smsmith } 134651973Smsmith break; 134751973Smsmith 134859136Smsmith case 0x0105: /* nothing running, finalise stats and report */ 134959136Smsmith switch(sc->mlx_background) { 135059136Smsmith case MLX_BACKGROUND_CHECK: 135159136Smsmith device_printf(sc->mlx_dev, "consistency check completed\n"); /* XXX print drive? */ 135259136Smsmith break; 135359136Smsmith case MLX_BACKGROUND_REBUILD: 135459136Smsmith device_printf(sc->mlx_dev, "drive rebuild completed\n"); /* XXX print channel/target? */ 135559136Smsmith break; 135659136Smsmith case MLX_BACKGROUND_SPONTANEOUS: 135759136Smsmith default: 135859136Smsmith /* if we have previously been non-idle, report the transition */ 135959136Smsmith if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) { 136059136Smsmith device_printf(sc->mlx_dev, "background check/rebuild operation completed\n"); 136159136Smsmith } 136251973Smsmith } 136359136Smsmith sc->mlx_background = 0; 136459136Smsmith sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE; 136551973Smsmith break; 136651973Smsmith } 136751973Smsmith free(mc->mc_data, M_DEVBUF); 136851973Smsmith mlx_releasecmd(mc); 136951973Smsmith} 137051973Smsmith 137151973Smsmith/******************************************************************************** 137251973Smsmith ******************************************************************************** 137351973Smsmith Channel Pause 137451973Smsmith ******************************************************************************** 137551973Smsmith ********************************************************************************/ 137651973Smsmith 137751973Smsmith/******************************************************************************** 137851973Smsmith * It's time to perform a channel pause action for (sc), either start or stop 137951973Smsmith * the pause. 138051973Smsmith */ 138151973Smsmithstatic void 138251973Smsmithmlx_pause_action(struct mlx_softc *sc) 138351973Smsmith{ 138451973Smsmith struct mlx_command *mc; 138551973Smsmith int failsafe, i, command; 138651973Smsmith 138751973Smsmith /* What are we doing here? */ 138851973Smsmith if (sc->mlx_pause.mp_when == 0) { 138951973Smsmith command = MLX_CMD_STARTCHANNEL; 139051973Smsmith failsafe = 0; 139151973Smsmith 139251973Smsmith } else { 139351973Smsmith command = MLX_CMD_STOPCHANNEL; 139451973Smsmith 139551973Smsmith /* 139651973Smsmith * Channels will always start again after the failsafe period, 139751973Smsmith * which is specified in multiples of 30 seconds. 139851973Smsmith * This constrains us to a maximum pause of 450 seconds. 139951973Smsmith */ 140051973Smsmith failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30; 140151973Smsmith if (failsafe > 0xf) { 140251973Smsmith failsafe = 0xf; 140351973Smsmith sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5; 140451973Smsmith } 140551973Smsmith } 140651973Smsmith 140751973Smsmith /* build commands for every channel requested */ 140854979Smsmith for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) { 140951973Smsmith if ((1 << i) & sc->mlx_pause.mp_which) { 141051973Smsmith 141151973Smsmith /* get ourselves a command buffer */ 141251973Smsmith if ((mc = mlx_alloccmd(sc)) == NULL) 141351973Smsmith goto fail; 141451973Smsmith /* get a command slot */ 141551973Smsmith mc->mc_flags |= MLX_CMD_PRIORITY; 141651973Smsmith if (mlx_getslot(mc)) 141751973Smsmith goto fail; 141851973Smsmith 141951973Smsmith /* build the command */ 142051973Smsmith mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0); 142151973Smsmith mc->mc_complete = mlx_pause_done; 142251973Smsmith mc->mc_private = sc; /* XXX not needed */ 142351973Smsmith if (mlx_start(mc)) 142451973Smsmith goto fail; 142551973Smsmith /* command submitted OK */ 142651973Smsmith return; 142751973Smsmith 142851973Smsmith fail: 142951973Smsmith device_printf(sc->mlx_dev, "%s failed for channel %d\n", 143051973Smsmith command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i); 143151973Smsmith if (mc != NULL) 143251973Smsmith mlx_releasecmd(mc); 143351973Smsmith } 143451973Smsmith } 143551973Smsmith} 143651973Smsmith 143751973Smsmithstatic void 143851973Smsmithmlx_pause_done(struct mlx_command *mc) 143951973Smsmith{ 144051973Smsmith struct mlx_softc *sc = mc->mc_sc; 144151973Smsmith int command = mc->mc_mailbox[0]; 144251973Smsmith int channel = mc->mc_mailbox[2] & 0xf; 144351973Smsmith 144451973Smsmith if (mc->mc_status != 0) { 144551973Smsmith device_printf(sc->mlx_dev, "%s command failed - %s\n", 144651973Smsmith command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc)); 144751973Smsmith } else if (command == MLX_CMD_STOPCHANNEL) { 144851973Smsmith device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n", 144954515Speter channel, (long)(sc->mlx_pause.mp_howlong - time_second)); 145051973Smsmith } else { 145151973Smsmith device_printf(sc->mlx_dev, "channel %d resuming\n", channel); 145251973Smsmith } 145351973Smsmith mlx_releasecmd(mc); 145451973Smsmith} 145551973Smsmith 145651973Smsmith/******************************************************************************** 145751973Smsmith ******************************************************************************** 145851973Smsmith Command Submission 145951973Smsmith ******************************************************************************** 146051973Smsmith ********************************************************************************/ 146151973Smsmith 1462119665Sscottlstatic void 1463119665Sscottlmlx_enquire_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 1464119665Sscottl{ 1465119665Sscottl struct mlx_softc *sc; 1466119665Sscottl struct mlx_command *mc; 1467119665Sscottl 1468119665Sscottl mc = (struct mlx_command *)arg; 1469119665Sscottl if (error) 1470119665Sscottl return; 1471119665Sscottl 1472119665Sscottl mlx_setup_dmamap(mc, segs, nsegments, error); 1473119665Sscottl 1474119665Sscottl /* build an enquiry command */ 1475119665Sscottl sc = mc->mc_sc; 1476119665Sscottl mlx_make_type2(mc, mc->mc_command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0); 1477119665Sscottl 1478119665Sscottl /* do we want a completion callback? */ 1479119665Sscottl if (mc->mc_complete != NULL) { 1480119665Sscottl if ((error = mlx_start(mc)) != 0) 1481119665Sscottl return; 1482119665Sscottl } else { 1483119665Sscottl /* run the command in either polled or wait mode */ 1484119665Sscottl if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : 1485119665Sscottl mlx_poll_command(mc)) 1486119665Sscottl return; 1487119665Sscottl 1488119665Sscottl /* command completed OK? */ 1489119665Sscottl if (mc->mc_status != 0) { 1490119665Sscottl device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", 1491119665Sscottl mlx_diagnose_command(mc)); 1492119665Sscottl return; 1493119665Sscottl } 1494119665Sscottl } 1495119665Sscottl} 1496119665Sscottl 149751973Smsmith/******************************************************************************** 149851973Smsmith * Perform an Enquiry command using a type-3 command buffer and a return a single 149951973Smsmith * linear result buffer. If the completion function is specified, it will 150051973Smsmith * be called with the completed command (and the result response will not be 150151973Smsmith * valid until that point). Otherwise, the command will either be busy-waited 150251973Smsmith * for (interrupts not enabled), or slept for. 150351973Smsmith */ 150451973Smsmithstatic void * 150551973Smsmithmlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc)) 150651973Smsmith{ 150751973Smsmith struct mlx_command *mc; 150851973Smsmith void *result; 150951973Smsmith int error; 151051973Smsmith 151158188Smsmith debug_called(1); 151251973Smsmith 151351973Smsmith /* get ourselves a command buffer */ 151451973Smsmith error = 1; 151551973Smsmith result = NULL; 151651973Smsmith if ((mc = mlx_alloccmd(sc)) == NULL) 151751973Smsmith goto out; 151851973Smsmith /* allocate the response structure */ 151951973Smsmith if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL) 152051973Smsmith goto out; 152151973Smsmith /* get a command slot */ 152251973Smsmith mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT; 152351973Smsmith if (mlx_getslot(mc)) 152451973Smsmith goto out; 152551973Smsmith 152651973Smsmith /* map the command so the controller can see it */ 152751973Smsmith mc->mc_data = result; 152851973Smsmith mc->mc_length = bufsize; 1529119665Sscottl mc->mc_command = command; 153051973Smsmith 153151973Smsmith if (complete != NULL) { 153251973Smsmith mc->mc_complete = complete; 153351973Smsmith mc->mc_private = mc; 153451973Smsmith } 1535119665Sscottl 1536119665Sscottl error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, 1537119665Sscottl mc->mc_length, mlx_enquire_cb, mc, BUS_DMA_NOWAIT); 1538119665Sscottl 153951973Smsmith out: 154051973Smsmith /* we got a command, but nobody else will free it */ 1541144434Ssam if ((mc != NULL) && (mc->mc_complete == NULL)) 154251973Smsmith mlx_releasecmd(mc); 154352276Smsmith /* we got an error, and we allocated a result */ 1544126461Sscottl if ((error != 0) && (result != NULL)) { 1545126461Sscottl free(result, M_DEVBUF); 1546126463Sscottl result = NULL; 154751973Smsmith } 154851973Smsmith return(result); 154951973Smsmith} 155051973Smsmith 155151973Smsmith 155251973Smsmith/******************************************************************************** 155351973Smsmith * Perform a Flush command on the nominated controller. 155451973Smsmith * 155551973Smsmith * May be called with interrupts enabled or disabled; will not return until 155651973Smsmith * the flush operation completes or fails. 155751973Smsmith */ 155851973Smsmithstatic int 155951973Smsmithmlx_flush(struct mlx_softc *sc) 156051973Smsmith{ 156151973Smsmith struct mlx_command *mc; 156251973Smsmith int error; 156351973Smsmith 156458188Smsmith debug_called(1); 156551973Smsmith 156651973Smsmith /* get ourselves a command buffer */ 156751973Smsmith error = 1; 156851973Smsmith if ((mc = mlx_alloccmd(sc)) == NULL) 156951973Smsmith goto out; 157051973Smsmith /* get a command slot */ 157151973Smsmith if (mlx_getslot(mc)) 157251973Smsmith goto out; 157351973Smsmith 157451973Smsmith /* build a flush command */ 157551973Smsmith mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0); 157651973Smsmith 157754419Smsmith /* can't assume that interrupts are going to work here, so play it safe */ 157854419Smsmith if (mlx_poll_command(mc)) 157951973Smsmith goto out; 158051973Smsmith 158151973Smsmith /* command completed OK? */ 158251973Smsmith if (mc->mc_status != 0) { 158351973Smsmith device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc)); 158451973Smsmith goto out; 158551973Smsmith } 158651973Smsmith 158751973Smsmith error = 0; /* success */ 158851973Smsmith out: 158951973Smsmith if (mc != NULL) 159051973Smsmith mlx_releasecmd(mc); 159151973Smsmith return(error); 159251973Smsmith} 159351973Smsmith 159451973Smsmith/******************************************************************************** 159559136Smsmith * Start a background consistency check on (drive). 159651973Smsmith * 159751973Smsmith * May be called with interrupts enabled or disabled; will return as soon as the 159851973Smsmith * operation has started or been refused. 159951973Smsmith */ 160051973Smsmithstatic int 160159136Smsmithmlx_check(struct mlx_softc *sc, int drive) 160259136Smsmith{ 160359136Smsmith struct mlx_command *mc; 160459136Smsmith int error; 160559136Smsmith 160659136Smsmith debug_called(1); 160759136Smsmith 160859136Smsmith /* get ourselves a command buffer */ 160959136Smsmith error = 0x10000; 161059136Smsmith if ((mc = mlx_alloccmd(sc)) == NULL) 161159136Smsmith goto out; 161259136Smsmith /* get a command slot */ 161359136Smsmith if (mlx_getslot(mc)) 161459136Smsmith goto out; 161559136Smsmith 161659136Smsmith /* build a checkasync command, set the "fix it" flag */ 161759136Smsmith mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0); 161859136Smsmith 161959136Smsmith /* start the command and wait for it to be returned */ 162059136Smsmith if (mlx_wait_command(mc)) 162159136Smsmith goto out; 162259136Smsmith 162359136Smsmith /* command completed OK? */ 162459136Smsmith if (mc->mc_status != 0) { 162559136Smsmith device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc)); 162659136Smsmith } else { 162759136Smsmith device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started"); 162859136Smsmith } 162959136Smsmith error = mc->mc_status; 163059136Smsmith 163159136Smsmith out: 163259136Smsmith if (mc != NULL) 163359136Smsmith mlx_releasecmd(mc); 163459136Smsmith return(error); 163559136Smsmith} 163659136Smsmith 163759136Smsmith/******************************************************************************** 163859136Smsmith * Start a background rebuild of the physical drive at (channel),(target). 163959136Smsmith * 164059136Smsmith * May be called with interrupts enabled or disabled; will return as soon as the 164159136Smsmith * operation has started or been refused. 164259136Smsmith */ 164359136Smsmithstatic int 164451973Smsmithmlx_rebuild(struct mlx_softc *sc, int channel, int target) 164551973Smsmith{ 164651973Smsmith struct mlx_command *mc; 164751973Smsmith int error; 164851973Smsmith 164958188Smsmith debug_called(1); 165051973Smsmith 165151973Smsmith /* get ourselves a command buffer */ 165251973Smsmith error = 0x10000; 165351973Smsmith if ((mc = mlx_alloccmd(sc)) == NULL) 165451973Smsmith goto out; 165551973Smsmith /* get a command slot */ 165651973Smsmith if (mlx_getslot(mc)) 165751973Smsmith goto out; 165851973Smsmith 165959136Smsmith /* build a checkasync command, set the "fix it" flag */ 166051973Smsmith mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0); 166151973Smsmith 166259136Smsmith /* start the command and wait for it to be returned */ 166359136Smsmith if (mlx_wait_command(mc)) 166451973Smsmith goto out; 166551973Smsmith 166651973Smsmith /* command completed OK? */ 166751973Smsmith if (mc->mc_status != 0) { 166851973Smsmith device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc)); 166951973Smsmith } else { 167059136Smsmith device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target); 167151973Smsmith } 167251973Smsmith error = mc->mc_status; 167351973Smsmith 167451973Smsmith out: 167551973Smsmith if (mc != NULL) 167651973Smsmith mlx_releasecmd(mc); 167751973Smsmith return(error); 167851973Smsmith} 167951973Smsmith 168051973Smsmith/******************************************************************************** 168151973Smsmith * Run the command (mc) and return when it completes. 168251973Smsmith * 168351973Smsmith * Interrupts need to be enabled; returns nonzero on error. 168451973Smsmith */ 168551973Smsmithstatic int 168651973Smsmithmlx_wait_command(struct mlx_command *mc) 168751973Smsmith{ 168851973Smsmith struct mlx_softc *sc = mc->mc_sc; 168951973Smsmith int error, count; 169051973Smsmith 169158188Smsmith debug_called(1); 169251973Smsmith 169351973Smsmith mc->mc_complete = NULL; 169451973Smsmith mc->mc_private = mc; /* wake us when you're done */ 169551973Smsmith if ((error = mlx_start(mc)) != 0) 169651973Smsmith return(error); 169751973Smsmith 169851973Smsmith count = 0; 169951973Smsmith /* XXX better timeout? */ 170051973Smsmith while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) { 170151973Smsmith tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz); 170251973Smsmith } 170351973Smsmith 170451973Smsmith if (mc->mc_status != 0) { 170558188Smsmith device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc)); 170651973Smsmith return(EIO); 170751973Smsmith } 170851973Smsmith return(0); 170951973Smsmith} 171051973Smsmith 171151973Smsmith 171251973Smsmith/******************************************************************************** 171351973Smsmith * Start the command (mc) and busy-wait for it to complete. 171451973Smsmith * 171558188Smsmith * Should only be used when interrupts can't be relied upon. Returns 0 on 171651973Smsmith * success, nonzero on error. 171751973Smsmith * Successfully completed commands are dequeued. 171851973Smsmith */ 171951973Smsmithstatic int 172051973Smsmithmlx_poll_command(struct mlx_command *mc) 172151973Smsmith{ 172251973Smsmith struct mlx_softc *sc = mc->mc_sc; 172351973Smsmith int error, count, s; 172451973Smsmith 172558188Smsmith debug_called(1); 172651973Smsmith 172751973Smsmith mc->mc_complete = NULL; 172851973Smsmith mc->mc_private = NULL; /* we will poll for it */ 172951973Smsmith if ((error = mlx_start(mc)) != 0) 173051973Smsmith return(error); 173151973Smsmith 173251973Smsmith count = 0; 173351973Smsmith do { 173451973Smsmith /* poll for completion */ 173551973Smsmith mlx_done(mc->mc_sc); 173658188Smsmith 173758188Smsmith } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000)); 173851973Smsmith if (mc->mc_status != MLX_STATUS_BUSY) { 173951973Smsmith s = splbio(); 174052544Smsmith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 174151973Smsmith splx(s); 174251973Smsmith return(0); 174351973Smsmith } 174459136Smsmith device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc)); 174551973Smsmith return(EIO); 174651973Smsmith} 174751973Smsmith 1748119665Sscottlvoid 1749119665Sscottlmlx_startio_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 1750119665Sscottl{ 1751119665Sscottl struct mlx_command *mc; 1752119665Sscottl struct mlxd_softc *mlxd; 1753119665Sscottl struct mlx_softc *sc; 1754119665Sscottl mlx_bio *bp; 1755119665Sscottl int blkcount; 1756119665Sscottl int driveno; 1757119665Sscottl int cmd; 1758119665Sscottl 1759119665Sscottl mc = (struct mlx_command *)arg; 1760119665Sscottl mlx_setup_dmamap(mc, segs, nsegments, error); 1761119665Sscottl 1762119665Sscottl sc = mc->mc_sc; 1763119665Sscottl bp = mc->mc_private; 1764119665Sscottl 1765119665Sscottl if (MLX_BIO_IS_READ(bp)) { 1766119665Sscottl mc->mc_flags |= MLX_CMD_DATAIN; 1767119665Sscottl cmd = MLX_CMD_READSG; 1768119665Sscottl } else { 1769119665Sscottl mc->mc_flags |= MLX_CMD_DATAOUT; 1770119665Sscottl cmd = MLX_CMD_WRITESG; 1771119665Sscottl } 1772119665Sscottl 1773119665Sscottl /* build a suitable I/O command (assumes 512-byte rounded transfers) */ 1774119665Sscottl mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp); 1775119665Sscottl driveno = mlxd->mlxd_drive - sc->mlx_sysdrive; 1776119665Sscottl blkcount = (MLX_BIO_LENGTH(bp) + MLX_BLKSIZE - 1) / MLX_BLKSIZE; 1777119665Sscottl 1778119665Sscottl if ((MLX_BIO_LBA(bp) + blkcount) > sc->mlx_sysdrive[driveno].ms_size) 1779119665Sscottl device_printf(sc->mlx_dev, 1780119665Sscottl "I/O beyond end of unit (%lld,%d > %lu)\n", 1781119665Sscottl (long long)MLX_BIO_LBA(bp), blkcount, 1782119665Sscottl (u_long)sc->mlx_sysdrive[driveno].ms_size); 1783119665Sscottl 1784119665Sscottl /* 1785119665Sscottl * Build the I/O command. Note that the SG list type bits are set to zero, 1786119665Sscottl * denoting the format of SG list that we are using. 1787119665Sscottl */ 1788119665Sscottl if (sc->mlx_iftype == MLX_IFTYPE_2) { 1789119665Sscottl mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD : 1790119665Sscottl MLX_CMD_READSG_OLD, 1791119665Sscottl blkcount & 0xff, /* xfer length low byte */ 1792119665Sscottl MLX_BIO_LBA(bp), /* physical block number */ 1793119665Sscottl driveno, /* target drive number */ 1794119665Sscottl mc->mc_sgphys, /* location of SG list */ 1795119665Sscottl mc->mc_nsgent & 0x3f); /* size of SG list */ 1796119665Sscottl } else { 1797119665Sscottl mlx_make_type5(mc, cmd, 1798119665Sscottl blkcount & 0xff, /* xfer length low byte */ 1799119665Sscottl (driveno << 3) | ((blkcount >> 8) & 0x07), 1800119665Sscottl /* target+length high 3 bits */ 1801119665Sscottl MLX_BIO_LBA(bp), /* physical block number */ 1802119665Sscottl mc->mc_sgphys, /* location of SG list */ 1803119665Sscottl mc->mc_nsgent & 0x3f); /* size of SG list */ 1804119665Sscottl } 1805119665Sscottl 1806119665Sscottl /* try to give command to controller */ 1807119665Sscottl if (mlx_start(mc) != 0) { 1808119665Sscottl /* fail the command */ 1809119665Sscottl mc->mc_status = MLX_STATUS_WEDGED; 1810119665Sscottl mlx_completeio(mc); 1811119665Sscottl } 1812119665Sscottl} 1813119665Sscottl 181451973Smsmith/******************************************************************************** 181551973Smsmith * Pull as much work off the softc's work queue as possible and give it to the 181651973Smsmith * controller. Leave a couple of slots free for emergencies. 181751973Smsmith * 181851973Smsmith * Must be called at splbio or in an equivalent fashion that prevents 181959249Sphk * reentry or activity on the bioq. 182051973Smsmith */ 182151973Smsmithstatic void 182251973Smsmithmlx_startio(struct mlx_softc *sc) 182351973Smsmith{ 182451973Smsmith struct mlx_command *mc; 182578752Smsmith mlx_bio *bp; 182652544Smsmith int s; 1827119665Sscottl int error; 182851973Smsmith 182954419Smsmith /* avoid reentrancy */ 183054419Smsmith if (mlx_lock_tas(sc, MLX_LOCK_STARTING)) 183154419Smsmith return; 183254419Smsmith 183351973Smsmith /* spin until something prevents us from doing any work */ 183452544Smsmith s = splbio(); 183551973Smsmith for (;;) { 183651973Smsmith 183751973Smsmith /* see if there's work to be done */ 183878752Smsmith if ((bp = MLX_BIO_QFIRST(sc->mlx_bioq)) == NULL) 183951973Smsmith break; 184051973Smsmith /* get a command */ 184151973Smsmith if ((mc = mlx_alloccmd(sc)) == NULL) 184251973Smsmith break; 184351973Smsmith /* get a slot for the command */ 184451973Smsmith if (mlx_getslot(mc) != 0) { 184551973Smsmith mlx_releasecmd(mc); 184651973Smsmith break; 184751973Smsmith } 184851973Smsmith /* get the buf containing our work */ 184978752Smsmith MLX_BIO_QREMOVE(sc->mlx_bioq, bp); 185051973Smsmith sc->mlx_waitbufs--; 185152544Smsmith splx(s); 185251973Smsmith 185351973Smsmith /* connect the buf to the command */ 185451973Smsmith mc->mc_complete = mlx_completeio; 185551973Smsmith mc->mc_private = bp; 185678752Smsmith mc->mc_data = MLX_BIO_DATA(bp); 185778752Smsmith mc->mc_length = MLX_BIO_LENGTH(bp); 185851973Smsmith 185951973Smsmith /* map the command so the controller can work with it */ 1860119665Sscottl error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, 1861119665Sscottl mc->mc_length, mlx_startio_cb, mc, 0); 1862119665Sscottl if (error == EINPROGRESS) { 1863119665Sscottl break; 186458188Smsmith } 186551973Smsmith 186652544Smsmith s = splbio(); 186751973Smsmith } 186852544Smsmith splx(s); 186954419Smsmith mlx_lock_clr(sc, MLX_LOCK_STARTING); 187051973Smsmith} 187151973Smsmith 187251973Smsmith/******************************************************************************** 187351973Smsmith * Handle completion of an I/O command. 187451973Smsmith */ 187551973Smsmithstatic void 187651973Smsmithmlx_completeio(struct mlx_command *mc) 187751973Smsmith{ 187851973Smsmith struct mlx_softc *sc = mc->mc_sc; 187978752Smsmith mlx_bio *bp = (mlx_bio *)mc->mc_private; 188078752Smsmith struct mlxd_softc *mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp); 188151973Smsmith 188251973Smsmith if (mc->mc_status != MLX_STATUS_OK) { /* could be more verbose here? */ 188378752Smsmith MLX_BIO_SET_ERROR(bp, EIO); 188451973Smsmith 188551973Smsmith switch(mc->mc_status) { 188651973Smsmith case MLX_STATUS_RDWROFFLINE: /* system drive has gone offline */ 188751973Smsmith device_printf(mlxd->mlxd_dev, "drive offline\n"); 188852225Smsmith /* should signal this with a return code */ 188951973Smsmith mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE; 189051973Smsmith break; 189151973Smsmith 189251973Smsmith default: /* other I/O error */ 189351973Smsmith device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc)); 189451973Smsmith#if 0 189552439Smsmith device_printf(sc->mlx_dev, " b_bcount %ld blkcount %ld b_pblkno %d\n", 189678752Smsmith MLX_BIO_LENGTH(bp), MLX_BIO_LENGTH(bp) / MLX_BLKSIZE, MLX_BIO_LBA(bp)); 189751973Smsmith device_printf(sc->mlx_dev, " %13D\n", mc->mc_mailbox, " "); 189851973Smsmith#endif 189951973Smsmith break; 190051973Smsmith } 190151973Smsmith } 190251973Smsmith mlx_releasecmd(mc); 190351973Smsmith mlxd_intr(bp); 190451973Smsmith} 190551973Smsmith 1906119665Sscottlvoid 1907119665Sscottlmlx_user_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 1908119665Sscottl{ 1909119665Sscottl struct mlx_usercommand *mu; 1910119665Sscottl struct mlx_command *mc; 1911119665Sscottl struct mlx_dcdb *dcdb; 1912119665Sscottl 1913119665Sscottl mc = (struct mlx_command *)arg; 1914119665Sscottl if (error) 1915119665Sscottl return; 1916119665Sscottl 1917119665Sscottl mlx_setup_dmamap(mc, segs, nsegments, error); 1918119665Sscottl 1919119665Sscottl mu = (struct mlx_usercommand *)mc->mc_private; 1920119665Sscottl dcdb = NULL; 1921119665Sscottl 1922119665Sscottl /* 1923119665Sscottl * If this is a passthrough SCSI command, the DCDB is packed at the 1924119665Sscottl * beginning of the data area. Fix up the DCDB to point to the correct 1925119665Sscottl * physical address and override any bufptr supplied by the caller since 1926119665Sscottl * we know what it's meant to be. 1927119665Sscottl */ 1928119665Sscottl if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) { 1929119665Sscottl dcdb = (struct mlx_dcdb *)mc->mc_data; 1930119665Sscottl dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb); 1931119665Sscottl mu->mu_bufptr = 8; 1932119665Sscottl } 1933119665Sscottl 1934119665Sscottl /* 1935119665Sscottl * If there's a data buffer, fix up the command's buffer pointer. 1936119665Sscottl */ 1937119665Sscottl if (mu->mu_datasize > 0) { 1938119665Sscottl mc->mc_mailbox[mu->mu_bufptr ] = mc->mc_dataphys & 0xff; 1939119665Sscottl mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8) & 0xff; 1940119665Sscottl mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff; 1941119665Sscottl mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff; 1942119665Sscottl } 1943119665Sscottl debug(0, "command fixup"); 1944119665Sscottl 1945119665Sscottl /* submit the command and wait */ 1946119665Sscottl if (mlx_wait_command(mc) != 0) 1947119665Sscottl return; 1948119665Sscottl 1949119665Sscottl} 1950119665Sscottl 195151973Smsmith/******************************************************************************** 195251973Smsmith * Take a command from user-space and try to run it. 195358188Smsmith * 195458188Smsmith * XXX Note that this can't perform very much in the way of error checking, and 195558188Smsmith * as such, applications _must_ be considered trustworthy. 195658188Smsmith * XXX Commands using S/G for data are not supported. 195751973Smsmith */ 195851973Smsmithstatic int 195951973Smsmithmlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu) 196051973Smsmith{ 196151973Smsmith struct mlx_command *mc; 196251973Smsmith void *kbuf; 196351973Smsmith int error; 196451973Smsmith 196558188Smsmith debug_called(0); 196658188Smsmith 196751973Smsmith kbuf = NULL; 196851973Smsmith mc = NULL; 196951973Smsmith error = ENOMEM; 197058188Smsmith 197158188Smsmith /* get ourselves a command and copy in from user space */ 197258188Smsmith if ((mc = mlx_alloccmd(sc)) == NULL) 1973144433Ssam return(error); 197458188Smsmith bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox)); 197558188Smsmith debug(0, "got command buffer"); 197658188Smsmith 1977119665Sscottl /* 1978119665Sscottl * if we need a buffer for data transfer, allocate one and copy in its 1979119665Sscottl * initial contents 1980119665Sscottl */ 198151973Smsmith if (mu->mu_datasize > 0) { 1982195534Sscottl if (mu->mu_datasize > MLX_MAXPHYS) { 1983144436Ssam error = EINVAL; 1984144436Ssam goto out; 1985144436Ssam } 1986111119Simp if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) || 198758188Smsmith (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))) 198851973Smsmith goto out; 198958188Smsmith debug(0, "got kernel buffer"); 199051973Smsmith } 199151973Smsmith 199251973Smsmith /* get a command slot */ 199351973Smsmith if (mlx_getslot(mc)) 199451973Smsmith goto out; 199558188Smsmith debug(0, "got a slot"); 199651973Smsmith 199751973Smsmith if (mu->mu_datasize > 0) { 199858188Smsmith 199958188Smsmith /* range check the pointer to physical buffer address */ 2000119665Sscottl if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) - 2001119665Sscottl sizeof(u_int32_t)))) { 200258188Smsmith error = EINVAL; 200358188Smsmith goto out; 200458188Smsmith } 200551973Smsmith } 200651973Smsmith 2007119665Sscottl /* map the command so the controller can see it */ 2008119665Sscottl mc->mc_data = kbuf; 2009119665Sscottl mc->mc_length = mu->mu_datasize; 2010119665Sscottl mc->mc_private = mu; 2011119665Sscottl error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, 2012119665Sscottl mc->mc_length, mlx_user_cb, mc, BUS_DMA_NOWAIT); 201351973Smsmith 201451973Smsmith /* copy out status and data */ 201551973Smsmith mu->mu_status = mc->mc_status; 2016119665Sscottl if ((mu->mu_datasize > 0) && 2017119665Sscottl ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize)))) 201851973Smsmith goto out; 2019119665Sscottl 202051973Smsmith error = 0; 2021119665Sscottl 202251973Smsmith out: 202351973Smsmith mlx_releasecmd(mc); 202451973Smsmith if (kbuf != NULL) 202551973Smsmith free(kbuf, M_DEVBUF); 202651973Smsmith return(error); 202751973Smsmith} 202851973Smsmith 202951973Smsmith/******************************************************************************** 203051973Smsmith ******************************************************************************** 203151973Smsmith Command I/O to Controller 203251973Smsmith ******************************************************************************** 203351973Smsmith ********************************************************************************/ 203451973Smsmith 203551973Smsmith/******************************************************************************** 203651973Smsmith * Find a free command slot for (mc). 203751973Smsmith * 203851973Smsmith * Don't hand out a slot to a normal-priority command unless there are at least 203951973Smsmith * 4 slots free for priority commands. 204051973Smsmith */ 204151973Smsmithstatic int 204251973Smsmithmlx_getslot(struct mlx_command *mc) 204351973Smsmith{ 204451973Smsmith struct mlx_softc *sc = mc->mc_sc; 204560074Smsmith int s, slot, limit; 204651973Smsmith 204758188Smsmith debug_called(1); 204851973Smsmith 204960074Smsmith /* 205060074Smsmith * Enforce slot-usage limit, if we have the required information. 205160074Smsmith */ 205260074Smsmith if (sc->mlx_enq2 != NULL) { 205360074Smsmith limit = sc->mlx_enq2->me_max_commands; 205460074Smsmith } else { 205560074Smsmith limit = 2; 205660074Smsmith } 205760074Smsmith if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ? limit : limit - 4)) 205851973Smsmith return(EBUSY); 205951973Smsmith 206051973Smsmith /* 206151973Smsmith * Allocate an outstanding command slot 206251973Smsmith * 206351973Smsmith * XXX linear search is slow 206451973Smsmith */ 206551973Smsmith s = splbio(); 206660074Smsmith for (slot = 0; slot < limit; slot++) { 206758188Smsmith debug(2, "try slot %d", slot); 206851973Smsmith if (sc->mlx_busycmd[slot] == NULL) 206951973Smsmith break; 207051973Smsmith } 207160074Smsmith if (slot < limit) { 207251973Smsmith sc->mlx_busycmd[slot] = mc; 207351973Smsmith sc->mlx_busycmds++; 207451973Smsmith } 207551973Smsmith splx(s); 207651973Smsmith 207751973Smsmith /* out of slots? */ 207860074Smsmith if (slot >= limit) 207951973Smsmith return(EBUSY); 208051973Smsmith 208158188Smsmith debug(2, "got slot %d", slot); 208251973Smsmith mc->mc_slot = slot; 208351973Smsmith return(0); 208451973Smsmith} 208551973Smsmith 208651973Smsmith/******************************************************************************** 208751973Smsmith * Map/unmap (mc)'s data in the controller's addressable space. 208851973Smsmith */ 208951973Smsmithstatic void 2090119665Sscottlmlx_setup_dmamap(struct mlx_command *mc, bus_dma_segment_t *segs, int nsegments, 2091119665Sscottl int error) 209251973Smsmith{ 209351973Smsmith struct mlx_softc *sc = mc->mc_sc; 209451973Smsmith struct mlx_sgentry *sg; 209551973Smsmith int i; 209651973Smsmith 209758188Smsmith debug_called(1); 209851973Smsmith 209960074Smsmith /* XXX should be unnecessary */ 210060074Smsmith if (sc->mlx_enq2 && (nsegments > sc->mlx_enq2->me_max_sg)) 2101119665Sscottl panic("MLX: too many s/g segments (%d, max %d)", nsegments, 2102119665Sscottl sc->mlx_enq2->me_max_sg); 210360074Smsmith 210451973Smsmith /* get base address of s/g table */ 210560074Smsmith sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG); 210651973Smsmith 210751973Smsmith /* save s/g table information in command */ 210851973Smsmith mc->mc_nsgent = nsegments; 2109119665Sscottl mc->mc_sgphys = sc->mlx_sgbusaddr + 2110119665Sscottl (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry)); 211151973Smsmith mc->mc_dataphys = segs[0].ds_addr; 211251973Smsmith 211351973Smsmith /* populate s/g table */ 211451973Smsmith for (i = 0; i < nsegments; i++, sg++) { 211551973Smsmith sg->sg_addr = segs[i].ds_addr; 211651973Smsmith sg->sg_count = segs[i].ds_len; 211751973Smsmith } 211851973Smsmith 2119119665Sscottl /* Make sure the buffers are visible on the bus. */ 2120119665Sscottl if (mc->mc_flags & MLX_CMD_DATAIN) 2121119665Sscottl bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, 2122119665Sscottl BUS_DMASYNC_PREREAD); 2123119665Sscottl if (mc->mc_flags & MLX_CMD_DATAOUT) 2124119665Sscottl bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, 2125119665Sscottl BUS_DMASYNC_PREWRITE); 212651973Smsmith} 212751973Smsmith 212851973Smsmithstatic void 212951973Smsmithmlx_unmapcmd(struct mlx_command *mc) 213051973Smsmith{ 213151973Smsmith struct mlx_softc *sc = mc->mc_sc; 213251973Smsmith 213358188Smsmith debug_called(1); 213451973Smsmith 213551973Smsmith /* if the command involved data at all */ 213651973Smsmith if (mc->mc_data != NULL) { 213751973Smsmith 213851973Smsmith if (mc->mc_flags & MLX_CMD_DATAIN) 213951973Smsmith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD); 214051973Smsmith if (mc->mc_flags & MLX_CMD_DATAOUT) 214151973Smsmith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE); 214251973Smsmith 214351973Smsmith bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap); 214451973Smsmith } 214551973Smsmith} 214651973Smsmith 214751973Smsmith/******************************************************************************** 214854419Smsmith * Try to deliver (mc) to the controller. 214951973Smsmith * 215051973Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 215151973Smsmith */ 215251973Smsmithstatic int 215351973Smsmithmlx_start(struct mlx_command *mc) 215451973Smsmith{ 215551973Smsmith struct mlx_softc *sc = mc->mc_sc; 215654419Smsmith int i, s, done; 215751973Smsmith 215858188Smsmith debug_called(1); 215951973Smsmith 216051973Smsmith /* save the slot number as ident so we can handle this command when complete */ 216151973Smsmith mc->mc_mailbox[0x1] = mc->mc_slot; 216251973Smsmith 216352544Smsmith /* mark the command as currently being processed */ 216451973Smsmith mc->mc_status = MLX_STATUS_BUSY; 216554419Smsmith 216654419Smsmith /* set a default 60-second timeout XXX tunable? XXX not currently used */ 216754419Smsmith mc->mc_timeout = time_second + 60; 216851973Smsmith 216951973Smsmith /* spin waiting for the mailbox */ 217051973Smsmith for (i = 100000, done = 0; (i > 0) && !done; i--) { 217151973Smsmith s = splbio(); 217252544Smsmith if (sc->mlx_tryqueue(sc, mc)) { 217352544Smsmith done = 1; 217452544Smsmith /* move command to work queue */ 217552544Smsmith TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link); 217652544Smsmith } 217754419Smsmith splx(s); /* drop spl to allow completion interrupts */ 217851973Smsmith } 217951973Smsmith 218051973Smsmith /* command is enqueued */ 218151973Smsmith if (done) 218251973Smsmith return(0); 218351973Smsmith 218451973Smsmith /* 218551973Smsmith * We couldn't get the controller to take the command. Revoke the slot 218651973Smsmith * that the command was given and return it with a bad status. 218751973Smsmith */ 218851973Smsmith sc->mlx_busycmd[mc->mc_slot] = NULL; 218951973Smsmith device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n"); 219051973Smsmith mc->mc_status = MLX_STATUS_WEDGED; 219154419Smsmith mlx_complete(sc); 219251973Smsmith return(EIO); 219351973Smsmith} 219451973Smsmith 219551973Smsmith/******************************************************************************** 219654419Smsmith * Poll the controller (sc) for completed commands. 219754419Smsmith * Update command status and free slots for reuse. If any slots were freed, 219854419Smsmith * new commands may be posted. 219951973Smsmith * 220054419Smsmith * Returns nonzero if one or more commands were completed. 220151973Smsmith */ 220251973Smsmithstatic int 220351973Smsmithmlx_done(struct mlx_softc *sc) 220451973Smsmith{ 220551973Smsmith struct mlx_command *mc; 220654419Smsmith int s, result; 220751973Smsmith u_int8_t slot; 220851973Smsmith u_int16_t status; 220951973Smsmith 221058188Smsmith debug_called(2); 221151973Smsmith 221254419Smsmith result = 0; 221351973Smsmith 221454419Smsmith /* loop collecting completed commands */ 221552544Smsmith s = splbio(); 221654419Smsmith for (;;) { 221754419Smsmith /* poll for a completed command's identifier and status */ 221854419Smsmith if (sc->mlx_findcomplete(sc, &slot, &status)) { 221954419Smsmith result = 1; 222054419Smsmith mc = sc->mlx_busycmd[slot]; /* find command */ 222154419Smsmith if (mc != NULL) { /* paranoia */ 222254419Smsmith if (mc->mc_status == MLX_STATUS_BUSY) { 222354419Smsmith mc->mc_status = status; /* save status */ 222451973Smsmith 222554419Smsmith /* free slot for reuse */ 222654419Smsmith sc->mlx_busycmd[slot] = NULL; 222754419Smsmith sc->mlx_busycmds--; 222854419Smsmith } else { 222954419Smsmith device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot); 223054419Smsmith } 223151973Smsmith } else { 223254419Smsmith device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot); 223351973Smsmith } 223451973Smsmith } else { 223554419Smsmith break; 223651973Smsmith } 223751973Smsmith } 223860074Smsmith splx(s); 223951973Smsmith 224054419Smsmith /* if we've completed any commands, try posting some more */ 224154419Smsmith if (result) 224254419Smsmith mlx_startio(sc); 224354419Smsmith 224454419Smsmith /* handle completion and timeouts */ 224554419Smsmith mlx_complete(sc); 224654419Smsmith 224754419Smsmith return(result); 224851973Smsmith} 224951973Smsmith 225051973Smsmith/******************************************************************************** 225154419Smsmith * Perform post-completion processing for commands on (sc). 225251973Smsmith */ 225351973Smsmithstatic void 225451973Smsmithmlx_complete(struct mlx_softc *sc) 225551973Smsmith{ 225651973Smsmith struct mlx_command *mc, *nc; 225751973Smsmith int s, count; 225851973Smsmith 225958188Smsmith debug_called(2); 226051973Smsmith 226154419Smsmith /* avoid reentrancy XXX might want to signal and request a restart */ 226254419Smsmith if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING)) 226354419Smsmith return; 226454419Smsmith 226551973Smsmith s = splbio(); 226651973Smsmith count = 0; 226754419Smsmith 226854419Smsmith /* scan the list of busy/done commands */ 226952544Smsmith mc = TAILQ_FIRST(&sc->mlx_work); 227051973Smsmith while (mc != NULL) { 227151973Smsmith nc = TAILQ_NEXT(mc, mc_link); 227251973Smsmith 227354419Smsmith /* Command has been completed in some fashion */ 227452544Smsmith if (mc->mc_status != MLX_STATUS_BUSY) { 227554419Smsmith 227654419Smsmith /* unmap the command's data buffer */ 227754419Smsmith mlx_unmapcmd(mc); 227852544Smsmith /* 227954419Smsmith * Does the command have a completion handler? 228054419Smsmith */ 228152544Smsmith if (mc->mc_complete != NULL) { 228252544Smsmith /* remove from list and give to handler */ 228352544Smsmith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 228452544Smsmith mc->mc_complete(mc); 228551973Smsmith 228652544Smsmith /* 228752544Smsmith * Is there a sleeper waiting on this command? 228852544Smsmith */ 228952544Smsmith } else if (mc->mc_private != NULL) { /* sleeping caller wants to know about it */ 229051973Smsmith 229152544Smsmith /* remove from list and wake up sleeper */ 229252544Smsmith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 229352544Smsmith wakeup_one(mc->mc_private); 229451973Smsmith 229552544Smsmith /* 229652544Smsmith * Leave the command for a caller that's polling for it. 229752544Smsmith */ 229852544Smsmith } else { 229952544Smsmith } 230051973Smsmith } 230151973Smsmith mc = nc; 230251973Smsmith } 230351973Smsmith splx(s); 230451973Smsmith 230554419Smsmith mlx_lock_clr(sc, MLX_LOCK_COMPLETING); 230651973Smsmith} 230751973Smsmith 230851973Smsmith/******************************************************************************** 230951973Smsmith ******************************************************************************** 231051973Smsmith Command Buffer Management 231151973Smsmith ******************************************************************************** 231251973Smsmith ********************************************************************************/ 231351973Smsmith 231451973Smsmith/******************************************************************************** 231551973Smsmith * Get a new command buffer. 231651973Smsmith * 231751973Smsmith * This may return NULL in low-memory cases. 231851973Smsmith * 231951973Smsmith * Note that using malloc() is expensive (the command buffer is << 1 page) but 232051973Smsmith * necessary if we are to be a loadable module before the zone allocator is fixed. 232151973Smsmith * 232251973Smsmith * If possible, we recycle a command buffer that's been used before. 232351973Smsmith * 232451973Smsmith * XXX Note that command buffers are not cleaned out - it is the caller's 232551973Smsmith * responsibility to ensure that all required fields are filled in before 232651973Smsmith * using a buffer. 232751973Smsmith */ 232851973Smsmithstatic struct mlx_command * 232951973Smsmithmlx_alloccmd(struct mlx_softc *sc) 233051973Smsmith{ 233151973Smsmith struct mlx_command *mc; 233251973Smsmith int error; 233351973Smsmith int s; 233451973Smsmith 233558188Smsmith debug_called(1); 233651973Smsmith 233751973Smsmith s = splbio(); 233851973Smsmith if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) 233951973Smsmith TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); 234051973Smsmith splx(s); 234151973Smsmith 234251973Smsmith /* allocate a new command buffer? */ 234351973Smsmith if (mc == NULL) { 234468877Sdwmalone mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT | M_ZERO); 234551973Smsmith if (mc != NULL) { 234651973Smsmith mc->mc_sc = sc; 234751973Smsmith error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap); 234851973Smsmith if (error) { 234951973Smsmith free(mc, M_DEVBUF); 235051973Smsmith return(NULL); 235151973Smsmith } 235251973Smsmith } 235351973Smsmith } 235451973Smsmith return(mc); 235551973Smsmith} 235651973Smsmith 235751973Smsmith/******************************************************************************** 235851973Smsmith * Release a command buffer for recycling. 235951973Smsmith * 236051973Smsmith * XXX It might be a good idea to limit the number of commands we save for reuse 236151973Smsmith * if it's shown that this list bloats out massively. 236251973Smsmith */ 236351973Smsmithstatic void 236451973Smsmithmlx_releasecmd(struct mlx_command *mc) 236551973Smsmith{ 236651973Smsmith int s; 236751973Smsmith 236858188Smsmith debug_called(1); 236951973Smsmith 237051973Smsmith s = splbio(); 237151973Smsmith TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link); 237251973Smsmith splx(s); 237351973Smsmith} 237451973Smsmith 237551973Smsmith/******************************************************************************** 237651973Smsmith * Permanently discard a command buffer. 237751973Smsmith */ 237851973Smsmithstatic void 237951973Smsmithmlx_freecmd(struct mlx_command *mc) 238051973Smsmith{ 238151973Smsmith struct mlx_softc *sc = mc->mc_sc; 238251973Smsmith 238358188Smsmith debug_called(1); 238451973Smsmith bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap); 238551973Smsmith free(mc, M_DEVBUF); 238651973Smsmith} 238751973Smsmith 238851973Smsmith 238951973Smsmith/******************************************************************************** 239051973Smsmith ******************************************************************************** 239151973Smsmith Type 3 interface accessor methods 239251973Smsmith ******************************************************************************** 239351973Smsmith ********************************************************************************/ 239451973Smsmith 239551973Smsmith/******************************************************************************** 239651973Smsmith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 239751973Smsmith * (the controller is not ready to take a command). 239851973Smsmith * 239951973Smsmith * Must be called at splbio or in a fashion that prevents reentry. 240051973Smsmith */ 240151973Smsmithstatic int 240251973Smsmithmlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 240351973Smsmith{ 240451973Smsmith int i; 240551973Smsmith 240658188Smsmith debug_called(2); 240751973Smsmith 240851973Smsmith /* ready for our command? */ 240951973Smsmith if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) { 241051973Smsmith /* copy mailbox data to window */ 241151973Smsmith for (i = 0; i < 13; i++) 241251973Smsmith MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 241351973Smsmith 241451973Smsmith /* post command */ 241552225Smsmith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL); 241651973Smsmith return(1); 241751973Smsmith } 241851973Smsmith return(0); 241951973Smsmith} 242051973Smsmith 242151973Smsmith/******************************************************************************** 242251973Smsmith * See if a command has been completed, if so acknowledge its completion 242351973Smsmith * and recover the slot number and status code. 242451973Smsmith * 242551973Smsmith * Must be called at splbio or in a fashion that prevents reentry. 242651973Smsmith */ 242751973Smsmithstatic int 242851973Smsmithmlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 242951973Smsmith{ 243051973Smsmith 243158188Smsmith debug_called(2); 243251973Smsmith 243351973Smsmith /* status available? */ 243451973Smsmith if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) { 243551973Smsmith *slot = MLX_V3_GET_STATUS_IDENT(sc); /* get command identifier */ 243651973Smsmith *status = MLX_V3_GET_STATUS(sc); /* get status */ 243751973Smsmith 243851973Smsmith /* acknowledge completion */ 243952225Smsmith MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL); 244052225Smsmith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK); 244151973Smsmith return(1); 244251973Smsmith } 244351973Smsmith return(0); 244451973Smsmith} 244551973Smsmith 244651973Smsmith/******************************************************************************** 244751973Smsmith * Enable/disable interrupts as requested. (No acknowledge required) 244851973Smsmith * 244951973Smsmith * Must be called at splbio or in a fashion that prevents reentry. 245051973Smsmith */ 245151973Smsmithstatic void 245251973Smsmithmlx_v3_intaction(struct mlx_softc *sc, int action) 245351973Smsmith{ 245458188Smsmith debug_called(1); 245551973Smsmith 245651973Smsmith switch(action) { 245751973Smsmith case MLX_INTACTION_DISABLE: 245851973Smsmith MLX_V3_PUT_IER(sc, 0); 245951973Smsmith sc->mlx_state &= ~MLX_STATE_INTEN; 246051973Smsmith break; 246151973Smsmith case MLX_INTACTION_ENABLE: 246251973Smsmith MLX_V3_PUT_IER(sc, 1); 246351973Smsmith sc->mlx_state |= MLX_STATE_INTEN; 246451973Smsmith break; 246551973Smsmith } 246651973Smsmith} 246751973Smsmith 246858188Smsmith/******************************************************************************** 246958188Smsmith * Poll for firmware error codes during controller initialisation. 247058188Smsmith * Returns 0 if initialisation is complete, 1 if still in progress but no 247158188Smsmith * error has been fetched, 2 if an error has been retrieved. 247258188Smsmith */ 247358188Smsmithstatic int 247458188Smsmithmlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 247558188Smsmith{ 247658188Smsmith u_int8_t fwerror; 247758188Smsmith static int initted = 0; 247851973Smsmith 247958188Smsmith debug_called(2); 248058188Smsmith 248158188Smsmith /* first time around, clear any hardware completion status */ 248258188Smsmith if (!initted) { 248358188Smsmith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK); 248458188Smsmith DELAY(1000); 248558188Smsmith initted = 1; 248658188Smsmith } 248758188Smsmith 248858188Smsmith /* init in progress? */ 248958188Smsmith if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY)) 249058188Smsmith return(0); 249158188Smsmith 249258188Smsmith /* test error value */ 249358188Smsmith fwerror = MLX_V3_GET_FWERROR(sc); 249458188Smsmith if (!(fwerror & MLX_V3_FWERROR_PEND)) 249558188Smsmith return(1); 249658188Smsmith 249758188Smsmith /* mask status pending bit, fetch status */ 249858188Smsmith *error = fwerror & ~MLX_V3_FWERROR_PEND; 249958188Smsmith *param1 = MLX_V3_GET_FWERROR_PARAM1(sc); 250058188Smsmith *param2 = MLX_V3_GET_FWERROR_PARAM2(sc); 250158188Smsmith 250258188Smsmith /* acknowledge */ 250358188Smsmith MLX_V3_PUT_FWERROR(sc, 0); 250458188Smsmith 250558188Smsmith return(2); 250658188Smsmith} 250758188Smsmith 250851973Smsmith/******************************************************************************** 250951973Smsmith ******************************************************************************** 251052225Smsmith Type 4 interface accessor methods 251152225Smsmith ******************************************************************************** 251252225Smsmith ********************************************************************************/ 251352225Smsmith 251452225Smsmith/******************************************************************************** 251552225Smsmith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 251652225Smsmith * (the controller is not ready to take a command). 251752225Smsmith * 251852225Smsmith * Must be called at splbio or in a fashion that prevents reentry. 251952225Smsmith */ 252052225Smsmithstatic int 252152225Smsmithmlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 252252225Smsmith{ 252352225Smsmith int i; 252452225Smsmith 252558188Smsmith debug_called(2); 252652225Smsmith 252752225Smsmith /* ready for our command? */ 252852225Smsmith if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) { 252952225Smsmith /* copy mailbox data to window */ 253052225Smsmith for (i = 0; i < 13; i++) 253152225Smsmith MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 253252225Smsmith 253358188Smsmith /* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */ 253458188Smsmith bus_space_barrier(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH, 253558188Smsmith BUS_SPACE_BARRIER_WRITE); 253658188Smsmith 253752225Smsmith /* post command */ 253852225Smsmith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD); 253952225Smsmith return(1); 254052225Smsmith } 254152225Smsmith return(0); 254252225Smsmith} 254352225Smsmith 254452225Smsmith/******************************************************************************** 254552225Smsmith * See if a command has been completed, if so acknowledge its completion 254652225Smsmith * and recover the slot number and status code. 254752225Smsmith * 254852225Smsmith * Must be called at splbio or in a fashion that prevents reentry. 254952225Smsmith */ 255052225Smsmithstatic int 255152225Smsmithmlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 255252225Smsmith{ 255352225Smsmith 255458188Smsmith debug_called(2); 255552225Smsmith 255652225Smsmith /* status available? */ 255752225Smsmith if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) { 255852225Smsmith *slot = MLX_V4_GET_STATUS_IDENT(sc); /* get command identifier */ 255952225Smsmith *status = MLX_V4_GET_STATUS(sc); /* get status */ 256052225Smsmith 256152225Smsmith /* acknowledge completion */ 256252225Smsmith MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK); 256352225Smsmith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK); 256452225Smsmith return(1); 256552225Smsmith } 256652225Smsmith return(0); 256752225Smsmith} 256852225Smsmith 256952225Smsmith/******************************************************************************** 257052225Smsmith * Enable/disable interrupts as requested. 257152225Smsmith * 257252225Smsmith * Must be called at splbio or in a fashion that prevents reentry. 257352225Smsmith */ 257452225Smsmithstatic void 257552225Smsmithmlx_v4_intaction(struct mlx_softc *sc, int action) 257652225Smsmith{ 257758188Smsmith debug_called(1); 257852225Smsmith 257952225Smsmith switch(action) { 258052225Smsmith case MLX_INTACTION_DISABLE: 258152225Smsmith MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT); 258252225Smsmith sc->mlx_state &= ~MLX_STATE_INTEN; 258352225Smsmith break; 258452225Smsmith case MLX_INTACTION_ENABLE: 258552225Smsmith MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT); 258652225Smsmith sc->mlx_state |= MLX_STATE_INTEN; 258752225Smsmith break; 258852225Smsmith } 258952225Smsmith} 259052225Smsmith 259158188Smsmith/******************************************************************************** 259258188Smsmith * Poll for firmware error codes during controller initialisation. 259358188Smsmith * Returns 0 if initialisation is complete, 1 if still in progress but no 259458188Smsmith * error has been fetched, 2 if an error has been retrieved. 259558188Smsmith */ 259658188Smsmithstatic int 259758188Smsmithmlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 259858188Smsmith{ 259958188Smsmith u_int8_t fwerror; 260058188Smsmith static int initted = 0; 260152225Smsmith 260258188Smsmith debug_called(2); 260358188Smsmith 260458188Smsmith /* first time around, clear any hardware completion status */ 260558188Smsmith if (!initted) { 260658188Smsmith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK); 260758188Smsmith DELAY(1000); 260858188Smsmith initted = 1; 260958188Smsmith } 261058188Smsmith 261158188Smsmith /* init in progress? */ 261258188Smsmith if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY)) 261358188Smsmith return(0); 261458188Smsmith 261558188Smsmith /* test error value */ 261658188Smsmith fwerror = MLX_V4_GET_FWERROR(sc); 261758188Smsmith if (!(fwerror & MLX_V4_FWERROR_PEND)) 261858188Smsmith return(1); 261958188Smsmith 262058188Smsmith /* mask status pending bit, fetch status */ 262158188Smsmith *error = fwerror & ~MLX_V4_FWERROR_PEND; 262258188Smsmith *param1 = MLX_V4_GET_FWERROR_PARAM1(sc); 262358188Smsmith *param2 = MLX_V4_GET_FWERROR_PARAM2(sc); 262458188Smsmith 262558188Smsmith /* acknowledge */ 262658188Smsmith MLX_V4_PUT_FWERROR(sc, 0); 262758188Smsmith 262858188Smsmith return(2); 262958188Smsmith} 263058188Smsmith 263152225Smsmith/******************************************************************************** 263252225Smsmith ******************************************************************************** 263354419Smsmith Type 5 interface accessor methods 263454419Smsmith ******************************************************************************** 263554419Smsmith ********************************************************************************/ 263654419Smsmith 263754419Smsmith/******************************************************************************** 263854419Smsmith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 263954419Smsmith * (the controller is not ready to take a command). 264054419Smsmith * 264154419Smsmith * Must be called at splbio or in a fashion that prevents reentry. 264254419Smsmith */ 264354419Smsmithstatic int 264454419Smsmithmlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 264554419Smsmith{ 264654419Smsmith int i; 264754419Smsmith 264858188Smsmith debug_called(2); 264958188Smsmith 265054419Smsmith /* ready for our command? */ 265154419Smsmith if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) { 265254419Smsmith /* copy mailbox data to window */ 265354419Smsmith for (i = 0; i < 13; i++) 265454419Smsmith MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 265558188Smsmith 265654419Smsmith /* post command */ 265754419Smsmith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD); 265854419Smsmith return(1); 265954419Smsmith } 266054419Smsmith return(0); 266154419Smsmith} 266254419Smsmith 266354419Smsmith/******************************************************************************** 266454419Smsmith * See if a command has been completed, if so acknowledge its completion 266554419Smsmith * and recover the slot number and status code. 266654419Smsmith * 266754419Smsmith * Must be called at splbio or in a fashion that prevents reentry. 266854419Smsmith */ 266954419Smsmithstatic int 267054419Smsmithmlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 267154419Smsmith{ 267254419Smsmith 267358188Smsmith debug_called(2); 267454419Smsmith 267554419Smsmith /* status available? */ 267654419Smsmith if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) { 267754419Smsmith *slot = MLX_V5_GET_STATUS_IDENT(sc); /* get command identifier */ 267854419Smsmith *status = MLX_V5_GET_STATUS(sc); /* get status */ 267954419Smsmith 268054419Smsmith /* acknowledge completion */ 268154419Smsmith MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK); 268254419Smsmith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK); 268354419Smsmith return(1); 268454419Smsmith } 268554419Smsmith return(0); 268654419Smsmith} 268754419Smsmith 268854419Smsmith/******************************************************************************** 268954419Smsmith * Enable/disable interrupts as requested. 269054419Smsmith * 269154419Smsmith * Must be called at splbio or in a fashion that prevents reentry. 269254419Smsmith */ 269354419Smsmithstatic void 269454419Smsmithmlx_v5_intaction(struct mlx_softc *sc, int action) 269554419Smsmith{ 269658188Smsmith debug_called(1); 269754419Smsmith 269854419Smsmith switch(action) { 269954419Smsmith case MLX_INTACTION_DISABLE: 270058188Smsmith MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT); 270154419Smsmith sc->mlx_state &= ~MLX_STATE_INTEN; 270254419Smsmith break; 270354419Smsmith case MLX_INTACTION_ENABLE: 270458188Smsmith MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT); 270554419Smsmith sc->mlx_state |= MLX_STATE_INTEN; 270654419Smsmith break; 270754419Smsmith } 270854419Smsmith} 270954419Smsmith 271058188Smsmith/******************************************************************************** 271158188Smsmith * Poll for firmware error codes during controller initialisation. 271258188Smsmith * Returns 0 if initialisation is complete, 1 if still in progress but no 271358188Smsmith * error has been fetched, 2 if an error has been retrieved. 271458188Smsmith */ 271558188Smsmithstatic int 271658188Smsmithmlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 271758188Smsmith{ 271858188Smsmith u_int8_t fwerror; 271958188Smsmith static int initted = 0; 272054419Smsmith 272158188Smsmith debug_called(2); 272258188Smsmith 272358188Smsmith /* first time around, clear any hardware completion status */ 272458188Smsmith if (!initted) { 272558188Smsmith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK); 272658188Smsmith DELAY(1000); 272758188Smsmith initted = 1; 272858188Smsmith } 272958188Smsmith 273058188Smsmith /* init in progress? */ 273158188Smsmith if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE) 273258188Smsmith return(0); 273358188Smsmith 273458188Smsmith /* test for error value */ 273558188Smsmith fwerror = MLX_V5_GET_FWERROR(sc); 273658188Smsmith if (!(fwerror & MLX_V5_FWERROR_PEND)) 273758188Smsmith return(1); 273858188Smsmith 273958188Smsmith /* mask status pending bit, fetch status */ 274058188Smsmith *error = fwerror & ~MLX_V5_FWERROR_PEND; 274158188Smsmith *param1 = MLX_V5_GET_FWERROR_PARAM1(sc); 274258188Smsmith *param2 = MLX_V5_GET_FWERROR_PARAM2(sc); 274358188Smsmith 274458188Smsmith /* acknowledge */ 274558188Smsmith MLX_V5_PUT_FWERROR(sc, 0xff); 274658188Smsmith 274758188Smsmith return(2); 274858188Smsmith} 274958188Smsmith 275054419Smsmith/******************************************************************************** 275154419Smsmith ******************************************************************************** 275251973Smsmith Debugging 275351973Smsmith ******************************************************************************** 275451973Smsmith ********************************************************************************/ 275551973Smsmith 275651973Smsmith/******************************************************************************** 275751973Smsmith * Return a status message describing (mc) 275851973Smsmith */ 275951973Smsmithstatic char *mlx_status_messages[] = { 276051973Smsmith "normal completion", /* 00 */ 276151973Smsmith "irrecoverable data error", /* 01 */ 276251973Smsmith "drive does not exist, or is offline", /* 02 */ 276351973Smsmith "attempt to write beyond end of drive", /* 03 */ 276451973Smsmith "bad data encountered", /* 04 */ 276551973Smsmith "invalid log entry request", /* 05 */ 276651973Smsmith "attempt to rebuild online drive", /* 06 */ 276751973Smsmith "new disk failed during rebuild", /* 07 */ 276851973Smsmith "invalid channel/target", /* 08 */ 276951973Smsmith "rebuild/check already in progress", /* 09 */ 277051973Smsmith "one or more disks are dead", /* 10 */ 277151973Smsmith "invalid or non-redundant drive", /* 11 */ 277251973Smsmith "channel is busy", /* 12 */ 277351973Smsmith "channel is not stopped", /* 13 */ 277458188Smsmith "rebuild successfully terminated", /* 14 */ 277558188Smsmith "unsupported command", /* 15 */ 277658188Smsmith "check condition received", /* 16 */ 277758188Smsmith "device is busy", /* 17 */ 277858188Smsmith "selection or command timeout", /* 18 */ 277958188Smsmith "command terminated abnormally", /* 19 */ 278058188Smsmith "" 278151973Smsmith}; 278251973Smsmith 278351973Smsmithstatic struct 278451973Smsmith{ 278551973Smsmith int command; 278651973Smsmith u_int16_t status; 278751973Smsmith int msg; 278851973Smsmith} mlx_messages[] = { 278958188Smsmith {MLX_CMD_READSG, 0x0001, 1}, 279058188Smsmith {MLX_CMD_READSG, 0x0002, 1}, 279158188Smsmith {MLX_CMD_READSG, 0x0105, 3}, 279258188Smsmith {MLX_CMD_READSG, 0x010c, 4}, 279358188Smsmith {MLX_CMD_WRITESG, 0x0001, 1}, 279458188Smsmith {MLX_CMD_WRITESG, 0x0002, 1}, 279558188Smsmith {MLX_CMD_WRITESG, 0x0105, 3}, 279658188Smsmith {MLX_CMD_READSG_OLD, 0x0001, 1}, 279758188Smsmith {MLX_CMD_READSG_OLD, 0x0002, 1}, 279858188Smsmith {MLX_CMD_READSG_OLD, 0x0105, 3}, 279958188Smsmith {MLX_CMD_WRITESG_OLD, 0x0001, 1}, 280058188Smsmith {MLX_CMD_WRITESG_OLD, 0x0002, 1}, 280158188Smsmith {MLX_CMD_WRITESG_OLD, 0x0105, 3}, 280251973Smsmith {MLX_CMD_LOGOP, 0x0105, 5}, 280351973Smsmith {MLX_CMD_REBUILDASYNC, 0x0002, 6}, 280451973Smsmith {MLX_CMD_REBUILDASYNC, 0x0004, 7}, 280551973Smsmith {MLX_CMD_REBUILDASYNC, 0x0105, 8}, 280651973Smsmith {MLX_CMD_REBUILDASYNC, 0x0106, 9}, 280758188Smsmith {MLX_CMD_REBUILDASYNC, 0x0107, 14}, 280851973Smsmith {MLX_CMD_CHECKASYNC, 0x0002, 10}, 280951973Smsmith {MLX_CMD_CHECKASYNC, 0x0105, 11}, 281051973Smsmith {MLX_CMD_CHECKASYNC, 0x0106, 9}, 281151973Smsmith {MLX_CMD_STOPCHANNEL, 0x0106, 12}, 281251973Smsmith {MLX_CMD_STOPCHANNEL, 0x0105, 8}, 281351973Smsmith {MLX_CMD_STARTCHANNEL, 0x0005, 13}, 281451973Smsmith {MLX_CMD_STARTCHANNEL, 0x0105, 8}, 281558188Smsmith {MLX_CMD_DIRECT_CDB, 0x0002, 16}, 281658188Smsmith {MLX_CMD_DIRECT_CDB, 0x0008, 17}, 281758188Smsmith {MLX_CMD_DIRECT_CDB, 0x000e, 18}, 281858188Smsmith {MLX_CMD_DIRECT_CDB, 0x000f, 19}, 281958188Smsmith {MLX_CMD_DIRECT_CDB, 0x0105, 8}, 282058188Smsmith 282158188Smsmith {0, 0x0104, 14}, 282251973Smsmith {-1, 0, 0} 282351973Smsmith}; 282451973Smsmith 282551973Smsmithstatic char * 282651973Smsmithmlx_diagnose_command(struct mlx_command *mc) 282751973Smsmith{ 282851973Smsmith static char unkmsg[80]; 282951973Smsmith int i; 283051973Smsmith 283151973Smsmith /* look up message in table */ 283251973Smsmith for (i = 0; mlx_messages[i].command != -1; i++) 283358188Smsmith if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) && 283452275Smsmith (mc->mc_status == mlx_messages[i].status)) 283551973Smsmith return(mlx_status_messages[mlx_messages[i].msg]); 283651973Smsmith 283751973Smsmith sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]); 283851973Smsmith return(unkmsg); 283951973Smsmith} 284051973Smsmith 284151973Smsmith/******************************************************************************* 284258188Smsmith * Print a string describing the controller (sc) 284351973Smsmith */ 284454419Smsmithstatic struct 284554419Smsmith{ 284654419Smsmith int hwid; 284754419Smsmith char *name; 284854419Smsmith} mlx_controller_names[] = { 284954419Smsmith {0x01, "960P/PD"}, 285054419Smsmith {0x02, "960PL"}, 285154419Smsmith {0x10, "960PG"}, 285254419Smsmith {0x11, "960PJ"}, 285354979Smsmith {0x12, "960PR"}, 285454979Smsmith {0x13, "960PT"}, 285554979Smsmith {0x14, "960PTL0"}, 285654979Smsmith {0x15, "960PRL"}, 285754979Smsmith {0x16, "960PTL1"}, 285854979Smsmith {0x20, "1164PVX"}, 285954419Smsmith {-1, NULL} 286054419Smsmith}; 286154419Smsmith 286254979Smsmithstatic void 286354979Smsmithmlx_describe_controller(struct mlx_softc *sc) 286451973Smsmith{ 286551973Smsmith static char buf[80]; 286654419Smsmith char *model; 286754979Smsmith int i; 286854419Smsmith 286954419Smsmith for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) { 287054979Smsmith if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) { 287154419Smsmith model = mlx_controller_names[i].name; 287254419Smsmith break; 287354419Smsmith } 287451973Smsmith } 287554419Smsmith if (model == NULL) { 287654979Smsmith sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff); 287754419Smsmith model = buf; 287854419Smsmith } 287958188Smsmith device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n", 288054979Smsmith model, 288154979Smsmith sc->mlx_enq2->me_actual_channels, 288254979Smsmith sc->mlx_enq2->me_actual_channels > 1 ? "s" : "", 288354979Smsmith sc->mlx_enq2->me_firmware_id & 0xff, 288454979Smsmith (sc->mlx_enq2->me_firmware_id >> 8) & 0xff, 288555080Smsmith (sc->mlx_enq2->me_firmware_id >> 24) & 0xff, 288655093Smsmith (sc->mlx_enq2->me_firmware_id >> 16) & 0xff, 288754979Smsmith sc->mlx_enq2->me_mem_size / (1024 * 1024)); 288854979Smsmith 288954979Smsmith if (bootverbose) { 289054979Smsmith device_printf(sc->mlx_dev, " Hardware ID 0x%08x\n", sc->mlx_enq2->me_hardware_id); 289154979Smsmith device_printf(sc->mlx_dev, " Firmware ID 0x%08x\n", sc->mlx_enq2->me_firmware_id); 289254979Smsmith device_printf(sc->mlx_dev, " Configured/Actual channels %d/%d\n", sc->mlx_enq2->me_configured_channels, 289354979Smsmith sc->mlx_enq2->me_actual_channels); 289454979Smsmith device_printf(sc->mlx_dev, " Max Targets %d\n", sc->mlx_enq2->me_max_targets); 289554979Smsmith device_printf(sc->mlx_dev, " Max Tags %d\n", sc->mlx_enq2->me_max_tags); 289654979Smsmith device_printf(sc->mlx_dev, " Max System Drives %d\n", sc->mlx_enq2->me_max_sys_drives); 289754979Smsmith device_printf(sc->mlx_dev, " Max Arms %d\n", sc->mlx_enq2->me_max_arms); 289854979Smsmith device_printf(sc->mlx_dev, " Max Spans %d\n", sc->mlx_enq2->me_max_spans); 289954979Smsmith device_printf(sc->mlx_dev, " DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size, 290054979Smsmith sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size); 290154979Smsmith device_printf(sc->mlx_dev, " DRAM type %d\n", sc->mlx_enq2->me_mem_type); 290254979Smsmith device_printf(sc->mlx_dev, " Clock Speed %dns\n", sc->mlx_enq2->me_clock_speed); 290354979Smsmith device_printf(sc->mlx_dev, " Hardware Speed %dns\n", sc->mlx_enq2->me_hardware_speed); 290454979Smsmith device_printf(sc->mlx_dev, " Max Commands %d\n", sc->mlx_enq2->me_max_commands); 290554979Smsmith device_printf(sc->mlx_dev, " Max SG Entries %d\n", sc->mlx_enq2->me_max_sg); 290654979Smsmith device_printf(sc->mlx_dev, " Max DP %d\n", sc->mlx_enq2->me_max_dp); 290754979Smsmith device_printf(sc->mlx_dev, " Max IOD %d\n", sc->mlx_enq2->me_max_iod); 290854979Smsmith device_printf(sc->mlx_dev, " Max Comb %d\n", sc->mlx_enq2->me_max_comb); 290954979Smsmith device_printf(sc->mlx_dev, " Latency %ds\n", sc->mlx_enq2->me_latency); 291054979Smsmith device_printf(sc->mlx_dev, " SCSI Timeout %ds\n", sc->mlx_enq2->me_scsi_timeout); 291154979Smsmith device_printf(sc->mlx_dev, " Min Free Lines %d\n", sc->mlx_enq2->me_min_freelines); 291254979Smsmith device_printf(sc->mlx_dev, " Rate Constant %d\n", sc->mlx_enq2->me_rate_const); 291354979Smsmith device_printf(sc->mlx_dev, " MAXBLK %d\n", sc->mlx_enq2->me_maxblk); 291454979Smsmith device_printf(sc->mlx_dev, " Blocking Factor %d sectors\n", sc->mlx_enq2->me_blocking_factor); 291554979Smsmith device_printf(sc->mlx_dev, " Cache Line Size %d blocks\n", sc->mlx_enq2->me_cacheline); 291654979Smsmith device_printf(sc->mlx_dev, " SCSI Capability %s%dMHz, %d bit\n", 291754979Smsmith sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "", 291854979Smsmith (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10, 291954979Smsmith 8 << (sc->mlx_enq2->me_scsi_cap & 0x3)); 292054979Smsmith device_printf(sc->mlx_dev, " Firmware Build Number %d\n", sc->mlx_enq2->me_firmware_build); 292154979Smsmith device_printf(sc->mlx_dev, " Fault Management Type %d\n", sc->mlx_enq2->me_fault_mgmt_type); 292254979Smsmith device_printf(sc->mlx_dev, " Features %b\n", sc->mlx_enq2->me_firmware_features, 292354979Smsmith "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n"); 292454979Smsmith 292554979Smsmith } 292651973Smsmith} 292751973Smsmith 292858188Smsmith/******************************************************************************* 292958188Smsmith * Emit a string describing the firmware handshake status code, and return a flag 293058188Smsmith * indicating whether the code represents a fatal error. 293158188Smsmith * 293258188Smsmith * Error code interpretations are from the Linux driver, and don't directly match 293358188Smsmith * the messages printed by Mylex's BIOS. This may change if documentation on the 293458188Smsmith * codes is forthcoming. 293558188Smsmith */ 293658188Smsmithstatic int 293758188Smsmithmlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2) 293858188Smsmith{ 293958188Smsmith switch(error) { 294058188Smsmith case 0x00: 294158188Smsmith device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1); 294258188Smsmith break; 294358188Smsmith case 0x08: 294458188Smsmith /* we could be neater about this and give some indication when we receive more of them */ 294558188Smsmith if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) { 294658188Smsmith device_printf(sc->mlx_dev, "spinning up drives...\n"); 294758188Smsmith sc->mlx_flags |= MLX_SPINUP_REPORTED; 294858188Smsmith } 294958188Smsmith break; 295058188Smsmith case 0x30: 295158188Smsmith device_printf(sc->mlx_dev, "configuration checksum error\n"); 295258188Smsmith break; 295358188Smsmith case 0x60: 295458188Smsmith device_printf(sc->mlx_dev, "mirror race recovery failed\n"); 295558188Smsmith break; 295658188Smsmith case 0x70: 295758188Smsmith device_printf(sc->mlx_dev, "mirror race recovery in progress\n"); 295858188Smsmith break; 295958188Smsmith case 0x90: 296058188Smsmith device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1); 296158188Smsmith break; 296258188Smsmith case 0xa0: 296358188Smsmith device_printf(sc->mlx_dev, "logical drive installation aborted\n"); 296458188Smsmith break; 296558188Smsmith case 0xb0: 296658188Smsmith device_printf(sc->mlx_dev, "mirror race on a critical system drive\n"); 296758188Smsmith break; 296858188Smsmith case 0xd0: 296958188Smsmith device_printf(sc->mlx_dev, "new controller configuration found\n"); 297058188Smsmith break; 297158188Smsmith case 0xf0: 297258188Smsmith device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n"); 297358188Smsmith return(1); 297458188Smsmith default: 297558188Smsmith device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2); 297658188Smsmith break; 297758188Smsmith } 297858188Smsmith return(0); 297958188Smsmith} 298058188Smsmith 298151973Smsmith/******************************************************************************** 298251973Smsmith ******************************************************************************** 298351973Smsmith Utility Functions 298451973Smsmith ******************************************************************************** 298551973Smsmith ********************************************************************************/ 298651973Smsmith 298751973Smsmith/******************************************************************************** 298851973Smsmith * Find the disk whose unit number is (unit) on this controller 298951973Smsmith */ 299051973Smsmithstatic struct mlx_sysdrive * 299151973Smsmithmlx_findunit(struct mlx_softc *sc, int unit) 299251973Smsmith{ 299351973Smsmith int i; 299451973Smsmith 299551973Smsmith /* search system drives */ 299651973Smsmith for (i = 0; i < MLX_MAXDRIVES; i++) { 299751973Smsmith /* is this one attached? */ 299851973Smsmith if (sc->mlx_sysdrive[i].ms_disk != 0) { 299951973Smsmith /* is this the one? */ 300051973Smsmith if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) 300151973Smsmith return(&sc->mlx_sysdrive[i]); 300251973Smsmith } 300351973Smsmith } 300451973Smsmith return(NULL); 300551973Smsmith} 3006