11ac4b82bSMike Smith /*- 21ac4b82bSMike Smith * Copyright (c) 1999 Michael Smith 31ac4b82bSMike Smith * All rights reserved. 41ac4b82bSMike Smith * 51ac4b82bSMike Smith * Redistribution and use in source and binary forms, with or without 61ac4b82bSMike Smith * modification, are permitted provided that the following conditions 71ac4b82bSMike Smith * are met: 81ac4b82bSMike Smith * 1. Redistributions of source code must retain the above copyright 91ac4b82bSMike Smith * notice, this list of conditions and the following disclaimer. 101ac4b82bSMike Smith * 2. Redistributions in binary form must reproduce the above copyright 111ac4b82bSMike Smith * notice, this list of conditions and the following disclaimer in the 121ac4b82bSMike Smith * documentation and/or other materials provided with the distribution. 131ac4b82bSMike Smith * 141ac4b82bSMike Smith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151ac4b82bSMike Smith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161ac4b82bSMike Smith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171ac4b82bSMike Smith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181ac4b82bSMike Smith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191ac4b82bSMike Smith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201ac4b82bSMike Smith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211ac4b82bSMike Smith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221ac4b82bSMike Smith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231ac4b82bSMike Smith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241ac4b82bSMike Smith * SUCH DAMAGE. 251ac4b82bSMike Smith * 261ac4b82bSMike Smith * $FreeBSD$ 271ac4b82bSMike Smith */ 281ac4b82bSMike Smith 291ac4b82bSMike Smith /* 301ac4b82bSMike Smith * Driver for the Mylex DAC960 family of RAID controllers. 311ac4b82bSMike Smith */ 321ac4b82bSMike Smith 331ac4b82bSMike Smith #include <sys/param.h> 341ac4b82bSMike Smith #include <sys/systm.h> 351ac4b82bSMike Smith #include <sys/malloc.h> 361ac4b82bSMike Smith #include <sys/kernel.h> 371ac4b82bSMike Smith 381ac4b82bSMike Smith #include <sys/buf.h> 391ac4b82bSMike Smith #include <sys/bus.h> 401ac4b82bSMike Smith #include <sys/conf.h> 411ac4b82bSMike Smith #include <sys/devicestat.h> 421ac4b82bSMike Smith #include <sys/disk.h> 431ac4b82bSMike Smith 441ac4b82bSMike Smith #include <machine/resource.h> 451ac4b82bSMike Smith #include <machine/bus.h> 461ac4b82bSMike Smith #include <machine/clock.h> 471ac4b82bSMike Smith #include <sys/rman.h> 481ac4b82bSMike Smith 491ac4b82bSMike Smith #include <dev/mlx/mlxio.h> 501ac4b82bSMike Smith #include <dev/mlx/mlxvar.h> 511ac4b82bSMike Smith #include <dev/mlx/mlxreg.h> 521ac4b82bSMike Smith 531ac4b82bSMike Smith #if 0 541ac4b82bSMike Smith #define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args) 551ac4b82bSMike Smith #else 561ac4b82bSMike Smith #define debug(fmt, args...) 571ac4b82bSMike Smith #endif 581ac4b82bSMike Smith 591ac4b82bSMike Smith #define MLX_CDEV_MAJOR 130 601ac4b82bSMike Smith 611ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = { 621ac4b82bSMike Smith /* open */ mlx_open, 631ac4b82bSMike Smith /* close */ mlx_close, 641ac4b82bSMike Smith /* read */ noread, 651ac4b82bSMike Smith /* write */ nowrite, 661ac4b82bSMike Smith /* ioctl */ mlx_ioctl, 671ac4b82bSMike Smith /* poll */ nopoll, 681ac4b82bSMike Smith /* mmap */ nommap, 691ac4b82bSMike Smith /* strategy */ nostrategy, 701ac4b82bSMike Smith /* name */ "mlx", 711ac4b82bSMike Smith /* maj */ MLX_CDEV_MAJOR, 721ac4b82bSMike Smith /* dump */ nodump, 731ac4b82bSMike Smith /* psize */ nopsize, 741ac4b82bSMike Smith /* flags */ 0, 75f6b84b08SMike Smith /* bmaj */ -1 761ac4b82bSMike Smith }; 771ac4b82bSMike Smith 781ac4b82bSMike Smith static int cdev_registered = 0; 791ac4b82bSMike Smith devclass_t mlx_devclass; 801ac4b82bSMike Smith 811ac4b82bSMike Smith /* 821ac4b82bSMike Smith * Per-interface accessor methods 831ac4b82bSMike Smith */ 841ac4b82bSMike Smith static int mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 851ac4b82bSMike Smith static int mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 861ac4b82bSMike Smith static void mlx_v3_intaction(struct mlx_softc *sc, int action); 871ac4b82bSMike Smith 88f6b84b08SMike Smith static int mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 89f6b84b08SMike Smith static int mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 90f6b84b08SMike Smith static void mlx_v4_intaction(struct mlx_softc *sc, int action); 91f6b84b08SMike Smith 921ac4b82bSMike Smith /* 931ac4b82bSMike Smith * Status monitoring 941ac4b82bSMike Smith */ 951ac4b82bSMike Smith static void mlx_periodic(void *data); 961ac4b82bSMike Smith static void mlx_periodic_enquiry(struct mlx_command *mc); 971ac4b82bSMike Smith static void mlx_periodic_eventlog_poll(struct mlx_softc *sc); 981ac4b82bSMike Smith static void mlx_periodic_eventlog_respond(struct mlx_command *mc); 991ac4b82bSMike Smith static void mlx_periodic_rebuild(struct mlx_command *mc); 1001ac4b82bSMike Smith 1011ac4b82bSMike Smith /* 1021ac4b82bSMike Smith * Channel Pause 1031ac4b82bSMike Smith */ 1041ac4b82bSMike Smith static void mlx_pause_action(struct mlx_softc *sc); 1051ac4b82bSMike Smith static void mlx_pause_done(struct mlx_command *mc); 1061ac4b82bSMike Smith 1071ac4b82bSMike Smith /* 1081ac4b82bSMike Smith * Command submission. 1091ac4b82bSMike Smith */ 1101ac4b82bSMike Smith static void *mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, 1111ac4b82bSMike Smith void (*complete)(struct mlx_command *mc)); 1121ac4b82bSMike Smith static int mlx_flush(struct mlx_softc *sc); 1131ac4b82bSMike Smith static int mlx_rebuild(struct mlx_softc *sc, int channel, int target); 1141ac4b82bSMike Smith static int mlx_wait_command(struct mlx_command *mc); 1151ac4b82bSMike Smith static int mlx_poll_command(struct mlx_command *mc); 1161ac4b82bSMike Smith static void mlx_startio(struct mlx_softc *sc); 1171ac4b82bSMike Smith static void mlx_completeio(struct mlx_command *mc); 1181ac4b82bSMike Smith static int mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu); 1191ac4b82bSMike Smith 1201ac4b82bSMike Smith /* 1211ac4b82bSMike Smith * Command buffer allocation. 1221ac4b82bSMike Smith */ 1231ac4b82bSMike Smith static struct mlx_command *mlx_alloccmd(struct mlx_softc *sc); 1241ac4b82bSMike Smith static void mlx_releasecmd(struct mlx_command *mc); 1251ac4b82bSMike Smith static void mlx_freecmd(struct mlx_command *mc); 1261ac4b82bSMike Smith 1271ac4b82bSMike Smith /* 1281ac4b82bSMike Smith * Command management. 1291ac4b82bSMike Smith */ 1301ac4b82bSMike Smith static int mlx_getslot(struct mlx_command *mc); 1311ac4b82bSMike Smith static void mlx_mapcmd(struct mlx_command *mc); 1321ac4b82bSMike Smith static void mlx_unmapcmd(struct mlx_command *mc); 1331ac4b82bSMike Smith static int mlx_start(struct mlx_command *mc); 1341ac4b82bSMike Smith static int mlx_done(struct mlx_softc *sc); 1351ac4b82bSMike Smith static void mlx_complete(struct mlx_softc *sc); 1361ac4b82bSMike Smith 1371ac4b82bSMike Smith /* 1381ac4b82bSMike Smith * Debugging. 1391ac4b82bSMike Smith */ 1401ac4b82bSMike Smith static char *mlx_diagnose_command(struct mlx_command *mc); 1411ac4b82bSMike Smith static char *mlx_name_controller(u_int32_t hwid); 1421ac4b82bSMike Smith 1431ac4b82bSMike Smith 1441ac4b82bSMike Smith /* 1451ac4b82bSMike Smith * Utility functions. 1461ac4b82bSMike Smith */ 1471ac4b82bSMike Smith static struct mlx_sysdrive *mlx_findunit(struct mlx_softc *sc, int unit); 1481ac4b82bSMike Smith 1491ac4b82bSMike Smith /******************************************************************************** 1501ac4b82bSMike Smith ******************************************************************************** 1511ac4b82bSMike Smith Public Interfaces 1521ac4b82bSMike Smith ******************************************************************************** 1531ac4b82bSMike Smith ********************************************************************************/ 1541ac4b82bSMike Smith 1551ac4b82bSMike Smith /******************************************************************************** 1561ac4b82bSMike Smith * Free all of the resources associated with (sc) 1571ac4b82bSMike Smith * 1581ac4b82bSMike Smith * Should not be called if the controller is active. 1591ac4b82bSMike Smith */ 1601ac4b82bSMike Smith void 1611ac4b82bSMike Smith mlx_free(struct mlx_softc *sc) 1621ac4b82bSMike Smith { 1631ac4b82bSMike Smith struct mlx_command *mc; 1641ac4b82bSMike Smith 1651ac4b82bSMike Smith debug("called"); 1661ac4b82bSMike Smith 1671ac4b82bSMike Smith /* cancel status timeout */ 1681ac4b82bSMike Smith untimeout(mlx_periodic, sc, sc->mlx_timeout); 1691ac4b82bSMike Smith 1701ac4b82bSMike Smith /* throw away any command buffers */ 1711ac4b82bSMike Smith while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) { 1721ac4b82bSMike Smith TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); 1731ac4b82bSMike Smith mlx_freecmd(mc); 1741ac4b82bSMike Smith } 1751ac4b82bSMike Smith 1761ac4b82bSMike Smith /* destroy data-transfer DMA tag */ 1771ac4b82bSMike Smith if (sc->mlx_buffer_dmat) 1781ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_buffer_dmat); 1791ac4b82bSMike Smith 1801ac4b82bSMike Smith /* free and destroy DMA memory and tag for s/g lists */ 1811ac4b82bSMike Smith if (sc->mlx_sgtable) 1821ac4b82bSMike Smith bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); 1831ac4b82bSMike Smith if (sc->mlx_sg_dmat) 1841ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_sg_dmat); 1851ac4b82bSMike Smith 1861ac4b82bSMike Smith /* disconnect the interrupt handler */ 1871ac4b82bSMike Smith if (sc->mlx_intr) 1881ac4b82bSMike Smith bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr); 1891ac4b82bSMike Smith if (sc->mlx_irq != NULL) 1901ac4b82bSMike Smith bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq); 1911ac4b82bSMike Smith 1921ac4b82bSMike Smith /* destroy the parent DMA tag */ 1931ac4b82bSMike Smith if (sc->mlx_parent_dmat) 1941ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_parent_dmat); 1951ac4b82bSMike Smith 1961ac4b82bSMike Smith /* release the register window mapping */ 1971ac4b82bSMike Smith if (sc->mlx_mem != NULL) 1981ac4b82bSMike Smith bus_release_resource(sc->mlx_dev, SYS_RES_MEMORY, 1991ac4b82bSMike Smith (sc->mlx_iftype == MLX_IFTYPE_3) ? MLX_CFG_BASE1 : MLX_CFG_BASE0, sc->mlx_mem); 2001ac4b82bSMike Smith } 2011ac4b82bSMike Smith 2021ac4b82bSMike Smith /******************************************************************************** 2031ac4b82bSMike Smith * Map the scatter/gather table into bus space 2041ac4b82bSMike Smith */ 2051ac4b82bSMike Smith static void 2061ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error) 2071ac4b82bSMike Smith { 2081ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)arg; 2091ac4b82bSMike Smith 2101ac4b82bSMike Smith debug("called"); 2111ac4b82bSMike Smith 2121ac4b82bSMike Smith /* save base of s/g table's address in bus space */ 2131ac4b82bSMike Smith sc->mlx_sgbusaddr = segs->ds_addr; 2141ac4b82bSMike Smith } 2151ac4b82bSMike Smith 2161ac4b82bSMike Smith static int 2171ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc) 2181ac4b82bSMike Smith { 2191ac4b82bSMike Smith size_t segsize; 2201ac4b82bSMike Smith int error; 2211ac4b82bSMike Smith 2221ac4b82bSMike Smith debug("called"); 2231ac4b82bSMike Smith 2241ac4b82bSMike Smith /* destroy any existing mappings */ 2251ac4b82bSMike Smith if (sc->mlx_sgtable) 2261ac4b82bSMike Smith bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); 2271ac4b82bSMike Smith if (sc->mlx_sg_dmat) 2281ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_sg_dmat); 2291ac4b82bSMike Smith 2301ac4b82bSMike Smith /* 2311ac4b82bSMike Smith * Create a single tag describing a region large enough to hold all of 2321ac4b82bSMike Smith * the s/g lists we will need. 2331ac4b82bSMike Smith */ 2341ac4b82bSMike Smith segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * sc->mlx_maxiop; 2351ac4b82bSMike Smith error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 2361ac4b82bSMike Smith 1, 0, /* alignment, boundary */ 2371ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* lowaddr */ 2381ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* highaddr */ 2391ac4b82bSMike Smith NULL, NULL, /* filter, filterarg */ 2401ac4b82bSMike Smith segsize, 1, /* maxsize, nsegments */ 2411ac4b82bSMike Smith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 2421ac4b82bSMike Smith 0, /* flags */ 2431ac4b82bSMike Smith &sc->mlx_sg_dmat); 2441ac4b82bSMike Smith if (error != 0) { 2451ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n"); 2461ac4b82bSMike Smith return(ENOMEM); 2471ac4b82bSMike Smith } 2481ac4b82bSMike Smith 2491ac4b82bSMike Smith /* 2501ac4b82bSMike Smith * Allocate enough s/g maps for all commands and permanently map them into 2511ac4b82bSMike Smith * controller-visible space. 2521ac4b82bSMike Smith * 2531ac4b82bSMike Smith * XXX this assumes we can get enough space for all the s/g maps in one 2541ac4b82bSMike Smith * contiguous slab. We may need to switch to a more complex arrangement where 2551ac4b82bSMike Smith * we allocate in smaller chunks and keep a lookup table from slot to bus address. 2561ac4b82bSMike Smith */ 2571ac4b82bSMike Smith error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable, BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap); 2581ac4b82bSMike Smith if (error) { 2591ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate s/g table\n"); 2601ac4b82bSMike Smith return(ENOMEM); 2611ac4b82bSMike Smith } 2621ac4b82bSMike Smith bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable, segsize, mlx_dma_map_sg, sc, 0); 2631ac4b82bSMike Smith return(0); 2641ac4b82bSMike Smith } 2651ac4b82bSMike Smith 2661ac4b82bSMike Smith /******************************************************************************** 2671ac4b82bSMike Smith * Initialise the controller and softc 2681ac4b82bSMike Smith */ 2691ac4b82bSMike Smith int 2701ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc) 2711ac4b82bSMike Smith { 2721ac4b82bSMike Smith struct mlx_enquiry *me; 2731ac4b82bSMike Smith struct mlx_enquiry2 *me2; 2741ac4b82bSMike Smith int rid, error; 2751ac4b82bSMike Smith 2761ac4b82bSMike Smith debug("called"); 2771ac4b82bSMike Smith 2781ac4b82bSMike Smith /* 2791ac4b82bSMike Smith * Initialise per-controller queues. 2801ac4b82bSMike Smith */ 2814b006d7bSMike Smith TAILQ_INIT(&sc->mlx_work); 2821ac4b82bSMike Smith TAILQ_INIT(&sc->mlx_freecmds); 2831ac4b82bSMike Smith bufq_init(&sc->mlx_bufq); 2841ac4b82bSMike Smith 2851ac4b82bSMike Smith /* 2861ac4b82bSMike Smith * Select accessor methods based on controller interface type. 2871ac4b82bSMike Smith */ 2881ac4b82bSMike Smith switch(sc->mlx_iftype) { 2891ac4b82bSMike Smith case MLX_IFTYPE_3: 2901ac4b82bSMike Smith sc->mlx_tryqueue = mlx_v3_tryqueue; 2911ac4b82bSMike Smith sc->mlx_findcomplete = mlx_v3_findcomplete; 2921ac4b82bSMike Smith sc->mlx_intaction = mlx_v3_intaction; 2931ac4b82bSMike Smith break; 294f6b84b08SMike Smith case MLX_IFTYPE_4: 295f6b84b08SMike Smith sc->mlx_tryqueue = mlx_v4_tryqueue; 296f6b84b08SMike Smith sc->mlx_findcomplete = mlx_v4_findcomplete; 297f6b84b08SMike Smith sc->mlx_intaction = mlx_v4_intaction; 298f6b84b08SMike Smith break; 2991ac4b82bSMike Smith default: 3001ac4b82bSMike Smith device_printf(sc->mlx_dev, "attaching unsupported interface version %d\n", sc->mlx_iftype); 3011ac4b82bSMike Smith return(ENXIO); /* should never happen */ 3021ac4b82bSMike Smith } 3031ac4b82bSMike Smith 3041ac4b82bSMike Smith /* disable interrupts before we start talking to the controller */ 3051ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 3061ac4b82bSMike Smith 3071ac4b82bSMike Smith /* 3081ac4b82bSMike Smith * Allocate and connect our interrupt. 3091ac4b82bSMike Smith */ 3101ac4b82bSMike Smith rid = 0; 3111ac4b82bSMike Smith sc->mlx_irq = bus_alloc_resource(sc->mlx_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); 3121ac4b82bSMike Smith if (sc->mlx_irq == NULL) { 3131ac4b82bSMike Smith device_printf(sc->mlx_dev, "couldn't allocate interrupt\n"); 3141ac4b82bSMike Smith mlx_free(sc); 3151ac4b82bSMike Smith return(ENXIO); 3161ac4b82bSMike Smith } 3171ac4b82bSMike Smith error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO, mlx_intr, sc, &sc->mlx_intr); 3181ac4b82bSMike Smith if (error) { 3191ac4b82bSMike Smith device_printf(sc->mlx_dev, "couldn't set up interrupt\n"); 3201ac4b82bSMike Smith mlx_free(sc); 3211ac4b82bSMike Smith return(ENXIO); 3221ac4b82bSMike Smith } 3231ac4b82bSMike Smith 3241ac4b82bSMike Smith /* 3251ac4b82bSMike Smith * Create DMA tag for mapping buffers into controller-addressable space. 3261ac4b82bSMike Smith */ 3271ac4b82bSMike Smith error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 3281ac4b82bSMike Smith 1, 0, /* alignment, boundary */ 3291ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* lowaddr */ 3301ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* highaddr */ 3311ac4b82bSMike Smith NULL, NULL, /* filter, filterarg */ 3321ac4b82bSMike Smith MAXBSIZE, MLX_NSEG, /* maxsize, nsegments */ 3331ac4b82bSMike Smith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 3341ac4b82bSMike Smith 0, /* flags */ 3351ac4b82bSMike Smith &sc->mlx_buffer_dmat); 3361ac4b82bSMike Smith if (error != 0) { 3371ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n"); 3381ac4b82bSMike Smith return(ENOMEM); 3391ac4b82bSMike Smith } 3401ac4b82bSMike Smith 3411ac4b82bSMike Smith /* 3421ac4b82bSMike Smith * Create an initial set of s/g mappings. 3431ac4b82bSMike Smith */ 3441ac4b82bSMike Smith sc->mlx_maxiop = 2; 3451ac4b82bSMike Smith error = mlx_sglist_map(sc); 3461ac4b82bSMike Smith if (error != 0) { 3471ac4b82bSMike Smith device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n"); 3481ac4b82bSMike Smith return(error); 3491ac4b82bSMike Smith } 3501ac4b82bSMike Smith 3511ac4b82bSMike Smith /* 3521ac4b82bSMike Smith * Probe the controller for more information. 3531ac4b82bSMike Smith */ 3541ac4b82bSMike Smith /* send an ENQUIRY to the controller */ 3551ac4b82bSMike Smith if ((me = mlx_enquire(sc, MLX_CMD_ENQUIRY, sizeof(*me), NULL)) == NULL) { 3561ac4b82bSMike Smith device_printf(sc->mlx_dev, "ENQUIRY failed\n"); 3571ac4b82bSMike Smith return(ENXIO); 3581ac4b82bSMike Smith } 3591ac4b82bSMike Smith 3601ac4b82bSMike Smith /* pull information out of the ENQUIRY result */ 3611ac4b82bSMike Smith sc->mlx_fwminor = me->me_fwminor; 3621ac4b82bSMike Smith sc->mlx_fwmajor = me->me_fwmajor; 3631ac4b82bSMike Smith sc->mlx_maxiop = me->me_max_commands; 3641ac4b82bSMike Smith sc->mlx_lastevent = sc->mlx_currevent = me->me_event_log_seq_num; 3651ac4b82bSMike Smith free(me, M_DEVBUF); 3661ac4b82bSMike Smith 3671ac4b82bSMike Smith /* send an ENQUIRY2 to the controller */ 3681ac4b82bSMike Smith if ((me2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(*me2), NULL)) == NULL) { 3691ac4b82bSMike Smith device_printf(sc->mlx_dev, "ENQUIRY2 failed\n"); 3701ac4b82bSMike Smith return(ENXIO); 3711ac4b82bSMike Smith } 3721ac4b82bSMike Smith 3731ac4b82bSMike Smith /* pull information out of the ENQUIRY2 result */ 3741ac4b82bSMike Smith sc->mlx_nchan = me2->me_configured_channels; 3751ac4b82bSMike Smith sc->mlx_maxiosize = me2->me_maxblk; 3761ac4b82bSMike Smith sc->mlx_maxtarg = me2->me_max_targets; 3771ac4b82bSMike Smith sc->mlx_maxtags = me2->me_max_tags; 3781ac4b82bSMike Smith sc->mlx_scsicap = me2->me_scsi_cap; 3791ac4b82bSMike Smith sc->mlx_hwid = me2->me_hardware_id; 3801ac4b82bSMike Smith 3811ac4b82bSMike Smith /* print a little information about the controller and ourselves */ 3824b006d7bSMike Smith device_printf(sc->mlx_dev, "Mylex %s, firmware %d.%02d, %dMB RAM\n", 3831ac4b82bSMike Smith mlx_name_controller(sc->mlx_hwid), sc->mlx_fwmajor, sc->mlx_fwminor, 3841ac4b82bSMike Smith me2->me_mem_size / (1024 * 1024)); 3851ac4b82bSMike Smith free(me2, M_DEVBUF); 3861ac4b82bSMike Smith 3871ac4b82bSMike Smith /* 3881ac4b82bSMike Smith * Do quirk/feature related things. 3891ac4b82bSMike Smith */ 3901ac4b82bSMike Smith switch(sc->mlx_iftype) { 3911ac4b82bSMike Smith case MLX_IFTYPE_3: 392f6b84b08SMike Smith /* XXX certify 3.52? */ 3934b006d7bSMike Smith if (sc->mlx_fwminor < 51) { 3944b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 3954b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n"); 3961ac4b82bSMike Smith } 3971ac4b82bSMike Smith break; 398f6b84b08SMike Smith case MLX_IFTYPE_4: 399f6b84b08SMike Smith /* XXX certify firmware versions? */ 4004b006d7bSMike Smith if (sc->mlx_fwminor < 6) { 4014b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 4024b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n"); 403f6b84b08SMike Smith } 404f6b84b08SMike Smith break; 4051ac4b82bSMike Smith default: 4061ac4b82bSMike Smith device_printf(sc->mlx_dev, "interface version corrupted to %d\n", sc->mlx_iftype); 4071ac4b82bSMike Smith return(ENXIO); /* should never happen */ 4081ac4b82bSMike Smith } 4091ac4b82bSMike Smith 4101ac4b82bSMike Smith /* 4111ac4b82bSMike Smith * Create the final set of s/g mappings now that we know how many commands 4121ac4b82bSMike Smith * the controller actually supports. 4131ac4b82bSMike Smith */ 4141ac4b82bSMike Smith error = mlx_sglist_map(sc); 4151ac4b82bSMike Smith if (error != 0) { 4161ac4b82bSMike Smith device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n"); 4171ac4b82bSMike Smith return(error); 4181ac4b82bSMike Smith } 4191ac4b82bSMike Smith 4201ac4b82bSMike Smith /* 4211ac4b82bSMike Smith * No rebuild or check is in progress. 4221ac4b82bSMike Smith */ 4231ac4b82bSMike Smith sc->mlx_rebuild = -1; 4241ac4b82bSMike Smith sc->mlx_check = -1; 4251ac4b82bSMike Smith 4261ac4b82bSMike Smith /* 4271ac4b82bSMike Smith * Register the control device on first attach. 4281ac4b82bSMike Smith */ 4291ac4b82bSMike Smith if (cdev_registered++ == 0) 4301ac4b82bSMike Smith cdevsw_add(&mlx_cdevsw); 4311ac4b82bSMike Smith 4321ac4b82bSMike Smith /* 4331ac4b82bSMike Smith * Start the timeout routine. 4341ac4b82bSMike Smith */ 4351ac4b82bSMike Smith sc->mlx_timeout = timeout(mlx_periodic, sc, hz); 4361ac4b82bSMike Smith 4371ac4b82bSMike Smith return(0); 4381ac4b82bSMike Smith } 4391ac4b82bSMike Smith 4401ac4b82bSMike Smith /******************************************************************************** 4411ac4b82bSMike Smith * Locate disk resources and attach children to them. 4421ac4b82bSMike Smith */ 4431ac4b82bSMike Smith void 4441ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc) 4451ac4b82bSMike Smith { 4461ac4b82bSMike Smith struct mlx_enq_sys_drive *mes; 4471ac4b82bSMike Smith struct mlx_sysdrive *dr; 4481ac4b82bSMike Smith int i, error; 4491ac4b82bSMike Smith 4501ac4b82bSMike Smith debug("called"); 4511ac4b82bSMike Smith 4521ac4b82bSMike Smith /* 4531ac4b82bSMike Smith * Scan all the system drives and attach children for those that 4541ac4b82bSMike Smith * don't currently have them. 4551ac4b82bSMike Smith */ 4561ac4b82bSMike Smith mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL); 4571ac4b82bSMike Smith if (mes == NULL) { 4581ac4b82bSMike Smith device_printf(sc->mlx_dev, "error fetching drive status"); 4591ac4b82bSMike Smith return; 4601ac4b82bSMike Smith } 4611ac4b82bSMike Smith 4621ac4b82bSMike Smith /* iterate over drives returned */ 4631ac4b82bSMike Smith for (i = 0, dr = &sc->mlx_sysdrive[0]; 4641ac4b82bSMike Smith (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); 4651ac4b82bSMike Smith i++, dr++) { 4661ac4b82bSMike Smith /* are we already attached to this drive? */ 4671ac4b82bSMike Smith if (dr->ms_disk == 0) { 4681ac4b82bSMike Smith /* pick up drive information */ 4691ac4b82bSMike Smith dr->ms_size = mes[i].sd_size; 470f6b84b08SMike Smith dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf; 4711ac4b82bSMike Smith dr->ms_state = mes[i].sd_state; 4721ac4b82bSMike Smith 4731ac4b82bSMike Smith /* generate geometry information */ 4741ac4b82bSMike Smith if (sc->mlx_geom == MLX_GEOM_128_32) { 4751ac4b82bSMike Smith dr->ms_heads = 128; 4761ac4b82bSMike Smith dr->ms_sectors = 32; 4771ac4b82bSMike Smith dr->ms_cylinders = dr->ms_size / (128 * 32); 4781ac4b82bSMike Smith } else { /* MLX_GEOM_255/63 */ 4791ac4b82bSMike Smith dr->ms_heads = 255; 4801ac4b82bSMike Smith dr->ms_sectors = 63; 4811ac4b82bSMike Smith dr->ms_cylinders = dr->ms_size / (255 * 63); 4821ac4b82bSMike Smith } 483fe0d4089SMatthew N. Dodd dr->ms_disk = device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1); 4841ac4b82bSMike Smith if (dr->ms_disk == 0) 4851ac4b82bSMike Smith device_printf(sc->mlx_dev, "device_add_child failed\n"); 486fe0d4089SMatthew N. Dodd device_set_ivars(dr->ms_disk, dr); 4871ac4b82bSMike Smith } 4881ac4b82bSMike Smith } 4891ac4b82bSMike Smith free(mes, M_DEVBUF); 4901ac4b82bSMike Smith if ((error = bus_generic_attach(sc->mlx_dev)) != 0) 4911ac4b82bSMike Smith device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error); 4921ac4b82bSMike Smith 4931ac4b82bSMike Smith /* mark controller back up */ 4941ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_SHUTDOWN; 4951ac4b82bSMike Smith 4961ac4b82bSMike Smith /* enable interrupts */ 4971ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); 4981ac4b82bSMike Smith } 4991ac4b82bSMike Smith 5001ac4b82bSMike Smith /******************************************************************************** 5011ac4b82bSMike Smith * Disconnect from the controller completely, in preparation for unload. 5021ac4b82bSMike Smith */ 5031ac4b82bSMike Smith int 5041ac4b82bSMike Smith mlx_detach(device_t dev) 5051ac4b82bSMike Smith { 5061ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 5071ac4b82bSMike Smith int error; 5081ac4b82bSMike Smith 5091ac4b82bSMike Smith debug("called"); 5101ac4b82bSMike Smith 5111ac4b82bSMike Smith if (sc->mlx_state & MLX_STATE_OPEN) 5121ac4b82bSMike Smith return(EBUSY); 5131ac4b82bSMike Smith 5141ac4b82bSMike Smith if ((error = mlx_shutdown(dev))) 5151ac4b82bSMike Smith return(error); 5161ac4b82bSMike Smith 5171ac4b82bSMike Smith mlx_free(sc); 5181ac4b82bSMike Smith 5191ac4b82bSMike Smith /* 5201ac4b82bSMike Smith * Deregister the control device on last detach. 5211ac4b82bSMike Smith */ 5221ac4b82bSMike Smith if (--cdev_registered == 0) 5231ac4b82bSMike Smith cdevsw_remove(&mlx_cdevsw); 5241ac4b82bSMike Smith 5251ac4b82bSMike Smith return(0); 5261ac4b82bSMike Smith } 5271ac4b82bSMike Smith 5281ac4b82bSMike Smith /******************************************************************************** 5291ac4b82bSMike Smith * Bring the controller down to a dormant state and detach all child devices. 5301ac4b82bSMike Smith * 5311ac4b82bSMike Smith * This function is called before detach, system shutdown, or before performing 5321ac4b82bSMike Smith * an operation which may add or delete system disks. (Call mlx_startup to 5331ac4b82bSMike Smith * resume normal operation.) 5341ac4b82bSMike Smith * 5351ac4b82bSMike Smith * Note that we can assume that the bufq on the controller is empty, as we won't 5361ac4b82bSMike Smith * allow shutdown if any device is open. 5371ac4b82bSMike Smith */ 5381ac4b82bSMike Smith int 5391ac4b82bSMike Smith mlx_shutdown(device_t dev) 5401ac4b82bSMike Smith { 5411ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 5421ac4b82bSMike Smith struct mlxd_softc *mlxd; 5431ac4b82bSMike Smith int i, s, error; 5441ac4b82bSMike Smith 5451ac4b82bSMike Smith debug("called"); 5461ac4b82bSMike Smith 5471ac4b82bSMike Smith s = splbio(); 5481ac4b82bSMike Smith error = 0; 5491ac4b82bSMike Smith 5501ac4b82bSMike Smith /* assume we're going to shut down */ 5511ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_SHUTDOWN; 5521ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 5531ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 5541ac4b82bSMike Smith mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk); 5551ac4b82bSMike Smith if (mlxd->mlxd_flags & MLXD_OPEN) { /* drive is mounted, abort shutdown */ 5561ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_SHUTDOWN; 5571ac4b82bSMike Smith device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't shutdown\n"); 5581ac4b82bSMike Smith error = EBUSY; 5591ac4b82bSMike Smith goto out; 5601ac4b82bSMike Smith } 5611ac4b82bSMike Smith } 5621ac4b82bSMike Smith } 5631ac4b82bSMike Smith 5641ac4b82bSMike Smith /* flush controller */ 5651ac4b82bSMike Smith device_printf(sc->mlx_dev, "flushing cache..."); 5661ac4b82bSMike Smith if (mlx_flush(sc)) { 5671ac4b82bSMike Smith printf("failed\n"); 5681ac4b82bSMike Smith } else { 5691ac4b82bSMike Smith printf("done\n"); 5701ac4b82bSMike Smith } 5711ac4b82bSMike Smith 5721ac4b82bSMike Smith /* delete all our child devices */ 5731ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 5741ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 5751ac4b82bSMike Smith if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0) 5761ac4b82bSMike Smith goto out; 5771ac4b82bSMike Smith sc->mlx_sysdrive[i].ms_disk = 0; 5781ac4b82bSMike Smith } 5791ac4b82bSMike Smith } 5801ac4b82bSMike Smith 5811ac4b82bSMike Smith bus_generic_detach(sc->mlx_dev); 5821ac4b82bSMike Smith 5831ac4b82bSMike Smith out: 5841ac4b82bSMike Smith splx(s); 5851ac4b82bSMike Smith return(error); 5861ac4b82bSMike Smith } 5871ac4b82bSMike Smith 5881ac4b82bSMike Smith /******************************************************************************** 5891ac4b82bSMike Smith * Bring the controller to a quiescent state, ready for system suspend. 5901ac4b82bSMike Smith */ 5911ac4b82bSMike Smith int 5921ac4b82bSMike Smith mlx_suspend(device_t dev) 5931ac4b82bSMike Smith { 5941ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 5951ac4b82bSMike Smith int s; 5961ac4b82bSMike Smith 5971ac4b82bSMike Smith debug("called"); 5981ac4b82bSMike Smith 5991ac4b82bSMike Smith s = splbio(); 6001ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_SUSPEND; 6011ac4b82bSMike Smith 6021ac4b82bSMike Smith /* flush controller */ 6031ac4b82bSMike Smith device_printf(sc->mlx_dev, "flushing cache..."); 6041ac4b82bSMike Smith printf("%s\n", mlx_flush(sc) ? "failed" : "done"); 6051ac4b82bSMike Smith 6061ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 6071ac4b82bSMike Smith splx(s); 6081ac4b82bSMike Smith 6091ac4b82bSMike Smith return(0); 6101ac4b82bSMike Smith } 6111ac4b82bSMike Smith 6121ac4b82bSMike Smith /******************************************************************************** 6131ac4b82bSMike Smith * Bring the controller back to a state ready for operation. 6141ac4b82bSMike Smith */ 6151ac4b82bSMike Smith int 6161ac4b82bSMike Smith mlx_resume(device_t dev) 6171ac4b82bSMike Smith { 6181ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 6191ac4b82bSMike Smith 6201ac4b82bSMike Smith debug("called"); 6211ac4b82bSMike Smith 6221ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_SUSPEND; 6231ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); 6241ac4b82bSMike Smith 6251ac4b82bSMike Smith return(0); 6261ac4b82bSMike Smith } 6271ac4b82bSMike Smith 6281ac4b82bSMike Smith /******************************************************************************* 6291ac4b82bSMike Smith * Take an interrupt, or be poked by other code to look for interrupt-worthy 6301ac4b82bSMike Smith * status. 6311ac4b82bSMike Smith */ 6321ac4b82bSMike Smith void 6331ac4b82bSMike Smith mlx_intr(void *arg) 6341ac4b82bSMike Smith { 6351ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)arg; 6361ac4b82bSMike Smith int worked; 6371ac4b82bSMike Smith 6381ac4b82bSMike Smith debug("called"); 6391ac4b82bSMike Smith 6401ac4b82bSMike Smith /* spin collecting finished commands */ 6411ac4b82bSMike Smith worked = 0; 6421ac4b82bSMike Smith while (mlx_done(sc)) 6431ac4b82bSMike Smith worked = 1; 6441ac4b82bSMike Smith 6451ac4b82bSMike Smith /* did we do anything? */ 6461ac4b82bSMike Smith if (worked) 6471ac4b82bSMike Smith mlx_complete(sc); 6481ac4b82bSMike Smith }; 6491ac4b82bSMike Smith 6501ac4b82bSMike Smith /******************************************************************************* 6511ac4b82bSMike Smith * Receive a buf structure from a child device and queue it on a particular 6521ac4b82bSMike Smith * disk resource, then poke the disk resource to start as much work as it can. 6531ac4b82bSMike Smith */ 6541ac4b82bSMike Smith int 6551ac4b82bSMike Smith mlx_submit_buf(struct mlx_softc *sc, struct buf *bp) 6561ac4b82bSMike Smith { 6574b006d7bSMike Smith int s; 6584b006d7bSMike Smith 6591ac4b82bSMike Smith debug("called"); 6601ac4b82bSMike Smith 6614b006d7bSMike Smith s = splbio(); 6621ac4b82bSMike Smith bufq_insert_tail(&sc->mlx_bufq, bp); 6631ac4b82bSMike Smith sc->mlx_waitbufs++; 6644b006d7bSMike Smith splx(s); 6651ac4b82bSMike Smith mlx_startio(sc); 6661ac4b82bSMike Smith return(0); 6671ac4b82bSMike Smith } 6681ac4b82bSMike Smith 6691ac4b82bSMike Smith /******************************************************************************** 6701ac4b82bSMike Smith * Accept an open operation on the control device. 6711ac4b82bSMike Smith */ 6721ac4b82bSMike Smith int 6731ac4b82bSMike Smith mlx_open(dev_t dev, int flags, int fmt, struct proc *p) 6741ac4b82bSMike Smith { 6751ac4b82bSMike Smith int unit = minor(dev); 6761ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 6771ac4b82bSMike Smith 6781ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_OPEN; 6791ac4b82bSMike Smith return(0); 6801ac4b82bSMike Smith } 6811ac4b82bSMike Smith 6821ac4b82bSMike Smith /******************************************************************************** 6831ac4b82bSMike Smith * Accept the last close on the control device. 6841ac4b82bSMike Smith */ 6851ac4b82bSMike Smith int 6861ac4b82bSMike Smith mlx_close(dev_t dev, int flags, int fmt, struct proc *p) 6871ac4b82bSMike Smith { 6881ac4b82bSMike Smith int unit = minor(dev); 6891ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 6901ac4b82bSMike Smith 6911ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_OPEN; 6921ac4b82bSMike Smith return (0); 6931ac4b82bSMike Smith } 6941ac4b82bSMike Smith 6951ac4b82bSMike Smith /******************************************************************************** 6961ac4b82bSMike Smith * Handle controller-specific control operations. 6971ac4b82bSMike Smith */ 6981ac4b82bSMike Smith int 6991ac4b82bSMike Smith mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) 7001ac4b82bSMike Smith { 7011ac4b82bSMike Smith int unit = minor(dev); 7021ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 7031ac4b82bSMike Smith int *arg = (int *)addr; 7041ac4b82bSMike Smith struct mlx_pause *mp; 7051ac4b82bSMike Smith struct mlx_sysdrive *dr; 7061ac4b82bSMike Smith struct mlxd_softc *mlxd; 7071ac4b82bSMike Smith int i, error; 7081ac4b82bSMike Smith 7091ac4b82bSMike Smith switch(cmd) { 7101ac4b82bSMike Smith /* 7111ac4b82bSMike Smith * Enumerate connected system drives; returns the first system drive's 7121ac4b82bSMike Smith * unit number if *arg is -1, or the next unit after *arg if it's 7131ac4b82bSMike Smith * a valid unit on this controller. 7141ac4b82bSMike Smith */ 7151ac4b82bSMike Smith case MLX_NEXT_CHILD: 7161ac4b82bSMike Smith /* search system drives */ 7171ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 7181ac4b82bSMike Smith /* is this one attached? */ 7191ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 7201ac4b82bSMike Smith /* looking for the next one we come across? */ 7211ac4b82bSMike Smith if (*arg == -1) { 7221ac4b82bSMike Smith *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk); 7231ac4b82bSMike Smith return(0); 7241ac4b82bSMike Smith } 7251ac4b82bSMike Smith /* we want the one after this one */ 7261ac4b82bSMike Smith if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) 7271ac4b82bSMike Smith *arg = -1; 7281ac4b82bSMike Smith } 7291ac4b82bSMike Smith } 7301ac4b82bSMike Smith return(ENOENT); 7311ac4b82bSMike Smith 7321ac4b82bSMike Smith /* 7331ac4b82bSMike Smith * Scan the controller to see whether new drives have appeared. 7341ac4b82bSMike Smith */ 7351ac4b82bSMike Smith case MLX_RESCAN_DRIVES: 7361ac4b82bSMike Smith mlx_startup(sc); 7371ac4b82bSMike Smith return(0); 7381ac4b82bSMike Smith 7391ac4b82bSMike Smith /* 7401ac4b82bSMike Smith * Disconnect from the specified drive; it may be about to go 7411ac4b82bSMike Smith * away. 7421ac4b82bSMike Smith */ 7431ac4b82bSMike Smith case MLX_DETACH_DRIVE: /* detach one drive */ 7441ac4b82bSMike Smith 7451ac4b82bSMike Smith if (((dr = mlx_findunit(sc, *arg)) == NULL) || 7461ac4b82bSMike Smith ((mlxd = device_get_softc(dr->ms_disk)) == NULL)) 7471ac4b82bSMike Smith return(ENOENT); 7481ac4b82bSMike Smith 7491ac4b82bSMike Smith device_printf(dr->ms_disk, "detaching..."); 7501ac4b82bSMike Smith error = 0; 7511ac4b82bSMike Smith if (mlxd->mlxd_flags & MLXD_OPEN) { 7521ac4b82bSMike Smith error = EBUSY; 7531ac4b82bSMike Smith goto detach_out; 7541ac4b82bSMike Smith } 7551ac4b82bSMike Smith 7561ac4b82bSMike Smith /* flush controller */ 7571ac4b82bSMike Smith if (mlx_flush(sc)) { 7581ac4b82bSMike Smith error = EBUSY; 7591ac4b82bSMike Smith goto detach_out; 7601ac4b82bSMike Smith } 7611ac4b82bSMike Smith 7621ac4b82bSMike Smith /* nuke drive */ 7631ac4b82bSMike Smith if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0) 7641ac4b82bSMike Smith goto detach_out; 7651ac4b82bSMike Smith dr->ms_disk = 0; 7661ac4b82bSMike Smith bus_generic_detach(sc->mlx_dev); 7671ac4b82bSMike Smith 7681ac4b82bSMike Smith detach_out: 7691ac4b82bSMike Smith if (error) { 7701ac4b82bSMike Smith printf("failed\n"); 7711ac4b82bSMike Smith } else { 7721ac4b82bSMike Smith printf("done\n"); 7731ac4b82bSMike Smith } 7741ac4b82bSMike Smith return(error); 7751ac4b82bSMike Smith 7761ac4b82bSMike Smith /* 7771ac4b82bSMike Smith * Pause one or more SCSI channels for a period of time, to assist 7781ac4b82bSMike Smith * in the process of hot-swapping devices. 7791ac4b82bSMike Smith * 7801ac4b82bSMike Smith * Note that at least the 3.51 firmware on the DAC960PL doesn't seem 7811ac4b82bSMike Smith * to do this right. 7821ac4b82bSMike Smith */ 7831ac4b82bSMike Smith case MLX_PAUSE_CHANNEL: /* schedule a channel pause */ 7841ac4b82bSMike Smith /* Does this command work on this firmware? */ 7851ac4b82bSMike Smith if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS)) 7861ac4b82bSMike Smith return(EOPNOTSUPP); 7871ac4b82bSMike Smith 7881ac4b82bSMike Smith mp = (struct mlx_pause *)addr; 7891ac4b82bSMike Smith if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) { 7901ac4b82bSMike Smith /* cancel a pending pause operation */ 7911ac4b82bSMike Smith sc->mlx_pause.mp_which = 0; 7921ac4b82bSMike Smith } else { 7931ac4b82bSMike Smith /* fix for legal channels */ 7941ac4b82bSMike Smith mp->mp_which &= ((1 << sc->mlx_nchan) -1); 7951ac4b82bSMike Smith /* check time values */ 7961ac4b82bSMike Smith if ((mp->mp_when < 0) || (mp->mp_when > 3600)) 7971ac4b82bSMike Smith return(EINVAL); 7981ac4b82bSMike Smith if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30))) 7991ac4b82bSMike Smith return(EINVAL); 8001ac4b82bSMike Smith 8011ac4b82bSMike Smith /* check for a pause currently running */ 8021ac4b82bSMike Smith if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) 8031ac4b82bSMike Smith return(EBUSY); 8041ac4b82bSMike Smith 8051ac4b82bSMike Smith /* looks ok, go with it */ 8061ac4b82bSMike Smith sc->mlx_pause.mp_which = mp->mp_which; 8071ac4b82bSMike Smith sc->mlx_pause.mp_when = time_second + mp->mp_when; 8081ac4b82bSMike Smith sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong; 8091ac4b82bSMike Smith } 8101ac4b82bSMike Smith return(0); 8111ac4b82bSMike Smith 8121ac4b82bSMike Smith /* 8131ac4b82bSMike Smith * Accept a command passthrough-style. 8141ac4b82bSMike Smith */ 8151ac4b82bSMike Smith case MLX_COMMAND: 8161ac4b82bSMike Smith return(mlx_user_command(sc, (struct mlx_usercommand *)addr)); 8171ac4b82bSMike Smith 8181ac4b82bSMike Smith default: 8191ac4b82bSMike Smith return(ENOTTY); 8201ac4b82bSMike Smith } 8211ac4b82bSMike Smith } 8221ac4b82bSMike Smith 8231ac4b82bSMike Smith /******************************************************************************** 8241ac4b82bSMike Smith * Handle operations requested by a System Drive connected to this controller. 8251ac4b82bSMike Smith */ 8261ac4b82bSMike Smith int 8271ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd, 8281ac4b82bSMike Smith caddr_t addr, int32_t flag, struct proc *p) 8291ac4b82bSMike Smith { 8301ac4b82bSMike Smith struct mlxd_rebuild *mr = (struct mlxd_rebuild *)addr; 8311ac4b82bSMike Smith struct mlxd_rebuild_status *mp = (struct mlxd_rebuild_status *)addr; 8321ac4b82bSMike Smith int *arg = (int *)addr; 8331ac4b82bSMike Smith int error; 8341ac4b82bSMike Smith 8351ac4b82bSMike Smith switch(cmd) { 8361ac4b82bSMike Smith /* 8371ac4b82bSMike Smith * Return the current status of this drive. 8381ac4b82bSMike Smith */ 8391ac4b82bSMike Smith case MLXD_STATUS: 8401ac4b82bSMike Smith *arg = drive->ms_state; 8411ac4b82bSMike Smith return(0); 8421ac4b82bSMike Smith 8431ac4b82bSMike Smith /* 8441ac4b82bSMike Smith * Start a background rebuild on this drive. 8451ac4b82bSMike Smith */ 8461ac4b82bSMike Smith case MLXD_REBUILDASYNC: 8471ac4b82bSMike Smith /* XXX lock? */ 8481ac4b82bSMike Smith if (sc->mlx_rebuild >= 0) 8491ac4b82bSMike Smith return(EBUSY); 8501ac4b82bSMike Smith sc->mlx_rebuild = drive - &sc->mlx_sysdrive[0]; 8511ac4b82bSMike Smith 8521ac4b82bSMike Smith switch (mlx_rebuild(sc, mr->rb_channel, mr->rb_target)) { 8531ac4b82bSMike Smith case 0: 8541ac4b82bSMike Smith drive->ms_state = MLX_SYSD_REBUILD; 8551ac4b82bSMike Smith error = 0; 8561ac4b82bSMike Smith break; 8571ac4b82bSMike Smith case 0x10000: 8581ac4b82bSMike Smith error = ENOMEM; /* couldn't set up the command */ 8591ac4b82bSMike Smith break; 8601ac4b82bSMike Smith case 0x0002: 8611ac4b82bSMike Smith case 0x0106: 8621ac4b82bSMike Smith error = EBUSY; 8631ac4b82bSMike Smith break; 8641ac4b82bSMike Smith case 0x0004: 8651ac4b82bSMike Smith error = EIO; 8661ac4b82bSMike Smith break; 8671ac4b82bSMike Smith case 0x0105: 8681ac4b82bSMike Smith error = ERANGE; 8691ac4b82bSMike Smith break; 8701ac4b82bSMike Smith default: 8711ac4b82bSMike Smith error = EINVAL; 8721ac4b82bSMike Smith break; 8731ac4b82bSMike Smith } 8741ac4b82bSMike Smith if (error != 0) 8751ac4b82bSMike Smith sc->mlx_rebuild = -1; 8761ac4b82bSMike Smith return(error); 8771ac4b82bSMike Smith 8781ac4b82bSMike Smith /* 8791ac4b82bSMike Smith * Start a background consistency check on this drive. 8801ac4b82bSMike Smith */ 8811ac4b82bSMike Smith case MLXD_CHECKASYNC: /* start a background consistency check */ 8821ac4b82bSMike Smith /* XXX implement */ 8831ac4b82bSMike Smith break; 8841ac4b82bSMike Smith 8851ac4b82bSMike Smith /* 8861ac4b82bSMike Smith * Get the status of the current rebuild or consistency check. 8871ac4b82bSMike Smith */ 8881ac4b82bSMike Smith case MLXD_REBUILDSTAT: 8891ac4b82bSMike Smith 8901ac4b82bSMike Smith if (sc->mlx_rebuild >= 0) { /* may be a second or so out of date */ 8911ac4b82bSMike Smith mp->rs_drive = sc->mlx_rebuild; 8921ac4b82bSMike Smith mp->rs_size = sc->mlx_sysdrive[sc->mlx_rebuild].ms_size; 8931ac4b82bSMike Smith mp->rs_remaining = sc->mlx_rebuildstat; 8941ac4b82bSMike Smith return(0); 8951ac4b82bSMike Smith } else if (sc->mlx_check >= 0) { 8961ac4b82bSMike Smith /* XXX implement */ 8971ac4b82bSMike Smith } else { 8981ac4b82bSMike Smith /* XXX should return status of last completed operation? */ 8991ac4b82bSMike Smith return(EINVAL); 9001ac4b82bSMike Smith } 9011ac4b82bSMike Smith 9021ac4b82bSMike Smith } 9031ac4b82bSMike Smith return(ENOIOCTL); 9041ac4b82bSMike Smith } 9051ac4b82bSMike Smith 9061ac4b82bSMike Smith 9071ac4b82bSMike Smith /******************************************************************************** 9081ac4b82bSMike Smith ******************************************************************************** 9091ac4b82bSMike Smith Status Monitoring 9101ac4b82bSMike Smith ******************************************************************************** 9111ac4b82bSMike Smith ********************************************************************************/ 9121ac4b82bSMike Smith 9131ac4b82bSMike Smith #define MLX_PERIODIC_ISBUSY(sc) (sc->mlx_polling <= 0) 9141ac4b82bSMike Smith #define MLX_PERIODIC_BUSY(sc) atomic_add_int(&sc->mlx_polling, 1); 9151ac4b82bSMike Smith #define MLX_PERIODIC_UNBUSY(sc) atomic_subtract_int(&sc->mlx_polling, 1); 9161ac4b82bSMike Smith 9171ac4b82bSMike Smith /******************************************************************************** 9181ac4b82bSMike Smith * Fire off commands to periodically check the status of connected drives. 9191ac4b82bSMike Smith */ 9201ac4b82bSMike Smith static void 9211ac4b82bSMike Smith mlx_periodic(void *data) 9221ac4b82bSMike Smith { 9231ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)data; 9241ac4b82bSMike Smith 9251ac4b82bSMike Smith debug("called"); 9261ac4b82bSMike Smith 9271ac4b82bSMike Smith /* 9281ac4b82bSMike Smith * Run a bus pause? 9291ac4b82bSMike Smith */ 9301ac4b82bSMike Smith if ((sc->mlx_pause.mp_which != 0) && 9311ac4b82bSMike Smith (sc->mlx_pause.mp_when > 0) && 9321ac4b82bSMike Smith (time_second >= sc->mlx_pause.mp_when)){ 9331ac4b82bSMike Smith 9341ac4b82bSMike Smith mlx_pause_action(sc); /* pause is running */ 9351ac4b82bSMike Smith sc->mlx_pause.mp_when = 0; 9361ac4b82bSMike Smith sysbeep(500, hz); 9371ac4b82bSMike Smith 9381ac4b82bSMike Smith /* 9391ac4b82bSMike Smith * Bus pause still running? 9401ac4b82bSMike Smith */ 9411ac4b82bSMike Smith } else if ((sc->mlx_pause.mp_which != 0) && 9421ac4b82bSMike Smith (sc->mlx_pause.mp_when == 0)) { 9431ac4b82bSMike Smith 9441ac4b82bSMike Smith /* time to stop bus pause? */ 9451ac4b82bSMike Smith if (time_second >= sc->mlx_pause.mp_howlong) { 9461ac4b82bSMike Smith mlx_pause_action(sc); 9471ac4b82bSMike Smith sc->mlx_pause.mp_which = 0; /* pause is complete */ 9481ac4b82bSMike Smith sysbeep(500, hz); 9491ac4b82bSMike Smith } else { 9501ac4b82bSMike Smith sysbeep((time_second % 5) * 100 + 500, hz/8); 9511ac4b82bSMike Smith } 9521ac4b82bSMike Smith 9531ac4b82bSMike Smith /* 9541ac4b82bSMike Smith * Run normal periodic activities? 9551ac4b82bSMike Smith */ 9561ac4b82bSMike Smith } else if (MLX_PERIODIC_ISBUSY(sc)) { 9571ac4b82bSMike Smith 9581ac4b82bSMike Smith /* time to perform a periodic status poll? XXX tuneable interval? */ 959f6b84b08SMike Smith if (time_second > (sc->mlx_lastpoll + 10)) { 9601ac4b82bSMike Smith sc->mlx_lastpoll = time_second; 9611ac4b82bSMike Smith 9621ac4b82bSMike Smith /* for caution's sake */ 9631ac4b82bSMike Smith if (sc->mlx_polling < 0) { 9641ac4b82bSMike Smith device_printf(sc->mlx_dev, "mlx_polling < 0\n"); 9651ac4b82bSMike Smith atomic_set_int(&sc->mlx_polling, 0); 9661ac4b82bSMike Smith } 9671ac4b82bSMike Smith 9681ac4b82bSMike Smith /* 9691ac4b82bSMike Smith * Check controller status. 9701ac4b82bSMike Smith */ 9711ac4b82bSMike Smith MLX_PERIODIC_BUSY(sc); 9721ac4b82bSMike Smith mlx_enquire(sc, MLX_CMD_ENQUIRY, sizeof(struct mlx_enquiry), mlx_periodic_enquiry); 9731ac4b82bSMike Smith 9741ac4b82bSMike Smith /* 9751ac4b82bSMike Smith * Check system drive status. 9761ac4b82bSMike Smith * 9771ac4b82bSMike Smith * XXX This might be better left to event-driven detection, eg. I/O to an offline 9781ac4b82bSMike Smith * drive will detect it's offline, rebuilds etc. should detect the drive is back 9791ac4b82bSMike Smith * online. 9801ac4b82bSMike Smith */ 9811ac4b82bSMike Smith MLX_PERIODIC_BUSY(sc); 9821ac4b82bSMike Smith mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES, 9831ac4b82bSMike Smith mlx_periodic_enquiry); 9841ac4b82bSMike Smith } 9851ac4b82bSMike Smith 9861ac4b82bSMike Smith /* 9871ac4b82bSMike Smith * Get drive rebuild/check status 9881ac4b82bSMike Smith */ 9891ac4b82bSMike Smith if (sc->mlx_rebuild >= 0) { 9901ac4b82bSMike Smith MLX_PERIODIC_BUSY(sc); 9911ac4b82bSMike Smith mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild); 9921ac4b82bSMike Smith } 9931ac4b82bSMike Smith } else { 9941ac4b82bSMike Smith /* 9951ac4b82bSMike Smith * If things are still running from the last poll, complain about it. 9961ac4b82bSMike Smith * 9971ac4b82bSMike Smith * XXX If this becomes an issue, we should have some way of telling what 9981ac4b82bSMike Smith * has become stuck. 9991ac4b82bSMike Smith */ 10001ac4b82bSMike Smith device_printf(sc->mlx_dev, "poll still busy (%d)\n", sc->mlx_polling); 10011ac4b82bSMike Smith } 10021ac4b82bSMike Smith 10031ac4b82bSMike Smith /* XXX check for wedged/timed out commands? */ 10041ac4b82bSMike Smith 10051ac4b82bSMike Smith /* reschedule another poll next second or so */ 10061ac4b82bSMike Smith sc->mlx_timeout = timeout(mlx_periodic, sc, hz); 10071ac4b82bSMike Smith } 10081ac4b82bSMike Smith 10091ac4b82bSMike Smith /******************************************************************************** 10101ac4b82bSMike Smith * Handle the result of an ENQUIRY command instigated by periodic status polling. 10111ac4b82bSMike Smith */ 10121ac4b82bSMike Smith static void 10131ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc) 10141ac4b82bSMike Smith { 10151ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 10161ac4b82bSMike Smith 10171ac4b82bSMike Smith debug("called"); 10181ac4b82bSMike Smith 10191ac4b82bSMike Smith /* Command completed OK? */ 10201ac4b82bSMike Smith if (mc->mc_status != 0) { 10211ac4b82bSMike Smith device_printf(sc->mlx_dev, "periodic enquiry failed\n"); 10221ac4b82bSMike Smith goto out; 10231ac4b82bSMike Smith } 10241ac4b82bSMike Smith 10251ac4b82bSMike Smith /* respond to command */ 10261ac4b82bSMike Smith switch(mc->mc_mailbox[0]) { 10271ac4b82bSMike Smith /* 10281ac4b82bSMike Smith * Generic controller status update. We could do more with this than just 10291ac4b82bSMike Smith * checking the event log. 10301ac4b82bSMike Smith */ 10311ac4b82bSMike Smith case MLX_CMD_ENQUIRY: 10321ac4b82bSMike Smith { 10331ac4b82bSMike Smith struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data; 10341ac4b82bSMike Smith 10351ac4b82bSMike Smith /* New stuff in the event log? */ 10361ac4b82bSMike Smith if (me->me_event_log_seq_num != sc->mlx_lastevent) { 10371ac4b82bSMike Smith /* record where current events are up to */ 10381ac4b82bSMike Smith sc->mlx_currevent = me->me_event_log_seq_num; 10391ac4b82bSMike Smith device_printf(sc->mlx_dev, "event log pointer was %d, now %d\n", 10401ac4b82bSMike Smith sc->mlx_lastevent, sc->mlx_currevent); 10411ac4b82bSMike Smith 10421ac4b82bSMike Smith /* start poll of event log */ 10431ac4b82bSMike Smith mlx_periodic_eventlog_poll(sc); 10441ac4b82bSMike Smith } 10451ac4b82bSMike Smith break; 10461ac4b82bSMike Smith } 10471ac4b82bSMike Smith case MLX_CMD_ENQSYSDRIVE: 10481ac4b82bSMike Smith { 10491ac4b82bSMike Smith struct mlx_enq_sys_drive *mes = (struct mlx_enq_sys_drive *)mc->mc_data; 10501ac4b82bSMike Smith struct mlx_sysdrive *dr; 10511ac4b82bSMike Smith int i; 10521ac4b82bSMike Smith 10531ac4b82bSMike Smith for (i = 0, dr = &sc->mlx_sysdrive[0]; 10541ac4b82bSMike Smith (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); 10551ac4b82bSMike Smith i++) { 10561ac4b82bSMike Smith 10571ac4b82bSMike Smith /* if disk is being rebuilt, we should not check it */ 10581ac4b82bSMike Smith if (dr->ms_state == MLX_SYSD_REBUILD) { 10591ac4b82bSMike Smith /* has state been changed by controller? */ 10601ac4b82bSMike Smith if (dr->ms_state != mes[i].sd_state) { 10611ac4b82bSMike Smith switch(mes[i].sd_state) { 10621ac4b82bSMike Smith case MLX_SYSD_OFFLINE: 10631ac4b82bSMike Smith device_printf(dr->ms_disk, "drive offline\n"); 10641ac4b82bSMike Smith break; 10651ac4b82bSMike Smith case MLX_SYSD_ONLINE: 10661ac4b82bSMike Smith device_printf(dr->ms_disk, "drive online\n"); 10671ac4b82bSMike Smith break; 10681ac4b82bSMike Smith case MLX_SYSD_CRITICAL: 10691ac4b82bSMike Smith device_printf(dr->ms_disk, "drive critical\n"); 10701ac4b82bSMike Smith break; 10711ac4b82bSMike Smith } 10721ac4b82bSMike Smith /* save new state */ 10731ac4b82bSMike Smith dr->ms_state = mes[i].sd_state; 10741ac4b82bSMike Smith } 10751ac4b82bSMike Smith } 10761ac4b82bSMike Smith } 10771ac4b82bSMike Smith break; 10781ac4b82bSMike Smith } 10791ac4b82bSMike Smith default: 10801ac4b82bSMike Smith break; 10811ac4b82bSMike Smith } 10821ac4b82bSMike Smith 10831ac4b82bSMike Smith out: 10841ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 10851ac4b82bSMike Smith mlx_releasecmd(mc); 10861ac4b82bSMike Smith /* this event is done */ 10871ac4b82bSMike Smith MLX_PERIODIC_UNBUSY(sc); 10881ac4b82bSMike Smith } 10891ac4b82bSMike Smith 10901ac4b82bSMike Smith /******************************************************************************** 10911ac4b82bSMike Smith * Instigate a poll for one event log message on (sc). 10921ac4b82bSMike Smith * We only poll for one message at a time, to keep our command usage down. 10931ac4b82bSMike Smith */ 10941ac4b82bSMike Smith static void 10951ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc) 10961ac4b82bSMike Smith { 10971ac4b82bSMike Smith struct mlx_command *mc; 10981ac4b82bSMike Smith void *result = NULL; 10991ac4b82bSMike Smith int error; 11001ac4b82bSMike Smith 11011ac4b82bSMike Smith debug("called"); 11021ac4b82bSMike Smith 11031ac4b82bSMike Smith /* presume we are going to create another event */ 11041ac4b82bSMike Smith MLX_PERIODIC_BUSY(sc); 11051ac4b82bSMike Smith 11061ac4b82bSMike Smith /* get ourselves a command buffer */ 11071ac4b82bSMike Smith error = 1; 11081ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 11091ac4b82bSMike Smith goto out; 11101ac4b82bSMike Smith /* allocate the response structure */ 111133c8cb18SMike Smith if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF, M_NOWAIT)) == NULL) 11121ac4b82bSMike Smith goto out; 11131ac4b82bSMike Smith /* get a command slot */ 11141ac4b82bSMike Smith if (mlx_getslot(mc)) 11151ac4b82bSMike Smith goto out; 11161ac4b82bSMike Smith 11171ac4b82bSMike Smith /* map the command so the controller can see it */ 11181ac4b82bSMike Smith mc->mc_data = result; 111933c8cb18SMike Smith mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024; 11201ac4b82bSMike Smith mlx_mapcmd(mc); 11211ac4b82bSMike Smith 11221ac4b82bSMike Smith /* build the command to get one entry */ 11231ac4b82bSMike Smith mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0); 11241ac4b82bSMike Smith mc->mc_complete = mlx_periodic_eventlog_respond; 11251ac4b82bSMike Smith mc->mc_private = mc; 11261ac4b82bSMike Smith 11271ac4b82bSMike Smith /* start the command */ 11281ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 11291ac4b82bSMike Smith goto out; 11301ac4b82bSMike Smith 11311ac4b82bSMike Smith error = 0; /* success */ 11321ac4b82bSMike Smith out: 113333c8cb18SMike Smith if (error != 0) { 11341ac4b82bSMike Smith if (mc != NULL) 11351ac4b82bSMike Smith mlx_releasecmd(mc); 113633c8cb18SMike Smith if (result != NULL) 11371ac4b82bSMike Smith free(result, M_DEVBUF); 11381ac4b82bSMike Smith /* abort this event */ 11391ac4b82bSMike Smith MLX_PERIODIC_UNBUSY(sc); 11401ac4b82bSMike Smith } 114133c8cb18SMike Smith } 11421ac4b82bSMike Smith 11431ac4b82bSMike Smith /******************************************************************************** 11441ac4b82bSMike Smith * Handle the result of polling for a log message, generate diagnostic output. 11451ac4b82bSMike Smith * If this wasn't the last message waiting for us, we'll go collect another. 11461ac4b82bSMike Smith */ 11471ac4b82bSMike Smith static char *mlx_sense_messages[] = { 11481ac4b82bSMike Smith "because write recovery failed", 11491ac4b82bSMike Smith "because of SCSI bus reset failure", 11501ac4b82bSMike Smith "because of double check condition", 11511ac4b82bSMike Smith "because it was removed", 11521ac4b82bSMike Smith "because of gross error on SCSI chip", 11531ac4b82bSMike Smith "because of bad tag returned from drive", 11541ac4b82bSMike Smith "because of timeout on SCSI command", 11551ac4b82bSMike Smith "because of reset SCSI command issued from system", 11561ac4b82bSMike Smith "because busy or parity error count exceeded limit", 11571ac4b82bSMike Smith "because of 'kill drive' command from system", 11581ac4b82bSMike Smith "because of selection timeout", 11591ac4b82bSMike Smith "due to SCSI phase sequence error", 11601ac4b82bSMike Smith "due to unknown status" 11611ac4b82bSMike Smith }; 11621ac4b82bSMike Smith 11631ac4b82bSMike Smith static void 11641ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc) 11651ac4b82bSMike Smith { 11661ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 11671ac4b82bSMike Smith struct mlx_eventlog_entry *el = (struct mlx_eventlog_entry *)mc->mc_data; 11681ac4b82bSMike Smith char *reason; 11691ac4b82bSMike Smith 11701ac4b82bSMike Smith debug("called"); 11711ac4b82bSMike Smith 11721ac4b82bSMike Smith if (mc->mc_status == 0) { 11731ac4b82bSMike Smith sc->mlx_lastevent++; /* got the message OK */ 11741ac4b82bSMike Smith 11751ac4b82bSMike Smith /* handle event log message */ 11761ac4b82bSMike Smith switch(el->el_type) { 11771ac4b82bSMike Smith /* 11781ac4b82bSMike Smith * This is the only sort of message we understand at the moment. 11791ac4b82bSMike Smith * The tests here are probably incomplete. 11801ac4b82bSMike Smith */ 11811ac4b82bSMike Smith case MLX_LOGMSG_SENSE: /* sense data */ 11821ac4b82bSMike Smith /* Mylex vendor-specific message indicating a drive was killed? */ 11831ac4b82bSMike Smith if ((el->el_sensekey == 9) && 11841ac4b82bSMike Smith (el->el_asc == 0x80)) { 11851ac4b82bSMike Smith if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) { 11861ac4b82bSMike Smith reason = mlx_sense_messages[el->el_asq]; 11871ac4b82bSMike Smith } else { 11881ac4b82bSMike Smith reason = "for unknown reason"; 11891ac4b82bSMike Smith } 11901ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n", 11911ac4b82bSMike Smith el->el_channel, el->el_target, reason); 11921ac4b82bSMike Smith } 11931ac4b82bSMike Smith /* SCSI drive was reset? */ 11941ac4b82bSMike Smith if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) { 11951ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d reset\n", 11961ac4b82bSMike Smith el->el_channel, el->el_target); 11971ac4b82bSMike Smith } 11981ac4b82bSMike Smith /* SCSI drive error? */ 11991ac4b82bSMike Smith if (!((el->el_sensekey == 0) || 12001ac4b82bSMike Smith ((el->el_sensekey == 2) && 12011ac4b82bSMike Smith (el->el_asc == 0x04) && 12021ac4b82bSMike Smith ((el->el_asq == 0x01) || 12031ac4b82bSMike Smith (el->el_asq == 0x02))))) { 12041ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n", 12051ac4b82bSMike Smith el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq); 12061ac4b82bSMike Smith device_printf(sc->mlx_dev, " info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":"); 12071ac4b82bSMike Smith } 12081ac4b82bSMike Smith break; 12091ac4b82bSMike Smith 12101ac4b82bSMike Smith default: 12111ac4b82bSMike Smith device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type); 12121ac4b82bSMike Smith break; 12131ac4b82bSMike Smith } 12141ac4b82bSMike Smith } else { 12151ac4b82bSMike Smith device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc)); 12161ac4b82bSMike Smith } 12171ac4b82bSMike Smith 12181ac4b82bSMike Smith /* dispose of command and data */ 12191ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 12201ac4b82bSMike Smith mlx_releasecmd(mc); 12211ac4b82bSMike Smith 12221ac4b82bSMike Smith /* is there another message to obtain? */ 12231ac4b82bSMike Smith if (sc->mlx_lastevent != sc->mlx_currevent) 12241ac4b82bSMike Smith mlx_periodic_eventlog_poll(sc); 12251ac4b82bSMike Smith 12261ac4b82bSMike Smith /* this event is done */ 12271ac4b82bSMike Smith MLX_PERIODIC_UNBUSY(sc); 12281ac4b82bSMike Smith } 12291ac4b82bSMike Smith 12301ac4b82bSMike Smith /******************************************************************************** 12311ac4b82bSMike Smith * Handle the completion of a rebuild operation. 12321ac4b82bSMike Smith */ 12331ac4b82bSMike Smith static void 12341ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc) 12351ac4b82bSMike Smith { 12361ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 12371ac4b82bSMike Smith struct mlx_rebuild_stat *mr = (struct mlx_rebuild_stat *)mc->mc_private; 12381ac4b82bSMike Smith 12391ac4b82bSMike Smith switch(mc->mc_status) { 12401ac4b82bSMike Smith case 0: /* all OK, rebuild still running */ 12411ac4b82bSMike Smith sc->mlx_rebuildstat = mr->rb_remaining; 12421ac4b82bSMike Smith break; 12431ac4b82bSMike Smith 12441ac4b82bSMike Smith case 0x0105: /* rebuild/check finished */ 12451ac4b82bSMike Smith if (sc->mlx_rebuild >= 0) { 12461ac4b82bSMike Smith device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild completed\n"); 12471ac4b82bSMike Smith sc->mlx_rebuild = -1; 12481ac4b82bSMike Smith } else if (sc->mlx_check >= 0) { 12491ac4b82bSMike Smith device_printf(sc->mlx_sysdrive[sc->mlx_check].ms_disk, "consistency check completed\n"); 12501ac4b82bSMike Smith sc->mlx_check = -1; 12511ac4b82bSMike Smith } else { 12521ac4b82bSMike Smith device_printf(sc->mlx_dev, "consistency check completed\n"); 12531ac4b82bSMike Smith } 12541ac4b82bSMike Smith break; 12551ac4b82bSMike Smith } 12561ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 12571ac4b82bSMike Smith mlx_releasecmd(mc); 12581ac4b82bSMike Smith /* this event is done */ 12591ac4b82bSMike Smith MLX_PERIODIC_UNBUSY(sc); 12601ac4b82bSMike Smith } 12611ac4b82bSMike Smith 12621ac4b82bSMike Smith /******************************************************************************** 12631ac4b82bSMike Smith ******************************************************************************** 12641ac4b82bSMike Smith Channel Pause 12651ac4b82bSMike Smith ******************************************************************************** 12661ac4b82bSMike Smith ********************************************************************************/ 12671ac4b82bSMike Smith 12681ac4b82bSMike Smith /******************************************************************************** 12691ac4b82bSMike Smith * It's time to perform a channel pause action for (sc), either start or stop 12701ac4b82bSMike Smith * the pause. 12711ac4b82bSMike Smith */ 12721ac4b82bSMike Smith static void 12731ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc) 12741ac4b82bSMike Smith { 12751ac4b82bSMike Smith struct mlx_command *mc; 12761ac4b82bSMike Smith int failsafe, i, command; 12771ac4b82bSMike Smith 12781ac4b82bSMike Smith /* What are we doing here? */ 12791ac4b82bSMike Smith if (sc->mlx_pause.mp_when == 0) { 12801ac4b82bSMike Smith command = MLX_CMD_STARTCHANNEL; 12811ac4b82bSMike Smith failsafe = 0; 12821ac4b82bSMike Smith 12831ac4b82bSMike Smith } else { 12841ac4b82bSMike Smith command = MLX_CMD_STOPCHANNEL; 12851ac4b82bSMike Smith 12861ac4b82bSMike Smith /* 12871ac4b82bSMike Smith * Channels will always start again after the failsafe period, 12881ac4b82bSMike Smith * which is specified in multiples of 30 seconds. 12891ac4b82bSMike Smith * This constrains us to a maximum pause of 450 seconds. 12901ac4b82bSMike Smith */ 12911ac4b82bSMike Smith failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30; 12921ac4b82bSMike Smith if (failsafe > 0xf) { 12931ac4b82bSMike Smith failsafe = 0xf; 12941ac4b82bSMike Smith sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5; 12951ac4b82bSMike Smith } 12961ac4b82bSMike Smith } 12971ac4b82bSMike Smith 12981ac4b82bSMike Smith /* build commands for every channel requested */ 12991ac4b82bSMike Smith for (i = 0; i < sc->mlx_nchan; i++) { 13001ac4b82bSMike Smith if ((1 << i) & sc->mlx_pause.mp_which) { 13011ac4b82bSMike Smith 13021ac4b82bSMike Smith /* get ourselves a command buffer */ 13031ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 13041ac4b82bSMike Smith goto fail; 13051ac4b82bSMike Smith /* get a command slot */ 13061ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_PRIORITY; 13071ac4b82bSMike Smith if (mlx_getslot(mc)) 13081ac4b82bSMike Smith goto fail; 13091ac4b82bSMike Smith 13101ac4b82bSMike Smith /* build the command */ 13111ac4b82bSMike Smith mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0); 13121ac4b82bSMike Smith mc->mc_complete = mlx_pause_done; 13131ac4b82bSMike Smith mc->mc_private = sc; /* XXX not needed */ 13141ac4b82bSMike Smith if (mlx_start(mc)) 13151ac4b82bSMike Smith goto fail; 13161ac4b82bSMike Smith /* command submitted OK */ 13171ac4b82bSMike Smith return; 13181ac4b82bSMike Smith 13191ac4b82bSMike Smith fail: 13201ac4b82bSMike Smith device_printf(sc->mlx_dev, "%s failed for channel %d\n", 13211ac4b82bSMike Smith command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i); 13221ac4b82bSMike Smith if (mc != NULL) 13231ac4b82bSMike Smith mlx_releasecmd(mc); 13241ac4b82bSMike Smith } 13251ac4b82bSMike Smith } 13261ac4b82bSMike Smith } 13271ac4b82bSMike Smith 13281ac4b82bSMike Smith static void 13291ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc) 13301ac4b82bSMike Smith { 13311ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 13321ac4b82bSMike Smith int command = mc->mc_mailbox[0]; 13331ac4b82bSMike Smith int channel = mc->mc_mailbox[2] & 0xf; 13341ac4b82bSMike Smith 13351ac4b82bSMike Smith if (mc->mc_status != 0) { 13361ac4b82bSMike Smith device_printf(sc->mlx_dev, "%s command failed - %s\n", 13371ac4b82bSMike Smith command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc)); 13381ac4b82bSMike Smith } else if (command == MLX_CMD_STOPCHANNEL) { 13391ac4b82bSMike Smith device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n", 13401ac4b82bSMike Smith channel, sc->mlx_pause.mp_howlong - time_second); 13411ac4b82bSMike Smith } else { 13421ac4b82bSMike Smith device_printf(sc->mlx_dev, "channel %d resuming\n", channel); 13431ac4b82bSMike Smith } 13441ac4b82bSMike Smith mlx_releasecmd(mc); 13451ac4b82bSMike Smith } 13461ac4b82bSMike Smith 13471ac4b82bSMike Smith /******************************************************************************** 13481ac4b82bSMike Smith ******************************************************************************** 13491ac4b82bSMike Smith Command Submission 13501ac4b82bSMike Smith ******************************************************************************** 13511ac4b82bSMike Smith ********************************************************************************/ 13521ac4b82bSMike Smith 13531ac4b82bSMike Smith /******************************************************************************** 13541ac4b82bSMike Smith * Perform an Enquiry command using a type-3 command buffer and a return a single 13551ac4b82bSMike Smith * linear result buffer. If the completion function is specified, it will 13561ac4b82bSMike Smith * be called with the completed command (and the result response will not be 13571ac4b82bSMike Smith * valid until that point). Otherwise, the command will either be busy-waited 13581ac4b82bSMike Smith * for (interrupts not enabled), or slept for. 13591ac4b82bSMike Smith */ 13601ac4b82bSMike Smith static void * 13611ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc)) 13621ac4b82bSMike Smith { 13631ac4b82bSMike Smith struct mlx_command *mc; 13641ac4b82bSMike Smith void *result; 13651ac4b82bSMike Smith int error; 13661ac4b82bSMike Smith 13671ac4b82bSMike Smith debug("called"); 13681ac4b82bSMike Smith 13691ac4b82bSMike Smith /* get ourselves a command buffer */ 13701ac4b82bSMike Smith error = 1; 13711ac4b82bSMike Smith result = NULL; 13721ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 13731ac4b82bSMike Smith goto out; 13741ac4b82bSMike Smith /* allocate the response structure */ 13751ac4b82bSMike Smith if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL) 13761ac4b82bSMike Smith goto out; 13771ac4b82bSMike Smith /* get a command slot */ 13781ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT; 13791ac4b82bSMike Smith if (mlx_getslot(mc)) 13801ac4b82bSMike Smith goto out; 13811ac4b82bSMike Smith 13821ac4b82bSMike Smith /* map the command so the controller can see it */ 13831ac4b82bSMike Smith mc->mc_data = result; 13841ac4b82bSMike Smith mc->mc_length = bufsize; 13851ac4b82bSMike Smith mlx_mapcmd(mc); 13861ac4b82bSMike Smith 13871ac4b82bSMike Smith /* build an enquiry command */ 13881ac4b82bSMike Smith mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0); 13891ac4b82bSMike Smith 13901ac4b82bSMike Smith /* do we want a completion callback? */ 13911ac4b82bSMike Smith if (complete != NULL) { 13921ac4b82bSMike Smith mc->mc_complete = complete; 13931ac4b82bSMike Smith mc->mc_private = mc; 13941ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 13951ac4b82bSMike Smith goto out; 13961ac4b82bSMike Smith } else { 13971ac4b82bSMike Smith /* run the command in either polled or wait mode */ 13981ac4b82bSMike Smith if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc)) 13991ac4b82bSMike Smith goto out; 14001ac4b82bSMike Smith 14011ac4b82bSMike Smith /* command completed OK? */ 14021ac4b82bSMike Smith if (mc->mc_status != 0) { 14031ac4b82bSMike Smith device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", mlx_diagnose_command(mc)); 14041ac4b82bSMike Smith goto out; 14051ac4b82bSMike Smith } 14061ac4b82bSMike Smith } 14071ac4b82bSMike Smith error = 0; /* success */ 14081ac4b82bSMike Smith out: 14091ac4b82bSMike Smith /* we got a command, but nobody else will free it */ 14101ac4b82bSMike Smith if ((complete == NULL) && (mc != NULL)) 14111ac4b82bSMike Smith mlx_releasecmd(mc); 141233c8cb18SMike Smith /* we got an error, and we allocated a result */ 14131ac4b82bSMike Smith if ((error != 0) && (result != NULL)) { 14141ac4b82bSMike Smith free(result, M_DEVBUF); 14151ac4b82bSMike Smith result = NULL; 14161ac4b82bSMike Smith } 14171ac4b82bSMike Smith return(result); 14181ac4b82bSMike Smith } 14191ac4b82bSMike Smith 14201ac4b82bSMike Smith 14211ac4b82bSMike Smith /******************************************************************************** 14221ac4b82bSMike Smith * Perform a Flush command on the nominated controller. 14231ac4b82bSMike Smith * 14241ac4b82bSMike Smith * May be called with interrupts enabled or disabled; will not return until 14251ac4b82bSMike Smith * the flush operation completes or fails. 14261ac4b82bSMike Smith */ 14271ac4b82bSMike Smith static int 14281ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc) 14291ac4b82bSMike Smith { 14301ac4b82bSMike Smith struct mlx_command *mc; 14311ac4b82bSMike Smith int error; 14321ac4b82bSMike Smith 14331ac4b82bSMike Smith debug("called"); 14341ac4b82bSMike Smith 14351ac4b82bSMike Smith /* get ourselves a command buffer */ 14361ac4b82bSMike Smith error = 1; 14371ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 14381ac4b82bSMike Smith goto out; 14391ac4b82bSMike Smith /* get a command slot */ 14401ac4b82bSMike Smith if (mlx_getslot(mc)) 14411ac4b82bSMike Smith goto out; 14421ac4b82bSMike Smith 14431ac4b82bSMike Smith /* build a flush command */ 14441ac4b82bSMike Smith mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0); 14451ac4b82bSMike Smith 14461ac4b82bSMike Smith /* run the command in either polled or wait mode */ 14471ac4b82bSMike Smith if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc)) 14481ac4b82bSMike Smith goto out; 14491ac4b82bSMike Smith 14501ac4b82bSMike Smith /* command completed OK? */ 14511ac4b82bSMike Smith if (mc->mc_status != 0) { 14521ac4b82bSMike Smith device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc)); 14531ac4b82bSMike Smith goto out; 14541ac4b82bSMike Smith } 14551ac4b82bSMike Smith 14561ac4b82bSMike Smith error = 0; /* success */ 14571ac4b82bSMike Smith out: 14581ac4b82bSMike Smith if (mc != NULL) 14591ac4b82bSMike Smith mlx_releasecmd(mc); 14601ac4b82bSMike Smith return(error); 14611ac4b82bSMike Smith } 14621ac4b82bSMike Smith 14631ac4b82bSMike Smith /******************************************************************************** 14641ac4b82bSMike Smith * Start a background rebuild on the nominated controller/channel/target. 14651ac4b82bSMike Smith * 14661ac4b82bSMike Smith * May be called with interrupts enabled or disabled; will return as soon as the 14671ac4b82bSMike Smith * operation has started or been refused. 14681ac4b82bSMike Smith */ 14691ac4b82bSMike Smith static int 14701ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target) 14711ac4b82bSMike Smith { 14721ac4b82bSMike Smith struct mlx_command *mc; 14731ac4b82bSMike Smith int error; 14741ac4b82bSMike Smith 14751ac4b82bSMike Smith debug("called"); 14761ac4b82bSMike Smith 14771ac4b82bSMike Smith /* get ourselves a command buffer */ 14781ac4b82bSMike Smith error = 0x10000; 14791ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 14801ac4b82bSMike Smith goto out; 14811ac4b82bSMike Smith /* get a command slot */ 14821ac4b82bSMike Smith if (mlx_getslot(mc)) 14831ac4b82bSMike Smith goto out; 14841ac4b82bSMike Smith 14851ac4b82bSMike Smith /* build a rebuild command */ 14861ac4b82bSMike Smith mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0); 14871ac4b82bSMike Smith 14881ac4b82bSMike Smith /* run the command in either polled or wait mode */ 14891ac4b82bSMike Smith if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc)) 14901ac4b82bSMike Smith goto out; 14911ac4b82bSMike Smith 14921ac4b82bSMike Smith /* command completed OK? */ 14931ac4b82bSMike Smith if (mc->mc_status != 0) { 14941ac4b82bSMike Smith device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc)); 14951ac4b82bSMike Smith } else { 14961ac4b82bSMike Smith device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild started"); 14971ac4b82bSMike Smith } 14981ac4b82bSMike Smith error = mc->mc_status; 14991ac4b82bSMike Smith 15001ac4b82bSMike Smith out: 15011ac4b82bSMike Smith if (mc != NULL) 15021ac4b82bSMike Smith mlx_releasecmd(mc); 15031ac4b82bSMike Smith return(error); 15041ac4b82bSMike Smith } 15051ac4b82bSMike Smith 15061ac4b82bSMike Smith /******************************************************************************** 15071ac4b82bSMike Smith * Run the command (mc) and return when it completes. 15081ac4b82bSMike Smith * 15091ac4b82bSMike Smith * Interrupts need to be enabled; returns nonzero on error. 15101ac4b82bSMike Smith */ 15111ac4b82bSMike Smith static int 15121ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc) 15131ac4b82bSMike Smith { 15141ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 15151ac4b82bSMike Smith int error, count; 15161ac4b82bSMike Smith 15171ac4b82bSMike Smith debug("called"); 15181ac4b82bSMike Smith 15191ac4b82bSMike Smith mc->mc_complete = NULL; 15201ac4b82bSMike Smith mc->mc_private = mc; /* wake us when you're done */ 15211ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 15221ac4b82bSMike Smith return(error); 15231ac4b82bSMike Smith 15241ac4b82bSMike Smith count = 0; 15251ac4b82bSMike Smith /* XXX better timeout? */ 15261ac4b82bSMike Smith while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) { 15271ac4b82bSMike Smith tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz); 15281ac4b82bSMike Smith } 15291ac4b82bSMike Smith 15301ac4b82bSMike Smith if (mc->mc_status != 0) { 15311ac4b82bSMike Smith device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status); 15321ac4b82bSMike Smith return(EIO); 15331ac4b82bSMike Smith } 15341ac4b82bSMike Smith return(0); 15351ac4b82bSMike Smith } 15361ac4b82bSMike Smith 15371ac4b82bSMike Smith 15381ac4b82bSMike Smith /******************************************************************************** 15391ac4b82bSMike Smith * Start the command (mc) and busy-wait for it to complete. 15401ac4b82bSMike Smith * 15411ac4b82bSMike Smith * Should only be used when interrupts are not available. Returns 0 on 15421ac4b82bSMike Smith * success, nonzero on error. 15431ac4b82bSMike Smith * Successfully completed commands are dequeued. 15441ac4b82bSMike Smith */ 15451ac4b82bSMike Smith static int 15461ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc) 15471ac4b82bSMike Smith { 15481ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 15491ac4b82bSMike Smith int error, count, s; 15501ac4b82bSMike Smith 15511ac4b82bSMike Smith debug("called"); 15521ac4b82bSMike Smith 15531ac4b82bSMike Smith mc->mc_complete = NULL; 15541ac4b82bSMike Smith mc->mc_private = NULL; /* we will poll for it */ 15551ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 15561ac4b82bSMike Smith return(error); 15571ac4b82bSMike Smith 15581ac4b82bSMike Smith count = 0; 15591ac4b82bSMike Smith do { 15601ac4b82bSMike Smith /* poll for completion */ 15611ac4b82bSMike Smith mlx_done(mc->mc_sc); 15621ac4b82bSMike Smith } while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 10000)); 15631ac4b82bSMike Smith if (mc->mc_status != MLX_STATUS_BUSY) { 15641ac4b82bSMike Smith s = splbio(); 15654b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 15661ac4b82bSMike Smith splx(s); 15671ac4b82bSMike Smith return(0); 15681ac4b82bSMike Smith } 15691ac4b82bSMike Smith device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status); 15701ac4b82bSMike Smith return(EIO); 15711ac4b82bSMike Smith } 15721ac4b82bSMike Smith 15731ac4b82bSMike Smith /******************************************************************************** 15741ac4b82bSMike Smith * Pull as much work off the softc's work queue as possible and give it to the 15751ac4b82bSMike Smith * controller. Leave a couple of slots free for emergencies. 15761ac4b82bSMike Smith * 15771ac4b82bSMike Smith * Must be called at splbio or in an equivalent fashion that prevents 15781ac4b82bSMike Smith * reentry or activity on the bufq.. 15791ac4b82bSMike Smith */ 15801ac4b82bSMike Smith static void 15811ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc) 15821ac4b82bSMike Smith { 15831ac4b82bSMike Smith struct mlx_command *mc; 15841ac4b82bSMike Smith struct mlxd_softc *mlxd; 15851ac4b82bSMike Smith struct buf *bp; 15861ac4b82bSMike Smith int blkcount; 15871ac4b82bSMike Smith int driveno; 15881ac4b82bSMike Smith int cmd; 15894b006d7bSMike Smith int s; 15901ac4b82bSMike Smith 15911ac4b82bSMike Smith /* spin until something prevents us from doing any work */ 15924b006d7bSMike Smith s = splbio(); 15931ac4b82bSMike Smith for (;;) { 15941ac4b82bSMike Smith 15951ac4b82bSMike Smith /* see if there's work to be done */ 15961ac4b82bSMike Smith if ((bp = bufq_first(&sc->mlx_bufq)) == NULL) 15971ac4b82bSMike Smith break; 15981ac4b82bSMike Smith /* get a command */ 15991ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 16001ac4b82bSMike Smith break; 16011ac4b82bSMike Smith /* get a slot for the command */ 16021ac4b82bSMike Smith if (mlx_getslot(mc) != 0) { 16031ac4b82bSMike Smith mlx_releasecmd(mc); 16041ac4b82bSMike Smith break; 16051ac4b82bSMike Smith } 16061ac4b82bSMike Smith /* get the buf containing our work */ 16071ac4b82bSMike Smith bufq_remove(&sc->mlx_bufq, bp); 16081ac4b82bSMike Smith sc->mlx_waitbufs--; 16094b006d7bSMike Smith splx(s); 16101ac4b82bSMike Smith 16111ac4b82bSMike Smith /* connect the buf to the command */ 16121ac4b82bSMike Smith mc->mc_complete = mlx_completeio; 16131ac4b82bSMike Smith mc->mc_private = bp; 16141ac4b82bSMike Smith mc->mc_data = bp->b_data; 16151ac4b82bSMike Smith mc->mc_length = bp->b_bcount; 16161ac4b82bSMike Smith if (bp->b_flags & B_READ) { 16171ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_DATAIN; 16181ac4b82bSMike Smith cmd = MLX_CMD_READOLDSG; 16191ac4b82bSMike Smith } else { 16201ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_DATAOUT; 16211ac4b82bSMike Smith cmd = MLX_CMD_WRITEOLDSG; 16221ac4b82bSMike Smith } 16231ac4b82bSMike Smith 16241ac4b82bSMike Smith /* map the command so the controller can work with it */ 16251ac4b82bSMike Smith mlx_mapcmd(mc); 16261ac4b82bSMike Smith 16271ac4b82bSMike Smith /* build a suitable I/O command (assumes 512-byte rounded transfers) */ 1628f6b84b08SMike Smith mlxd = (struct mlxd_softc *)bp->b_dev->si_drv1; 16291ac4b82bSMike Smith driveno = mlxd->mlxd_drive - &sc->mlx_sysdrive[0]; 163097adfbafSMike Smith blkcount = (bp->b_bcount + MLX_BLKSIZE - 1) / MLX_BLKSIZE; 16311ac4b82bSMike Smith 1632cd4ace0cSMike Smith if ((bp->b_pblkno + blkcount) > sc->mlx_sysdrive[driveno].ms_size) 16331ac4b82bSMike Smith device_printf(sc->mlx_dev, "I/O beyond end of unit (%u,%d > %u)\n", 1634cd4ace0cSMike Smith bp->b_pblkno, blkcount, sc->mlx_sysdrive[driveno].ms_size); 16351ac4b82bSMike Smith 16361ac4b82bSMike Smith /* 16371ac4b82bSMike Smith * Build the I/O command. Note that the SG list type bits are set to zero, 16381ac4b82bSMike Smith * denoting the format of SG list that we are using. 16391ac4b82bSMike Smith */ 16401ac4b82bSMike Smith mlx_make_type5(mc, cmd, 16411ac4b82bSMike Smith blkcount & 0xff, /* xfer length low byte */ 1642f01f2af6SMike Smith (driveno << 3) | ((blkcount >> 8) & 0x07), /* target and length high 3 bits */ 1643cd4ace0cSMike Smith bp->b_pblkno, /* physical block number */ 16441ac4b82bSMike Smith mc->mc_sgphys, /* location of SG list */ 16451ac4b82bSMike Smith mc->mc_nsgent & 0x3f); /* size of SG list (top 2 bits clear) */ 16461ac4b82bSMike Smith 16471ac4b82bSMike Smith 16481ac4b82bSMike Smith /* try to give command to controller */ 16491ac4b82bSMike Smith if (mlx_start(mc) != 0) { 16501ac4b82bSMike Smith /* fail the command */ 16511ac4b82bSMike Smith mc->mc_status = MLX_STATUS_WEDGED; 16521ac4b82bSMike Smith mlx_completeio(mc); 16531ac4b82bSMike Smith } 16544b006d7bSMike Smith s = splbio(); 16551ac4b82bSMike Smith } 16564b006d7bSMike Smith splx(s); 16571ac4b82bSMike Smith } 16581ac4b82bSMike Smith 16591ac4b82bSMike Smith /******************************************************************************** 16601ac4b82bSMike Smith * Handle completion of an I/O command. 16611ac4b82bSMike Smith */ 16621ac4b82bSMike Smith static void 16631ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc) 16641ac4b82bSMike Smith { 16651ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 16661ac4b82bSMike Smith struct buf *bp = (struct buf *)mc->mc_private; 1667f6b84b08SMike Smith struct mlxd_softc *mlxd = (struct mlxd_softc *)bp->b_dev->si_drv1; 16681ac4b82bSMike Smith 16691ac4b82bSMike Smith if (mc->mc_status != MLX_STATUS_OK) { /* could be more verbose here? */ 16701ac4b82bSMike Smith bp->b_error = EIO; 16711ac4b82bSMike Smith bp->b_flags |= B_ERROR; 16721ac4b82bSMike Smith 16731ac4b82bSMike Smith switch(mc->mc_status) { 16741ac4b82bSMike Smith case MLX_STATUS_RDWROFFLINE: /* system drive has gone offline */ 16751ac4b82bSMike Smith device_printf(mlxd->mlxd_dev, "drive offline\n"); 1676f6b84b08SMike Smith /* should signal this with a return code */ 16771ac4b82bSMike Smith mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE; 16781ac4b82bSMike Smith break; 16791ac4b82bSMike Smith 16801ac4b82bSMike Smith default: /* other I/O error */ 16811ac4b82bSMike Smith device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc)); 16821ac4b82bSMike Smith #if 0 1683cd4ace0cSMike Smith device_printf(sc->mlx_dev, " b_bcount %ld blkcount %ld b_pblkno %d\n", 1684cd4ace0cSMike Smith bp->b_bcount, bp->b_bcount / MLX_BLKSIZE, bp->b_pblkno); 16851ac4b82bSMike Smith device_printf(sc->mlx_dev, " %13D\n", mc->mc_mailbox, " "); 16861ac4b82bSMike Smith #endif 16871ac4b82bSMike Smith break; 16881ac4b82bSMike Smith } 16891ac4b82bSMike Smith } 16901ac4b82bSMike Smith mlx_releasecmd(mc); 16911ac4b82bSMike Smith mlxd_intr(bp); 16921ac4b82bSMike Smith } 16931ac4b82bSMike Smith 16941ac4b82bSMike Smith /******************************************************************************** 16951ac4b82bSMike Smith * Take a command from user-space and try to run it. 16961ac4b82bSMike Smith */ 16971ac4b82bSMike Smith static int 16981ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu) 16991ac4b82bSMike Smith { 17001ac4b82bSMike Smith struct mlx_command *mc; 17011ac4b82bSMike Smith void *kbuf; 17021ac4b82bSMike Smith int error; 17031ac4b82bSMike Smith 17041ac4b82bSMike Smith kbuf = NULL; 17051ac4b82bSMike Smith mc = NULL; 17061ac4b82bSMike Smith error = ENOMEM; 17071ac4b82bSMike Smith /* get a kernel buffer for the transfer */ 17081ac4b82bSMike Smith if (mu->mu_datasize > 0) { 17091ac4b82bSMike Smith if ((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) 17101ac4b82bSMike Smith goto out; 17111ac4b82bSMike Smith if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) < sizeof(u_int32_t)))) { 17121ac4b82bSMike Smith error = EINVAL; 17131ac4b82bSMike Smith goto out; 17141ac4b82bSMike Smith } 17151ac4b82bSMike Smith } 17161ac4b82bSMike Smith /* get ourselves a command buffer */ 17171ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 17181ac4b82bSMike Smith goto out; 17191ac4b82bSMike Smith 17201ac4b82bSMike Smith /* copy the command and data */ 17211ac4b82bSMike Smith bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox)); 17221ac4b82bSMike Smith if ((mu->mu_datasize > 0) && ((error = copyin(mu->mu_buf, kbuf, mu->mu_datasize)))) 17231ac4b82bSMike Smith goto out; 17241ac4b82bSMike Smith 17251ac4b82bSMike Smith /* get a command slot */ 17261ac4b82bSMike Smith if (mlx_getslot(mc)) 17271ac4b82bSMike Smith goto out; 17281ac4b82bSMike Smith 17291ac4b82bSMike Smith /* map the command so the controller can see it */ 17301ac4b82bSMike Smith mc->mc_data = kbuf; 17311ac4b82bSMike Smith mc->mc_length = mu->mu_datasize; 17321ac4b82bSMike Smith mlx_mapcmd(mc); 17331ac4b82bSMike Smith 17341ac4b82bSMike Smith /* if there's a data buffer, fix up the command */ 17351ac4b82bSMike Smith if (mu->mu_datasize > 0) { 17361ac4b82bSMike Smith mc->mc_mailbox[mu->mu_bufptr ] = mc->mc_length & 0xff; 17371ac4b82bSMike Smith mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_length >> 8) & 0xff; 17381ac4b82bSMike Smith mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_length >> 16) & 0xff; 17391ac4b82bSMike Smith mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_length >> 24) & 0xff; 17401ac4b82bSMike Smith } 17411ac4b82bSMike Smith 17421ac4b82bSMike Smith /* submit the command and wait */ 17431ac4b82bSMike Smith if ((error = mlx_wait_command(mc)) != 0) 17441ac4b82bSMike Smith goto out; 17451ac4b82bSMike Smith 17461ac4b82bSMike Smith /* copy out status and data */ 17471ac4b82bSMike Smith mu->mu_status = mc->mc_status; 17481ac4b82bSMike Smith if ((mu->mu_datasize > 0) && ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize)))) 17491ac4b82bSMike Smith goto out; 17501ac4b82bSMike Smith error = 0; 17511ac4b82bSMike Smith 17521ac4b82bSMike Smith out: 17531ac4b82bSMike Smith mlx_releasecmd(mc); 17541ac4b82bSMike Smith if (kbuf != NULL) 17551ac4b82bSMike Smith free(kbuf, M_DEVBUF); 17561ac4b82bSMike Smith return(error); 17571ac4b82bSMike Smith } 17581ac4b82bSMike Smith 17591ac4b82bSMike Smith /******************************************************************************** 17601ac4b82bSMike Smith ******************************************************************************** 17611ac4b82bSMike Smith Command I/O to Controller 17621ac4b82bSMike Smith ******************************************************************************** 17631ac4b82bSMike Smith ********************************************************************************/ 17641ac4b82bSMike Smith 17651ac4b82bSMike Smith /******************************************************************************** 17661ac4b82bSMike Smith * Find a free command slot for (mc). 17671ac4b82bSMike Smith * 17681ac4b82bSMike Smith * Don't hand out a slot to a normal-priority command unless there are at least 17691ac4b82bSMike Smith * 4 slots free for priority commands. 17701ac4b82bSMike Smith */ 17711ac4b82bSMike Smith static int 17721ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc) 17731ac4b82bSMike Smith { 17741ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 17751ac4b82bSMike Smith int s, slot, limit; 17761ac4b82bSMike Smith 17771ac4b82bSMike Smith debug("called mc %p sc %p", mc, sc); 17781ac4b82bSMike Smith 17791ac4b82bSMike Smith /* enforce slot-usage limit */ 17801ac4b82bSMike Smith limit = (mc->mc_flags & MLX_CMD_PRIORITY) ? sc->mlx_maxiop : sc->mlx_maxiop - 4; 17811ac4b82bSMike Smith if (sc->mlx_busycmds > limit) 17821ac4b82bSMike Smith return(EBUSY); 17831ac4b82bSMike Smith 17841ac4b82bSMike Smith /* 17851ac4b82bSMike Smith * Allocate an outstanding command slot 17861ac4b82bSMike Smith * 17871ac4b82bSMike Smith * XXX linear search is slow 17881ac4b82bSMike Smith */ 17891ac4b82bSMike Smith s = splbio(); 17901ac4b82bSMike Smith for (slot = 0; slot < sc->mlx_maxiop; slot++) { 17911ac4b82bSMike Smith debug("try slot %d", slot); 17921ac4b82bSMike Smith if (sc->mlx_busycmd[slot] == NULL) 17931ac4b82bSMike Smith break; 17941ac4b82bSMike Smith } 17951ac4b82bSMike Smith if (slot < sc->mlx_maxiop) { 17961ac4b82bSMike Smith sc->mlx_busycmd[slot] = mc; 17971ac4b82bSMike Smith sc->mlx_busycmds++; 17981ac4b82bSMike Smith } 17991ac4b82bSMike Smith splx(s); 18001ac4b82bSMike Smith 18011ac4b82bSMike Smith /* out of slots? */ 18021ac4b82bSMike Smith if (slot >= sc->mlx_maxiop) 18031ac4b82bSMike Smith return(EBUSY); 18041ac4b82bSMike Smith 18051ac4b82bSMike Smith debug("got slot %d", slot); 18061ac4b82bSMike Smith mc->mc_slot = slot; 18071ac4b82bSMike Smith return(0); 18081ac4b82bSMike Smith } 18091ac4b82bSMike Smith 18101ac4b82bSMike Smith /******************************************************************************** 18111ac4b82bSMike Smith * Map/unmap (mc)'s data in the controller's addressable space. 18121ac4b82bSMike Smith */ 18131ac4b82bSMike Smith static void 18141ac4b82bSMike Smith mlx_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 18151ac4b82bSMike Smith { 18161ac4b82bSMike Smith struct mlx_command *mc = (struct mlx_command *)arg; 18171ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 18181ac4b82bSMike Smith struct mlx_sgentry *sg; 18191ac4b82bSMike Smith int i; 18201ac4b82bSMike Smith 18211ac4b82bSMike Smith debug("called"); 18221ac4b82bSMike Smith 18231ac4b82bSMike Smith /* get base address of s/g table */ 18241ac4b82bSMike Smith sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG); 18251ac4b82bSMike Smith 18261ac4b82bSMike Smith /* save s/g table information in command */ 18271ac4b82bSMike Smith mc->mc_nsgent = nsegments; 18281ac4b82bSMike Smith mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry)); 18291ac4b82bSMike Smith mc->mc_dataphys = segs[0].ds_addr; 18301ac4b82bSMike Smith 18311ac4b82bSMike Smith /* populate s/g table */ 18321ac4b82bSMike Smith for (i = 0; i < nsegments; i++, sg++) { 18331ac4b82bSMike Smith sg->sg_addr = segs[i].ds_addr; 18341ac4b82bSMike Smith sg->sg_count = segs[i].ds_len; 18351ac4b82bSMike Smith } 18361ac4b82bSMike Smith } 18371ac4b82bSMike Smith 18381ac4b82bSMike Smith static void 18391ac4b82bSMike Smith mlx_mapcmd(struct mlx_command *mc) 18401ac4b82bSMike Smith { 18411ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 18421ac4b82bSMike Smith 18431ac4b82bSMike Smith debug("called"); 18441ac4b82bSMike Smith 18451ac4b82bSMike Smith /* if the command involves data at all */ 18461ac4b82bSMike Smith if (mc->mc_data != NULL) { 18471ac4b82bSMike Smith 18481ac4b82bSMike Smith /* map the data buffer into bus space and build the s/g list */ 18491ac4b82bSMike Smith bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length, 18501ac4b82bSMike Smith mlx_setup_dmamap, mc, 0); 18511ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAIN) 18521ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREREAD); 18531ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAOUT) 18541ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREWRITE); 18551ac4b82bSMike Smith } 18561ac4b82bSMike Smith } 18571ac4b82bSMike Smith 18581ac4b82bSMike Smith static void 18591ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc) 18601ac4b82bSMike Smith { 18611ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 18621ac4b82bSMike Smith 18631ac4b82bSMike Smith debug("called"); 18641ac4b82bSMike Smith 18651ac4b82bSMike Smith /* if the command involved data at all */ 18661ac4b82bSMike Smith if (mc->mc_data != NULL) { 18671ac4b82bSMike Smith 18681ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAIN) 18691ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD); 18701ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAOUT) 18711ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE); 18721ac4b82bSMike Smith 18731ac4b82bSMike Smith bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap); 18741ac4b82bSMike Smith } 18751ac4b82bSMike Smith } 18761ac4b82bSMike Smith 18771ac4b82bSMike Smith /******************************************************************************** 18781ac4b82bSMike Smith * Try to deliver (mc) to the controller. Take care of any completed commands 18791ac4b82bSMike Smith * that we encounter while doing so. 18801ac4b82bSMike Smith * 18811ac4b82bSMike Smith * Can be called at any interrupt level, with or without interrupts enabled. 18821ac4b82bSMike Smith */ 18831ac4b82bSMike Smith static int 18841ac4b82bSMike Smith mlx_start(struct mlx_command *mc) 18851ac4b82bSMike Smith { 18861ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 18871ac4b82bSMike Smith int i, s, done, worked; 18881ac4b82bSMike Smith 18891ac4b82bSMike Smith debug("called"); 18901ac4b82bSMike Smith 18911ac4b82bSMike Smith /* save the slot number as ident so we can handle this command when complete */ 18921ac4b82bSMike Smith mc->mc_mailbox[0x1] = mc->mc_slot; 18931ac4b82bSMike Smith 18944b006d7bSMike Smith /* mark the command as currently being processed */ 18951ac4b82bSMike Smith mc->mc_status = MLX_STATUS_BUSY; 18961ac4b82bSMike Smith 18971ac4b82bSMike Smith /* assume we don't collect any completed commands */ 18981ac4b82bSMike Smith worked = 0; 18991ac4b82bSMike Smith 19001ac4b82bSMike Smith /* spin waiting for the mailbox */ 19011ac4b82bSMike Smith for (i = 100000, done = 0; (i > 0) && !done; i--) { 19021ac4b82bSMike Smith s = splbio(); 19034b006d7bSMike Smith if (sc->mlx_tryqueue(sc, mc)) { 19044b006d7bSMike Smith done = 1; 19054b006d7bSMike Smith /* move command to work queue */ 19064b006d7bSMike Smith TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link); 19074b006d7bSMike Smith } 19081ac4b82bSMike Smith splx(s); 19091ac4b82bSMike Smith /* check for command completion while we're at it */ 19101ac4b82bSMike Smith if (mlx_done(sc)) 19111ac4b82bSMike Smith worked = 1; 19121ac4b82bSMike Smith } 19131ac4b82bSMike Smith /* check to see if we picked up any completed commands */ 19141ac4b82bSMike Smith if (worked) 19151ac4b82bSMike Smith mlx_complete(sc); 19161ac4b82bSMike Smith 19171ac4b82bSMike Smith /* command is enqueued */ 19181ac4b82bSMike Smith if (done) 19191ac4b82bSMike Smith return(0); 19201ac4b82bSMike Smith 19211ac4b82bSMike Smith /* 19221ac4b82bSMike Smith * We couldn't get the controller to take the command. Revoke the slot 19231ac4b82bSMike Smith * that the command was given and return it with a bad status. 19241ac4b82bSMike Smith */ 19251ac4b82bSMike Smith sc->mlx_busycmd[mc->mc_slot] = NULL; 19261ac4b82bSMike Smith device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n"); 19271ac4b82bSMike Smith mc->mc_status = MLX_STATUS_WEDGED; 19281ac4b82bSMike Smith return(EIO); 19291ac4b82bSMike Smith } 19301ac4b82bSMike Smith 19311ac4b82bSMike Smith /******************************************************************************** 19321ac4b82bSMike Smith * Look at the controller (sc) and see if a command has been completed. 19331ac4b82bSMike Smith * If so, move the command buffer to the done queue for later collection 19341ac4b82bSMike Smith * and free the slot for immediate reuse. 19351ac4b82bSMike Smith * 19361ac4b82bSMike Smith * Returns nonzero if anything was added to the done queue. 19371ac4b82bSMike Smith */ 19381ac4b82bSMike Smith static int 19391ac4b82bSMike Smith mlx_done(struct mlx_softc *sc) 19401ac4b82bSMike Smith { 19411ac4b82bSMike Smith struct mlx_command *mc; 19421ac4b82bSMike Smith int s; 19431ac4b82bSMike Smith u_int8_t slot; 19441ac4b82bSMike Smith u_int16_t status; 19451ac4b82bSMike Smith 19461ac4b82bSMike Smith debug("called"); 19471ac4b82bSMike Smith 19481ac4b82bSMike Smith mc = NULL; 19491ac4b82bSMike Smith slot = 0; 19501ac4b82bSMike Smith 19511ac4b82bSMike Smith /* poll for a completed command's identifier and status */ 19524b006d7bSMike Smith s = splbio(); 19531ac4b82bSMike Smith if (sc->mlx_findcomplete(sc, &slot, &status)) { 19541ac4b82bSMike Smith mc = sc->mlx_busycmd[slot]; /* find command */ 19551ac4b82bSMike Smith if (mc != NULL) { /* paranoia */ 19561ac4b82bSMike Smith if (mc->mc_status == MLX_STATUS_BUSY) { 19571ac4b82bSMike Smith mc->mc_status = status; /* save status */ 19581ac4b82bSMike Smith 19591ac4b82bSMike Smith /* free slot for reuse */ 19601ac4b82bSMike Smith sc->mlx_busycmd[slot] = NULL; 19611ac4b82bSMike Smith sc->mlx_busycmds--; 19621ac4b82bSMike Smith } else { 19631ac4b82bSMike Smith device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot); 19641ac4b82bSMike Smith mc = NULL; 19651ac4b82bSMike Smith } 19661ac4b82bSMike Smith } else { 19671ac4b82bSMike Smith device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot); 19681ac4b82bSMike Smith } 19691ac4b82bSMike Smith } 19701ac4b82bSMike Smith splx(s); 19711ac4b82bSMike Smith 19721ac4b82bSMike Smith if (mc != NULL) { 19731ac4b82bSMike Smith /* unmap the command's data buffer */ 19741ac4b82bSMike Smith mlx_unmapcmd(mc); 19751ac4b82bSMike Smith return(1); 19761ac4b82bSMike Smith } 19771ac4b82bSMike Smith return(0); 19781ac4b82bSMike Smith } 19791ac4b82bSMike Smith 19801ac4b82bSMike Smith /******************************************************************************** 19811ac4b82bSMike Smith * Handle completion for all commands on (sc)'s done queue. 19821ac4b82bSMike Smith */ 19831ac4b82bSMike Smith static void 19841ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc) 19851ac4b82bSMike Smith { 19861ac4b82bSMike Smith struct mlx_command *mc, *nc; 19871ac4b82bSMike Smith int s, count; 19881ac4b82bSMike Smith 19891ac4b82bSMike Smith debug("called"); 19901ac4b82bSMike Smith 19911ac4b82bSMike Smith s = splbio(); 19921ac4b82bSMike Smith count = 0; 19931ac4b82bSMike Smith 19941ac4b82bSMike Smith /* scan the list of done commands */ 19954b006d7bSMike Smith mc = TAILQ_FIRST(&sc->mlx_work); 19961ac4b82bSMike Smith while (mc != NULL) { 19971ac4b82bSMike Smith nc = TAILQ_NEXT(mc, mc_link); 19981ac4b82bSMike Smith 19991ac4b82bSMike Smith /* XXX this is slightly bogus */ 20001ac4b82bSMike Smith if (count++ > (sc->mlx_maxiop * 2)) 20014b006d7bSMike Smith panic("mlx_work list corrupt!"); 20024b006d7bSMike Smith 20034b006d7bSMike Smith /* Skip commands that are still busy */ 20044b006d7bSMike Smith if (mc->mc_status != MLX_STATUS_BUSY) { 20054b006d7bSMike Smith 20061ac4b82bSMike Smith 20071ac4b82bSMike Smith /* 20081ac4b82bSMike Smith * Does the command have a completion handler? 20091ac4b82bSMike Smith */ 20101ac4b82bSMike Smith if (mc->mc_complete != NULL) { 20111ac4b82bSMike Smith /* remove from list and give to handler */ 20124b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 20131ac4b82bSMike Smith mc->mc_complete(mc); 20141ac4b82bSMike Smith 20151ac4b82bSMike Smith /* 20161ac4b82bSMike Smith * Is there a sleeper waiting on this command? 20171ac4b82bSMike Smith */ 20181ac4b82bSMike Smith } else if (mc->mc_private != NULL) { /* sleeping caller wants to know about it */ 20191ac4b82bSMike Smith 20201ac4b82bSMike Smith /* remove from list and wake up sleeper */ 20214b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 20221ac4b82bSMike Smith wakeup_one(mc->mc_private); 20231ac4b82bSMike Smith 20241ac4b82bSMike Smith /* 20251ac4b82bSMike Smith * Leave the command for a caller that's polling for it. 20261ac4b82bSMike Smith */ 20271ac4b82bSMike Smith } else { 20281ac4b82bSMike Smith } 20294b006d7bSMike Smith } 20301ac4b82bSMike Smith mc = nc; 20311ac4b82bSMike Smith } 20321ac4b82bSMike Smith splx(s); 20331ac4b82bSMike Smith 20341ac4b82bSMike Smith /* queue some more work if there is any */ 20351ac4b82bSMike Smith mlx_startio(sc); 20361ac4b82bSMike Smith } 20371ac4b82bSMike Smith 20381ac4b82bSMike Smith /******************************************************************************** 20391ac4b82bSMike Smith ******************************************************************************** 20401ac4b82bSMike Smith Command Buffer Management 20411ac4b82bSMike Smith ******************************************************************************** 20421ac4b82bSMike Smith ********************************************************************************/ 20431ac4b82bSMike Smith 20441ac4b82bSMike Smith /******************************************************************************** 20451ac4b82bSMike Smith * Get a new command buffer. 20461ac4b82bSMike Smith * 20471ac4b82bSMike Smith * This may return NULL in low-memory cases. 20481ac4b82bSMike Smith * 20491ac4b82bSMike Smith * Note that using malloc() is expensive (the command buffer is << 1 page) but 20501ac4b82bSMike Smith * necessary if we are to be a loadable module before the zone allocator is fixed. 20511ac4b82bSMike Smith * 20521ac4b82bSMike Smith * If possible, we recycle a command buffer that's been used before. 20531ac4b82bSMike Smith * 20541ac4b82bSMike Smith * XXX Note that command buffers are not cleaned out - it is the caller's 20551ac4b82bSMike Smith * responsibility to ensure that all required fields are filled in before 20561ac4b82bSMike Smith * using a buffer. 20571ac4b82bSMike Smith */ 20581ac4b82bSMike Smith static struct mlx_command * 20591ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc) 20601ac4b82bSMike Smith { 20611ac4b82bSMike Smith struct mlx_command *mc; 20621ac4b82bSMike Smith int error; 20631ac4b82bSMike Smith int s; 20641ac4b82bSMike Smith 20651ac4b82bSMike Smith debug("called"); 20661ac4b82bSMike Smith 20671ac4b82bSMike Smith s = splbio(); 20681ac4b82bSMike Smith if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) 20691ac4b82bSMike Smith TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); 20701ac4b82bSMike Smith splx(s); 20711ac4b82bSMike Smith 20721ac4b82bSMike Smith /* allocate a new command buffer? */ 20731ac4b82bSMike Smith if (mc == NULL) { 20741ac4b82bSMike Smith mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT); 20751ac4b82bSMike Smith if (mc != NULL) { 20761ac4b82bSMike Smith bzero(mc, sizeof(*mc)); 20771ac4b82bSMike Smith mc->mc_sc = sc; 20781ac4b82bSMike Smith error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap); 20791ac4b82bSMike Smith if (error) { 20801ac4b82bSMike Smith free(mc, M_DEVBUF); 20811ac4b82bSMike Smith return(NULL); 20821ac4b82bSMike Smith } 20831ac4b82bSMike Smith } 20841ac4b82bSMike Smith } 20851ac4b82bSMike Smith return(mc); 20861ac4b82bSMike Smith } 20871ac4b82bSMike Smith 20881ac4b82bSMike Smith /******************************************************************************** 20891ac4b82bSMike Smith * Release a command buffer for recycling. 20901ac4b82bSMike Smith * 20911ac4b82bSMike Smith * XXX It might be a good idea to limit the number of commands we save for reuse 20921ac4b82bSMike Smith * if it's shown that this list bloats out massively. 20931ac4b82bSMike Smith */ 20941ac4b82bSMike Smith static void 20951ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc) 20961ac4b82bSMike Smith { 20971ac4b82bSMike Smith int s; 20981ac4b82bSMike Smith 20991ac4b82bSMike Smith debug("called"); 21001ac4b82bSMike Smith 21011ac4b82bSMike Smith s = splbio(); 21021ac4b82bSMike Smith TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link); 21031ac4b82bSMike Smith splx(s); 21041ac4b82bSMike Smith } 21051ac4b82bSMike Smith 21061ac4b82bSMike Smith /******************************************************************************** 21071ac4b82bSMike Smith * Permanently discard a command buffer. 21081ac4b82bSMike Smith */ 21091ac4b82bSMike Smith static void 21101ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc) 21111ac4b82bSMike Smith { 21121ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 21131ac4b82bSMike Smith 21141ac4b82bSMike Smith debug("called"); 21151ac4b82bSMike Smith 21161ac4b82bSMike Smith bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap); 21171ac4b82bSMike Smith free(mc, M_DEVBUF); 21181ac4b82bSMike Smith } 21191ac4b82bSMike Smith 21201ac4b82bSMike Smith 21211ac4b82bSMike Smith /******************************************************************************** 21221ac4b82bSMike Smith ******************************************************************************** 21231ac4b82bSMike Smith Type 3 interface accessor methods 21241ac4b82bSMike Smith ******************************************************************************** 21251ac4b82bSMike Smith ********************************************************************************/ 21261ac4b82bSMike Smith 21271ac4b82bSMike Smith /******************************************************************************** 21281ac4b82bSMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 21291ac4b82bSMike Smith * (the controller is not ready to take a command). 21301ac4b82bSMike Smith * 21311ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 21321ac4b82bSMike Smith */ 21331ac4b82bSMike Smith static int 21341ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 21351ac4b82bSMike Smith { 21361ac4b82bSMike Smith int i; 21371ac4b82bSMike Smith 21381ac4b82bSMike Smith debug("called"); 21391ac4b82bSMike Smith 21401ac4b82bSMike Smith /* ready for our command? */ 21411ac4b82bSMike Smith if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) { 21421ac4b82bSMike Smith /* copy mailbox data to window */ 21431ac4b82bSMike Smith for (i = 0; i < 13; i++) 21441ac4b82bSMike Smith MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 21451ac4b82bSMike Smith 21461ac4b82bSMike Smith /* post command */ 2147f6b84b08SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL); 21481ac4b82bSMike Smith return(1); 21491ac4b82bSMike Smith } 21501ac4b82bSMike Smith return(0); 21511ac4b82bSMike Smith } 21521ac4b82bSMike Smith 21531ac4b82bSMike Smith /******************************************************************************** 21541ac4b82bSMike Smith * See if a command has been completed, if so acknowledge its completion 21551ac4b82bSMike Smith * and recover the slot number and status code. 21561ac4b82bSMike Smith * 21571ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 21581ac4b82bSMike Smith */ 21591ac4b82bSMike Smith static int 21601ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 21611ac4b82bSMike Smith { 21621ac4b82bSMike Smith 21631ac4b82bSMike Smith debug("called"); 21641ac4b82bSMike Smith 21651ac4b82bSMike Smith /* status available? */ 21661ac4b82bSMike Smith if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) { 21671ac4b82bSMike Smith *slot = MLX_V3_GET_STATUS_IDENT(sc); /* get command identifier */ 21681ac4b82bSMike Smith *status = MLX_V3_GET_STATUS(sc); /* get status */ 21691ac4b82bSMike Smith 21701ac4b82bSMike Smith /* acknowledge completion */ 2171f6b84b08SMike Smith MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL); 2172f6b84b08SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK); 21731ac4b82bSMike Smith return(1); 21741ac4b82bSMike Smith } 21751ac4b82bSMike Smith return(0); 21761ac4b82bSMike Smith } 21771ac4b82bSMike Smith 21781ac4b82bSMike Smith /******************************************************************************** 21791ac4b82bSMike Smith * Enable/disable interrupts as requested. (No acknowledge required) 21801ac4b82bSMike Smith * 21811ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 21821ac4b82bSMike Smith */ 21831ac4b82bSMike Smith static void 21841ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action) 21851ac4b82bSMike Smith { 21861ac4b82bSMike Smith debug("called"); 21871ac4b82bSMike Smith 21881ac4b82bSMike Smith switch(action) { 21891ac4b82bSMike Smith case MLX_INTACTION_DISABLE: 21901ac4b82bSMike Smith MLX_V3_PUT_IER(sc, 0); 21911ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 21921ac4b82bSMike Smith break; 21931ac4b82bSMike Smith case MLX_INTACTION_ENABLE: 21941ac4b82bSMike Smith MLX_V3_PUT_IER(sc, 1); 21951ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_INTEN; 21961ac4b82bSMike Smith break; 21971ac4b82bSMike Smith } 21981ac4b82bSMike Smith } 21991ac4b82bSMike Smith 22001ac4b82bSMike Smith 22011ac4b82bSMike Smith /******************************************************************************** 22021ac4b82bSMike Smith ******************************************************************************** 2203f6b84b08SMike Smith Type 4 interface accessor methods 2204f6b84b08SMike Smith ******************************************************************************** 2205f6b84b08SMike Smith ********************************************************************************/ 2206f6b84b08SMike Smith 2207f6b84b08SMike Smith /******************************************************************************** 2208f6b84b08SMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 2209f6b84b08SMike Smith * (the controller is not ready to take a command). 2210f6b84b08SMike Smith * 2211f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2212f6b84b08SMike Smith */ 2213f6b84b08SMike Smith static int 2214f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 2215f6b84b08SMike Smith { 2216f6b84b08SMike Smith int i; 2217f6b84b08SMike Smith 2218f6b84b08SMike Smith debug("called"); 2219f6b84b08SMike Smith 2220f6b84b08SMike Smith /* ready for our command? */ 2221f6b84b08SMike Smith if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) { 2222f6b84b08SMike Smith /* copy mailbox data to window */ 2223f6b84b08SMike Smith for (i = 0; i < 13; i++) 2224f6b84b08SMike Smith MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 2225f6b84b08SMike Smith 2226f6b84b08SMike Smith /* post command */ 2227f6b84b08SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD); 2228f6b84b08SMike Smith return(1); 2229f6b84b08SMike Smith } 2230f6b84b08SMike Smith return(0); 2231f6b84b08SMike Smith } 2232f6b84b08SMike Smith 2233f6b84b08SMike Smith /******************************************************************************** 2234f6b84b08SMike Smith * See if a command has been completed, if so acknowledge its completion 2235f6b84b08SMike Smith * and recover the slot number and status code. 2236f6b84b08SMike Smith * 2237f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2238f6b84b08SMike Smith */ 2239f6b84b08SMike Smith static int 2240f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 2241f6b84b08SMike Smith { 2242f6b84b08SMike Smith 2243f6b84b08SMike Smith debug("called"); 2244f6b84b08SMike Smith 2245f6b84b08SMike Smith /* status available? */ 2246f6b84b08SMike Smith if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) { 2247f6b84b08SMike Smith *slot = MLX_V4_GET_STATUS_IDENT(sc); /* get command identifier */ 2248f6b84b08SMike Smith *status = MLX_V4_GET_STATUS(sc); /* get status */ 2249f6b84b08SMike Smith 2250f6b84b08SMike Smith /* acknowledge completion */ 2251f6b84b08SMike Smith MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK); 2252f6b84b08SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK); 2253f6b84b08SMike Smith return(1); 2254f6b84b08SMike Smith } 2255f6b84b08SMike Smith return(0); 2256f6b84b08SMike Smith } 2257f6b84b08SMike Smith 2258f6b84b08SMike Smith /******************************************************************************** 2259f6b84b08SMike Smith * Enable/disable interrupts as requested. 2260f6b84b08SMike Smith * 2261f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2262f6b84b08SMike Smith */ 2263f6b84b08SMike Smith static void 2264f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action) 2265f6b84b08SMike Smith { 2266f6b84b08SMike Smith debug("called"); 2267f6b84b08SMike Smith 2268f6b84b08SMike Smith switch(action) { 2269f6b84b08SMike Smith case MLX_INTACTION_DISABLE: 2270f6b84b08SMike Smith MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT); 2271f6b84b08SMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 2272f6b84b08SMike Smith break; 2273f6b84b08SMike Smith case MLX_INTACTION_ENABLE: 2274f6b84b08SMike Smith MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT); 2275f6b84b08SMike Smith sc->mlx_state |= MLX_STATE_INTEN; 2276f6b84b08SMike Smith break; 2277f6b84b08SMike Smith } 2278f6b84b08SMike Smith } 2279f6b84b08SMike Smith 2280f6b84b08SMike Smith 2281f6b84b08SMike Smith /******************************************************************************** 2282f6b84b08SMike Smith ******************************************************************************** 22831ac4b82bSMike Smith Debugging 22841ac4b82bSMike Smith ******************************************************************************** 22851ac4b82bSMike Smith ********************************************************************************/ 22861ac4b82bSMike Smith 22871ac4b82bSMike Smith /******************************************************************************** 22881ac4b82bSMike Smith * Return a status message describing (mc) 22891ac4b82bSMike Smith */ 22901ac4b82bSMike Smith static char *mlx_status_messages[] = { 22911ac4b82bSMike Smith "normal completion", /* 00 */ 22921ac4b82bSMike Smith "irrecoverable data error", /* 01 */ 22931ac4b82bSMike Smith "drive does not exist, or is offline", /* 02 */ 22941ac4b82bSMike Smith "attempt to write beyond end of drive", /* 03 */ 22951ac4b82bSMike Smith "bad data encountered", /* 04 */ 22961ac4b82bSMike Smith "invalid log entry request", /* 05 */ 22971ac4b82bSMike Smith "attempt to rebuild online drive", /* 06 */ 22981ac4b82bSMike Smith "new disk failed during rebuild", /* 07 */ 22991ac4b82bSMike Smith "invalid channel/target", /* 08 */ 23001ac4b82bSMike Smith "rebuild/check already in progress", /* 09 */ 23011ac4b82bSMike Smith "one or more disks are dead", /* 10 */ 23021ac4b82bSMike Smith "invalid or non-redundant drive", /* 11 */ 23031ac4b82bSMike Smith "channel is busy", /* 12 */ 23041ac4b82bSMike Smith "channel is not stopped", /* 13 */ 23051ac4b82bSMike Smith }; 23061ac4b82bSMike Smith 23071ac4b82bSMike Smith static struct 23081ac4b82bSMike Smith { 23091ac4b82bSMike Smith int command; 23101ac4b82bSMike Smith u_int16_t status; 23111ac4b82bSMike Smith int msg; 23121ac4b82bSMike Smith } mlx_messages[] = { 23131ac4b82bSMike Smith {MLX_CMD_READOLDSG, 0x0001, 1}, 23141ac4b82bSMike Smith {MLX_CMD_READOLDSG, 0x0002, 1}, 23151ac4b82bSMike Smith {MLX_CMD_READOLDSG, 0x0105, 3}, 23161ac4b82bSMike Smith {MLX_CMD_READOLDSG, 0x010c, 4}, 23171ac4b82bSMike Smith {MLX_CMD_WRITEOLDSG, 0x0001, 1}, 23181ac4b82bSMike Smith {MLX_CMD_WRITEOLDSG, 0x0002, 1}, 23191ac4b82bSMike Smith {MLX_CMD_WRITEOLDSG, 0x0105, 3}, 23201ac4b82bSMike Smith {MLX_CMD_LOGOP, 0x0105, 5}, 23211ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0002, 6}, 23221ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0004, 7}, 23231ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0105, 8}, 23241ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0106, 9}, 23251ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0002, 10}, 23261ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0105, 11}, 23271ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0106, 9}, 23281ac4b82bSMike Smith {MLX_CMD_STOPCHANNEL, 0x0106, 12}, 23291ac4b82bSMike Smith {MLX_CMD_STOPCHANNEL, 0x0105, 8}, 23301ac4b82bSMike Smith {MLX_CMD_STARTCHANNEL, 0x0005, 13}, 23311ac4b82bSMike Smith {MLX_CMD_STARTCHANNEL, 0x0105, 8}, 23321ac4b82bSMike Smith {-1, 0, 0} 23331ac4b82bSMike Smith }; 23341ac4b82bSMike Smith 23351ac4b82bSMike Smith static char * 23361ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc) 23371ac4b82bSMike Smith { 23381ac4b82bSMike Smith static char unkmsg[80]; 23391ac4b82bSMike Smith int i; 23401ac4b82bSMike Smith 23411ac4b82bSMike Smith /* look up message in table */ 23421ac4b82bSMike Smith for (i = 0; mlx_messages[i].command != -1; i++) 23431ac4b82bSMike Smith if ((mc->mc_mailbox[0] == mlx_messages[i].command) && 2344466454bdSMike Smith (mc->mc_status == mlx_messages[i].status)) 23451ac4b82bSMike Smith return(mlx_status_messages[mlx_messages[i].msg]); 23461ac4b82bSMike Smith 23471ac4b82bSMike Smith sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]); 23481ac4b82bSMike Smith return(unkmsg); 23491ac4b82bSMike Smith } 23501ac4b82bSMike Smith 23511ac4b82bSMike Smith /******************************************************************************* 23521ac4b82bSMike Smith * Return a string describing the controller (hwid) 23531ac4b82bSMike Smith */ 23541ac4b82bSMike Smith static char * 23551ac4b82bSMike Smith mlx_name_controller(u_int32_t hwid) 23561ac4b82bSMike Smith { 23571ac4b82bSMike Smith static char buf[80]; 23581ac4b82bSMike Smith char smbuf[16]; 23591ac4b82bSMike Smith char *submodel; 23601ac4b82bSMike Smith int nchn; 23611ac4b82bSMike Smith 23621ac4b82bSMike Smith switch(hwid & 0xff) { 23631ac4b82bSMike Smith case 0x01: 23641ac4b82bSMike Smith submodel = "P/PD"; 23651ac4b82bSMike Smith break; 23661ac4b82bSMike Smith case 0x02: 23671ac4b82bSMike Smith submodel = "PL"; 23681ac4b82bSMike Smith break; 23691ac4b82bSMike Smith case 0x10: 23701ac4b82bSMike Smith submodel = "PG"; 23711ac4b82bSMike Smith break; 2372f6b84b08SMike Smith case 0x11: 2373f6b84b08SMike Smith submodel = "PJ"; 2374f6b84b08SMike Smith break; 23754b006d7bSMike Smith case 0x16: 23764b006d7bSMike Smith submodel = "PTL"; 23774b006d7bSMike Smith break; 23781ac4b82bSMike Smith default: 23791ac4b82bSMike Smith sprintf(smbuf, " model 0x%x", hwid & 0xff); 23801ac4b82bSMike Smith submodel = smbuf; 23811ac4b82bSMike Smith break; 23821ac4b82bSMike Smith } 23831ac4b82bSMike Smith nchn = (hwid >> 8) & 0xff; 23841ac4b82bSMike Smith sprintf(buf, "DAC960%s, %d channel%s", submodel, nchn, nchn > 1 ? "s" : ""); 23851ac4b82bSMike Smith return(buf); 23861ac4b82bSMike Smith } 23871ac4b82bSMike Smith 23881ac4b82bSMike Smith /******************************************************************************** 23891ac4b82bSMike Smith ******************************************************************************** 23901ac4b82bSMike Smith Utility Functions 23911ac4b82bSMike Smith ******************************************************************************** 23921ac4b82bSMike Smith ********************************************************************************/ 23931ac4b82bSMike Smith 23941ac4b82bSMike Smith /******************************************************************************** 23951ac4b82bSMike Smith * Find the disk whose unit number is (unit) on this controller 23961ac4b82bSMike Smith */ 23971ac4b82bSMike Smith static struct mlx_sysdrive * 23981ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit) 23991ac4b82bSMike Smith { 24001ac4b82bSMike Smith int i; 24011ac4b82bSMike Smith 24021ac4b82bSMike Smith /* search system drives */ 24031ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 24041ac4b82bSMike Smith /* is this one attached? */ 24051ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 24061ac4b82bSMike Smith /* is this the one? */ 24071ac4b82bSMike Smith if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) 24081ac4b82bSMike Smith return(&sc->mlx_sysdrive[i]); 24091ac4b82bSMike Smith } 24101ac4b82bSMike Smith } 24111ac4b82bSMike Smith return(NULL); 24121ac4b82bSMike Smith } 2413