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> 43da8bb3a3SMike Smith #include <sys/stat.h> 441ac4b82bSMike Smith 451ac4b82bSMike Smith #include <machine/resource.h> 461ac4b82bSMike Smith #include <machine/bus.h> 471ac4b82bSMike Smith #include <machine/clock.h> 481ac4b82bSMike Smith #include <sys/rman.h> 491ac4b82bSMike Smith 501ac4b82bSMike Smith #include <dev/mlx/mlxio.h> 511ac4b82bSMike Smith #include <dev/mlx/mlxvar.h> 521ac4b82bSMike Smith #include <dev/mlx/mlxreg.h> 531ac4b82bSMike Smith 541ac4b82bSMike Smith #define MLX_CDEV_MAJOR 130 551ac4b82bSMike Smith 561ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = { 571ac4b82bSMike Smith /* open */ mlx_open, 581ac4b82bSMike Smith /* close */ mlx_close, 591ac4b82bSMike Smith /* read */ noread, 601ac4b82bSMike Smith /* write */ nowrite, 611ac4b82bSMike Smith /* ioctl */ mlx_ioctl, 621ac4b82bSMike Smith /* poll */ nopoll, 631ac4b82bSMike Smith /* mmap */ nommap, 641ac4b82bSMike Smith /* strategy */ nostrategy, 651ac4b82bSMike Smith /* name */ "mlx", 661ac4b82bSMike Smith /* maj */ MLX_CDEV_MAJOR, 671ac4b82bSMike Smith /* dump */ nodump, 681ac4b82bSMike Smith /* psize */ nopsize, 691ac4b82bSMike Smith /* flags */ 0, 70f6b84b08SMike Smith /* bmaj */ -1 711ac4b82bSMike Smith }; 721ac4b82bSMike Smith 731ac4b82bSMike Smith devclass_t mlx_devclass; 741ac4b82bSMike Smith 751ac4b82bSMike Smith /* 761ac4b82bSMike Smith * Per-interface accessor methods 771ac4b82bSMike Smith */ 781ac4b82bSMike Smith static int mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 791ac4b82bSMike Smith static int mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 801ac4b82bSMike Smith static void mlx_v3_intaction(struct mlx_softc *sc, int action); 81da8bb3a3SMike Smith static int mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 821ac4b82bSMike Smith 83f6b84b08SMike Smith static int mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 84f6b84b08SMike Smith static int mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 85f6b84b08SMike Smith static void mlx_v4_intaction(struct mlx_softc *sc, int action); 86da8bb3a3SMike Smith static int mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 87f6b84b08SMike Smith 885792b7feSMike Smith static int mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 895792b7feSMike Smith static int mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 905792b7feSMike Smith static void mlx_v5_intaction(struct mlx_softc *sc, int action); 91da8bb3a3SMike Smith static int mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 925792b7feSMike Smith 931ac4b82bSMike Smith /* 941ac4b82bSMike Smith * Status monitoring 951ac4b82bSMike Smith */ 961ac4b82bSMike Smith static void mlx_periodic(void *data); 971ac4b82bSMike Smith static void mlx_periodic_enquiry(struct mlx_command *mc); 981ac4b82bSMike Smith static void mlx_periodic_eventlog_poll(struct mlx_softc *sc); 991ac4b82bSMike Smith static void mlx_periodic_eventlog_respond(struct mlx_command *mc); 1001ac4b82bSMike Smith static void mlx_periodic_rebuild(struct mlx_command *mc); 1011ac4b82bSMike Smith 1021ac4b82bSMike Smith /* 1031ac4b82bSMike Smith * Channel Pause 1041ac4b82bSMike Smith */ 1051ac4b82bSMike Smith static void mlx_pause_action(struct mlx_softc *sc); 1061ac4b82bSMike Smith static void mlx_pause_done(struct mlx_command *mc); 1071ac4b82bSMike Smith 1081ac4b82bSMike Smith /* 1091ac4b82bSMike Smith * Command submission. 1101ac4b82bSMike Smith */ 1111ac4b82bSMike Smith static void *mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, 1121ac4b82bSMike Smith void (*complete)(struct mlx_command *mc)); 1131ac4b82bSMike Smith static int mlx_flush(struct mlx_softc *sc); 114421f2f7dSMike Smith static int mlx_check(struct mlx_softc *sc, int drive); 1151ac4b82bSMike Smith static int mlx_rebuild(struct mlx_softc *sc, int channel, int target); 1161ac4b82bSMike Smith static int mlx_wait_command(struct mlx_command *mc); 1171ac4b82bSMike Smith static int mlx_poll_command(struct mlx_command *mc); 1181ac4b82bSMike Smith static void mlx_startio(struct mlx_softc *sc); 1191ac4b82bSMike Smith static void mlx_completeio(struct mlx_command *mc); 1201ac4b82bSMike Smith static int mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu); 1211ac4b82bSMike Smith 1221ac4b82bSMike Smith /* 1231ac4b82bSMike Smith * Command buffer allocation. 1241ac4b82bSMike Smith */ 1251ac4b82bSMike Smith static struct mlx_command *mlx_alloccmd(struct mlx_softc *sc); 1261ac4b82bSMike Smith static void mlx_releasecmd(struct mlx_command *mc); 1271ac4b82bSMike Smith static void mlx_freecmd(struct mlx_command *mc); 1281ac4b82bSMike Smith 1291ac4b82bSMike Smith /* 1301ac4b82bSMike Smith * Command management. 1311ac4b82bSMike Smith */ 1321ac4b82bSMike Smith static int mlx_getslot(struct mlx_command *mc); 1331ac4b82bSMike Smith static void mlx_mapcmd(struct mlx_command *mc); 1341ac4b82bSMike Smith static void mlx_unmapcmd(struct mlx_command *mc); 1351ac4b82bSMike Smith static int mlx_start(struct mlx_command *mc); 1361ac4b82bSMike Smith static int mlx_done(struct mlx_softc *sc); 1371ac4b82bSMike Smith static void mlx_complete(struct mlx_softc *sc); 1381ac4b82bSMike Smith 1391ac4b82bSMike Smith /* 1401ac4b82bSMike Smith * Debugging. 1411ac4b82bSMike Smith */ 1421ac4b82bSMike Smith static char *mlx_diagnose_command(struct mlx_command *mc); 1439eee27f1SMike Smith static void mlx_describe_controller(struct mlx_softc *sc); 144da8bb3a3SMike Smith static int mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2); 1451ac4b82bSMike Smith 1461ac4b82bSMike Smith /* 1471ac4b82bSMike Smith * Utility functions. 1481ac4b82bSMike Smith */ 1491ac4b82bSMike Smith static struct mlx_sysdrive *mlx_findunit(struct mlx_softc *sc, int unit); 1501ac4b82bSMike Smith 1511ac4b82bSMike Smith /******************************************************************************** 1521ac4b82bSMike Smith ******************************************************************************** 1531ac4b82bSMike Smith Public Interfaces 1541ac4b82bSMike Smith ******************************************************************************** 1551ac4b82bSMike Smith ********************************************************************************/ 1561ac4b82bSMike Smith 1571ac4b82bSMike Smith /******************************************************************************** 1581ac4b82bSMike Smith * Free all of the resources associated with (sc) 1591ac4b82bSMike Smith * 1601ac4b82bSMike Smith * Should not be called if the controller is active. 1611ac4b82bSMike Smith */ 1621ac4b82bSMike Smith void 1631ac4b82bSMike Smith mlx_free(struct mlx_softc *sc) 1641ac4b82bSMike Smith { 1651ac4b82bSMike Smith struct mlx_command *mc; 1661ac4b82bSMike Smith 167da8bb3a3SMike Smith debug_called(1); 1681ac4b82bSMike Smith 1691ac4b82bSMike Smith /* cancel status timeout */ 1701ac4b82bSMike Smith untimeout(mlx_periodic, sc, sc->mlx_timeout); 1711ac4b82bSMike Smith 1721ac4b82bSMike Smith /* throw away any command buffers */ 1731ac4b82bSMike Smith while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) { 1741ac4b82bSMike Smith TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); 1751ac4b82bSMike Smith mlx_freecmd(mc); 1761ac4b82bSMike Smith } 1771ac4b82bSMike Smith 1781ac4b82bSMike Smith /* destroy data-transfer DMA tag */ 1791ac4b82bSMike Smith if (sc->mlx_buffer_dmat) 1801ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_buffer_dmat); 1811ac4b82bSMike Smith 1821ac4b82bSMike Smith /* free and destroy DMA memory and tag for s/g lists */ 1831ac4b82bSMike Smith if (sc->mlx_sgtable) 1841ac4b82bSMike Smith bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); 1851ac4b82bSMike Smith if (sc->mlx_sg_dmat) 1861ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_sg_dmat); 1871ac4b82bSMike Smith 1881ac4b82bSMike Smith /* disconnect the interrupt handler */ 1891ac4b82bSMike Smith if (sc->mlx_intr) 1901ac4b82bSMike Smith bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr); 1911ac4b82bSMike Smith if (sc->mlx_irq != NULL) 1921ac4b82bSMike Smith bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq); 1931ac4b82bSMike Smith 1941ac4b82bSMike Smith /* destroy the parent DMA tag */ 1951ac4b82bSMike Smith if (sc->mlx_parent_dmat) 1961ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_parent_dmat); 1971ac4b82bSMike Smith 1981ac4b82bSMike Smith /* release the register window mapping */ 1991ac4b82bSMike Smith if (sc->mlx_mem != NULL) 2001ac4b82bSMike Smith bus_release_resource(sc->mlx_dev, SYS_RES_MEMORY, 2011ac4b82bSMike Smith (sc->mlx_iftype == MLX_IFTYPE_3) ? MLX_CFG_BASE1 : MLX_CFG_BASE0, sc->mlx_mem); 2029eee27f1SMike Smith 2039eee27f1SMike Smith /* free controller enquiry data */ 2049eee27f1SMike Smith if (sc->mlx_enq2 != NULL) 2059eee27f1SMike Smith free(sc->mlx_enq2, M_DEVBUF); 206da8bb3a3SMike Smith 207da8bb3a3SMike Smith /* destroy control device */ 208da8bb3a3SMike Smith if (sc->mlx_dev_t != (dev_t)NULL) 209da8bb3a3SMike Smith destroy_dev(sc->mlx_dev_t); 2101ac4b82bSMike Smith } 2111ac4b82bSMike Smith 2121ac4b82bSMike Smith /******************************************************************************** 2131ac4b82bSMike Smith * Map the scatter/gather table into bus space 2141ac4b82bSMike Smith */ 2151ac4b82bSMike Smith static void 2161ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error) 2171ac4b82bSMike Smith { 2181ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)arg; 2191ac4b82bSMike Smith 220da8bb3a3SMike Smith debug_called(1); 2211ac4b82bSMike Smith 2221ac4b82bSMike Smith /* save base of s/g table's address in bus space */ 2231ac4b82bSMike Smith sc->mlx_sgbusaddr = segs->ds_addr; 2241ac4b82bSMike Smith } 2251ac4b82bSMike Smith 2261ac4b82bSMike Smith static int 2271ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc) 2281ac4b82bSMike Smith { 2291ac4b82bSMike Smith size_t segsize; 2301ac4b82bSMike Smith int error; 2311ac4b82bSMike Smith 232da8bb3a3SMike Smith debug_called(1); 2331ac4b82bSMike Smith 2341ac4b82bSMike Smith /* destroy any existing mappings */ 2351ac4b82bSMike Smith if (sc->mlx_sgtable) 2361ac4b82bSMike Smith bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); 2371ac4b82bSMike Smith if (sc->mlx_sg_dmat) 2381ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_sg_dmat); 2391ac4b82bSMike Smith 2401ac4b82bSMike Smith /* 2411ac4b82bSMike Smith * Create a single tag describing a region large enough to hold all of 2421ac4b82bSMike Smith * the s/g lists we will need. 2431ac4b82bSMike Smith */ 244da8bb3a3SMike Smith segsize = sizeof(struct mlx_sgentry) * sc->mlx_sg_nseg * sc->mlx_maxiop; 2451ac4b82bSMike Smith error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 2461ac4b82bSMike Smith 1, 0, /* alignment, boundary */ 2471ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* lowaddr */ 2481ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* highaddr */ 2491ac4b82bSMike Smith NULL, NULL, /* filter, filterarg */ 2501ac4b82bSMike Smith segsize, 1, /* maxsize, nsegments */ 2511ac4b82bSMike Smith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 2521ac4b82bSMike Smith 0, /* flags */ 2531ac4b82bSMike Smith &sc->mlx_sg_dmat); 2541ac4b82bSMike Smith if (error != 0) { 2551ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n"); 2561ac4b82bSMike Smith return(ENOMEM); 2571ac4b82bSMike Smith } 2581ac4b82bSMike Smith 2591ac4b82bSMike Smith /* 2601ac4b82bSMike Smith * Allocate enough s/g maps for all commands and permanently map them into 2611ac4b82bSMike Smith * controller-visible space. 2621ac4b82bSMike Smith * 2631ac4b82bSMike Smith * XXX this assumes we can get enough space for all the s/g maps in one 2641ac4b82bSMike Smith * contiguous slab. We may need to switch to a more complex arrangement where 2651ac4b82bSMike Smith * we allocate in smaller chunks and keep a lookup table from slot to bus address. 2661ac4b82bSMike Smith */ 2671ac4b82bSMike Smith error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable, BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap); 2681ac4b82bSMike Smith if (error) { 2691ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate s/g table\n"); 2701ac4b82bSMike Smith return(ENOMEM); 2711ac4b82bSMike Smith } 2721ac4b82bSMike Smith bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable, segsize, mlx_dma_map_sg, sc, 0); 2731ac4b82bSMike Smith return(0); 2741ac4b82bSMike Smith } 2751ac4b82bSMike Smith 2761ac4b82bSMike Smith /******************************************************************************** 2771ac4b82bSMike Smith * Initialise the controller and softc 2781ac4b82bSMike Smith */ 2791ac4b82bSMike Smith int 2801ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc) 2811ac4b82bSMike Smith { 282da8bb3a3SMike Smith struct mlx_enquiry_old *meo; 283da8bb3a3SMike Smith int rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg; 2841ac4b82bSMike Smith 285da8bb3a3SMike Smith debug_called(1); 2861ac4b82bSMike Smith 2871ac4b82bSMike Smith /* 2881ac4b82bSMike Smith * Initialise per-controller queues. 2891ac4b82bSMike Smith */ 2904b006d7bSMike Smith TAILQ_INIT(&sc->mlx_work); 2911ac4b82bSMike Smith TAILQ_INIT(&sc->mlx_freecmds); 2921ac4b82bSMike Smith bufq_init(&sc->mlx_bufq); 2931ac4b82bSMike Smith 2941ac4b82bSMike Smith /* 2951ac4b82bSMike Smith * Select accessor methods based on controller interface type. 2961ac4b82bSMike Smith */ 2971ac4b82bSMike Smith switch(sc->mlx_iftype) { 298da8bb3a3SMike Smith case MLX_IFTYPE_2: 2991ac4b82bSMike Smith case MLX_IFTYPE_3: 3001ac4b82bSMike Smith sc->mlx_tryqueue = mlx_v3_tryqueue; 3011ac4b82bSMike Smith sc->mlx_findcomplete = mlx_v3_findcomplete; 3021ac4b82bSMike Smith sc->mlx_intaction = mlx_v3_intaction; 303da8bb3a3SMike Smith sc->mlx_fw_handshake = mlx_v3_fw_handshake; 304da8bb3a3SMike Smith sc->mlx_sg_nseg = MLX_NSEG_OLD; 3051ac4b82bSMike Smith break; 306f6b84b08SMike Smith case MLX_IFTYPE_4: 307f6b84b08SMike Smith sc->mlx_tryqueue = mlx_v4_tryqueue; 308f6b84b08SMike Smith sc->mlx_findcomplete = mlx_v4_findcomplete; 309f6b84b08SMike Smith sc->mlx_intaction = mlx_v4_intaction; 310da8bb3a3SMike Smith sc->mlx_fw_handshake = mlx_v4_fw_handshake; 311da8bb3a3SMike Smith sc->mlx_sg_nseg = MLX_NSEG_NEW; 312f6b84b08SMike Smith break; 3135792b7feSMike Smith case MLX_IFTYPE_5: 3145792b7feSMike Smith sc->mlx_tryqueue = mlx_v5_tryqueue; 3155792b7feSMike Smith sc->mlx_findcomplete = mlx_v5_findcomplete; 3165792b7feSMike Smith sc->mlx_intaction = mlx_v5_intaction; 317da8bb3a3SMike Smith sc->mlx_fw_handshake = mlx_v5_fw_handshake; 318da8bb3a3SMike Smith sc->mlx_sg_nseg = MLX_NSEG_NEW; 3195792b7feSMike Smith break; 3201ac4b82bSMike Smith default: 3211ac4b82bSMike Smith return(ENXIO); /* should never happen */ 3221ac4b82bSMike Smith } 3231ac4b82bSMike Smith 3241ac4b82bSMike Smith /* disable interrupts before we start talking to the controller */ 3251ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 3261ac4b82bSMike Smith 3271ac4b82bSMike Smith /* 328da8bb3a3SMike Smith * Wait for the controller to come ready, handshake with the firmware if required. 329da8bb3a3SMike Smith * This is typically only necessary on platforms where the controller BIOS does not 330da8bb3a3SMike Smith * run. 331da8bb3a3SMike Smith */ 332da8bb3a3SMike Smith hsmsg = 0; 333da8bb3a3SMike Smith DELAY(1000); 334da8bb3a3SMike Smith while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) { 335da8bb3a3SMike Smith /* report first time around... */ 336da8bb3a3SMike Smith if (hsmsg == 0) { 337da8bb3a3SMike Smith device_printf(sc->mlx_dev, "controller initialisation in progress...\n"); 338da8bb3a3SMike Smith hsmsg = 1; 339da8bb3a3SMike Smith } 340da8bb3a3SMike Smith /* did we get a real message? */ 341da8bb3a3SMike Smith if (hscode == 2) { 342da8bb3a3SMike Smith hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2); 343da8bb3a3SMike Smith /* fatal initialisation error? */ 344da8bb3a3SMike Smith if (hscode != 0) { 345da8bb3a3SMike Smith mlx_free(sc); 346da8bb3a3SMike Smith return(ENXIO); 347da8bb3a3SMike Smith } 348da8bb3a3SMike Smith } 349da8bb3a3SMike Smith } 350da8bb3a3SMike Smith if (hsmsg == 1) 351da8bb3a3SMike Smith device_printf(sc->mlx_dev, "initialisation complete.\n"); 352da8bb3a3SMike Smith 353da8bb3a3SMike Smith /* 3541ac4b82bSMike Smith * Allocate and connect our interrupt. 3551ac4b82bSMike Smith */ 3561ac4b82bSMike Smith rid = 0; 3571ac4b82bSMike Smith sc->mlx_irq = bus_alloc_resource(sc->mlx_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); 3581ac4b82bSMike Smith if (sc->mlx_irq == NULL) { 359421f2f7dSMike Smith device_printf(sc->mlx_dev, "can't allocate interrupt\n"); 3601ac4b82bSMike Smith mlx_free(sc); 3611ac4b82bSMike Smith return(ENXIO); 3621ac4b82bSMike Smith } 3631ac4b82bSMike Smith error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO, mlx_intr, sc, &sc->mlx_intr); 3641ac4b82bSMike Smith if (error) { 365421f2f7dSMike Smith device_printf(sc->mlx_dev, "can't set up interrupt\n"); 3661ac4b82bSMike Smith mlx_free(sc); 3671ac4b82bSMike Smith return(ENXIO); 3681ac4b82bSMike Smith } 3691ac4b82bSMike Smith 3701ac4b82bSMike Smith /* 3711ac4b82bSMike Smith * Create DMA tag for mapping buffers into controller-addressable space. 3721ac4b82bSMike Smith */ 3731ac4b82bSMike Smith error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 3741ac4b82bSMike Smith 1, 0, /* alignment, boundary */ 3751ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* lowaddr */ 3761ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* highaddr */ 3771ac4b82bSMike Smith NULL, NULL, /* filter, filterarg */ 378da8bb3a3SMike Smith MAXBSIZE, sc->mlx_sg_nseg, /* maxsize, nsegments */ 3791ac4b82bSMike Smith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 3801ac4b82bSMike Smith 0, /* flags */ 3811ac4b82bSMike Smith &sc->mlx_buffer_dmat); 3821ac4b82bSMike Smith if (error != 0) { 3831ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n"); 3841ac4b82bSMike Smith return(ENOMEM); 3851ac4b82bSMike Smith } 3861ac4b82bSMike Smith 3871ac4b82bSMike Smith /* 3881ac4b82bSMike Smith * Create an initial set of s/g mappings. 3891ac4b82bSMike Smith */ 3909eee27f1SMike Smith sc->mlx_maxiop = 8; 3911ac4b82bSMike Smith error = mlx_sglist_map(sc); 3921ac4b82bSMike Smith if (error != 0) { 393421f2f7dSMike Smith device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n"); 3941ac4b82bSMike Smith return(error); 3951ac4b82bSMike Smith } 3961ac4b82bSMike Smith 3971ac4b82bSMike Smith /* send an ENQUIRY2 to the controller */ 3989eee27f1SMike Smith if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) { 3991ac4b82bSMike Smith device_printf(sc->mlx_dev, "ENQUIRY2 failed\n"); 4001ac4b82bSMike Smith return(ENXIO); 4011ac4b82bSMike Smith } 4021ac4b82bSMike Smith 4039eee27f1SMike Smith /* 4049eee27f1SMike Smith * We don't (yet) know where the event log is up to. 4059eee27f1SMike Smith */ 406421f2f7dSMike Smith sc->mlx_currevent = -1; 4071ac4b82bSMike Smith 4081ac4b82bSMike Smith /* 4091ac4b82bSMike Smith * Do quirk/feature related things. 4101ac4b82bSMike Smith */ 4119eee27f1SMike Smith fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff; 4121ac4b82bSMike Smith switch(sc->mlx_iftype) { 413da8bb3a3SMike Smith case MLX_IFTYPE_2: 414da8bb3a3SMike Smith /* These controllers don't report the firmware version in the ENQUIRY2 response */ 415da8bb3a3SMike Smith if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) { 416da8bb3a3SMike Smith device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n"); 417da8bb3a3SMike Smith return(ENXIO); 418da8bb3a3SMike Smith } 419da8bb3a3SMike Smith sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor; 420da8bb3a3SMike Smith free(meo, M_DEVBUF); 421da8bb3a3SMike Smith 422da8bb3a3SMike Smith /* XXX require 2.42 or better (PCI) or 2.14 or better (EISA) */ 423da8bb3a3SMike Smith if (meo->me_fwminor < 42) { 424da8bb3a3SMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 425da8bb3a3SMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n"); 426da8bb3a3SMike Smith } 427da8bb3a3SMike Smith break; 4281ac4b82bSMike Smith case MLX_IFTYPE_3: 429f6b84b08SMike Smith /* XXX certify 3.52? */ 4309eee27f1SMike Smith if (fwminor < 51) { 4314b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 4324b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n"); 4331ac4b82bSMike Smith } 4341ac4b82bSMike Smith break; 435f6b84b08SMike Smith case MLX_IFTYPE_4: 436f6b84b08SMike Smith /* XXX certify firmware versions? */ 4379eee27f1SMike Smith if (fwminor < 6) { 4384b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 4394b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n"); 440f6b84b08SMike Smith } 441f6b84b08SMike Smith break; 4425792b7feSMike Smith case MLX_IFTYPE_5: 4439eee27f1SMike Smith if (fwminor < 7) { 4445792b7feSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 4455792b7feSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n"); 4465792b7feSMike Smith } 4475792b7feSMike Smith break; 4481ac4b82bSMike Smith default: 4491ac4b82bSMike Smith return(ENXIO); /* should never happen */ 4501ac4b82bSMike Smith } 4511ac4b82bSMike Smith 4521ac4b82bSMike Smith /* 4531ac4b82bSMike Smith * Create the final set of s/g mappings now that we know how many commands 4541ac4b82bSMike Smith * the controller actually supports. 4551ac4b82bSMike Smith */ 4569eee27f1SMike Smith sc->mlx_maxiop = sc->mlx_enq2->me_max_commands; 4571ac4b82bSMike Smith error = mlx_sglist_map(sc); 4581ac4b82bSMike Smith if (error != 0) { 459421f2f7dSMike Smith device_printf(sc->mlx_dev, "can't make permanent s/g list mapping\n"); 4601ac4b82bSMike Smith return(error); 4611ac4b82bSMike Smith } 4621ac4b82bSMike Smith 4631ac4b82bSMike Smith /* 464421f2f7dSMike Smith * No user-requested background operation is in progress. 4651ac4b82bSMike Smith */ 466421f2f7dSMike Smith sc->mlx_background = 0; 467421f2f7dSMike Smith sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE; 4681ac4b82bSMike Smith 4691ac4b82bSMike Smith /* 470da8bb3a3SMike Smith * Create the control device. 4711ac4b82bSMike Smith */ 472da8bb3a3SMike Smith sc->mlx_dev_t = make_dev(&mlx_cdevsw, device_get_unit(sc->mlx_dev), UID_ROOT, GID_OPERATOR, 473da8bb3a3SMike Smith S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev)); 4741ac4b82bSMike Smith 4751ac4b82bSMike Smith /* 4761ac4b82bSMike Smith * Start the timeout routine. 4771ac4b82bSMike Smith */ 4781ac4b82bSMike Smith sc->mlx_timeout = timeout(mlx_periodic, sc, hz); 4791ac4b82bSMike Smith 480da8bb3a3SMike Smith /* print a little information about the controller */ 481da8bb3a3SMike Smith mlx_describe_controller(sc); 482da8bb3a3SMike Smith 4831ac4b82bSMike Smith return(0); 4841ac4b82bSMike Smith } 4851ac4b82bSMike Smith 4861ac4b82bSMike Smith /******************************************************************************** 4871ac4b82bSMike Smith * Locate disk resources and attach children to them. 4881ac4b82bSMike Smith */ 4891ac4b82bSMike Smith void 4901ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc) 4911ac4b82bSMike Smith { 4921ac4b82bSMike Smith struct mlx_enq_sys_drive *mes; 4931ac4b82bSMike Smith struct mlx_sysdrive *dr; 4941ac4b82bSMike Smith int i, error; 4951ac4b82bSMike Smith 496da8bb3a3SMike Smith debug_called(1); 4971ac4b82bSMike Smith 4981ac4b82bSMike Smith /* 4991ac4b82bSMike Smith * Scan all the system drives and attach children for those that 5001ac4b82bSMike Smith * don't currently have them. 5011ac4b82bSMike Smith */ 5021ac4b82bSMike Smith mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL); 5031ac4b82bSMike Smith if (mes == NULL) { 5049eee27f1SMike Smith device_printf(sc->mlx_dev, "error fetching drive status\n"); 5051ac4b82bSMike Smith return; 5061ac4b82bSMike Smith } 5071ac4b82bSMike Smith 5081ac4b82bSMike Smith /* iterate over drives returned */ 5091ac4b82bSMike Smith for (i = 0, dr = &sc->mlx_sysdrive[0]; 5101ac4b82bSMike Smith (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); 5111ac4b82bSMike Smith i++, dr++) { 5121ac4b82bSMike Smith /* are we already attached to this drive? */ 5131ac4b82bSMike Smith if (dr->ms_disk == 0) { 5141ac4b82bSMike Smith /* pick up drive information */ 5151ac4b82bSMike Smith dr->ms_size = mes[i].sd_size; 516f6b84b08SMike Smith dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf; 5171ac4b82bSMike Smith dr->ms_state = mes[i].sd_state; 5181ac4b82bSMike Smith 5191ac4b82bSMike Smith /* generate geometry information */ 5201ac4b82bSMike Smith if (sc->mlx_geom == MLX_GEOM_128_32) { 5211ac4b82bSMike Smith dr->ms_heads = 128; 5221ac4b82bSMike Smith dr->ms_sectors = 32; 5231ac4b82bSMike Smith dr->ms_cylinders = dr->ms_size / (128 * 32); 5241ac4b82bSMike Smith } else { /* MLX_GEOM_255/63 */ 5251ac4b82bSMike Smith dr->ms_heads = 255; 5261ac4b82bSMike Smith dr->ms_sectors = 63; 5271ac4b82bSMike Smith dr->ms_cylinders = dr->ms_size / (255 * 63); 5281ac4b82bSMike Smith } 529fe0d4089SMatthew N. Dodd dr->ms_disk = device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1); 5301ac4b82bSMike Smith if (dr->ms_disk == 0) 5311ac4b82bSMike Smith device_printf(sc->mlx_dev, "device_add_child failed\n"); 532fe0d4089SMatthew N. Dodd device_set_ivars(dr->ms_disk, dr); 5331ac4b82bSMike Smith } 5341ac4b82bSMike Smith } 5351ac4b82bSMike Smith free(mes, M_DEVBUF); 5361ac4b82bSMike Smith if ((error = bus_generic_attach(sc->mlx_dev)) != 0) 5371ac4b82bSMike Smith device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error); 5381ac4b82bSMike Smith 5391ac4b82bSMike Smith /* mark controller back up */ 5401ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_SHUTDOWN; 5411ac4b82bSMike Smith 5421ac4b82bSMike Smith /* enable interrupts */ 5431ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); 5441ac4b82bSMike Smith } 5451ac4b82bSMike Smith 5461ac4b82bSMike Smith /******************************************************************************** 5471ac4b82bSMike Smith * Disconnect from the controller completely, in preparation for unload. 5481ac4b82bSMike Smith */ 5491ac4b82bSMike Smith int 5501ac4b82bSMike Smith mlx_detach(device_t dev) 5511ac4b82bSMike Smith { 5521ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 5535792b7feSMike Smith struct mlxd_softc *mlxd; 5545792b7feSMike Smith int i, s, error; 5551ac4b82bSMike Smith 556da8bb3a3SMike Smith debug_called(1); 5571ac4b82bSMike Smith 5585792b7feSMike Smith error = EBUSY; 5595792b7feSMike Smith s = splbio(); 5601ac4b82bSMike Smith if (sc->mlx_state & MLX_STATE_OPEN) 5615792b7feSMike Smith goto out; 5621ac4b82bSMike Smith 5635792b7feSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 5645792b7feSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 5655792b7feSMike Smith mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk); 5665792b7feSMike Smith if (mlxd->mlxd_flags & MLXD_OPEN) { /* drive is mounted, abort detach */ 5675792b7feSMike Smith device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n"); 5685792b7feSMike Smith goto out; 5695792b7feSMike Smith } 5705792b7feSMike Smith } 5715792b7feSMike Smith } 5721ac4b82bSMike Smith if ((error = mlx_shutdown(dev))) 5735792b7feSMike Smith goto out; 5741ac4b82bSMike Smith 5751ac4b82bSMike Smith mlx_free(sc); 5761ac4b82bSMike Smith 5775792b7feSMike Smith error = 0; 5785792b7feSMike Smith out: 5795792b7feSMike Smith splx(s); 5805792b7feSMike Smith return(error); 5811ac4b82bSMike Smith } 5821ac4b82bSMike Smith 5831ac4b82bSMike Smith /******************************************************************************** 5841ac4b82bSMike Smith * Bring the controller down to a dormant state and detach all child devices. 5851ac4b82bSMike Smith * 5861ac4b82bSMike Smith * This function is called before detach, system shutdown, or before performing 5871ac4b82bSMike Smith * an operation which may add or delete system disks. (Call mlx_startup to 5881ac4b82bSMike Smith * resume normal operation.) 5891ac4b82bSMike Smith * 5901ac4b82bSMike Smith * Note that we can assume that the bufq on the controller is empty, as we won't 5911ac4b82bSMike Smith * allow shutdown if any device is open. 5921ac4b82bSMike Smith */ 5931ac4b82bSMike Smith int 5941ac4b82bSMike Smith mlx_shutdown(device_t dev) 5951ac4b82bSMike Smith { 5961ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 5971ac4b82bSMike Smith int i, s, error; 5981ac4b82bSMike Smith 599da8bb3a3SMike Smith debug_called(1); 6001ac4b82bSMike Smith 6011ac4b82bSMike Smith s = splbio(); 6021ac4b82bSMike Smith error = 0; 6031ac4b82bSMike Smith 6041ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_SHUTDOWN; 6055792b7feSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 6061ac4b82bSMike Smith 6071ac4b82bSMike Smith /* flush controller */ 6081ac4b82bSMike Smith device_printf(sc->mlx_dev, "flushing cache..."); 6091ac4b82bSMike Smith if (mlx_flush(sc)) { 6101ac4b82bSMike Smith printf("failed\n"); 6111ac4b82bSMike Smith } else { 6121ac4b82bSMike Smith printf("done\n"); 6131ac4b82bSMike Smith } 6141ac4b82bSMike Smith 6151ac4b82bSMike Smith /* delete all our child devices */ 6161ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 6171ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 6181ac4b82bSMike Smith if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0) 6191ac4b82bSMike Smith goto out; 6201ac4b82bSMike Smith sc->mlx_sysdrive[i].ms_disk = 0; 6211ac4b82bSMike Smith } 6221ac4b82bSMike Smith } 6231ac4b82bSMike Smith 6241ac4b82bSMike Smith out: 6251ac4b82bSMike Smith splx(s); 6261ac4b82bSMike Smith return(error); 6271ac4b82bSMike Smith } 6281ac4b82bSMike Smith 6291ac4b82bSMike Smith /******************************************************************************** 6301ac4b82bSMike Smith * Bring the controller to a quiescent state, ready for system suspend. 6311ac4b82bSMike Smith */ 6321ac4b82bSMike Smith int 6331ac4b82bSMike Smith mlx_suspend(device_t dev) 6341ac4b82bSMike Smith { 6351ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 6361ac4b82bSMike Smith int s; 6371ac4b82bSMike Smith 638da8bb3a3SMike Smith debug_called(1); 6391ac4b82bSMike Smith 6401ac4b82bSMike Smith s = splbio(); 6411ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_SUSPEND; 6421ac4b82bSMike Smith 6431ac4b82bSMike Smith /* flush controller */ 6441ac4b82bSMike Smith device_printf(sc->mlx_dev, "flushing cache..."); 6451ac4b82bSMike Smith printf("%s\n", mlx_flush(sc) ? "failed" : "done"); 6461ac4b82bSMike Smith 6471ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 6481ac4b82bSMike Smith splx(s); 6491ac4b82bSMike Smith 6501ac4b82bSMike Smith return(0); 6511ac4b82bSMike Smith } 6521ac4b82bSMike Smith 6531ac4b82bSMike Smith /******************************************************************************** 6541ac4b82bSMike Smith * Bring the controller back to a state ready for operation. 6551ac4b82bSMike Smith */ 6561ac4b82bSMike Smith int 6571ac4b82bSMike Smith mlx_resume(device_t dev) 6581ac4b82bSMike Smith { 6591ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 6601ac4b82bSMike Smith 661da8bb3a3SMike Smith debug_called(1); 6621ac4b82bSMike Smith 6631ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_SUSPEND; 6641ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); 6651ac4b82bSMike Smith 6661ac4b82bSMike Smith return(0); 6671ac4b82bSMike Smith } 6681ac4b82bSMike Smith 6691ac4b82bSMike Smith /******************************************************************************* 6701ac4b82bSMike Smith * Take an interrupt, or be poked by other code to look for interrupt-worthy 6711ac4b82bSMike Smith * status. 6721ac4b82bSMike Smith */ 6731ac4b82bSMike Smith void 6741ac4b82bSMike Smith mlx_intr(void *arg) 6751ac4b82bSMike Smith { 6761ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)arg; 6771ac4b82bSMike Smith 678da8bb3a3SMike Smith debug_called(1); 6791ac4b82bSMike Smith 6805792b7feSMike Smith /* collect finished commands, queue anything waiting */ 6815792b7feSMike Smith mlx_done(sc); 6821ac4b82bSMike Smith }; 6831ac4b82bSMike Smith 6841ac4b82bSMike Smith /******************************************************************************* 6851ac4b82bSMike Smith * Receive a buf structure from a child device and queue it on a particular 6861ac4b82bSMike Smith * disk resource, then poke the disk resource to start as much work as it can. 6871ac4b82bSMike Smith */ 6881ac4b82bSMike Smith int 6891ac4b82bSMike Smith mlx_submit_buf(struct mlx_softc *sc, struct buf *bp) 6901ac4b82bSMike Smith { 6914b006d7bSMike Smith int s; 6924b006d7bSMike Smith 693da8bb3a3SMike Smith debug_called(1); 6941ac4b82bSMike Smith 6954b006d7bSMike Smith s = splbio(); 6961ac4b82bSMike Smith bufq_insert_tail(&sc->mlx_bufq, bp); 6971ac4b82bSMike Smith sc->mlx_waitbufs++; 6984b006d7bSMike Smith splx(s); 6991ac4b82bSMike Smith mlx_startio(sc); 7001ac4b82bSMike Smith return(0); 7011ac4b82bSMike Smith } 7021ac4b82bSMike Smith 7031ac4b82bSMike Smith /******************************************************************************** 7041ac4b82bSMike Smith * Accept an open operation on the control device. 7051ac4b82bSMike Smith */ 7061ac4b82bSMike Smith int 7071ac4b82bSMike Smith mlx_open(dev_t dev, int flags, int fmt, struct proc *p) 7081ac4b82bSMike Smith { 7091ac4b82bSMike Smith int unit = minor(dev); 7101ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 7111ac4b82bSMike Smith 7121ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_OPEN; 7131ac4b82bSMike Smith return(0); 7141ac4b82bSMike Smith } 7151ac4b82bSMike Smith 7161ac4b82bSMike Smith /******************************************************************************** 7171ac4b82bSMike Smith * Accept the last close on the control device. 7181ac4b82bSMike Smith */ 7191ac4b82bSMike Smith int 7201ac4b82bSMike Smith mlx_close(dev_t dev, int flags, int fmt, struct proc *p) 7211ac4b82bSMike Smith { 7221ac4b82bSMike Smith int unit = minor(dev); 7231ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 7241ac4b82bSMike Smith 7251ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_OPEN; 7261ac4b82bSMike Smith return (0); 7271ac4b82bSMike Smith } 7281ac4b82bSMike Smith 7291ac4b82bSMike Smith /******************************************************************************** 7301ac4b82bSMike Smith * Handle controller-specific control operations. 7311ac4b82bSMike Smith */ 7321ac4b82bSMike Smith int 7331ac4b82bSMike Smith mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) 7341ac4b82bSMike Smith { 7351ac4b82bSMike Smith int unit = minor(dev); 7361ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 737421f2f7dSMike Smith struct mlx_rebuild_request *rb = (struct mlx_rebuild_request *)addr; 738421f2f7dSMike Smith struct mlx_rebuild_status *rs = (struct mlx_rebuild_status *)addr; 7391ac4b82bSMike Smith int *arg = (int *)addr; 7401ac4b82bSMike Smith struct mlx_pause *mp; 7411ac4b82bSMike Smith struct mlx_sysdrive *dr; 7421ac4b82bSMike Smith struct mlxd_softc *mlxd; 7431ac4b82bSMike Smith int i, error; 7441ac4b82bSMike Smith 7451ac4b82bSMike Smith switch(cmd) { 7461ac4b82bSMike Smith /* 7471ac4b82bSMike Smith * Enumerate connected system drives; returns the first system drive's 7481ac4b82bSMike Smith * unit number if *arg is -1, or the next unit after *arg if it's 7491ac4b82bSMike Smith * a valid unit on this controller. 7501ac4b82bSMike Smith */ 7511ac4b82bSMike Smith case MLX_NEXT_CHILD: 7521ac4b82bSMike Smith /* search system drives */ 7531ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 7541ac4b82bSMike Smith /* is this one attached? */ 7551ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 7561ac4b82bSMike Smith /* looking for the next one we come across? */ 7571ac4b82bSMike Smith if (*arg == -1) { 758421f2f7dSMike Smith *arg = device_get_unit(sc->mlx_sysdrive[0].ms_disk); 7591ac4b82bSMike Smith return(0); 7601ac4b82bSMike Smith } 7611ac4b82bSMike Smith /* we want the one after this one */ 7621ac4b82bSMike Smith if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) 7631ac4b82bSMike Smith *arg = -1; 7641ac4b82bSMike Smith } 7651ac4b82bSMike Smith } 7661ac4b82bSMike Smith return(ENOENT); 7671ac4b82bSMike Smith 7681ac4b82bSMike Smith /* 7691ac4b82bSMike Smith * Scan the controller to see whether new drives have appeared. 7701ac4b82bSMike Smith */ 7711ac4b82bSMike Smith case MLX_RESCAN_DRIVES: 7721ac4b82bSMike Smith mlx_startup(sc); 7731ac4b82bSMike Smith return(0); 7741ac4b82bSMike Smith 7751ac4b82bSMike Smith /* 7761ac4b82bSMike Smith * Disconnect from the specified drive; it may be about to go 7771ac4b82bSMike Smith * away. 7781ac4b82bSMike Smith */ 7791ac4b82bSMike Smith case MLX_DETACH_DRIVE: /* detach one drive */ 7801ac4b82bSMike Smith 7811ac4b82bSMike Smith if (((dr = mlx_findunit(sc, *arg)) == NULL) || 7821ac4b82bSMike Smith ((mlxd = device_get_softc(dr->ms_disk)) == NULL)) 7831ac4b82bSMike Smith return(ENOENT); 7841ac4b82bSMike Smith 7851ac4b82bSMike Smith device_printf(dr->ms_disk, "detaching..."); 7861ac4b82bSMike Smith error = 0; 7871ac4b82bSMike Smith if (mlxd->mlxd_flags & MLXD_OPEN) { 7881ac4b82bSMike Smith error = EBUSY; 7891ac4b82bSMike Smith goto detach_out; 7901ac4b82bSMike Smith } 7911ac4b82bSMike Smith 7921ac4b82bSMike Smith /* flush controller */ 7931ac4b82bSMike Smith if (mlx_flush(sc)) { 7941ac4b82bSMike Smith error = EBUSY; 7951ac4b82bSMike Smith goto detach_out; 7961ac4b82bSMike Smith } 7971ac4b82bSMike Smith 7981ac4b82bSMike Smith /* nuke drive */ 7991ac4b82bSMike Smith if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0) 8001ac4b82bSMike Smith goto detach_out; 8011ac4b82bSMike Smith dr->ms_disk = 0; 8021ac4b82bSMike Smith 8031ac4b82bSMike Smith detach_out: 8041ac4b82bSMike Smith if (error) { 8051ac4b82bSMike Smith printf("failed\n"); 8061ac4b82bSMike Smith } else { 8071ac4b82bSMike Smith printf("done\n"); 8081ac4b82bSMike Smith } 8091ac4b82bSMike Smith return(error); 8101ac4b82bSMike Smith 8111ac4b82bSMike Smith /* 8121ac4b82bSMike Smith * Pause one or more SCSI channels for a period of time, to assist 8131ac4b82bSMike Smith * in the process of hot-swapping devices. 8141ac4b82bSMike Smith * 8151ac4b82bSMike Smith * Note that at least the 3.51 firmware on the DAC960PL doesn't seem 8161ac4b82bSMike Smith * to do this right. 8171ac4b82bSMike Smith */ 8181ac4b82bSMike Smith case MLX_PAUSE_CHANNEL: /* schedule a channel pause */ 8191ac4b82bSMike Smith /* Does this command work on this firmware? */ 8201ac4b82bSMike Smith if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS)) 8211ac4b82bSMike Smith return(EOPNOTSUPP); 8221ac4b82bSMike Smith 8231ac4b82bSMike Smith mp = (struct mlx_pause *)addr; 8241ac4b82bSMike Smith if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) { 8251ac4b82bSMike Smith /* cancel a pending pause operation */ 8261ac4b82bSMike Smith sc->mlx_pause.mp_which = 0; 8271ac4b82bSMike Smith } else { 8281ac4b82bSMike Smith /* fix for legal channels */ 8299eee27f1SMike Smith mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1); 8301ac4b82bSMike Smith /* check time values */ 8311ac4b82bSMike Smith if ((mp->mp_when < 0) || (mp->mp_when > 3600)) 8321ac4b82bSMike Smith return(EINVAL); 8331ac4b82bSMike Smith if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30))) 8341ac4b82bSMike Smith return(EINVAL); 8351ac4b82bSMike Smith 8361ac4b82bSMike Smith /* check for a pause currently running */ 8371ac4b82bSMike Smith if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) 8381ac4b82bSMike Smith return(EBUSY); 8391ac4b82bSMike Smith 8401ac4b82bSMike Smith /* looks ok, go with it */ 8411ac4b82bSMike Smith sc->mlx_pause.mp_which = mp->mp_which; 8421ac4b82bSMike Smith sc->mlx_pause.mp_when = time_second + mp->mp_when; 8431ac4b82bSMike Smith sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong; 8441ac4b82bSMike Smith } 8451ac4b82bSMike Smith return(0); 8461ac4b82bSMike Smith 8471ac4b82bSMike Smith /* 8481ac4b82bSMike Smith * Accept a command passthrough-style. 8491ac4b82bSMike Smith */ 8501ac4b82bSMike Smith case MLX_COMMAND: 8511ac4b82bSMike Smith return(mlx_user_command(sc, (struct mlx_usercommand *)addr)); 8521ac4b82bSMike Smith 853421f2f7dSMike Smith /* 854421f2f7dSMike Smith * Start a rebuild on a given SCSI disk 855421f2f7dSMike Smith */ 856421f2f7dSMike Smith case MLX_REBUILDASYNC: 857421f2f7dSMike Smith if (sc->mlx_background != 0) { 858421f2f7dSMike Smith rb->rr_status = 0x0106; 859421f2f7dSMike Smith return(EBUSY); 860421f2f7dSMike Smith } 861421f2f7dSMike Smith rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target); 862421f2f7dSMike Smith switch (rb->rr_status) { 863421f2f7dSMike Smith case 0: 864421f2f7dSMike Smith error = 0; 865421f2f7dSMike Smith break; 866421f2f7dSMike Smith case 0x10000: 867421f2f7dSMike Smith error = ENOMEM; /* couldn't set up the command */ 868421f2f7dSMike Smith break; 869421f2f7dSMike Smith case 0x0002: 870421f2f7dSMike Smith error = EBUSY; 871421f2f7dSMike Smith break; 872421f2f7dSMike Smith case 0x0104: 873421f2f7dSMike Smith error = EIO; 874421f2f7dSMike Smith break; 875421f2f7dSMike Smith case 0x0105: 876421f2f7dSMike Smith error = ERANGE; 877421f2f7dSMike Smith break; 878421f2f7dSMike Smith case 0x0106: 879421f2f7dSMike Smith error = EBUSY; 880421f2f7dSMike Smith break; 881421f2f7dSMike Smith default: 882421f2f7dSMike Smith error = EINVAL; 883421f2f7dSMike Smith break; 884421f2f7dSMike Smith } 885421f2f7dSMike Smith if (error == 0) 886421f2f7dSMike Smith sc->mlx_background = MLX_BACKGROUND_REBUILD; 887421f2f7dSMike Smith return(error); 888421f2f7dSMike Smith 889421f2f7dSMike Smith /* 890421f2f7dSMike Smith * Get the status of the current rebuild or consistency check. 891421f2f7dSMike Smith */ 892421f2f7dSMike Smith case MLX_REBUILDSTAT: 893421f2f7dSMike Smith *rs = sc->mlx_rebuildstat; 894421f2f7dSMike Smith return(0); 895421f2f7dSMike Smith 896421f2f7dSMike Smith /* 897421f2f7dSMike Smith * Return the per-controller system drive number matching the 898421f2f7dSMike Smith * disk device number in (arg), if it happens to belong to us. 899421f2f7dSMike Smith */ 900421f2f7dSMike Smith case MLX_GET_SYSDRIVE: 901421f2f7dSMike Smith error = ENOENT; 902421f2f7dSMike Smith mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg); 903421f2f7dSMike Smith if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) && 904421f2f7dSMike Smith (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) { 905421f2f7dSMike Smith error = 0; 906421f2f7dSMike Smith *arg = mlxd->mlxd_drive - sc->mlx_sysdrive; 907421f2f7dSMike Smith } 908421f2f7dSMike Smith return(error); 909421f2f7dSMike Smith 9101ac4b82bSMike Smith default: 9111ac4b82bSMike Smith return(ENOTTY); 9121ac4b82bSMike Smith } 9131ac4b82bSMike Smith } 9141ac4b82bSMike Smith 9151ac4b82bSMike Smith /******************************************************************************** 9161ac4b82bSMike Smith * Handle operations requested by a System Drive connected to this controller. 9171ac4b82bSMike Smith */ 9181ac4b82bSMike Smith int 9191ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd, 9201ac4b82bSMike Smith caddr_t addr, int32_t flag, struct proc *p) 9211ac4b82bSMike Smith { 9221ac4b82bSMike Smith int *arg = (int *)addr; 923421f2f7dSMike Smith int error, result; 9241ac4b82bSMike Smith 9251ac4b82bSMike Smith switch(cmd) { 9261ac4b82bSMike Smith /* 9271ac4b82bSMike Smith * Return the current status of this drive. 9281ac4b82bSMike Smith */ 9291ac4b82bSMike Smith case MLXD_STATUS: 9301ac4b82bSMike Smith *arg = drive->ms_state; 9311ac4b82bSMike Smith return(0); 9321ac4b82bSMike Smith 9331ac4b82bSMike Smith /* 934421f2f7dSMike Smith * Start a background consistency check on this drive. 9351ac4b82bSMike Smith */ 936421f2f7dSMike Smith case MLXD_CHECKASYNC: /* start a background consistency check */ 937421f2f7dSMike Smith if (sc->mlx_background != 0) { 938421f2f7dSMike Smith *arg = 0x0106; 9391ac4b82bSMike Smith return(EBUSY); 940421f2f7dSMike Smith } 941421f2f7dSMike Smith result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]); 942421f2f7dSMike Smith switch (result) { 9431ac4b82bSMike Smith case 0: 9441ac4b82bSMike Smith error = 0; 9451ac4b82bSMike Smith break; 9461ac4b82bSMike Smith case 0x10000: 9471ac4b82bSMike Smith error = ENOMEM; /* couldn't set up the command */ 9481ac4b82bSMike Smith break; 9491ac4b82bSMike Smith case 0x0002: 9501ac4b82bSMike Smith error = EIO; 9511ac4b82bSMike Smith break; 9521ac4b82bSMike Smith case 0x0105: 9531ac4b82bSMike Smith error = ERANGE; 9541ac4b82bSMike Smith break; 955421f2f7dSMike Smith case 0x0106: 956421f2f7dSMike Smith error = EBUSY; 957421f2f7dSMike Smith break; 9581ac4b82bSMike Smith default: 9591ac4b82bSMike Smith error = EINVAL; 9601ac4b82bSMike Smith break; 9611ac4b82bSMike Smith } 962421f2f7dSMike Smith if (error == 0) 963421f2f7dSMike Smith sc->mlx_background = MLX_BACKGROUND_CHECK; 964421f2f7dSMike Smith *arg = result; 9651ac4b82bSMike Smith return(error); 9661ac4b82bSMike Smith 9671ac4b82bSMike Smith } 9681ac4b82bSMike Smith return(ENOIOCTL); 9691ac4b82bSMike Smith } 9701ac4b82bSMike Smith 9711ac4b82bSMike Smith 9721ac4b82bSMike Smith /******************************************************************************** 9731ac4b82bSMike Smith ******************************************************************************** 9741ac4b82bSMike Smith Status Monitoring 9751ac4b82bSMike Smith ******************************************************************************** 9761ac4b82bSMike Smith ********************************************************************************/ 9771ac4b82bSMike Smith 9781ac4b82bSMike Smith /******************************************************************************** 9791ac4b82bSMike Smith * Fire off commands to periodically check the status of connected drives. 9801ac4b82bSMike Smith */ 9811ac4b82bSMike Smith static void 9821ac4b82bSMike Smith mlx_periodic(void *data) 9831ac4b82bSMike Smith { 9841ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)data; 9851ac4b82bSMike Smith 986da8bb3a3SMike Smith debug_called(1); 9871ac4b82bSMike Smith 9881ac4b82bSMike Smith /* 9891ac4b82bSMike Smith * Run a bus pause? 9901ac4b82bSMike Smith */ 9911ac4b82bSMike Smith if ((sc->mlx_pause.mp_which != 0) && 9921ac4b82bSMike Smith (sc->mlx_pause.mp_when > 0) && 9931ac4b82bSMike Smith (time_second >= sc->mlx_pause.mp_when)){ 9941ac4b82bSMike Smith 9951ac4b82bSMike Smith mlx_pause_action(sc); /* pause is running */ 9961ac4b82bSMike Smith sc->mlx_pause.mp_when = 0; 9971ac4b82bSMike Smith sysbeep(500, hz); 9981ac4b82bSMike Smith 9991ac4b82bSMike Smith /* 10001ac4b82bSMike Smith * Bus pause still running? 10011ac4b82bSMike Smith */ 10021ac4b82bSMike Smith } else if ((sc->mlx_pause.mp_which != 0) && 10031ac4b82bSMike Smith (sc->mlx_pause.mp_when == 0)) { 10041ac4b82bSMike Smith 10051ac4b82bSMike Smith /* time to stop bus pause? */ 10061ac4b82bSMike Smith if (time_second >= sc->mlx_pause.mp_howlong) { 10071ac4b82bSMike Smith mlx_pause_action(sc); 10081ac4b82bSMike Smith sc->mlx_pause.mp_which = 0; /* pause is complete */ 10091ac4b82bSMike Smith sysbeep(500, hz); 10101ac4b82bSMike Smith } else { 10111ac4b82bSMike Smith sysbeep((time_second % 5) * 100 + 500, hz/8); 10121ac4b82bSMike Smith } 10131ac4b82bSMike Smith 10141ac4b82bSMike Smith /* 10151ac4b82bSMike Smith * Run normal periodic activities? 10161ac4b82bSMike Smith */ 10175792b7feSMike Smith } else if (time_second > (sc->mlx_lastpoll + 10)) { 10181ac4b82bSMike Smith sc->mlx_lastpoll = time_second; 10191ac4b82bSMike Smith 10201ac4b82bSMike Smith /* 10211ac4b82bSMike Smith * Check controller status. 10225792b7feSMike Smith * 10235792b7feSMike Smith * XXX Note that this may not actually launch a command in situations of high load. 10241ac4b82bSMike Smith */ 1025da8bb3a3SMike Smith mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY, 1026da8bb3a3SMike Smith imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry); 10271ac4b82bSMike Smith 10281ac4b82bSMike Smith /* 10291ac4b82bSMike Smith * Check system drive status. 10301ac4b82bSMike Smith * 10311ac4b82bSMike Smith * XXX This might be better left to event-driven detection, eg. I/O to an offline 10321ac4b82bSMike Smith * drive will detect it's offline, rebuilds etc. should detect the drive is back 10331ac4b82bSMike Smith * online. 10341ac4b82bSMike Smith */ 10351ac4b82bSMike Smith mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES, 10361ac4b82bSMike Smith mlx_periodic_enquiry); 10371ac4b82bSMike Smith 10381ac4b82bSMike Smith } 10391ac4b82bSMike Smith 1040421f2f7dSMike Smith /* get drive rebuild/check status */ 1041421f2f7dSMike Smith /* XXX should check sc->mlx_background if this is only valid while in progress */ 1042421f2f7dSMike Smith mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild); 1043421f2f7dSMike Smith 10445792b7feSMike Smith /* deal with possibly-missed interrupts and timed-out commands */ 10455792b7feSMike Smith mlx_done(sc); 10461ac4b82bSMike Smith 10471ac4b82bSMike Smith /* reschedule another poll next second or so */ 10481ac4b82bSMike Smith sc->mlx_timeout = timeout(mlx_periodic, sc, hz); 10491ac4b82bSMike Smith } 10501ac4b82bSMike Smith 10511ac4b82bSMike Smith /******************************************************************************** 10521ac4b82bSMike Smith * Handle the result of an ENQUIRY command instigated by periodic status polling. 10531ac4b82bSMike Smith */ 10541ac4b82bSMike Smith static void 10551ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc) 10561ac4b82bSMike Smith { 10571ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 10581ac4b82bSMike Smith 1059da8bb3a3SMike Smith debug_called(1); 10601ac4b82bSMike Smith 10611ac4b82bSMike Smith /* Command completed OK? */ 10621ac4b82bSMike Smith if (mc->mc_status != 0) { 1063da8bb3a3SMike Smith device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc)); 10641ac4b82bSMike Smith goto out; 10651ac4b82bSMike Smith } 10661ac4b82bSMike Smith 10671ac4b82bSMike Smith /* respond to command */ 10681ac4b82bSMike Smith switch(mc->mc_mailbox[0]) { 10691ac4b82bSMike Smith /* 1070da8bb3a3SMike Smith * This is currently a bit fruitless, as we don't know how to extract the eventlog 1071da8bb3a3SMike Smith * pointer yet. 1072da8bb3a3SMike Smith */ 1073da8bb3a3SMike Smith case MLX_CMD_ENQUIRY_OLD: 1074da8bb3a3SMike Smith { 1075da8bb3a3SMike Smith struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data; 1076da8bb3a3SMike Smith struct mlx_enquiry_old *meo = (struct mlx_enquiry_old *)mc->mc_data; 1077da8bb3a3SMike Smith int i; 1078da8bb3a3SMike Smith 1079da8bb3a3SMike Smith /* convert data in-place to new format */ 1080da8bb3a3SMike Smith for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) { 1081da8bb3a3SMike Smith me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan; 1082da8bb3a3SMike Smith me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ; 1083da8bb3a3SMike Smith } 1084da8bb3a3SMike Smith me->me_misc_flags = 0; 1085da8bb3a3SMike Smith me->me_rebuild_count = meo->me_rebuild_count; 1086da8bb3a3SMike Smith me->me_dead_count = meo->me_dead_count; 1087da8bb3a3SMike Smith me->me_critical_sd_count = meo->me_critical_sd_count; 1088da8bb3a3SMike Smith me->me_event_log_seq_num = 0; 1089da8bb3a3SMike Smith me->me_offline_sd_count = meo->me_offline_sd_count; 1090da8bb3a3SMike Smith me->me_max_commands = meo->me_max_commands; 1091da8bb3a3SMike Smith me->me_rebuild_flag = meo->me_rebuild_flag; 1092da8bb3a3SMike Smith me->me_fwmajor = meo->me_fwmajor; 1093da8bb3a3SMike Smith me->me_fwminor = meo->me_fwminor; 1094da8bb3a3SMike Smith me->me_status_flags = meo->me_status_flags; 1095da8bb3a3SMike Smith me->me_flash_age = meo->me_flash_age; 1096da8bb3a3SMike Smith for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) { 1097da8bb3a3SMike Smith if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) { 1098da8bb3a3SMike Smith me->me_drvsize[i] = 0; /* drive beyond supported range */ 1099da8bb3a3SMike Smith } else { 1100da8bb3a3SMike Smith me->me_drvsize[i] = meo->me_drvsize[i]; 1101da8bb3a3SMike Smith } 1102da8bb3a3SMike Smith } 1103da8bb3a3SMike Smith me->me_num_sys_drvs = meo->me_num_sys_drvs; 1104da8bb3a3SMike Smith } 1105da8bb3a3SMike Smith /* FALLTHROUGH */ 1106da8bb3a3SMike Smith 1107da8bb3a3SMike Smith /* 11081ac4b82bSMike Smith * Generic controller status update. We could do more with this than just 11091ac4b82bSMike Smith * checking the event log. 11101ac4b82bSMike Smith */ 11111ac4b82bSMike Smith case MLX_CMD_ENQUIRY: 11121ac4b82bSMike Smith { 11131ac4b82bSMike Smith struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data; 11141ac4b82bSMike Smith 1115421f2f7dSMike Smith if (sc->mlx_currevent == -1) { 11169eee27f1SMike Smith /* initialise our view of the event log */ 11179eee27f1SMike Smith sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num; 11189eee27f1SMike Smith } else if (me->me_event_log_seq_num != sc->mlx_lastevent) { 11191ac4b82bSMike Smith /* record where current events are up to */ 11201ac4b82bSMike Smith sc->mlx_currevent = me->me_event_log_seq_num; 1121da8bb3a3SMike Smith debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent); 11221ac4b82bSMike Smith 11239eee27f1SMike Smith /* drain new eventlog entries */ 11241ac4b82bSMike Smith mlx_periodic_eventlog_poll(sc); 11251ac4b82bSMike Smith } 11261ac4b82bSMike Smith break; 11271ac4b82bSMike Smith } 11281ac4b82bSMike Smith case MLX_CMD_ENQSYSDRIVE: 11291ac4b82bSMike Smith { 11301ac4b82bSMike Smith struct mlx_enq_sys_drive *mes = (struct mlx_enq_sys_drive *)mc->mc_data; 11311ac4b82bSMike Smith struct mlx_sysdrive *dr; 11321ac4b82bSMike Smith int i; 11331ac4b82bSMike Smith 11341ac4b82bSMike Smith for (i = 0, dr = &sc->mlx_sysdrive[0]; 11351ac4b82bSMike Smith (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); 11361ac4b82bSMike Smith i++) { 11371ac4b82bSMike Smith 11381ac4b82bSMike Smith /* has state been changed by controller? */ 11391ac4b82bSMike Smith if (dr->ms_state != mes[i].sd_state) { 11401ac4b82bSMike Smith switch(mes[i].sd_state) { 11411ac4b82bSMike Smith case MLX_SYSD_OFFLINE: 11421ac4b82bSMike Smith device_printf(dr->ms_disk, "drive offline\n"); 11431ac4b82bSMike Smith break; 11441ac4b82bSMike Smith case MLX_SYSD_ONLINE: 11451ac4b82bSMike Smith device_printf(dr->ms_disk, "drive online\n"); 11461ac4b82bSMike Smith break; 11471ac4b82bSMike Smith case MLX_SYSD_CRITICAL: 11481ac4b82bSMike Smith device_printf(dr->ms_disk, "drive critical\n"); 11491ac4b82bSMike Smith break; 11501ac4b82bSMike Smith } 11511ac4b82bSMike Smith /* save new state */ 11521ac4b82bSMike Smith dr->ms_state = mes[i].sd_state; 11531ac4b82bSMike Smith } 11541ac4b82bSMike Smith } 11551ac4b82bSMike Smith break; 11561ac4b82bSMike Smith } 11571ac4b82bSMike Smith default: 11585792b7feSMike Smith device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __FUNCTION__, mc->mc_mailbox[0]); 11591ac4b82bSMike Smith break; 11601ac4b82bSMike Smith } 11611ac4b82bSMike Smith 11621ac4b82bSMike Smith out: 11631ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 11641ac4b82bSMike Smith mlx_releasecmd(mc); 11651ac4b82bSMike Smith } 11661ac4b82bSMike Smith 11671ac4b82bSMike Smith /******************************************************************************** 11681ac4b82bSMike Smith * Instigate a poll for one event log message on (sc). 11691ac4b82bSMike Smith * We only poll for one message at a time, to keep our command usage down. 11701ac4b82bSMike Smith */ 11711ac4b82bSMike Smith static void 11721ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc) 11731ac4b82bSMike Smith { 11741ac4b82bSMike Smith struct mlx_command *mc; 11751ac4b82bSMike Smith void *result = NULL; 11761ac4b82bSMike Smith int error; 11771ac4b82bSMike Smith 1178da8bb3a3SMike Smith debug_called(1); 11791ac4b82bSMike Smith 11801ac4b82bSMike Smith /* get ourselves a command buffer */ 11811ac4b82bSMike Smith error = 1; 11821ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 11831ac4b82bSMike Smith goto out; 11841ac4b82bSMike Smith /* allocate the response structure */ 118533c8cb18SMike Smith if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF, M_NOWAIT)) == NULL) 11861ac4b82bSMike Smith goto out; 11871ac4b82bSMike Smith /* get a command slot */ 11881ac4b82bSMike Smith if (mlx_getslot(mc)) 11891ac4b82bSMike Smith goto out; 11901ac4b82bSMike Smith 11911ac4b82bSMike Smith /* map the command so the controller can see it */ 11921ac4b82bSMike Smith mc->mc_data = result; 119333c8cb18SMike Smith mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024; 11941ac4b82bSMike Smith mlx_mapcmd(mc); 11951ac4b82bSMike Smith 11961ac4b82bSMike Smith /* build the command to get one entry */ 11971ac4b82bSMike Smith mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0); 11981ac4b82bSMike Smith mc->mc_complete = mlx_periodic_eventlog_respond; 11991ac4b82bSMike Smith mc->mc_private = mc; 12001ac4b82bSMike Smith 12011ac4b82bSMike Smith /* start the command */ 12021ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 12031ac4b82bSMike Smith goto out; 12041ac4b82bSMike Smith 12051ac4b82bSMike Smith error = 0; /* success */ 12061ac4b82bSMike Smith out: 120733c8cb18SMike Smith if (error != 0) { 12081ac4b82bSMike Smith if (mc != NULL) 12091ac4b82bSMike Smith mlx_releasecmd(mc); 121033c8cb18SMike Smith if (result != NULL) 12111ac4b82bSMike Smith free(result, M_DEVBUF); 12121ac4b82bSMike Smith } 121333c8cb18SMike Smith } 12141ac4b82bSMike Smith 12151ac4b82bSMike Smith /******************************************************************************** 12161ac4b82bSMike Smith * Handle the result of polling for a log message, generate diagnostic output. 12171ac4b82bSMike Smith * If this wasn't the last message waiting for us, we'll go collect another. 12181ac4b82bSMike Smith */ 12191ac4b82bSMike Smith static char *mlx_sense_messages[] = { 12201ac4b82bSMike Smith "because write recovery failed", 12211ac4b82bSMike Smith "because of SCSI bus reset failure", 12221ac4b82bSMike Smith "because of double check condition", 12231ac4b82bSMike Smith "because it was removed", 12241ac4b82bSMike Smith "because of gross error on SCSI chip", 12251ac4b82bSMike Smith "because of bad tag returned from drive", 12261ac4b82bSMike Smith "because of timeout on SCSI command", 12271ac4b82bSMike Smith "because of reset SCSI command issued from system", 12281ac4b82bSMike Smith "because busy or parity error count exceeded limit", 12291ac4b82bSMike Smith "because of 'kill drive' command from system", 12301ac4b82bSMike Smith "because of selection timeout", 12311ac4b82bSMike Smith "due to SCSI phase sequence error", 12321ac4b82bSMike Smith "due to unknown status" 12331ac4b82bSMike Smith }; 12341ac4b82bSMike Smith 12351ac4b82bSMike Smith static void 12361ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc) 12371ac4b82bSMike Smith { 12381ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 12391ac4b82bSMike Smith struct mlx_eventlog_entry *el = (struct mlx_eventlog_entry *)mc->mc_data; 12401ac4b82bSMike Smith char *reason; 12411ac4b82bSMike Smith 1242da8bb3a3SMike Smith debug_called(1); 12431ac4b82bSMike Smith 12445792b7feSMike Smith sc->mlx_lastevent++; /* next message... */ 12451ac4b82bSMike Smith if (mc->mc_status == 0) { 12461ac4b82bSMike Smith 12471ac4b82bSMike Smith /* handle event log message */ 12481ac4b82bSMike Smith switch(el->el_type) { 12491ac4b82bSMike Smith /* 12501ac4b82bSMike Smith * This is the only sort of message we understand at the moment. 12511ac4b82bSMike Smith * The tests here are probably incomplete. 12521ac4b82bSMike Smith */ 12531ac4b82bSMike Smith case MLX_LOGMSG_SENSE: /* sense data */ 12541ac4b82bSMike Smith /* Mylex vendor-specific message indicating a drive was killed? */ 12551ac4b82bSMike Smith if ((el->el_sensekey == 9) && 12561ac4b82bSMike Smith (el->el_asc == 0x80)) { 12571ac4b82bSMike Smith if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) { 12581ac4b82bSMike Smith reason = mlx_sense_messages[el->el_asq]; 12591ac4b82bSMike Smith } else { 12601ac4b82bSMike Smith reason = "for unknown reason"; 12611ac4b82bSMike Smith } 12621ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n", 12631ac4b82bSMike Smith el->el_channel, el->el_target, reason); 12641ac4b82bSMike Smith } 12651ac4b82bSMike Smith /* SCSI drive was reset? */ 12661ac4b82bSMike Smith if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) { 12671ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d reset\n", 12681ac4b82bSMike Smith el->el_channel, el->el_target); 12691ac4b82bSMike Smith } 12701ac4b82bSMike Smith /* SCSI drive error? */ 12711ac4b82bSMike Smith if (!((el->el_sensekey == 0) || 12721ac4b82bSMike Smith ((el->el_sensekey == 2) && 12731ac4b82bSMike Smith (el->el_asc == 0x04) && 12741ac4b82bSMike Smith ((el->el_asq == 0x01) || 12751ac4b82bSMike Smith (el->el_asq == 0x02))))) { 12761ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n", 12771ac4b82bSMike Smith el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq); 12781ac4b82bSMike Smith device_printf(sc->mlx_dev, " info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":"); 12791ac4b82bSMike Smith } 12801ac4b82bSMike Smith break; 12811ac4b82bSMike Smith 12821ac4b82bSMike Smith default: 12831ac4b82bSMike Smith device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type); 12841ac4b82bSMike Smith break; 12851ac4b82bSMike Smith } 12861ac4b82bSMike Smith } else { 12871ac4b82bSMike Smith device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc)); 1288421f2f7dSMike Smith panic("log operation failed: lastevent = %d, currevent = %d", 1289421f2f7dSMike Smith sc->mlx_lastevent, sc->mlx_currevent); 12901ac4b82bSMike Smith } 12911ac4b82bSMike Smith 12921ac4b82bSMike Smith /* dispose of command and data */ 12931ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 12941ac4b82bSMike Smith mlx_releasecmd(mc); 12951ac4b82bSMike Smith 12961ac4b82bSMike Smith /* is there another message to obtain? */ 12971ac4b82bSMike Smith if (sc->mlx_lastevent != sc->mlx_currevent) 12981ac4b82bSMike Smith mlx_periodic_eventlog_poll(sc); 12991ac4b82bSMike Smith } 13001ac4b82bSMike Smith 13011ac4b82bSMike Smith /******************************************************************************** 1302421f2f7dSMike Smith * Handle check/rebuild operations in progress. 13031ac4b82bSMike Smith */ 13041ac4b82bSMike Smith static void 13051ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc) 13061ac4b82bSMike Smith { 13071ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 1308421f2f7dSMike Smith struct mlx_rebuild_status *mr = (struct mlx_rebuild_status *)mc->mc_data; 13091ac4b82bSMike Smith 13101ac4b82bSMike Smith switch(mc->mc_status) { 1311421f2f7dSMike Smith case 0: /* operation running, update stats */ 1312421f2f7dSMike Smith sc->mlx_rebuildstat = *mr; 1313421f2f7dSMike Smith 1314421f2f7dSMike Smith /* spontaneous rebuild/check? */ 1315421f2f7dSMike Smith if (sc->mlx_background == 0) { 1316421f2f7dSMike Smith sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS; 1317421f2f7dSMike Smith device_printf(sc->mlx_dev, "background check/rebuild operation started\n"); 1318421f2f7dSMike Smith } 13191ac4b82bSMike Smith break; 13201ac4b82bSMike Smith 1321421f2f7dSMike Smith case 0x0105: /* nothing running, finalise stats and report */ 1322421f2f7dSMike Smith switch(sc->mlx_background) { 1323421f2f7dSMike Smith case MLX_BACKGROUND_CHECK: 1324421f2f7dSMike Smith device_printf(sc->mlx_dev, "consistency check completed\n"); /* XXX print drive? */ 1325421f2f7dSMike Smith break; 1326421f2f7dSMike Smith case MLX_BACKGROUND_REBUILD: 1327421f2f7dSMike Smith device_printf(sc->mlx_dev, "drive rebuild completed\n"); /* XXX print channel/target? */ 1328421f2f7dSMike Smith break; 1329421f2f7dSMike Smith case MLX_BACKGROUND_SPONTANEOUS: 1330421f2f7dSMike Smith default: 1331421f2f7dSMike Smith /* if we have previously been non-idle, report the transition */ 1332421f2f7dSMike Smith if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) { 1333421f2f7dSMike Smith device_printf(sc->mlx_dev, "background check/rebuild operation completed\n"); 13341ac4b82bSMike Smith } 1335421f2f7dSMike Smith } 1336421f2f7dSMike Smith sc->mlx_background = 0; 1337421f2f7dSMike Smith sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE; 13381ac4b82bSMike Smith break; 13391ac4b82bSMike Smith } 13401ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 13411ac4b82bSMike Smith mlx_releasecmd(mc); 13421ac4b82bSMike Smith } 13431ac4b82bSMike Smith 13441ac4b82bSMike Smith /******************************************************************************** 13451ac4b82bSMike Smith ******************************************************************************** 13461ac4b82bSMike Smith Channel Pause 13471ac4b82bSMike Smith ******************************************************************************** 13481ac4b82bSMike Smith ********************************************************************************/ 13491ac4b82bSMike Smith 13501ac4b82bSMike Smith /******************************************************************************** 13511ac4b82bSMike Smith * It's time to perform a channel pause action for (sc), either start or stop 13521ac4b82bSMike Smith * the pause. 13531ac4b82bSMike Smith */ 13541ac4b82bSMike Smith static void 13551ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc) 13561ac4b82bSMike Smith { 13571ac4b82bSMike Smith struct mlx_command *mc; 13581ac4b82bSMike Smith int failsafe, i, command; 13591ac4b82bSMike Smith 13601ac4b82bSMike Smith /* What are we doing here? */ 13611ac4b82bSMike Smith if (sc->mlx_pause.mp_when == 0) { 13621ac4b82bSMike Smith command = MLX_CMD_STARTCHANNEL; 13631ac4b82bSMike Smith failsafe = 0; 13641ac4b82bSMike Smith 13651ac4b82bSMike Smith } else { 13661ac4b82bSMike Smith command = MLX_CMD_STOPCHANNEL; 13671ac4b82bSMike Smith 13681ac4b82bSMike Smith /* 13691ac4b82bSMike Smith * Channels will always start again after the failsafe period, 13701ac4b82bSMike Smith * which is specified in multiples of 30 seconds. 13711ac4b82bSMike Smith * This constrains us to a maximum pause of 450 seconds. 13721ac4b82bSMike Smith */ 13731ac4b82bSMike Smith failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30; 13741ac4b82bSMike Smith if (failsafe > 0xf) { 13751ac4b82bSMike Smith failsafe = 0xf; 13761ac4b82bSMike Smith sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5; 13771ac4b82bSMike Smith } 13781ac4b82bSMike Smith } 13791ac4b82bSMike Smith 13801ac4b82bSMike Smith /* build commands for every channel requested */ 13819eee27f1SMike Smith for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) { 13821ac4b82bSMike Smith if ((1 << i) & sc->mlx_pause.mp_which) { 13831ac4b82bSMike Smith 13841ac4b82bSMike Smith /* get ourselves a command buffer */ 13851ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 13861ac4b82bSMike Smith goto fail; 13871ac4b82bSMike Smith /* get a command slot */ 13881ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_PRIORITY; 13891ac4b82bSMike Smith if (mlx_getslot(mc)) 13901ac4b82bSMike Smith goto fail; 13911ac4b82bSMike Smith 13921ac4b82bSMike Smith /* build the command */ 13931ac4b82bSMike Smith mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0); 13941ac4b82bSMike Smith mc->mc_complete = mlx_pause_done; 13951ac4b82bSMike Smith mc->mc_private = sc; /* XXX not needed */ 13961ac4b82bSMike Smith if (mlx_start(mc)) 13971ac4b82bSMike Smith goto fail; 13981ac4b82bSMike Smith /* command submitted OK */ 13991ac4b82bSMike Smith return; 14001ac4b82bSMike Smith 14011ac4b82bSMike Smith fail: 14021ac4b82bSMike Smith device_printf(sc->mlx_dev, "%s failed for channel %d\n", 14031ac4b82bSMike Smith command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i); 14041ac4b82bSMike Smith if (mc != NULL) 14051ac4b82bSMike Smith mlx_releasecmd(mc); 14061ac4b82bSMike Smith } 14071ac4b82bSMike Smith } 14081ac4b82bSMike Smith } 14091ac4b82bSMike Smith 14101ac4b82bSMike Smith static void 14111ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc) 14121ac4b82bSMike Smith { 14131ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 14141ac4b82bSMike Smith int command = mc->mc_mailbox[0]; 14151ac4b82bSMike Smith int channel = mc->mc_mailbox[2] & 0xf; 14161ac4b82bSMike Smith 14171ac4b82bSMike Smith if (mc->mc_status != 0) { 14181ac4b82bSMike Smith device_printf(sc->mlx_dev, "%s command failed - %s\n", 14191ac4b82bSMike Smith command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc)); 14201ac4b82bSMike Smith } else if (command == MLX_CMD_STOPCHANNEL) { 14211ac4b82bSMike Smith device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n", 142272c10febSPeter Wemm channel, (long)(sc->mlx_pause.mp_howlong - time_second)); 14231ac4b82bSMike Smith } else { 14241ac4b82bSMike Smith device_printf(sc->mlx_dev, "channel %d resuming\n", channel); 14251ac4b82bSMike Smith } 14261ac4b82bSMike Smith mlx_releasecmd(mc); 14271ac4b82bSMike Smith } 14281ac4b82bSMike Smith 14291ac4b82bSMike Smith /******************************************************************************** 14301ac4b82bSMike Smith ******************************************************************************** 14311ac4b82bSMike Smith Command Submission 14321ac4b82bSMike Smith ******************************************************************************** 14331ac4b82bSMike Smith ********************************************************************************/ 14341ac4b82bSMike Smith 14351ac4b82bSMike Smith /******************************************************************************** 14361ac4b82bSMike Smith * Perform an Enquiry command using a type-3 command buffer and a return a single 14371ac4b82bSMike Smith * linear result buffer. If the completion function is specified, it will 14381ac4b82bSMike Smith * be called with the completed command (and the result response will not be 14391ac4b82bSMike Smith * valid until that point). Otherwise, the command will either be busy-waited 14401ac4b82bSMike Smith * for (interrupts not enabled), or slept for. 14411ac4b82bSMike Smith */ 14421ac4b82bSMike Smith static void * 14431ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc)) 14441ac4b82bSMike Smith { 14451ac4b82bSMike Smith struct mlx_command *mc; 14461ac4b82bSMike Smith void *result; 14471ac4b82bSMike Smith int error; 14481ac4b82bSMike Smith 1449da8bb3a3SMike Smith debug_called(1); 14501ac4b82bSMike Smith 14511ac4b82bSMike Smith /* get ourselves a command buffer */ 14521ac4b82bSMike Smith error = 1; 14531ac4b82bSMike Smith result = NULL; 14541ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 14551ac4b82bSMike Smith goto out; 14561ac4b82bSMike Smith /* allocate the response structure */ 14571ac4b82bSMike Smith if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL) 14581ac4b82bSMike Smith goto out; 14591ac4b82bSMike Smith /* get a command slot */ 14601ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT; 14611ac4b82bSMike Smith if (mlx_getslot(mc)) 14621ac4b82bSMike Smith goto out; 14631ac4b82bSMike Smith 14641ac4b82bSMike Smith /* map the command so the controller can see it */ 14651ac4b82bSMike Smith mc->mc_data = result; 14661ac4b82bSMike Smith mc->mc_length = bufsize; 14671ac4b82bSMike Smith mlx_mapcmd(mc); 14681ac4b82bSMike Smith 14691ac4b82bSMike Smith /* build an enquiry command */ 14701ac4b82bSMike Smith mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0); 14711ac4b82bSMike Smith 14721ac4b82bSMike Smith /* do we want a completion callback? */ 14731ac4b82bSMike Smith if (complete != NULL) { 14741ac4b82bSMike Smith mc->mc_complete = complete; 14751ac4b82bSMike Smith mc->mc_private = mc; 14761ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 14771ac4b82bSMike Smith goto out; 14781ac4b82bSMike Smith } else { 14791ac4b82bSMike Smith /* run the command in either polled or wait mode */ 14801ac4b82bSMike Smith if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc)) 14811ac4b82bSMike Smith goto out; 14821ac4b82bSMike Smith 14831ac4b82bSMike Smith /* command completed OK? */ 14841ac4b82bSMike Smith if (mc->mc_status != 0) { 14851ac4b82bSMike Smith device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", mlx_diagnose_command(mc)); 14861ac4b82bSMike Smith goto out; 14871ac4b82bSMike Smith } 14881ac4b82bSMike Smith } 14891ac4b82bSMike Smith error = 0; /* success */ 14901ac4b82bSMike Smith out: 14911ac4b82bSMike Smith /* we got a command, but nobody else will free it */ 14921ac4b82bSMike Smith if ((complete == NULL) && (mc != NULL)) 14931ac4b82bSMike Smith mlx_releasecmd(mc); 149433c8cb18SMike Smith /* we got an error, and we allocated a result */ 14951ac4b82bSMike Smith if ((error != 0) && (result != NULL)) { 14961ac4b82bSMike Smith free(result, M_DEVBUF); 14971ac4b82bSMike Smith result = NULL; 14981ac4b82bSMike Smith } 14991ac4b82bSMike Smith return(result); 15001ac4b82bSMike Smith } 15011ac4b82bSMike Smith 15021ac4b82bSMike Smith 15031ac4b82bSMike Smith /******************************************************************************** 15041ac4b82bSMike Smith * Perform a Flush command on the nominated controller. 15051ac4b82bSMike Smith * 15061ac4b82bSMike Smith * May be called with interrupts enabled or disabled; will not return until 15071ac4b82bSMike Smith * the flush operation completes or fails. 15081ac4b82bSMike Smith */ 15091ac4b82bSMike Smith static int 15101ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc) 15111ac4b82bSMike Smith { 15121ac4b82bSMike Smith struct mlx_command *mc; 15131ac4b82bSMike Smith int error; 15141ac4b82bSMike Smith 1515da8bb3a3SMike Smith debug_called(1); 15161ac4b82bSMike Smith 15171ac4b82bSMike Smith /* get ourselves a command buffer */ 15181ac4b82bSMike Smith error = 1; 15191ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 15201ac4b82bSMike Smith goto out; 15211ac4b82bSMike Smith /* get a command slot */ 15221ac4b82bSMike Smith if (mlx_getslot(mc)) 15231ac4b82bSMike Smith goto out; 15241ac4b82bSMike Smith 15251ac4b82bSMike Smith /* build a flush command */ 15261ac4b82bSMike Smith mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0); 15271ac4b82bSMike Smith 15285792b7feSMike Smith /* can't assume that interrupts are going to work here, so play it safe */ 15295792b7feSMike Smith if (mlx_poll_command(mc)) 15301ac4b82bSMike Smith goto out; 15311ac4b82bSMike Smith 15321ac4b82bSMike Smith /* command completed OK? */ 15331ac4b82bSMike Smith if (mc->mc_status != 0) { 15341ac4b82bSMike Smith device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc)); 15351ac4b82bSMike Smith goto out; 15361ac4b82bSMike Smith } 15371ac4b82bSMike Smith 15381ac4b82bSMike Smith error = 0; /* success */ 15391ac4b82bSMike Smith out: 15401ac4b82bSMike Smith if (mc != NULL) 15411ac4b82bSMike Smith mlx_releasecmd(mc); 15421ac4b82bSMike Smith return(error); 15431ac4b82bSMike Smith } 15441ac4b82bSMike Smith 15451ac4b82bSMike Smith /******************************************************************************** 1546421f2f7dSMike Smith * Start a background consistency check on (drive). 1547421f2f7dSMike Smith * 1548421f2f7dSMike Smith * May be called with interrupts enabled or disabled; will return as soon as the 1549421f2f7dSMike Smith * operation has started or been refused. 1550421f2f7dSMike Smith */ 1551421f2f7dSMike Smith static int 1552421f2f7dSMike Smith mlx_check(struct mlx_softc *sc, int drive) 1553421f2f7dSMike Smith { 1554421f2f7dSMike Smith struct mlx_command *mc; 1555421f2f7dSMike Smith int error; 1556421f2f7dSMike Smith 1557421f2f7dSMike Smith debug_called(1); 1558421f2f7dSMike Smith 1559421f2f7dSMike Smith /* get ourselves a command buffer */ 1560421f2f7dSMike Smith error = 0x10000; 1561421f2f7dSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 1562421f2f7dSMike Smith goto out; 1563421f2f7dSMike Smith /* get a command slot */ 1564421f2f7dSMike Smith if (mlx_getslot(mc)) 1565421f2f7dSMike Smith goto out; 1566421f2f7dSMike Smith 1567421f2f7dSMike Smith /* build a checkasync command, set the "fix it" flag */ 1568421f2f7dSMike Smith mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0); 1569421f2f7dSMike Smith 1570421f2f7dSMike Smith /* start the command and wait for it to be returned */ 1571421f2f7dSMike Smith if (mlx_wait_command(mc)) 1572421f2f7dSMike Smith goto out; 1573421f2f7dSMike Smith 1574421f2f7dSMike Smith /* command completed OK? */ 1575421f2f7dSMike Smith if (mc->mc_status != 0) { 1576421f2f7dSMike Smith device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc)); 1577421f2f7dSMike Smith } else { 1578421f2f7dSMike Smith device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started"); 1579421f2f7dSMike Smith } 1580421f2f7dSMike Smith error = mc->mc_status; 1581421f2f7dSMike Smith 1582421f2f7dSMike Smith out: 1583421f2f7dSMike Smith if (mc != NULL) 1584421f2f7dSMike Smith mlx_releasecmd(mc); 1585421f2f7dSMike Smith return(error); 1586421f2f7dSMike Smith } 1587421f2f7dSMike Smith 1588421f2f7dSMike Smith /******************************************************************************** 1589421f2f7dSMike Smith * Start a background rebuild of the physical drive at (channel),(target). 15901ac4b82bSMike Smith * 15911ac4b82bSMike Smith * May be called with interrupts enabled or disabled; will return as soon as the 15921ac4b82bSMike Smith * operation has started or been refused. 15931ac4b82bSMike Smith */ 15941ac4b82bSMike Smith static int 15951ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target) 15961ac4b82bSMike Smith { 15971ac4b82bSMike Smith struct mlx_command *mc; 15981ac4b82bSMike Smith int error; 15991ac4b82bSMike Smith 1600da8bb3a3SMike Smith debug_called(1); 16011ac4b82bSMike Smith 16021ac4b82bSMike Smith /* get ourselves a command buffer */ 16031ac4b82bSMike Smith error = 0x10000; 16041ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 16051ac4b82bSMike Smith goto out; 16061ac4b82bSMike Smith /* get a command slot */ 16071ac4b82bSMike Smith if (mlx_getslot(mc)) 16081ac4b82bSMike Smith goto out; 16091ac4b82bSMike Smith 1610421f2f7dSMike Smith /* build a checkasync command, set the "fix it" flag */ 16111ac4b82bSMike Smith mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0); 16121ac4b82bSMike Smith 1613421f2f7dSMike Smith /* start the command and wait for it to be returned */ 1614421f2f7dSMike Smith if (mlx_wait_command(mc)) 16151ac4b82bSMike Smith goto out; 16161ac4b82bSMike Smith 16171ac4b82bSMike Smith /* command completed OK? */ 16181ac4b82bSMike Smith if (mc->mc_status != 0) { 16191ac4b82bSMike Smith device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc)); 16201ac4b82bSMike Smith } else { 1621421f2f7dSMike Smith device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target); 16221ac4b82bSMike Smith } 16231ac4b82bSMike Smith error = mc->mc_status; 16241ac4b82bSMike Smith 16251ac4b82bSMike Smith out: 16261ac4b82bSMike Smith if (mc != NULL) 16271ac4b82bSMike Smith mlx_releasecmd(mc); 16281ac4b82bSMike Smith return(error); 16291ac4b82bSMike Smith } 16301ac4b82bSMike Smith 16311ac4b82bSMike Smith /******************************************************************************** 16321ac4b82bSMike Smith * Run the command (mc) and return when it completes. 16331ac4b82bSMike Smith * 16341ac4b82bSMike Smith * Interrupts need to be enabled; returns nonzero on error. 16351ac4b82bSMike Smith */ 16361ac4b82bSMike Smith static int 16371ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc) 16381ac4b82bSMike Smith { 16391ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 16401ac4b82bSMike Smith int error, count; 16411ac4b82bSMike Smith 1642da8bb3a3SMike Smith debug_called(1); 16431ac4b82bSMike Smith 16441ac4b82bSMike Smith mc->mc_complete = NULL; 16451ac4b82bSMike Smith mc->mc_private = mc; /* wake us when you're done */ 16461ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 16471ac4b82bSMike Smith return(error); 16481ac4b82bSMike Smith 16491ac4b82bSMike Smith count = 0; 16501ac4b82bSMike Smith /* XXX better timeout? */ 16511ac4b82bSMike Smith while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) { 16521ac4b82bSMike Smith tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz); 16531ac4b82bSMike Smith } 16541ac4b82bSMike Smith 16551ac4b82bSMike Smith if (mc->mc_status != 0) { 1656da8bb3a3SMike Smith device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc)); 16571ac4b82bSMike Smith return(EIO); 16581ac4b82bSMike Smith } 16591ac4b82bSMike Smith return(0); 16601ac4b82bSMike Smith } 16611ac4b82bSMike Smith 16621ac4b82bSMike Smith 16631ac4b82bSMike Smith /******************************************************************************** 16641ac4b82bSMike Smith * Start the command (mc) and busy-wait for it to complete. 16651ac4b82bSMike Smith * 1666da8bb3a3SMike Smith * Should only be used when interrupts can't be relied upon. Returns 0 on 16671ac4b82bSMike Smith * success, nonzero on error. 16681ac4b82bSMike Smith * Successfully completed commands are dequeued. 16691ac4b82bSMike Smith */ 16701ac4b82bSMike Smith static int 16711ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc) 16721ac4b82bSMike Smith { 16731ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 16741ac4b82bSMike Smith int error, count, s; 16751ac4b82bSMike Smith 1676da8bb3a3SMike Smith debug_called(1); 16771ac4b82bSMike Smith 16781ac4b82bSMike Smith mc->mc_complete = NULL; 16791ac4b82bSMike Smith mc->mc_private = NULL; /* we will poll for it */ 16801ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 16811ac4b82bSMike Smith return(error); 16821ac4b82bSMike Smith 16831ac4b82bSMike Smith count = 0; 16841ac4b82bSMike Smith do { 16851ac4b82bSMike Smith /* poll for completion */ 16861ac4b82bSMike Smith mlx_done(mc->mc_sc); 1687da8bb3a3SMike Smith 1688da8bb3a3SMike Smith } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000)); 16891ac4b82bSMike Smith if (mc->mc_status != MLX_STATUS_BUSY) { 16901ac4b82bSMike Smith s = splbio(); 16914b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 16921ac4b82bSMike Smith splx(s); 16931ac4b82bSMike Smith return(0); 16941ac4b82bSMike Smith } 1695421f2f7dSMike Smith device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc)); 16961ac4b82bSMike Smith return(EIO); 16971ac4b82bSMike Smith } 16981ac4b82bSMike Smith 16991ac4b82bSMike Smith /******************************************************************************** 17001ac4b82bSMike Smith * Pull as much work off the softc's work queue as possible and give it to the 17011ac4b82bSMike Smith * controller. Leave a couple of slots free for emergencies. 17021ac4b82bSMike Smith * 17031ac4b82bSMike Smith * Must be called at splbio or in an equivalent fashion that prevents 1704da8bb3a3SMike Smith * reentry or activity on the bufq. 17051ac4b82bSMike Smith */ 17061ac4b82bSMike Smith static void 17071ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc) 17081ac4b82bSMike Smith { 17091ac4b82bSMike Smith struct mlx_command *mc; 17101ac4b82bSMike Smith struct mlxd_softc *mlxd; 17111ac4b82bSMike Smith struct buf *bp; 17121ac4b82bSMike Smith int blkcount; 17131ac4b82bSMike Smith int driveno; 17141ac4b82bSMike Smith int cmd; 17154b006d7bSMike Smith int s; 17161ac4b82bSMike Smith 17175792b7feSMike Smith /* avoid reentrancy */ 17185792b7feSMike Smith if (mlx_lock_tas(sc, MLX_LOCK_STARTING)) 17195792b7feSMike Smith return; 17205792b7feSMike Smith 17211ac4b82bSMike Smith /* spin until something prevents us from doing any work */ 17224b006d7bSMike Smith s = splbio(); 17231ac4b82bSMike Smith for (;;) { 17241ac4b82bSMike Smith 17251ac4b82bSMike Smith /* see if there's work to be done */ 17261ac4b82bSMike Smith if ((bp = bufq_first(&sc->mlx_bufq)) == NULL) 17271ac4b82bSMike Smith break; 17281ac4b82bSMike Smith /* get a command */ 17291ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 17301ac4b82bSMike Smith break; 17311ac4b82bSMike Smith /* get a slot for the command */ 17321ac4b82bSMike Smith if (mlx_getslot(mc) != 0) { 17331ac4b82bSMike Smith mlx_releasecmd(mc); 17341ac4b82bSMike Smith break; 17351ac4b82bSMike Smith } 17361ac4b82bSMike Smith /* get the buf containing our work */ 17371ac4b82bSMike Smith bufq_remove(&sc->mlx_bufq, bp); 17381ac4b82bSMike Smith sc->mlx_waitbufs--; 17394b006d7bSMike Smith splx(s); 17401ac4b82bSMike Smith 17411ac4b82bSMike Smith /* connect the buf to the command */ 17421ac4b82bSMike Smith mc->mc_complete = mlx_completeio; 17431ac4b82bSMike Smith mc->mc_private = bp; 17441ac4b82bSMike Smith mc->mc_data = bp->b_data; 17451ac4b82bSMike Smith mc->mc_length = bp->b_bcount; 174621144e3bSPoul-Henning Kamp if (bp->b_iocmd == BIO_READ) { 17471ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_DATAIN; 1748da8bb3a3SMike Smith cmd = MLX_CMD_READSG; 17491ac4b82bSMike Smith } else { 17501ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_DATAOUT; 1751da8bb3a3SMike Smith cmd = MLX_CMD_WRITESG; 17521ac4b82bSMike Smith } 17531ac4b82bSMike Smith 17541ac4b82bSMike Smith /* map the command so the controller can work with it */ 17551ac4b82bSMike Smith mlx_mapcmd(mc); 17561ac4b82bSMike Smith 17571ac4b82bSMike Smith /* build a suitable I/O command (assumes 512-byte rounded transfers) */ 1758f6b84b08SMike Smith mlxd = (struct mlxd_softc *)bp->b_dev->si_drv1; 17595792b7feSMike Smith driveno = mlxd->mlxd_drive - sc->mlx_sysdrive; 176097adfbafSMike Smith blkcount = (bp->b_bcount + MLX_BLKSIZE - 1) / MLX_BLKSIZE; 17611ac4b82bSMike Smith 1762cd4ace0cSMike Smith if ((bp->b_pblkno + blkcount) > sc->mlx_sysdrive[driveno].ms_size) 17631ac4b82bSMike Smith device_printf(sc->mlx_dev, "I/O beyond end of unit (%u,%d > %u)\n", 1764cd4ace0cSMike Smith bp->b_pblkno, blkcount, sc->mlx_sysdrive[driveno].ms_size); 17651ac4b82bSMike Smith 17661ac4b82bSMike Smith /* 17671ac4b82bSMike Smith * Build the I/O command. Note that the SG list type bits are set to zero, 17681ac4b82bSMike Smith * denoting the format of SG list that we are using. 17691ac4b82bSMike Smith */ 1770da8bb3a3SMike Smith if (sc->mlx_iftype == MLX_IFTYPE_2) { 1771da8bb3a3SMike Smith mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD : MLX_CMD_READSG_OLD, 1772da8bb3a3SMike Smith blkcount & 0xff, /* xfer length low byte */ 1773da8bb3a3SMike Smith bp->b_pblkno, /* physical block number */ 1774da8bb3a3SMike Smith driveno, /* target drive number */ 1775da8bb3a3SMike Smith mc->mc_sgphys, /* location of SG list */ 1776da8bb3a3SMike Smith mc->mc_nsgent & 0x3f); /* size of SG list (top 3 bits clear) */ 1777da8bb3a3SMike Smith } else { 17781ac4b82bSMike Smith mlx_make_type5(mc, cmd, 17791ac4b82bSMike Smith blkcount & 0xff, /* xfer length low byte */ 1780f01f2af6SMike Smith (driveno << 3) | ((blkcount >> 8) & 0x07), /* target and length high 3 bits */ 1781cd4ace0cSMike Smith bp->b_pblkno, /* physical block number */ 17821ac4b82bSMike Smith mc->mc_sgphys, /* location of SG list */ 1783da8bb3a3SMike Smith mc->mc_nsgent & 0x3f); /* size of SG list (top 3 bits clear) */ 1784da8bb3a3SMike Smith } 17851ac4b82bSMike Smith 17861ac4b82bSMike Smith /* try to give command to controller */ 17871ac4b82bSMike Smith if (mlx_start(mc) != 0) { 17881ac4b82bSMike Smith /* fail the command */ 17891ac4b82bSMike Smith mc->mc_status = MLX_STATUS_WEDGED; 17901ac4b82bSMike Smith mlx_completeio(mc); 17911ac4b82bSMike Smith } 17924b006d7bSMike Smith s = splbio(); 17931ac4b82bSMike Smith } 17944b006d7bSMike Smith splx(s); 17955792b7feSMike Smith mlx_lock_clr(sc, MLX_LOCK_STARTING); 17961ac4b82bSMike Smith } 17971ac4b82bSMike Smith 17981ac4b82bSMike Smith /******************************************************************************** 17991ac4b82bSMike Smith * Handle completion of an I/O command. 18001ac4b82bSMike Smith */ 18011ac4b82bSMike Smith static void 18021ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc) 18031ac4b82bSMike Smith { 18041ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 18051ac4b82bSMike Smith struct buf *bp = (struct buf *)mc->mc_private; 1806f6b84b08SMike Smith struct mlxd_softc *mlxd = (struct mlxd_softc *)bp->b_dev->si_drv1; 18071ac4b82bSMike Smith 18081ac4b82bSMike Smith if (mc->mc_status != MLX_STATUS_OK) { /* could be more verbose here? */ 18091ac4b82bSMike Smith bp->b_error = EIO; 1810c244d2deSPoul-Henning Kamp bp->b_ioflags |= BIO_ERROR; 18111ac4b82bSMike Smith 18121ac4b82bSMike Smith switch(mc->mc_status) { 18131ac4b82bSMike Smith case MLX_STATUS_RDWROFFLINE: /* system drive has gone offline */ 18141ac4b82bSMike Smith device_printf(mlxd->mlxd_dev, "drive offline\n"); 1815f6b84b08SMike Smith /* should signal this with a return code */ 18161ac4b82bSMike Smith mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE; 18171ac4b82bSMike Smith break; 18181ac4b82bSMike Smith 18191ac4b82bSMike Smith default: /* other I/O error */ 18201ac4b82bSMike Smith device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc)); 18211ac4b82bSMike Smith #if 0 1822cd4ace0cSMike Smith device_printf(sc->mlx_dev, " b_bcount %ld blkcount %ld b_pblkno %d\n", 1823cd4ace0cSMike Smith bp->b_bcount, bp->b_bcount / MLX_BLKSIZE, bp->b_pblkno); 18241ac4b82bSMike Smith device_printf(sc->mlx_dev, " %13D\n", mc->mc_mailbox, " "); 18251ac4b82bSMike Smith #endif 18261ac4b82bSMike Smith break; 18271ac4b82bSMike Smith } 18281ac4b82bSMike Smith } 18291ac4b82bSMike Smith mlx_releasecmd(mc); 18301ac4b82bSMike Smith mlxd_intr(bp); 18311ac4b82bSMike Smith } 18321ac4b82bSMike Smith 18331ac4b82bSMike Smith /******************************************************************************** 18341ac4b82bSMike Smith * Take a command from user-space and try to run it. 1835da8bb3a3SMike Smith * 1836da8bb3a3SMike Smith * XXX Note that this can't perform very much in the way of error checking, and 1837da8bb3a3SMike Smith * as such, applications _must_ be considered trustworthy. 1838da8bb3a3SMike Smith * XXX Commands using S/G for data are not supported. 18391ac4b82bSMike Smith */ 18401ac4b82bSMike Smith static int 18411ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu) 18421ac4b82bSMike Smith { 18431ac4b82bSMike Smith struct mlx_command *mc; 1844da8bb3a3SMike Smith struct mlx_dcdb *dcdb; 18451ac4b82bSMike Smith void *kbuf; 18461ac4b82bSMike Smith int error; 18471ac4b82bSMike Smith 1848da8bb3a3SMike Smith debug_called(0); 1849da8bb3a3SMike Smith 18501ac4b82bSMike Smith kbuf = NULL; 18511ac4b82bSMike Smith mc = NULL; 1852da8bb3a3SMike Smith dcdb = NULL; 18531ac4b82bSMike Smith error = ENOMEM; 1854da8bb3a3SMike Smith 1855da8bb3a3SMike Smith /* get ourselves a command and copy in from user space */ 18561ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 18571ac4b82bSMike Smith goto out; 18581ac4b82bSMike Smith bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox)); 1859da8bb3a3SMike Smith debug(0, "got command buffer"); 1860da8bb3a3SMike Smith 1861da8bb3a3SMike Smith /* if we need a buffer for data transfer, allocate one and copy in its initial contents */ 1862da8bb3a3SMike Smith if (mu->mu_datasize > 0) { 1863da8bb3a3SMike Smith if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) || 1864da8bb3a3SMike Smith (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))) 18651ac4b82bSMike Smith goto out; 1866da8bb3a3SMike Smith debug(0, "got kernel buffer"); 1867da8bb3a3SMike Smith } 18681ac4b82bSMike Smith 18691ac4b82bSMike Smith /* get a command slot */ 18701ac4b82bSMike Smith if (mlx_getslot(mc)) 18711ac4b82bSMike Smith goto out; 1872da8bb3a3SMike Smith debug(0, "got a slot"); 18731ac4b82bSMike Smith 18741ac4b82bSMike Smith /* map the command so the controller can see it */ 18751ac4b82bSMike Smith mc->mc_data = kbuf; 18761ac4b82bSMike Smith mc->mc_length = mu->mu_datasize; 18771ac4b82bSMike Smith mlx_mapcmd(mc); 1878da8bb3a3SMike Smith debug(0, "mapped"); 18791ac4b82bSMike Smith 1880da8bb3a3SMike Smith /* 1881da8bb3a3SMike Smith * If this is a passthrough SCSI command, the DCDB is packed at the 1882da8bb3a3SMike Smith * beginning of the data area. Fix up the DCDB to point to the correct physical 1883da8bb3a3SMike Smith * address and override any bufptr supplied by the caller since we know 1884da8bb3a3SMike Smith * what it's meant to be. 1885da8bb3a3SMike Smith */ 1886da8bb3a3SMike Smith if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) { 1887da8bb3a3SMike Smith dcdb = (struct mlx_dcdb *)kbuf; 1888da8bb3a3SMike Smith dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb); 1889da8bb3a3SMike Smith mu->mu_bufptr = 8; 18901ac4b82bSMike Smith } 18911ac4b82bSMike Smith 1892da8bb3a3SMike Smith /* 1893da8bb3a3SMike Smith * If there's a data buffer, fix up the command's buffer pointer. 1894da8bb3a3SMike Smith */ 1895da8bb3a3SMike Smith if (mu->mu_datasize > 0) { 1896da8bb3a3SMike Smith 1897da8bb3a3SMike Smith /* range check the pointer to physical buffer address */ 1898da8bb3a3SMike Smith if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) - sizeof(u_int32_t)))) { 1899da8bb3a3SMike Smith error = EINVAL; 1900da8bb3a3SMike Smith goto out; 1901da8bb3a3SMike Smith } 1902da8bb3a3SMike Smith mc->mc_mailbox[mu->mu_bufptr ] = mc->mc_dataphys & 0xff; 1903da8bb3a3SMike Smith mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8) & 0xff; 1904da8bb3a3SMike Smith mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff; 1905da8bb3a3SMike Smith mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff; 1906da8bb3a3SMike Smith } 1907da8bb3a3SMike Smith debug(0, "command fixup"); 1908da8bb3a3SMike Smith 19091ac4b82bSMike Smith /* submit the command and wait */ 19101ac4b82bSMike Smith if ((error = mlx_wait_command(mc)) != 0) 19111ac4b82bSMike Smith goto out; 19121ac4b82bSMike Smith 19131ac4b82bSMike Smith /* copy out status and data */ 19141ac4b82bSMike Smith mu->mu_status = mc->mc_status; 19151ac4b82bSMike Smith if ((mu->mu_datasize > 0) && ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize)))) 19161ac4b82bSMike Smith goto out; 19171ac4b82bSMike Smith error = 0; 19181ac4b82bSMike Smith 19191ac4b82bSMike Smith out: 19201ac4b82bSMike Smith mlx_releasecmd(mc); 19211ac4b82bSMike Smith if (kbuf != NULL) 19221ac4b82bSMike Smith free(kbuf, M_DEVBUF); 19231ac4b82bSMike Smith return(error); 19241ac4b82bSMike Smith } 19251ac4b82bSMike Smith 19261ac4b82bSMike Smith /******************************************************************************** 19271ac4b82bSMike Smith ******************************************************************************** 19281ac4b82bSMike Smith Command I/O to Controller 19291ac4b82bSMike Smith ******************************************************************************** 19301ac4b82bSMike Smith ********************************************************************************/ 19311ac4b82bSMike Smith 19321ac4b82bSMike Smith /******************************************************************************** 19331ac4b82bSMike Smith * Find a free command slot for (mc). 19341ac4b82bSMike Smith * 19351ac4b82bSMike Smith * Don't hand out a slot to a normal-priority command unless there are at least 19361ac4b82bSMike Smith * 4 slots free for priority commands. 19371ac4b82bSMike Smith */ 19381ac4b82bSMike Smith static int 19391ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc) 19401ac4b82bSMike Smith { 19411ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 19429eee27f1SMike Smith int s, slot; 19431ac4b82bSMike Smith 1944da8bb3a3SMike Smith debug_called(1); 19451ac4b82bSMike Smith 19461ac4b82bSMike Smith /* enforce slot-usage limit */ 19479eee27f1SMike Smith if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ? 19489eee27f1SMike Smith sc->mlx_maxiop : sc->mlx_maxiop - 4)) 19491ac4b82bSMike Smith return(EBUSY); 19501ac4b82bSMike Smith 19511ac4b82bSMike Smith /* 19521ac4b82bSMike Smith * Allocate an outstanding command slot 19531ac4b82bSMike Smith * 19541ac4b82bSMike Smith * XXX linear search is slow 19551ac4b82bSMike Smith */ 19561ac4b82bSMike Smith s = splbio(); 19571ac4b82bSMike Smith for (slot = 0; slot < sc->mlx_maxiop; slot++) { 1958da8bb3a3SMike Smith debug(2, "try slot %d", slot); 19591ac4b82bSMike Smith if (sc->mlx_busycmd[slot] == NULL) 19601ac4b82bSMike Smith break; 19611ac4b82bSMike Smith } 19621ac4b82bSMike Smith if (slot < sc->mlx_maxiop) { 19631ac4b82bSMike Smith sc->mlx_busycmd[slot] = mc; 19641ac4b82bSMike Smith sc->mlx_busycmds++; 19651ac4b82bSMike Smith } 19661ac4b82bSMike Smith splx(s); 19671ac4b82bSMike Smith 19681ac4b82bSMike Smith /* out of slots? */ 19691ac4b82bSMike Smith if (slot >= sc->mlx_maxiop) 19701ac4b82bSMike Smith return(EBUSY); 19711ac4b82bSMike Smith 1972da8bb3a3SMike Smith debug(2, "got slot %d", slot); 19731ac4b82bSMike Smith mc->mc_slot = slot; 19741ac4b82bSMike Smith return(0); 19751ac4b82bSMike Smith } 19761ac4b82bSMike Smith 19771ac4b82bSMike Smith /******************************************************************************** 19781ac4b82bSMike Smith * Map/unmap (mc)'s data in the controller's addressable space. 19791ac4b82bSMike Smith */ 19801ac4b82bSMike Smith static void 19811ac4b82bSMike Smith mlx_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 19821ac4b82bSMike Smith { 19831ac4b82bSMike Smith struct mlx_command *mc = (struct mlx_command *)arg; 19841ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 19851ac4b82bSMike Smith struct mlx_sgentry *sg; 19861ac4b82bSMike Smith int i; 19871ac4b82bSMike Smith 1988da8bb3a3SMike Smith debug_called(1); 19891ac4b82bSMike Smith 19901ac4b82bSMike Smith /* get base address of s/g table */ 1991da8bb3a3SMike Smith sg = sc->mlx_sgtable + (mc->mc_slot * sc->mlx_sg_nseg); 19921ac4b82bSMike Smith 19931ac4b82bSMike Smith /* save s/g table information in command */ 19941ac4b82bSMike Smith mc->mc_nsgent = nsegments; 1995da8bb3a3SMike Smith mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * sc->mlx_sg_nseg * sizeof(struct mlx_sgentry)); 19961ac4b82bSMike Smith mc->mc_dataphys = segs[0].ds_addr; 19971ac4b82bSMike Smith 19981ac4b82bSMike Smith /* populate s/g table */ 19991ac4b82bSMike Smith for (i = 0; i < nsegments; i++, sg++) { 20001ac4b82bSMike Smith sg->sg_addr = segs[i].ds_addr; 20011ac4b82bSMike Smith sg->sg_count = segs[i].ds_len; 20021ac4b82bSMike Smith } 20031ac4b82bSMike Smith } 20041ac4b82bSMike Smith 20051ac4b82bSMike Smith static void 20061ac4b82bSMike Smith mlx_mapcmd(struct mlx_command *mc) 20071ac4b82bSMike Smith { 20081ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 20091ac4b82bSMike Smith 2010da8bb3a3SMike Smith debug_called(1); 20111ac4b82bSMike Smith 20121ac4b82bSMike Smith /* if the command involves data at all */ 20131ac4b82bSMike Smith if (mc->mc_data != NULL) { 20141ac4b82bSMike Smith 20151ac4b82bSMike Smith /* map the data buffer into bus space and build the s/g list */ 20161ac4b82bSMike Smith bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length, 20171ac4b82bSMike Smith mlx_setup_dmamap, mc, 0); 20181ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAIN) 20191ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREREAD); 20201ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAOUT) 20211ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREWRITE); 20221ac4b82bSMike Smith } 20231ac4b82bSMike Smith } 20241ac4b82bSMike Smith 20251ac4b82bSMike Smith static void 20261ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc) 20271ac4b82bSMike Smith { 20281ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 20291ac4b82bSMike Smith 2030da8bb3a3SMike Smith debug_called(1); 20311ac4b82bSMike Smith 20321ac4b82bSMike Smith /* if the command involved data at all */ 20331ac4b82bSMike Smith if (mc->mc_data != NULL) { 20341ac4b82bSMike Smith 20351ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAIN) 20361ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD); 20371ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAOUT) 20381ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE); 20391ac4b82bSMike Smith 20401ac4b82bSMike Smith bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap); 20411ac4b82bSMike Smith } 20421ac4b82bSMike Smith } 20431ac4b82bSMike Smith 20441ac4b82bSMike Smith /******************************************************************************** 20455792b7feSMike Smith * Try to deliver (mc) to the controller. 20461ac4b82bSMike Smith * 20471ac4b82bSMike Smith * Can be called at any interrupt level, with or without interrupts enabled. 20481ac4b82bSMike Smith */ 20491ac4b82bSMike Smith static int 20501ac4b82bSMike Smith mlx_start(struct mlx_command *mc) 20511ac4b82bSMike Smith { 20521ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 20535792b7feSMike Smith int i, s, done; 20541ac4b82bSMike Smith 2055da8bb3a3SMike Smith debug_called(1); 20561ac4b82bSMike Smith 20571ac4b82bSMike Smith /* save the slot number as ident so we can handle this command when complete */ 20581ac4b82bSMike Smith mc->mc_mailbox[0x1] = mc->mc_slot; 20591ac4b82bSMike Smith 20604b006d7bSMike Smith /* mark the command as currently being processed */ 20611ac4b82bSMike Smith mc->mc_status = MLX_STATUS_BUSY; 20621ac4b82bSMike Smith 20635792b7feSMike Smith /* set a default 60-second timeout XXX tunable? XXX not currently used */ 20645792b7feSMike Smith mc->mc_timeout = time_second + 60; 20651ac4b82bSMike Smith 20661ac4b82bSMike Smith /* spin waiting for the mailbox */ 20671ac4b82bSMike Smith for (i = 100000, done = 0; (i > 0) && !done; i--) { 20681ac4b82bSMike Smith s = splbio(); 20694b006d7bSMike Smith if (sc->mlx_tryqueue(sc, mc)) { 20704b006d7bSMike Smith done = 1; 20714b006d7bSMike Smith /* move command to work queue */ 20724b006d7bSMike Smith TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link); 20734b006d7bSMike Smith } 20745792b7feSMike Smith splx(s); /* drop spl to allow completion interrupts */ 20751ac4b82bSMike Smith } 20761ac4b82bSMike Smith 20771ac4b82bSMike Smith /* command is enqueued */ 20781ac4b82bSMike Smith if (done) 20791ac4b82bSMike Smith return(0); 20801ac4b82bSMike Smith 20811ac4b82bSMike Smith /* 20821ac4b82bSMike Smith * We couldn't get the controller to take the command. Revoke the slot 20831ac4b82bSMike Smith * that the command was given and return it with a bad status. 20841ac4b82bSMike Smith */ 20851ac4b82bSMike Smith sc->mlx_busycmd[mc->mc_slot] = NULL; 20861ac4b82bSMike Smith device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n"); 20871ac4b82bSMike Smith mc->mc_status = MLX_STATUS_WEDGED; 20885792b7feSMike Smith mlx_complete(sc); 20891ac4b82bSMike Smith return(EIO); 20901ac4b82bSMike Smith } 20911ac4b82bSMike Smith 20921ac4b82bSMike Smith /******************************************************************************** 20935792b7feSMike Smith * Poll the controller (sc) for completed commands. 20945792b7feSMike Smith * Update command status and free slots for reuse. If any slots were freed, 20955792b7feSMike Smith * new commands may be posted. 20961ac4b82bSMike Smith * 20975792b7feSMike Smith * Returns nonzero if one or more commands were completed. 20981ac4b82bSMike Smith */ 20991ac4b82bSMike Smith static int 21001ac4b82bSMike Smith mlx_done(struct mlx_softc *sc) 21011ac4b82bSMike Smith { 21021ac4b82bSMike Smith struct mlx_command *mc; 21035792b7feSMike Smith int s, result; 21041ac4b82bSMike Smith u_int8_t slot; 21051ac4b82bSMike Smith u_int16_t status; 21061ac4b82bSMike Smith 2107da8bb3a3SMike Smith debug_called(2); 21081ac4b82bSMike Smith 21095792b7feSMike Smith result = 0; 21101ac4b82bSMike Smith 21115792b7feSMike Smith /* loop collecting completed commands */ 21124b006d7bSMike Smith s = splbio(); 21135792b7feSMike Smith for (;;) { 21145792b7feSMike Smith /* poll for a completed command's identifier and status */ 21151ac4b82bSMike Smith if (sc->mlx_findcomplete(sc, &slot, &status)) { 21165792b7feSMike Smith result = 1; 21171ac4b82bSMike Smith mc = sc->mlx_busycmd[slot]; /* find command */ 21181ac4b82bSMike Smith if (mc != NULL) { /* paranoia */ 21191ac4b82bSMike Smith if (mc->mc_status == MLX_STATUS_BUSY) { 21201ac4b82bSMike Smith mc->mc_status = status; /* save status */ 21211ac4b82bSMike Smith 21221ac4b82bSMike Smith /* free slot for reuse */ 21231ac4b82bSMike Smith sc->mlx_busycmd[slot] = NULL; 21241ac4b82bSMike Smith sc->mlx_busycmds--; 21251ac4b82bSMike Smith } else { 21261ac4b82bSMike Smith device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot); 21271ac4b82bSMike Smith } 21281ac4b82bSMike Smith } else { 21291ac4b82bSMike Smith device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot); 21301ac4b82bSMike Smith } 21315792b7feSMike Smith } else { 21325792b7feSMike Smith break; 21331ac4b82bSMike Smith } 21345792b7feSMike Smith } 21351ac4b82bSMike Smith 21365792b7feSMike Smith /* if we've completed any commands, try posting some more */ 21375792b7feSMike Smith if (result) 21385792b7feSMike Smith mlx_startio(sc); 21395792b7feSMike Smith 21405792b7feSMike Smith /* handle completion and timeouts */ 21415792b7feSMike Smith mlx_complete(sc); 21425792b7feSMike Smith 21435792b7feSMike Smith return(result); 21441ac4b82bSMike Smith } 21451ac4b82bSMike Smith 21461ac4b82bSMike Smith /******************************************************************************** 21475792b7feSMike Smith * Perform post-completion processing for commands on (sc). 21481ac4b82bSMike Smith */ 21491ac4b82bSMike Smith static void 21501ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc) 21511ac4b82bSMike Smith { 21521ac4b82bSMike Smith struct mlx_command *mc, *nc; 21531ac4b82bSMike Smith int s, count; 21541ac4b82bSMike Smith 2155da8bb3a3SMike Smith debug_called(2); 21561ac4b82bSMike Smith 21575792b7feSMike Smith /* avoid reentrancy XXX might want to signal and request a restart */ 21585792b7feSMike Smith if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING)) 21595792b7feSMike Smith return; 21605792b7feSMike Smith 21611ac4b82bSMike Smith s = splbio(); 21621ac4b82bSMike Smith count = 0; 21631ac4b82bSMike Smith 21645792b7feSMike Smith /* scan the list of busy/done commands */ 21654b006d7bSMike Smith mc = TAILQ_FIRST(&sc->mlx_work); 21661ac4b82bSMike Smith while (mc != NULL) { 21671ac4b82bSMike Smith nc = TAILQ_NEXT(mc, mc_link); 21681ac4b82bSMike Smith 21695792b7feSMike Smith /* Command has been completed in some fashion */ 21704b006d7bSMike Smith if (mc->mc_status != MLX_STATUS_BUSY) { 21714b006d7bSMike Smith 21725792b7feSMike Smith /* unmap the command's data buffer */ 21735792b7feSMike Smith mlx_unmapcmd(mc); 21741ac4b82bSMike Smith /* 21751ac4b82bSMike Smith * Does the command have a completion handler? 21761ac4b82bSMike Smith */ 21771ac4b82bSMike Smith if (mc->mc_complete != NULL) { 21781ac4b82bSMike Smith /* remove from list and give to handler */ 21794b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 21801ac4b82bSMike Smith mc->mc_complete(mc); 21811ac4b82bSMike Smith 21821ac4b82bSMike Smith /* 21831ac4b82bSMike Smith * Is there a sleeper waiting on this command? 21841ac4b82bSMike Smith */ 21851ac4b82bSMike Smith } else if (mc->mc_private != NULL) { /* sleeping caller wants to know about it */ 21861ac4b82bSMike Smith 21871ac4b82bSMike Smith /* remove from list and wake up sleeper */ 21884b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 21891ac4b82bSMike Smith wakeup_one(mc->mc_private); 21901ac4b82bSMike Smith 21911ac4b82bSMike Smith /* 21921ac4b82bSMike Smith * Leave the command for a caller that's polling for it. 21931ac4b82bSMike Smith */ 21941ac4b82bSMike Smith } else { 21951ac4b82bSMike Smith } 21964b006d7bSMike Smith } 21971ac4b82bSMike Smith mc = nc; 21981ac4b82bSMike Smith } 21991ac4b82bSMike Smith splx(s); 22001ac4b82bSMike Smith 22015792b7feSMike Smith mlx_lock_clr(sc, MLX_LOCK_COMPLETING); 22021ac4b82bSMike Smith } 22031ac4b82bSMike Smith 22041ac4b82bSMike Smith /******************************************************************************** 22051ac4b82bSMike Smith ******************************************************************************** 22061ac4b82bSMike Smith Command Buffer Management 22071ac4b82bSMike Smith ******************************************************************************** 22081ac4b82bSMike Smith ********************************************************************************/ 22091ac4b82bSMike Smith 22101ac4b82bSMike Smith /******************************************************************************** 22111ac4b82bSMike Smith * Get a new command buffer. 22121ac4b82bSMike Smith * 22131ac4b82bSMike Smith * This may return NULL in low-memory cases. 22141ac4b82bSMike Smith * 22151ac4b82bSMike Smith * Note that using malloc() is expensive (the command buffer is << 1 page) but 22161ac4b82bSMike Smith * necessary if we are to be a loadable module before the zone allocator is fixed. 22171ac4b82bSMike Smith * 22181ac4b82bSMike Smith * If possible, we recycle a command buffer that's been used before. 22191ac4b82bSMike Smith * 22201ac4b82bSMike Smith * XXX Note that command buffers are not cleaned out - it is the caller's 22211ac4b82bSMike Smith * responsibility to ensure that all required fields are filled in before 22221ac4b82bSMike Smith * using a buffer. 22231ac4b82bSMike Smith */ 22241ac4b82bSMike Smith static struct mlx_command * 22251ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc) 22261ac4b82bSMike Smith { 22271ac4b82bSMike Smith struct mlx_command *mc; 22281ac4b82bSMike Smith int error; 22291ac4b82bSMike Smith int s; 22301ac4b82bSMike Smith 2231da8bb3a3SMike Smith debug_called(1); 22321ac4b82bSMike Smith 22331ac4b82bSMike Smith s = splbio(); 22341ac4b82bSMike Smith if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) 22351ac4b82bSMike Smith TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); 22361ac4b82bSMike Smith splx(s); 22371ac4b82bSMike Smith 22381ac4b82bSMike Smith /* allocate a new command buffer? */ 22391ac4b82bSMike Smith if (mc == NULL) { 22401ac4b82bSMike Smith mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT); 22411ac4b82bSMike Smith if (mc != NULL) { 22421ac4b82bSMike Smith bzero(mc, sizeof(*mc)); 22431ac4b82bSMike Smith mc->mc_sc = sc; 22441ac4b82bSMike Smith error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap); 22451ac4b82bSMike Smith if (error) { 22461ac4b82bSMike Smith free(mc, M_DEVBUF); 22471ac4b82bSMike Smith return(NULL); 22481ac4b82bSMike Smith } 22491ac4b82bSMike Smith } 22501ac4b82bSMike Smith } 22511ac4b82bSMike Smith return(mc); 22521ac4b82bSMike Smith } 22531ac4b82bSMike Smith 22541ac4b82bSMike Smith /******************************************************************************** 22551ac4b82bSMike Smith * Release a command buffer for recycling. 22561ac4b82bSMike Smith * 22571ac4b82bSMike Smith * XXX It might be a good idea to limit the number of commands we save for reuse 22581ac4b82bSMike Smith * if it's shown that this list bloats out massively. 22591ac4b82bSMike Smith */ 22601ac4b82bSMike Smith static void 22611ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc) 22621ac4b82bSMike Smith { 22631ac4b82bSMike Smith int s; 22641ac4b82bSMike Smith 2265da8bb3a3SMike Smith debug_called(1); 22661ac4b82bSMike Smith 22671ac4b82bSMike Smith s = splbio(); 22681ac4b82bSMike Smith TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link); 22691ac4b82bSMike Smith splx(s); 22701ac4b82bSMike Smith } 22711ac4b82bSMike Smith 22721ac4b82bSMike Smith /******************************************************************************** 22731ac4b82bSMike Smith * Permanently discard a command buffer. 22741ac4b82bSMike Smith */ 22751ac4b82bSMike Smith static void 22761ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc) 22771ac4b82bSMike Smith { 22781ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 22791ac4b82bSMike Smith 2280da8bb3a3SMike Smith debug_called(1); 22811ac4b82bSMike Smith bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap); 22821ac4b82bSMike Smith free(mc, M_DEVBUF); 22831ac4b82bSMike Smith } 22841ac4b82bSMike Smith 22851ac4b82bSMike Smith 22861ac4b82bSMike Smith /******************************************************************************** 22871ac4b82bSMike Smith ******************************************************************************** 22881ac4b82bSMike Smith Type 3 interface accessor methods 22891ac4b82bSMike Smith ******************************************************************************** 22901ac4b82bSMike Smith ********************************************************************************/ 22911ac4b82bSMike Smith 22921ac4b82bSMike Smith /******************************************************************************** 22931ac4b82bSMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 22941ac4b82bSMike Smith * (the controller is not ready to take a command). 22951ac4b82bSMike Smith * 22961ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 22971ac4b82bSMike Smith */ 22981ac4b82bSMike Smith static int 22991ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 23001ac4b82bSMike Smith { 23011ac4b82bSMike Smith int i; 23021ac4b82bSMike Smith 2303da8bb3a3SMike Smith debug_called(2); 23041ac4b82bSMike Smith 23051ac4b82bSMike Smith /* ready for our command? */ 23061ac4b82bSMike Smith if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) { 23071ac4b82bSMike Smith /* copy mailbox data to window */ 23081ac4b82bSMike Smith for (i = 0; i < 13; i++) 23091ac4b82bSMike Smith MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 23101ac4b82bSMike Smith 23111ac4b82bSMike Smith /* post command */ 2312f6b84b08SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL); 23131ac4b82bSMike Smith return(1); 23141ac4b82bSMike Smith } 23151ac4b82bSMike Smith return(0); 23161ac4b82bSMike Smith } 23171ac4b82bSMike Smith 23181ac4b82bSMike Smith /******************************************************************************** 23191ac4b82bSMike Smith * See if a command has been completed, if so acknowledge its completion 23201ac4b82bSMike Smith * and recover the slot number and status code. 23211ac4b82bSMike Smith * 23221ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 23231ac4b82bSMike Smith */ 23241ac4b82bSMike Smith static int 23251ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 23261ac4b82bSMike Smith { 23271ac4b82bSMike Smith 2328da8bb3a3SMike Smith debug_called(2); 23291ac4b82bSMike Smith 23301ac4b82bSMike Smith /* status available? */ 23311ac4b82bSMike Smith if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) { 23321ac4b82bSMike Smith *slot = MLX_V3_GET_STATUS_IDENT(sc); /* get command identifier */ 23331ac4b82bSMike Smith *status = MLX_V3_GET_STATUS(sc); /* get status */ 23341ac4b82bSMike Smith 23351ac4b82bSMike Smith /* acknowledge completion */ 2336f6b84b08SMike Smith MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL); 2337f6b84b08SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK); 23381ac4b82bSMike Smith return(1); 23391ac4b82bSMike Smith } 23401ac4b82bSMike Smith return(0); 23411ac4b82bSMike Smith } 23421ac4b82bSMike Smith 23431ac4b82bSMike Smith /******************************************************************************** 23441ac4b82bSMike Smith * Enable/disable interrupts as requested. (No acknowledge required) 23451ac4b82bSMike Smith * 23461ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 23471ac4b82bSMike Smith */ 23481ac4b82bSMike Smith static void 23491ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action) 23501ac4b82bSMike Smith { 2351da8bb3a3SMike Smith debug_called(1); 23521ac4b82bSMike Smith 23531ac4b82bSMike Smith switch(action) { 23541ac4b82bSMike Smith case MLX_INTACTION_DISABLE: 23551ac4b82bSMike Smith MLX_V3_PUT_IER(sc, 0); 23561ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 23571ac4b82bSMike Smith break; 23581ac4b82bSMike Smith case MLX_INTACTION_ENABLE: 23591ac4b82bSMike Smith MLX_V3_PUT_IER(sc, 1); 23601ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_INTEN; 23611ac4b82bSMike Smith break; 23621ac4b82bSMike Smith } 23631ac4b82bSMike Smith } 23641ac4b82bSMike Smith 2365da8bb3a3SMike Smith /******************************************************************************** 2366da8bb3a3SMike Smith * Poll for firmware error codes during controller initialisation. 2367da8bb3a3SMike Smith * Returns 0 if initialisation is complete, 1 if still in progress but no 2368da8bb3a3SMike Smith * error has been fetched, 2 if an error has been retrieved. 2369da8bb3a3SMike Smith */ 2370da8bb3a3SMike Smith static int 2371da8bb3a3SMike Smith mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 2372da8bb3a3SMike Smith { 2373da8bb3a3SMike Smith u_int8_t fwerror; 2374da8bb3a3SMike Smith static int initted = 0; 2375da8bb3a3SMike Smith 2376da8bb3a3SMike Smith debug_called(2); 2377da8bb3a3SMike Smith 2378da8bb3a3SMike Smith /* first time around, clear any hardware completion status */ 2379da8bb3a3SMike Smith if (!initted) { 2380da8bb3a3SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK); 2381da8bb3a3SMike Smith DELAY(1000); 2382da8bb3a3SMike Smith initted = 1; 2383da8bb3a3SMike Smith } 2384da8bb3a3SMike Smith 2385da8bb3a3SMike Smith /* init in progress? */ 2386da8bb3a3SMike Smith if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY)) 2387da8bb3a3SMike Smith return(0); 2388da8bb3a3SMike Smith 2389da8bb3a3SMike Smith /* test error value */ 2390da8bb3a3SMike Smith fwerror = MLX_V3_GET_FWERROR(sc); 2391da8bb3a3SMike Smith if (!(fwerror & MLX_V3_FWERROR_PEND)) 2392da8bb3a3SMike Smith return(1); 2393da8bb3a3SMike Smith 2394da8bb3a3SMike Smith /* mask status pending bit, fetch status */ 2395da8bb3a3SMike Smith *error = fwerror & ~MLX_V3_FWERROR_PEND; 2396da8bb3a3SMike Smith *param1 = MLX_V3_GET_FWERROR_PARAM1(sc); 2397da8bb3a3SMike Smith *param2 = MLX_V3_GET_FWERROR_PARAM2(sc); 2398da8bb3a3SMike Smith 2399da8bb3a3SMike Smith /* acknowledge */ 2400da8bb3a3SMike Smith MLX_V3_PUT_FWERROR(sc, 0); 2401da8bb3a3SMike Smith 2402da8bb3a3SMike Smith return(2); 2403da8bb3a3SMike Smith } 24041ac4b82bSMike Smith 24051ac4b82bSMike Smith /******************************************************************************** 24061ac4b82bSMike Smith ******************************************************************************** 2407f6b84b08SMike Smith Type 4 interface accessor methods 2408f6b84b08SMike Smith ******************************************************************************** 2409f6b84b08SMike Smith ********************************************************************************/ 2410f6b84b08SMike Smith 2411f6b84b08SMike Smith /******************************************************************************** 2412f6b84b08SMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 2413f6b84b08SMike Smith * (the controller is not ready to take a command). 2414f6b84b08SMike Smith * 2415f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2416f6b84b08SMike Smith */ 2417f6b84b08SMike Smith static int 2418f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 2419f6b84b08SMike Smith { 2420f6b84b08SMike Smith int i; 2421f6b84b08SMike Smith 2422da8bb3a3SMike Smith debug_called(2); 2423f6b84b08SMike Smith 2424f6b84b08SMike Smith /* ready for our command? */ 2425f6b84b08SMike Smith if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) { 2426f6b84b08SMike Smith /* copy mailbox data to window */ 2427f6b84b08SMike Smith for (i = 0; i < 13; i++) 2428f6b84b08SMike Smith MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 2429f6b84b08SMike Smith 2430da8bb3a3SMike Smith /* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */ 2431da8bb3a3SMike Smith bus_space_barrier(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH, 2432da8bb3a3SMike Smith BUS_SPACE_BARRIER_WRITE); 2433da8bb3a3SMike Smith 2434f6b84b08SMike Smith /* post command */ 2435f6b84b08SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD); 2436f6b84b08SMike Smith return(1); 2437f6b84b08SMike Smith } 2438f6b84b08SMike Smith return(0); 2439f6b84b08SMike Smith } 2440f6b84b08SMike Smith 2441f6b84b08SMike Smith /******************************************************************************** 2442f6b84b08SMike Smith * See if a command has been completed, if so acknowledge its completion 2443f6b84b08SMike Smith * and recover the slot number and status code. 2444f6b84b08SMike Smith * 2445f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2446f6b84b08SMike Smith */ 2447f6b84b08SMike Smith static int 2448f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 2449f6b84b08SMike Smith { 2450f6b84b08SMike Smith 2451da8bb3a3SMike Smith debug_called(2); 2452f6b84b08SMike Smith 2453f6b84b08SMike Smith /* status available? */ 2454f6b84b08SMike Smith if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) { 2455f6b84b08SMike Smith *slot = MLX_V4_GET_STATUS_IDENT(sc); /* get command identifier */ 2456f6b84b08SMike Smith *status = MLX_V4_GET_STATUS(sc); /* get status */ 2457f6b84b08SMike Smith 2458f6b84b08SMike Smith /* acknowledge completion */ 2459f6b84b08SMike Smith MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK); 2460f6b84b08SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK); 2461f6b84b08SMike Smith return(1); 2462f6b84b08SMike Smith } 2463f6b84b08SMike Smith return(0); 2464f6b84b08SMike Smith } 2465f6b84b08SMike Smith 2466f6b84b08SMike Smith /******************************************************************************** 2467f6b84b08SMike Smith * Enable/disable interrupts as requested. 2468f6b84b08SMike Smith * 2469f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2470f6b84b08SMike Smith */ 2471f6b84b08SMike Smith static void 2472f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action) 2473f6b84b08SMike Smith { 2474da8bb3a3SMike Smith debug_called(1); 2475f6b84b08SMike Smith 2476f6b84b08SMike Smith switch(action) { 2477f6b84b08SMike Smith case MLX_INTACTION_DISABLE: 2478f6b84b08SMike Smith MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT); 2479f6b84b08SMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 2480f6b84b08SMike Smith break; 2481f6b84b08SMike Smith case MLX_INTACTION_ENABLE: 2482f6b84b08SMike Smith MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT); 2483f6b84b08SMike Smith sc->mlx_state |= MLX_STATE_INTEN; 2484f6b84b08SMike Smith break; 2485f6b84b08SMike Smith } 2486f6b84b08SMike Smith } 2487f6b84b08SMike Smith 2488da8bb3a3SMike Smith /******************************************************************************** 2489da8bb3a3SMike Smith * Poll for firmware error codes during controller initialisation. 2490da8bb3a3SMike Smith * Returns 0 if initialisation is complete, 1 if still in progress but no 2491da8bb3a3SMike Smith * error has been fetched, 2 if an error has been retrieved. 2492da8bb3a3SMike Smith */ 2493da8bb3a3SMike Smith static int 2494da8bb3a3SMike Smith mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 2495da8bb3a3SMike Smith { 2496da8bb3a3SMike Smith u_int8_t fwerror; 2497da8bb3a3SMike Smith static int initted = 0; 2498da8bb3a3SMike Smith 2499da8bb3a3SMike Smith debug_called(2); 2500da8bb3a3SMike Smith 2501da8bb3a3SMike Smith /* first time around, clear any hardware completion status */ 2502da8bb3a3SMike Smith if (!initted) { 2503da8bb3a3SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK); 2504da8bb3a3SMike Smith DELAY(1000); 2505da8bb3a3SMike Smith initted = 1; 2506da8bb3a3SMike Smith } 2507da8bb3a3SMike Smith 2508da8bb3a3SMike Smith /* init in progress? */ 2509da8bb3a3SMike Smith if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY)) 2510da8bb3a3SMike Smith return(0); 2511da8bb3a3SMike Smith 2512da8bb3a3SMike Smith /* test error value */ 2513da8bb3a3SMike Smith fwerror = MLX_V4_GET_FWERROR(sc); 2514da8bb3a3SMike Smith if (!(fwerror & MLX_V4_FWERROR_PEND)) 2515da8bb3a3SMike Smith return(1); 2516da8bb3a3SMike Smith 2517da8bb3a3SMike Smith /* mask status pending bit, fetch status */ 2518da8bb3a3SMike Smith *error = fwerror & ~MLX_V4_FWERROR_PEND; 2519da8bb3a3SMike Smith *param1 = MLX_V4_GET_FWERROR_PARAM1(sc); 2520da8bb3a3SMike Smith *param2 = MLX_V4_GET_FWERROR_PARAM2(sc); 2521da8bb3a3SMike Smith 2522da8bb3a3SMike Smith /* acknowledge */ 2523da8bb3a3SMike Smith MLX_V4_PUT_FWERROR(sc, 0); 2524da8bb3a3SMike Smith 2525da8bb3a3SMike Smith return(2); 2526da8bb3a3SMike Smith } 2527f6b84b08SMike Smith 2528f6b84b08SMike Smith /******************************************************************************** 2529f6b84b08SMike Smith ******************************************************************************** 25305792b7feSMike Smith Type 5 interface accessor methods 25315792b7feSMike Smith ******************************************************************************** 25325792b7feSMike Smith ********************************************************************************/ 25335792b7feSMike Smith 25345792b7feSMike Smith /******************************************************************************** 25355792b7feSMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 25365792b7feSMike Smith * (the controller is not ready to take a command). 25375792b7feSMike Smith * 25385792b7feSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 25395792b7feSMike Smith */ 25405792b7feSMike Smith static int 25415792b7feSMike Smith mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 25425792b7feSMike Smith { 25435792b7feSMike Smith int i; 25445792b7feSMike Smith 2545da8bb3a3SMike Smith debug_called(2); 25465792b7feSMike Smith 25475792b7feSMike Smith /* ready for our command? */ 25485792b7feSMike Smith if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) { 25495792b7feSMike Smith /* copy mailbox data to window */ 25505792b7feSMike Smith for (i = 0; i < 13; i++) 25515792b7feSMike Smith MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 25525792b7feSMike Smith 25535792b7feSMike Smith /* post command */ 25545792b7feSMike Smith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD); 25555792b7feSMike Smith return(1); 25565792b7feSMike Smith } 25575792b7feSMike Smith return(0); 25585792b7feSMike Smith } 25595792b7feSMike Smith 25605792b7feSMike Smith /******************************************************************************** 25615792b7feSMike Smith * See if a command has been completed, if so acknowledge its completion 25625792b7feSMike Smith * and recover the slot number and status code. 25635792b7feSMike Smith * 25645792b7feSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 25655792b7feSMike Smith */ 25665792b7feSMike Smith static int 25675792b7feSMike Smith mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 25685792b7feSMike Smith { 25695792b7feSMike Smith 2570da8bb3a3SMike Smith debug_called(2); 25715792b7feSMike Smith 25725792b7feSMike Smith /* status available? */ 25735792b7feSMike Smith if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) { 25745792b7feSMike Smith *slot = MLX_V5_GET_STATUS_IDENT(sc); /* get command identifier */ 25755792b7feSMike Smith *status = MLX_V5_GET_STATUS(sc); /* get status */ 25765792b7feSMike Smith 25775792b7feSMike Smith /* acknowledge completion */ 25785792b7feSMike Smith MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK); 25795792b7feSMike Smith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK); 25805792b7feSMike Smith return(1); 25815792b7feSMike Smith } 25825792b7feSMike Smith return(0); 25835792b7feSMike Smith } 25845792b7feSMike Smith 25855792b7feSMike Smith /******************************************************************************** 25865792b7feSMike Smith * Enable/disable interrupts as requested. 25875792b7feSMike Smith * 25885792b7feSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 25895792b7feSMike Smith */ 25905792b7feSMike Smith static void 25915792b7feSMike Smith mlx_v5_intaction(struct mlx_softc *sc, int action) 25925792b7feSMike Smith { 2593da8bb3a3SMike Smith debug_called(1); 25945792b7feSMike Smith 25955792b7feSMike Smith switch(action) { 25965792b7feSMike Smith case MLX_INTACTION_DISABLE: 2597da8bb3a3SMike Smith MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT); 25985792b7feSMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 25995792b7feSMike Smith break; 26005792b7feSMike Smith case MLX_INTACTION_ENABLE: 2601da8bb3a3SMike Smith MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT); 26025792b7feSMike Smith sc->mlx_state |= MLX_STATE_INTEN; 26035792b7feSMike Smith break; 26045792b7feSMike Smith } 26055792b7feSMike Smith } 26065792b7feSMike Smith 2607da8bb3a3SMike Smith /******************************************************************************** 2608da8bb3a3SMike Smith * Poll for firmware error codes during controller initialisation. 2609da8bb3a3SMike Smith * Returns 0 if initialisation is complete, 1 if still in progress but no 2610da8bb3a3SMike Smith * error has been fetched, 2 if an error has been retrieved. 2611da8bb3a3SMike Smith */ 2612da8bb3a3SMike Smith static int 2613da8bb3a3SMike Smith mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 2614da8bb3a3SMike Smith { 2615da8bb3a3SMike Smith u_int8_t fwerror; 2616da8bb3a3SMike Smith static int initted = 0; 2617da8bb3a3SMike Smith 2618da8bb3a3SMike Smith debug_called(2); 2619da8bb3a3SMike Smith 2620da8bb3a3SMike Smith /* first time around, clear any hardware completion status */ 2621da8bb3a3SMike Smith if (!initted) { 2622da8bb3a3SMike Smith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK); 2623da8bb3a3SMike Smith DELAY(1000); 2624da8bb3a3SMike Smith initted = 1; 2625da8bb3a3SMike Smith } 2626da8bb3a3SMike Smith 2627da8bb3a3SMike Smith /* init in progress? */ 2628da8bb3a3SMike Smith if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE) 2629da8bb3a3SMike Smith return(0); 2630da8bb3a3SMike Smith 2631da8bb3a3SMike Smith /* test for error value */ 2632da8bb3a3SMike Smith fwerror = MLX_V5_GET_FWERROR(sc); 2633da8bb3a3SMike Smith if (!(fwerror & MLX_V5_FWERROR_PEND)) 2634da8bb3a3SMike Smith return(1); 2635da8bb3a3SMike Smith 2636da8bb3a3SMike Smith /* mask status pending bit, fetch status */ 2637da8bb3a3SMike Smith *error = fwerror & ~MLX_V5_FWERROR_PEND; 2638da8bb3a3SMike Smith *param1 = MLX_V5_GET_FWERROR_PARAM1(sc); 2639da8bb3a3SMike Smith *param2 = MLX_V5_GET_FWERROR_PARAM2(sc); 2640da8bb3a3SMike Smith 2641da8bb3a3SMike Smith /* acknowledge */ 2642da8bb3a3SMike Smith MLX_V5_PUT_FWERROR(sc, 0xff); 2643da8bb3a3SMike Smith 2644da8bb3a3SMike Smith return(2); 2645da8bb3a3SMike Smith } 26465792b7feSMike Smith 26475792b7feSMike Smith /******************************************************************************** 26485792b7feSMike Smith ******************************************************************************** 26491ac4b82bSMike Smith Debugging 26501ac4b82bSMike Smith ******************************************************************************** 26511ac4b82bSMike Smith ********************************************************************************/ 26521ac4b82bSMike Smith 26531ac4b82bSMike Smith /******************************************************************************** 26541ac4b82bSMike Smith * Return a status message describing (mc) 26551ac4b82bSMike Smith */ 26561ac4b82bSMike Smith static char *mlx_status_messages[] = { 26571ac4b82bSMike Smith "normal completion", /* 00 */ 26581ac4b82bSMike Smith "irrecoverable data error", /* 01 */ 26591ac4b82bSMike Smith "drive does not exist, or is offline", /* 02 */ 26601ac4b82bSMike Smith "attempt to write beyond end of drive", /* 03 */ 26611ac4b82bSMike Smith "bad data encountered", /* 04 */ 26621ac4b82bSMike Smith "invalid log entry request", /* 05 */ 26631ac4b82bSMike Smith "attempt to rebuild online drive", /* 06 */ 26641ac4b82bSMike Smith "new disk failed during rebuild", /* 07 */ 26651ac4b82bSMike Smith "invalid channel/target", /* 08 */ 26661ac4b82bSMike Smith "rebuild/check already in progress", /* 09 */ 26671ac4b82bSMike Smith "one or more disks are dead", /* 10 */ 26681ac4b82bSMike Smith "invalid or non-redundant drive", /* 11 */ 26691ac4b82bSMike Smith "channel is busy", /* 12 */ 26701ac4b82bSMike Smith "channel is not stopped", /* 13 */ 2671da8bb3a3SMike Smith "rebuild successfully terminated", /* 14 */ 2672da8bb3a3SMike Smith "unsupported command", /* 15 */ 2673da8bb3a3SMike Smith "check condition received", /* 16 */ 2674da8bb3a3SMike Smith "device is busy", /* 17 */ 2675da8bb3a3SMike Smith "selection or command timeout", /* 18 */ 2676da8bb3a3SMike Smith "command terminated abnormally", /* 19 */ 2677da8bb3a3SMike Smith "" 26781ac4b82bSMike Smith }; 26791ac4b82bSMike Smith 26801ac4b82bSMike Smith static struct 26811ac4b82bSMike Smith { 26821ac4b82bSMike Smith int command; 26831ac4b82bSMike Smith u_int16_t status; 26841ac4b82bSMike Smith int msg; 26851ac4b82bSMike Smith } mlx_messages[] = { 2686da8bb3a3SMike Smith {MLX_CMD_READSG, 0x0001, 1}, 2687da8bb3a3SMike Smith {MLX_CMD_READSG, 0x0002, 1}, 2688da8bb3a3SMike Smith {MLX_CMD_READSG, 0x0105, 3}, 2689da8bb3a3SMike Smith {MLX_CMD_READSG, 0x010c, 4}, 2690da8bb3a3SMike Smith {MLX_CMD_WRITESG, 0x0001, 1}, 2691da8bb3a3SMike Smith {MLX_CMD_WRITESG, 0x0002, 1}, 2692da8bb3a3SMike Smith {MLX_CMD_WRITESG, 0x0105, 3}, 2693da8bb3a3SMike Smith {MLX_CMD_READSG_OLD, 0x0001, 1}, 2694da8bb3a3SMike Smith {MLX_CMD_READSG_OLD, 0x0002, 1}, 2695da8bb3a3SMike Smith {MLX_CMD_READSG_OLD, 0x0105, 3}, 2696da8bb3a3SMike Smith {MLX_CMD_WRITESG_OLD, 0x0001, 1}, 2697da8bb3a3SMike Smith {MLX_CMD_WRITESG_OLD, 0x0002, 1}, 2698da8bb3a3SMike Smith {MLX_CMD_WRITESG_OLD, 0x0105, 3}, 26991ac4b82bSMike Smith {MLX_CMD_LOGOP, 0x0105, 5}, 27001ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0002, 6}, 27011ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0004, 7}, 27021ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0105, 8}, 27031ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0106, 9}, 2704da8bb3a3SMike Smith {MLX_CMD_REBUILDASYNC, 0x0107, 14}, 27051ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0002, 10}, 27061ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0105, 11}, 27071ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0106, 9}, 27081ac4b82bSMike Smith {MLX_CMD_STOPCHANNEL, 0x0106, 12}, 27091ac4b82bSMike Smith {MLX_CMD_STOPCHANNEL, 0x0105, 8}, 27101ac4b82bSMike Smith {MLX_CMD_STARTCHANNEL, 0x0005, 13}, 27111ac4b82bSMike Smith {MLX_CMD_STARTCHANNEL, 0x0105, 8}, 2712da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x0002, 16}, 2713da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x0008, 17}, 2714da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x000e, 18}, 2715da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x000f, 19}, 2716da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x0105, 8}, 2717da8bb3a3SMike Smith 2718da8bb3a3SMike Smith {0, 0x0104, 14}, 27191ac4b82bSMike Smith {-1, 0, 0} 27201ac4b82bSMike Smith }; 27211ac4b82bSMike Smith 27221ac4b82bSMike Smith static char * 27231ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc) 27241ac4b82bSMike Smith { 27251ac4b82bSMike Smith static char unkmsg[80]; 27261ac4b82bSMike Smith int i; 27271ac4b82bSMike Smith 27281ac4b82bSMike Smith /* look up message in table */ 27291ac4b82bSMike Smith for (i = 0; mlx_messages[i].command != -1; i++) 2730da8bb3a3SMike Smith if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) && 2731466454bdSMike Smith (mc->mc_status == mlx_messages[i].status)) 27321ac4b82bSMike Smith return(mlx_status_messages[mlx_messages[i].msg]); 27331ac4b82bSMike Smith 27341ac4b82bSMike Smith sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]); 27351ac4b82bSMike Smith return(unkmsg); 27361ac4b82bSMike Smith } 27371ac4b82bSMike Smith 27381ac4b82bSMike Smith /******************************************************************************* 2739da8bb3a3SMike Smith * Print a string describing the controller (sc) 27401ac4b82bSMike Smith */ 27415792b7feSMike Smith static struct 27425792b7feSMike Smith { 27435792b7feSMike Smith int hwid; 27445792b7feSMike Smith char *name; 27455792b7feSMike Smith } mlx_controller_names[] = { 27465792b7feSMike Smith {0x01, "960P/PD"}, 27475792b7feSMike Smith {0x02, "960PL"}, 27485792b7feSMike Smith {0x10, "960PG"}, 27495792b7feSMike Smith {0x11, "960PJ"}, 27509eee27f1SMike Smith {0x12, "960PR"}, 27519eee27f1SMike Smith {0x13, "960PT"}, 27529eee27f1SMike Smith {0x14, "960PTL0"}, 27539eee27f1SMike Smith {0x15, "960PRL"}, 27549eee27f1SMike Smith {0x16, "960PTL1"}, 27559eee27f1SMike Smith {0x20, "1164PVX"}, 27565792b7feSMike Smith {-1, NULL} 27575792b7feSMike Smith }; 27585792b7feSMike Smith 27599eee27f1SMike Smith static void 27609eee27f1SMike Smith mlx_describe_controller(struct mlx_softc *sc) 27611ac4b82bSMike Smith { 27621ac4b82bSMike Smith static char buf[80]; 27635792b7feSMike Smith char *model; 27649eee27f1SMike Smith int i; 27651ac4b82bSMike Smith 27665792b7feSMike Smith for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) { 27679eee27f1SMike Smith if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) { 27685792b7feSMike Smith model = mlx_controller_names[i].name; 27691ac4b82bSMike Smith break; 27701ac4b82bSMike Smith } 27715792b7feSMike Smith } 27725792b7feSMike Smith if (model == NULL) { 27739eee27f1SMike Smith sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff); 27745792b7feSMike Smith model = buf; 27755792b7feSMike Smith } 2776da8bb3a3SMike Smith device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n", 27779eee27f1SMike Smith model, 27789eee27f1SMike Smith sc->mlx_enq2->me_actual_channels, 27799eee27f1SMike Smith sc->mlx_enq2->me_actual_channels > 1 ? "s" : "", 27809eee27f1SMike Smith sc->mlx_enq2->me_firmware_id & 0xff, 27819eee27f1SMike Smith (sc->mlx_enq2->me_firmware_id >> 8) & 0xff, 27829eee27f1SMike Smith (sc->mlx_enq2->me_firmware_id >> 24) & 0xff, 2783b9256fe3SMike Smith (sc->mlx_enq2->me_firmware_id >> 16) & 0xff, 27849eee27f1SMike Smith sc->mlx_enq2->me_mem_size / (1024 * 1024)); 27859eee27f1SMike Smith 27869eee27f1SMike Smith if (bootverbose) { 27879eee27f1SMike Smith device_printf(sc->mlx_dev, " Hardware ID 0x%08x\n", sc->mlx_enq2->me_hardware_id); 27889eee27f1SMike Smith device_printf(sc->mlx_dev, " Firmware ID 0x%08x\n", sc->mlx_enq2->me_firmware_id); 27899eee27f1SMike Smith device_printf(sc->mlx_dev, " Configured/Actual channels %d/%d\n", sc->mlx_enq2->me_configured_channels, 27909eee27f1SMike Smith sc->mlx_enq2->me_actual_channels); 27919eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Targets %d\n", sc->mlx_enq2->me_max_targets); 27929eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Tags %d\n", sc->mlx_enq2->me_max_tags); 27939eee27f1SMike Smith device_printf(sc->mlx_dev, " Max System Drives %d\n", sc->mlx_enq2->me_max_sys_drives); 27949eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Arms %d\n", sc->mlx_enq2->me_max_arms); 27959eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Spans %d\n", sc->mlx_enq2->me_max_spans); 27969eee27f1SMike Smith device_printf(sc->mlx_dev, " DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size, 27979eee27f1SMike Smith sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size); 27989eee27f1SMike Smith device_printf(sc->mlx_dev, " DRAM type %d\n", sc->mlx_enq2->me_mem_type); 27999eee27f1SMike Smith device_printf(sc->mlx_dev, " Clock Speed %dns\n", sc->mlx_enq2->me_clock_speed); 28009eee27f1SMike Smith device_printf(sc->mlx_dev, " Hardware Speed %dns\n", sc->mlx_enq2->me_hardware_speed); 28019eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Commands %d\n", sc->mlx_enq2->me_max_commands); 28029eee27f1SMike Smith device_printf(sc->mlx_dev, " Max SG Entries %d\n", sc->mlx_enq2->me_max_sg); 28039eee27f1SMike Smith device_printf(sc->mlx_dev, " Max DP %d\n", sc->mlx_enq2->me_max_dp); 28049eee27f1SMike Smith device_printf(sc->mlx_dev, " Max IOD %d\n", sc->mlx_enq2->me_max_iod); 28059eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Comb %d\n", sc->mlx_enq2->me_max_comb); 28069eee27f1SMike Smith device_printf(sc->mlx_dev, " Latency %ds\n", sc->mlx_enq2->me_latency); 28079eee27f1SMike Smith device_printf(sc->mlx_dev, " SCSI Timeout %ds\n", sc->mlx_enq2->me_scsi_timeout); 28089eee27f1SMike Smith device_printf(sc->mlx_dev, " Min Free Lines %d\n", sc->mlx_enq2->me_min_freelines); 28099eee27f1SMike Smith device_printf(sc->mlx_dev, " Rate Constant %d\n", sc->mlx_enq2->me_rate_const); 28109eee27f1SMike Smith device_printf(sc->mlx_dev, " MAXBLK %d\n", sc->mlx_enq2->me_maxblk); 28119eee27f1SMike Smith device_printf(sc->mlx_dev, " Blocking Factor %d sectors\n", sc->mlx_enq2->me_blocking_factor); 28129eee27f1SMike Smith device_printf(sc->mlx_dev, " Cache Line Size %d blocks\n", sc->mlx_enq2->me_cacheline); 28139eee27f1SMike Smith device_printf(sc->mlx_dev, " SCSI Capability %s%dMHz, %d bit\n", 28149eee27f1SMike Smith sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "", 28159eee27f1SMike Smith (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10, 28169eee27f1SMike Smith 8 << (sc->mlx_enq2->me_scsi_cap & 0x3)); 28179eee27f1SMike Smith device_printf(sc->mlx_dev, " Firmware Build Number %d\n", sc->mlx_enq2->me_firmware_build); 28189eee27f1SMike Smith device_printf(sc->mlx_dev, " Fault Management Type %d\n", sc->mlx_enq2->me_fault_mgmt_type); 28199eee27f1SMike Smith device_printf(sc->mlx_dev, " Features %b\n", sc->mlx_enq2->me_firmware_features, 28209eee27f1SMike Smith "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n"); 28219eee27f1SMike Smith 28229eee27f1SMike Smith } 28231ac4b82bSMike Smith } 28241ac4b82bSMike Smith 2825da8bb3a3SMike Smith /******************************************************************************* 2826da8bb3a3SMike Smith * Emit a string describing the firmware handshake status code, and return a flag 2827da8bb3a3SMike Smith * indicating whether the code represents a fatal error. 2828da8bb3a3SMike Smith * 2829da8bb3a3SMike Smith * Error code interpretations are from the Linux driver, and don't directly match 2830da8bb3a3SMike Smith * the messages printed by Mylex's BIOS. This may change if documentation on the 2831da8bb3a3SMike Smith * codes is forthcoming. 2832da8bb3a3SMike Smith */ 2833da8bb3a3SMike Smith static int 2834da8bb3a3SMike Smith mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2) 2835da8bb3a3SMike Smith { 2836da8bb3a3SMike Smith switch(error) { 2837da8bb3a3SMike Smith case 0x00: 2838da8bb3a3SMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1); 2839da8bb3a3SMike Smith break; 2840da8bb3a3SMike Smith case 0x08: 2841da8bb3a3SMike Smith /* we could be neater about this and give some indication when we receive more of them */ 2842da8bb3a3SMike Smith if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) { 2843da8bb3a3SMike Smith device_printf(sc->mlx_dev, "spinning up drives...\n"); 2844da8bb3a3SMike Smith sc->mlx_flags |= MLX_SPINUP_REPORTED; 2845da8bb3a3SMike Smith } 2846da8bb3a3SMike Smith break; 2847da8bb3a3SMike Smith case 0x30: 2848da8bb3a3SMike Smith device_printf(sc->mlx_dev, "configuration checksum error\n"); 2849da8bb3a3SMike Smith break; 2850da8bb3a3SMike Smith case 0x60: 2851da8bb3a3SMike Smith device_printf(sc->mlx_dev, "mirror race recovery failed\n"); 2852da8bb3a3SMike Smith break; 2853da8bb3a3SMike Smith case 0x70: 2854da8bb3a3SMike Smith device_printf(sc->mlx_dev, "mirror race recovery in progress\n"); 2855da8bb3a3SMike Smith break; 2856da8bb3a3SMike Smith case 0x90: 2857da8bb3a3SMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1); 2858da8bb3a3SMike Smith break; 2859da8bb3a3SMike Smith case 0xa0: 2860da8bb3a3SMike Smith device_printf(sc->mlx_dev, "logical drive installation aborted\n"); 2861da8bb3a3SMike Smith break; 2862da8bb3a3SMike Smith case 0xb0: 2863da8bb3a3SMike Smith device_printf(sc->mlx_dev, "mirror race on a critical system drive\n"); 2864da8bb3a3SMike Smith break; 2865da8bb3a3SMike Smith case 0xd0: 2866da8bb3a3SMike Smith device_printf(sc->mlx_dev, "new controller configuration found\n"); 2867da8bb3a3SMike Smith break; 2868da8bb3a3SMike Smith case 0xf0: 2869da8bb3a3SMike Smith device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n"); 2870da8bb3a3SMike Smith return(1); 2871da8bb3a3SMike Smith default: 2872da8bb3a3SMike Smith device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2); 2873da8bb3a3SMike Smith break; 2874da8bb3a3SMike Smith } 2875da8bb3a3SMike Smith return(0); 2876da8bb3a3SMike Smith } 2877da8bb3a3SMike Smith 28781ac4b82bSMike Smith /******************************************************************************** 28791ac4b82bSMike Smith ******************************************************************************** 28801ac4b82bSMike Smith Utility Functions 28811ac4b82bSMike Smith ******************************************************************************** 28821ac4b82bSMike Smith ********************************************************************************/ 28831ac4b82bSMike Smith 28841ac4b82bSMike Smith /******************************************************************************** 28851ac4b82bSMike Smith * Find the disk whose unit number is (unit) on this controller 28861ac4b82bSMike Smith */ 28871ac4b82bSMike Smith static struct mlx_sysdrive * 28881ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit) 28891ac4b82bSMike Smith { 28901ac4b82bSMike Smith int i; 28911ac4b82bSMike Smith 28921ac4b82bSMike Smith /* search system drives */ 28931ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 28941ac4b82bSMike Smith /* is this one attached? */ 28951ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 28961ac4b82bSMike Smith /* is this the one? */ 28971ac4b82bSMike Smith if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) 28981ac4b82bSMike Smith return(&sc->mlx_sysdrive[i]); 28991ac4b82bSMike Smith } 29001ac4b82bSMike Smith } 29011ac4b82bSMike Smith return(NULL); 29021ac4b82bSMike Smith } 2903