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/bus.h> 391ac4b82bSMike Smith #include <sys/conf.h> 40da8bb3a3SMike Smith #include <sys/stat.h> 411ac4b82bSMike Smith 421ac4b82bSMike Smith #include <machine/resource.h> 43786cd128SMike Smith #include <machine/bus_memio.h> 44786cd128SMike Smith #include <machine/bus_pio.h> 451ac4b82bSMike Smith #include <machine/bus.h> 461ac4b82bSMike Smith #include <machine/clock.h> 471ac4b82bSMike Smith #include <sys/rman.h> 481ac4b82bSMike Smith 49891619a6SPoul-Henning Kamp #include <geom/geom_disk.h> 50891619a6SPoul-Henning Kamp 5115fd5d22SMike Smith #include <dev/mlx/mlx_compat.h> 521ac4b82bSMike Smith #include <dev/mlx/mlxio.h> 531ac4b82bSMike Smith #include <dev/mlx/mlxvar.h> 541ac4b82bSMike Smith #include <dev/mlx/mlxreg.h> 551ac4b82bSMike Smith 561ac4b82bSMike Smith #define MLX_CDEV_MAJOR 130 571ac4b82bSMike Smith 581ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = { 597ac40f5fSPoul-Henning Kamp .d_open = mlx_open, 607ac40f5fSPoul-Henning Kamp .d_close = mlx_close, 617ac40f5fSPoul-Henning Kamp .d_ioctl = mlx_ioctl, 627ac40f5fSPoul-Henning Kamp .d_name = "mlx", 637ac40f5fSPoul-Henning Kamp .d_maj = MLX_CDEV_MAJOR, 641ac4b82bSMike Smith }; 651ac4b82bSMike Smith 661ac4b82bSMike Smith devclass_t mlx_devclass; 671ac4b82bSMike Smith 681ac4b82bSMike Smith /* 691ac4b82bSMike Smith * Per-interface accessor methods 701ac4b82bSMike Smith */ 711ac4b82bSMike Smith static int mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 721ac4b82bSMike Smith static int mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 731ac4b82bSMike Smith static void mlx_v3_intaction(struct mlx_softc *sc, int action); 74da8bb3a3SMike Smith static int mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 751ac4b82bSMike Smith 76f6b84b08SMike Smith static int mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 77f6b84b08SMike Smith static int mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 78f6b84b08SMike Smith static void mlx_v4_intaction(struct mlx_softc *sc, int action); 79da8bb3a3SMike Smith static int mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 80f6b84b08SMike Smith 815792b7feSMike Smith static int mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); 825792b7feSMike Smith static int mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); 835792b7feSMike Smith static void mlx_v5_intaction(struct mlx_softc *sc, int action); 84da8bb3a3SMike Smith static int mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); 855792b7feSMike Smith 861ac4b82bSMike Smith /* 871ac4b82bSMike Smith * Status monitoring 881ac4b82bSMike Smith */ 891ac4b82bSMike Smith static void mlx_periodic(void *data); 901ac4b82bSMike Smith static void mlx_periodic_enquiry(struct mlx_command *mc); 911ac4b82bSMike Smith static void mlx_periodic_eventlog_poll(struct mlx_softc *sc); 921ac4b82bSMike Smith static void mlx_periodic_eventlog_respond(struct mlx_command *mc); 931ac4b82bSMike Smith static void mlx_periodic_rebuild(struct mlx_command *mc); 941ac4b82bSMike Smith 951ac4b82bSMike Smith /* 961ac4b82bSMike Smith * Channel Pause 971ac4b82bSMike Smith */ 981ac4b82bSMike Smith static void mlx_pause_action(struct mlx_softc *sc); 991ac4b82bSMike Smith static void mlx_pause_done(struct mlx_command *mc); 1001ac4b82bSMike Smith 1011ac4b82bSMike Smith /* 1021ac4b82bSMike Smith * Command submission. 1031ac4b82bSMike Smith */ 1041ac4b82bSMike Smith static void *mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, 1051ac4b82bSMike Smith void (*complete)(struct mlx_command *mc)); 1061ac4b82bSMike Smith static int mlx_flush(struct mlx_softc *sc); 107421f2f7dSMike Smith static int mlx_check(struct mlx_softc *sc, int drive); 1081ac4b82bSMike Smith static int mlx_rebuild(struct mlx_softc *sc, int channel, int target); 1091ac4b82bSMike Smith static int mlx_wait_command(struct mlx_command *mc); 1101ac4b82bSMike Smith static int mlx_poll_command(struct mlx_command *mc); 1111ac4b82bSMike Smith static void mlx_startio(struct mlx_softc *sc); 1121ac4b82bSMike Smith static void mlx_completeio(struct mlx_command *mc); 1131ac4b82bSMike Smith static int mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu); 1141ac4b82bSMike Smith 1151ac4b82bSMike Smith /* 1161ac4b82bSMike Smith * Command buffer allocation. 1171ac4b82bSMike Smith */ 1181ac4b82bSMike Smith static struct mlx_command *mlx_alloccmd(struct mlx_softc *sc); 1191ac4b82bSMike Smith static void mlx_releasecmd(struct mlx_command *mc); 1201ac4b82bSMike Smith static void mlx_freecmd(struct mlx_command *mc); 1211ac4b82bSMike Smith 1221ac4b82bSMike Smith /* 1231ac4b82bSMike Smith * Command management. 1241ac4b82bSMike Smith */ 1251ac4b82bSMike Smith static int mlx_getslot(struct mlx_command *mc); 1261ac4b82bSMike Smith static void mlx_mapcmd(struct mlx_command *mc); 1271ac4b82bSMike Smith static void mlx_unmapcmd(struct mlx_command *mc); 1281ac4b82bSMike Smith static int mlx_start(struct mlx_command *mc); 1291ac4b82bSMike Smith static int mlx_done(struct mlx_softc *sc); 1301ac4b82bSMike Smith static void mlx_complete(struct mlx_softc *sc); 1311ac4b82bSMike Smith 1321ac4b82bSMike Smith /* 1331ac4b82bSMike Smith * Debugging. 1341ac4b82bSMike Smith */ 1351ac4b82bSMike Smith static char *mlx_diagnose_command(struct mlx_command *mc); 1369eee27f1SMike Smith static void mlx_describe_controller(struct mlx_softc *sc); 137da8bb3a3SMike Smith static int mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2); 1381ac4b82bSMike Smith 1391ac4b82bSMike Smith /* 1401ac4b82bSMike Smith * Utility functions. 1411ac4b82bSMike Smith */ 1421ac4b82bSMike Smith static struct mlx_sysdrive *mlx_findunit(struct mlx_softc *sc, int unit); 1431ac4b82bSMike Smith 1441ac4b82bSMike Smith /******************************************************************************** 1451ac4b82bSMike Smith ******************************************************************************** 1461ac4b82bSMike Smith Public Interfaces 1471ac4b82bSMike Smith ******************************************************************************** 1481ac4b82bSMike Smith ********************************************************************************/ 1491ac4b82bSMike Smith 1501ac4b82bSMike Smith /******************************************************************************** 1511ac4b82bSMike Smith * Free all of the resources associated with (sc) 1521ac4b82bSMike Smith * 1531ac4b82bSMike Smith * Should not be called if the controller is active. 1541ac4b82bSMike Smith */ 1551ac4b82bSMike Smith void 1561ac4b82bSMike Smith mlx_free(struct mlx_softc *sc) 1571ac4b82bSMike Smith { 1581ac4b82bSMike Smith struct mlx_command *mc; 1591ac4b82bSMike Smith 160da8bb3a3SMike Smith debug_called(1); 1611ac4b82bSMike Smith 1621ac4b82bSMike Smith /* cancel status timeout */ 1631ac4b82bSMike Smith untimeout(mlx_periodic, sc, sc->mlx_timeout); 1641ac4b82bSMike Smith 1651ac4b82bSMike Smith /* throw away any command buffers */ 1661ac4b82bSMike Smith while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) { 1671ac4b82bSMike Smith TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); 1681ac4b82bSMike Smith mlx_freecmd(mc); 1691ac4b82bSMike Smith } 1701ac4b82bSMike Smith 1711ac4b82bSMike Smith /* destroy data-transfer DMA tag */ 1721ac4b82bSMike Smith if (sc->mlx_buffer_dmat) 1731ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_buffer_dmat); 1741ac4b82bSMike Smith 1751ac4b82bSMike Smith /* free and destroy DMA memory and tag for s/g lists */ 1761ac4b82bSMike Smith if (sc->mlx_sgtable) 1771ac4b82bSMike Smith bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); 1781ac4b82bSMike Smith if (sc->mlx_sg_dmat) 1791ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_sg_dmat); 1801ac4b82bSMike Smith 1811ac4b82bSMike Smith /* disconnect the interrupt handler */ 1821ac4b82bSMike Smith if (sc->mlx_intr) 1831ac4b82bSMike Smith bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr); 1841ac4b82bSMike Smith if (sc->mlx_irq != NULL) 1851ac4b82bSMike Smith bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq); 1861ac4b82bSMike Smith 1871ac4b82bSMike Smith /* destroy the parent DMA tag */ 1881ac4b82bSMike Smith if (sc->mlx_parent_dmat) 1891ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_parent_dmat); 1901ac4b82bSMike Smith 1911ac4b82bSMike Smith /* release the register window mapping */ 1921ac4b82bSMike Smith if (sc->mlx_mem != NULL) 1939b11c7baSMatthew N. Dodd bus_release_resource(sc->mlx_dev, sc->mlx_mem_type, sc->mlx_mem_rid, sc->mlx_mem); 1949eee27f1SMike Smith 1959eee27f1SMike Smith /* free controller enquiry data */ 1969eee27f1SMike Smith if (sc->mlx_enq2 != NULL) 1979eee27f1SMike Smith free(sc->mlx_enq2, M_DEVBUF); 198da8bb3a3SMike Smith 199da8bb3a3SMike Smith /* destroy control device */ 200da8bb3a3SMike Smith if (sc->mlx_dev_t != (dev_t)NULL) 201da8bb3a3SMike Smith destroy_dev(sc->mlx_dev_t); 2021ac4b82bSMike Smith } 2031ac4b82bSMike Smith 2041ac4b82bSMike Smith /******************************************************************************** 2051ac4b82bSMike Smith * Map the scatter/gather table into bus space 2061ac4b82bSMike Smith */ 2071ac4b82bSMike Smith static void 2081ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error) 2091ac4b82bSMike Smith { 2101ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)arg; 2111ac4b82bSMike Smith 212da8bb3a3SMike Smith debug_called(1); 2131ac4b82bSMike Smith 2141ac4b82bSMike Smith /* save base of s/g table's address in bus space */ 2151ac4b82bSMike Smith sc->mlx_sgbusaddr = segs->ds_addr; 2161ac4b82bSMike Smith } 2171ac4b82bSMike Smith 2181ac4b82bSMike Smith static int 2191ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc) 2201ac4b82bSMike Smith { 2211ac4b82bSMike Smith size_t segsize; 222baff09dbSMike Smith int error, ncmd; 2231ac4b82bSMike Smith 224da8bb3a3SMike Smith debug_called(1); 2251ac4b82bSMike Smith 2261ac4b82bSMike Smith /* destroy any existing mappings */ 2271ac4b82bSMike Smith if (sc->mlx_sgtable) 2281ac4b82bSMike Smith bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap); 2291ac4b82bSMike Smith if (sc->mlx_sg_dmat) 2301ac4b82bSMike Smith bus_dma_tag_destroy(sc->mlx_sg_dmat); 2311ac4b82bSMike Smith 2321ac4b82bSMike Smith /* 2331ac4b82bSMike Smith * Create a single tag describing a region large enough to hold all of 234baff09dbSMike Smith * the s/g lists we will need. If we're called early on, we don't know how 235baff09dbSMike Smith * many commands we're going to be asked to support, so only allocate enough 236baff09dbSMike Smith * for a couple. 2371ac4b82bSMike Smith */ 238baff09dbSMike Smith if (sc->mlx_enq2 == NULL) { 239baff09dbSMike Smith ncmd = 2; 240baff09dbSMike Smith } else { 241baff09dbSMike Smith ncmd = sc->mlx_enq2->me_max_commands; 242baff09dbSMike Smith } 243baff09dbSMike Smith segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * ncmd; 2441ac4b82bSMike Smith error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 2451ac4b82bSMike Smith 1, 0, /* alignment,boundary */ 2461ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* lowaddr */ 2471ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* highaddr */ 2481ac4b82bSMike Smith NULL, NULL, /* filter, filterarg */ 2491ac4b82bSMike Smith segsize, 1, /* maxsize, nsegments */ 2501ac4b82bSMike Smith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 2511ac4b82bSMike Smith 0, /* flags */ 252fc3e87b3SScott Long NULL, NULL, /* lockfunc, lockarg */ 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 264fc3e87b3SScott Long * contiguous slab. We may need to switch to a more complex arrangement 265fc3e87b3SScott Long * where we allocate in smaller chunks and keep a lookup table from slot 266fc3e87b3SScott Long * to bus address. 2671ac4b82bSMike Smith */ 268fc3e87b3SScott Long error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable, 269fc3e87b3SScott Long BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap); 2701ac4b82bSMike Smith if (error) { 2711ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate s/g table\n"); 2721ac4b82bSMike Smith return(ENOMEM); 2731ac4b82bSMike Smith } 274fc3e87b3SScott Long bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable, 275fc3e87b3SScott Long segsize, mlx_dma_map_sg, sc, 0); 2761ac4b82bSMike Smith return(0); 2771ac4b82bSMike Smith } 2781ac4b82bSMike Smith 2791ac4b82bSMike Smith /******************************************************************************** 2801ac4b82bSMike Smith * Initialise the controller and softc 2811ac4b82bSMike Smith */ 2821ac4b82bSMike Smith int 2831ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc) 2841ac4b82bSMike Smith { 285da8bb3a3SMike Smith struct mlx_enquiry_old *meo; 286da8bb3a3SMike Smith int rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg; 2871ac4b82bSMike Smith 288da8bb3a3SMike Smith debug_called(1); 2891ac4b82bSMike Smith 2901ac4b82bSMike Smith /* 2911ac4b82bSMike Smith * Initialise per-controller queues. 2921ac4b82bSMike Smith */ 2934b006d7bSMike Smith TAILQ_INIT(&sc->mlx_work); 2941ac4b82bSMike Smith TAILQ_INIT(&sc->mlx_freecmds); 29515fd5d22SMike Smith MLX_BIO_QINIT(sc->mlx_bioq); 2961ac4b82bSMike Smith 2971ac4b82bSMike Smith /* 2981ac4b82bSMike Smith * Select accessor methods based on controller interface type. 2991ac4b82bSMike Smith */ 3001ac4b82bSMike Smith switch(sc->mlx_iftype) { 301da8bb3a3SMike Smith case MLX_IFTYPE_2: 3021ac4b82bSMike Smith case MLX_IFTYPE_3: 3031ac4b82bSMike Smith sc->mlx_tryqueue = mlx_v3_tryqueue; 3041ac4b82bSMike Smith sc->mlx_findcomplete = mlx_v3_findcomplete; 3051ac4b82bSMike Smith sc->mlx_intaction = mlx_v3_intaction; 306da8bb3a3SMike Smith sc->mlx_fw_handshake = mlx_v3_fw_handshake; 3071ac4b82bSMike Smith break; 308f6b84b08SMike Smith case MLX_IFTYPE_4: 309f6b84b08SMike Smith sc->mlx_tryqueue = mlx_v4_tryqueue; 310f6b84b08SMike Smith sc->mlx_findcomplete = mlx_v4_findcomplete; 311f6b84b08SMike Smith sc->mlx_intaction = mlx_v4_intaction; 312da8bb3a3SMike Smith sc->mlx_fw_handshake = mlx_v4_fw_handshake; 313f6b84b08SMike Smith break; 3145792b7feSMike Smith case MLX_IFTYPE_5: 3155792b7feSMike Smith sc->mlx_tryqueue = mlx_v5_tryqueue; 3165792b7feSMike Smith sc->mlx_findcomplete = mlx_v5_findcomplete; 3175792b7feSMike Smith sc->mlx_intaction = mlx_v5_intaction; 318da8bb3a3SMike Smith sc->mlx_fw_handshake = mlx_v5_fw_handshake; 3195792b7feSMike Smith break; 3201ac4b82bSMike Smith default: 3215d278f5cSMike Smith mlx_free(sc); 3221ac4b82bSMike Smith return(ENXIO); /* should never happen */ 3231ac4b82bSMike Smith } 3241ac4b82bSMike Smith 3251ac4b82bSMike Smith /* disable interrupts before we start talking to the controller */ 3261ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 3271ac4b82bSMike Smith 3281ac4b82bSMike Smith /* 329da8bb3a3SMike Smith * Wait for the controller to come ready, handshake with the firmware if required. 330da8bb3a3SMike Smith * This is typically only necessary on platforms where the controller BIOS does not 331da8bb3a3SMike Smith * run. 332da8bb3a3SMike Smith */ 333da8bb3a3SMike Smith hsmsg = 0; 334da8bb3a3SMike Smith DELAY(1000); 335da8bb3a3SMike Smith while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) { 336da8bb3a3SMike Smith /* report first time around... */ 337da8bb3a3SMike Smith if (hsmsg == 0) { 338da8bb3a3SMike Smith device_printf(sc->mlx_dev, "controller initialisation in progress...\n"); 339da8bb3a3SMike Smith hsmsg = 1; 340da8bb3a3SMike Smith } 341da8bb3a3SMike Smith /* did we get a real message? */ 342da8bb3a3SMike Smith if (hscode == 2) { 343da8bb3a3SMike Smith hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2); 344da8bb3a3SMike Smith /* fatal initialisation error? */ 345da8bb3a3SMike Smith if (hscode != 0) { 346da8bb3a3SMike Smith mlx_free(sc); 347da8bb3a3SMike Smith return(ENXIO); 348da8bb3a3SMike Smith } 349da8bb3a3SMike Smith } 350da8bb3a3SMike Smith } 351da8bb3a3SMike Smith if (hsmsg == 1) 352da8bb3a3SMike Smith device_printf(sc->mlx_dev, "initialisation complete.\n"); 353da8bb3a3SMike Smith 354da8bb3a3SMike Smith /* 3551ac4b82bSMike Smith * Allocate and connect our interrupt. 3561ac4b82bSMike Smith */ 3571ac4b82bSMike Smith rid = 0; 3581ac4b82bSMike Smith sc->mlx_irq = bus_alloc_resource(sc->mlx_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); 3591ac4b82bSMike Smith if (sc->mlx_irq == NULL) { 360421f2f7dSMike Smith device_printf(sc->mlx_dev, "can't allocate interrupt\n"); 3611ac4b82bSMike Smith mlx_free(sc); 3621ac4b82bSMike Smith return(ENXIO); 3631ac4b82bSMike Smith } 364ed34d0adSMark Murray error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO | INTR_ENTROPY, mlx_intr, sc, &sc->mlx_intr); 3651ac4b82bSMike Smith if (error) { 366421f2f7dSMike Smith device_printf(sc->mlx_dev, "can't set up interrupt\n"); 3671ac4b82bSMike Smith mlx_free(sc); 3681ac4b82bSMike Smith return(ENXIO); 3691ac4b82bSMike Smith } 3701ac4b82bSMike Smith 3711ac4b82bSMike Smith /* 3721ac4b82bSMike Smith * Create DMA tag for mapping buffers into controller-addressable space. 3731ac4b82bSMike Smith */ 3741ac4b82bSMike Smith error = bus_dma_tag_create(sc->mlx_parent_dmat, /* parent */ 3751ac4b82bSMike Smith 1, 0, /* alignment, boundary */ 3761ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* lowaddr */ 3771ac4b82bSMike Smith BUS_SPACE_MAXADDR, /* highaddr */ 3781ac4b82bSMike Smith NULL, NULL, /* filter, filterarg */ 379baff09dbSMike Smith MAXBSIZE, MLX_NSEG, /* maxsize, nsegments */ 3801ac4b82bSMike Smith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 3811ac4b82bSMike Smith 0, /* flags */ 382f6b1c44dSScott Long busdma_lock_mutex, /* lockfunc */ 383f6b1c44dSScott Long &Giant, /* lockarg */ 3841ac4b82bSMike Smith &sc->mlx_buffer_dmat); 3851ac4b82bSMike Smith if (error != 0) { 3861ac4b82bSMike Smith device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n"); 3875d278f5cSMike Smith mlx_free(sc); 3881ac4b82bSMike Smith return(ENOMEM); 3891ac4b82bSMike Smith } 3901ac4b82bSMike Smith 3911ac4b82bSMike Smith /* 392baff09dbSMike Smith * Create some initial scatter/gather mappings so we can run the probe commands. 3931ac4b82bSMike Smith */ 3941ac4b82bSMike Smith error = mlx_sglist_map(sc); 3951ac4b82bSMike Smith if (error != 0) { 396421f2f7dSMike Smith device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n"); 3975d278f5cSMike Smith mlx_free(sc); 3981ac4b82bSMike Smith return(error); 3991ac4b82bSMike Smith } 4001ac4b82bSMike Smith 401baff09dbSMike Smith /* 402baff09dbSMike Smith * We don't (yet) know where the event log is up to. 403baff09dbSMike Smith */ 404baff09dbSMike Smith sc->mlx_currevent = -1; 405baff09dbSMike Smith 406baff09dbSMike Smith /* 407baff09dbSMike Smith * Obtain controller feature information 408baff09dbSMike Smith */ 4099eee27f1SMike Smith if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) { 4101ac4b82bSMike Smith device_printf(sc->mlx_dev, "ENQUIRY2 failed\n"); 4115d278f5cSMike Smith mlx_free(sc); 4121ac4b82bSMike Smith return(ENXIO); 4131ac4b82bSMike Smith } 4141ac4b82bSMike Smith 4159eee27f1SMike Smith /* 4161ac4b82bSMike Smith * Do quirk/feature related things. 4171ac4b82bSMike Smith */ 4189eee27f1SMike Smith fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff; 4191ac4b82bSMike Smith switch(sc->mlx_iftype) { 420da8bb3a3SMike Smith case MLX_IFTYPE_2: 421da8bb3a3SMike Smith /* These controllers don't report the firmware version in the ENQUIRY2 response */ 422da8bb3a3SMike Smith if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) { 423da8bb3a3SMike Smith device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n"); 4245d278f5cSMike Smith mlx_free(sc); 425da8bb3a3SMike Smith return(ENXIO); 426da8bb3a3SMike Smith } 427da8bb3a3SMike Smith sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor; 428da8bb3a3SMike Smith free(meo, M_DEVBUF); 429da8bb3a3SMike Smith 430da8bb3a3SMike Smith /* XXX require 2.42 or better (PCI) or 2.14 or better (EISA) */ 431da8bb3a3SMike Smith if (meo->me_fwminor < 42) { 432da8bb3a3SMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 433da8bb3a3SMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n"); 434da8bb3a3SMike Smith } 435da8bb3a3SMike Smith break; 4361ac4b82bSMike Smith case MLX_IFTYPE_3: 437f6b84b08SMike Smith /* XXX certify 3.52? */ 4389eee27f1SMike Smith if (fwminor < 51) { 4394b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 4404b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n"); 4411ac4b82bSMike Smith } 4421ac4b82bSMike Smith break; 443f6b84b08SMike Smith case MLX_IFTYPE_4: 444f6b84b08SMike Smith /* XXX certify firmware versions? */ 4459eee27f1SMike Smith if (fwminor < 6) { 4464b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 4474b006d7bSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n"); 448f6b84b08SMike Smith } 449f6b84b08SMike Smith break; 4505792b7feSMike Smith case MLX_IFTYPE_5: 4519eee27f1SMike Smith if (fwminor < 7) { 4525792b7feSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n"); 4535792b7feSMike Smith device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n"); 4545792b7feSMike Smith } 4555792b7feSMike Smith break; 4561ac4b82bSMike Smith default: 4575d278f5cSMike Smith mlx_free(sc); 4581ac4b82bSMike Smith return(ENXIO); /* should never happen */ 4591ac4b82bSMike Smith } 4601ac4b82bSMike Smith 4611ac4b82bSMike Smith /* 462baff09dbSMike Smith * Create the final scatter/gather mappings now that we have characterised the controller. 4631ac4b82bSMike Smith */ 4641ac4b82bSMike Smith error = mlx_sglist_map(sc); 4651ac4b82bSMike Smith if (error != 0) { 466baff09dbSMike Smith device_printf(sc->mlx_dev, "can't make final s/g list mapping\n"); 4675d278f5cSMike Smith mlx_free(sc); 4681ac4b82bSMike Smith return(error); 4691ac4b82bSMike Smith } 4701ac4b82bSMike Smith 4711ac4b82bSMike Smith /* 472421f2f7dSMike Smith * No user-requested background operation is in progress. 4731ac4b82bSMike Smith */ 474421f2f7dSMike Smith sc->mlx_background = 0; 475421f2f7dSMike Smith sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE; 4761ac4b82bSMike Smith 4771ac4b82bSMike Smith /* 478da8bb3a3SMike Smith * Create the control device. 4791ac4b82bSMike Smith */ 480da8bb3a3SMike Smith sc->mlx_dev_t = make_dev(&mlx_cdevsw, device_get_unit(sc->mlx_dev), UID_ROOT, GID_OPERATOR, 481da8bb3a3SMike Smith S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev)); 4821ac4b82bSMike Smith 4831ac4b82bSMike Smith /* 4841ac4b82bSMike Smith * Start the timeout routine. 4851ac4b82bSMike Smith */ 4861ac4b82bSMike Smith sc->mlx_timeout = timeout(mlx_periodic, sc, hz); 4871ac4b82bSMike Smith 488da8bb3a3SMike Smith /* print a little information about the controller */ 489da8bb3a3SMike Smith mlx_describe_controller(sc); 490da8bb3a3SMike Smith 4911ac4b82bSMike Smith return(0); 4921ac4b82bSMike Smith } 4931ac4b82bSMike Smith 4941ac4b82bSMike Smith /******************************************************************************** 4951ac4b82bSMike Smith * Locate disk resources and attach children to them. 4961ac4b82bSMike Smith */ 4971ac4b82bSMike Smith void 4981ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc) 4991ac4b82bSMike Smith { 5001ac4b82bSMike Smith struct mlx_enq_sys_drive *mes; 5011ac4b82bSMike Smith struct mlx_sysdrive *dr; 5021ac4b82bSMike Smith int i, error; 5031ac4b82bSMike Smith 504da8bb3a3SMike Smith debug_called(1); 5051ac4b82bSMike Smith 5061ac4b82bSMike Smith /* 5071ac4b82bSMike Smith * Scan all the system drives and attach children for those that 5081ac4b82bSMike Smith * don't currently have them. 5091ac4b82bSMike Smith */ 5101ac4b82bSMike Smith mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL); 5111ac4b82bSMike Smith if (mes == NULL) { 5129eee27f1SMike Smith device_printf(sc->mlx_dev, "error fetching drive status\n"); 5131ac4b82bSMike Smith return; 5141ac4b82bSMike Smith } 5151ac4b82bSMike Smith 5161ac4b82bSMike Smith /* iterate over drives returned */ 5171ac4b82bSMike Smith for (i = 0, dr = &sc->mlx_sysdrive[0]; 5181ac4b82bSMike Smith (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); 5191ac4b82bSMike Smith i++, dr++) { 5201ac4b82bSMike Smith /* are we already attached to this drive? */ 5211ac4b82bSMike Smith if (dr->ms_disk == 0) { 5221ac4b82bSMike Smith /* pick up drive information */ 5231ac4b82bSMike Smith dr->ms_size = mes[i].sd_size; 524f6b84b08SMike Smith dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf; 5251ac4b82bSMike Smith dr->ms_state = mes[i].sd_state; 5261ac4b82bSMike Smith 5271ac4b82bSMike Smith /* generate geometry information */ 5281ac4b82bSMike Smith if (sc->mlx_geom == MLX_GEOM_128_32) { 5291ac4b82bSMike Smith dr->ms_heads = 128; 5301ac4b82bSMike Smith dr->ms_sectors = 32; 5311ac4b82bSMike Smith dr->ms_cylinders = dr->ms_size / (128 * 32); 5321ac4b82bSMike Smith } else { /* MLX_GEOM_255/63 */ 5331ac4b82bSMike Smith dr->ms_heads = 255; 5341ac4b82bSMike Smith dr->ms_sectors = 63; 5351ac4b82bSMike Smith dr->ms_cylinders = dr->ms_size / (255 * 63); 5361ac4b82bSMike Smith } 537fe0d4089SMatthew N. Dodd dr->ms_disk = device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1); 5381ac4b82bSMike Smith if (dr->ms_disk == 0) 5391ac4b82bSMike Smith device_printf(sc->mlx_dev, "device_add_child failed\n"); 540fe0d4089SMatthew N. Dodd device_set_ivars(dr->ms_disk, dr); 5411ac4b82bSMike Smith } 5421ac4b82bSMike Smith } 5431ac4b82bSMike Smith free(mes, M_DEVBUF); 5441ac4b82bSMike Smith if ((error = bus_generic_attach(sc->mlx_dev)) != 0) 5451ac4b82bSMike Smith device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error); 5461ac4b82bSMike Smith 5471ac4b82bSMike Smith /* mark controller back up */ 5481ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_SHUTDOWN; 5491ac4b82bSMike Smith 5501ac4b82bSMike Smith /* enable interrupts */ 5511ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); 5521ac4b82bSMike Smith } 5531ac4b82bSMike Smith 5541ac4b82bSMike Smith /******************************************************************************** 5551ac4b82bSMike Smith * Disconnect from the controller completely, in preparation for unload. 5561ac4b82bSMike Smith */ 5571ac4b82bSMike Smith int 5581ac4b82bSMike Smith mlx_detach(device_t dev) 5591ac4b82bSMike Smith { 5601ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 5615792b7feSMike Smith struct mlxd_softc *mlxd; 5625792b7feSMike Smith int i, s, error; 5631ac4b82bSMike Smith 564da8bb3a3SMike Smith debug_called(1); 5651ac4b82bSMike Smith 5665792b7feSMike Smith error = EBUSY; 5675792b7feSMike Smith s = splbio(); 5681ac4b82bSMike Smith if (sc->mlx_state & MLX_STATE_OPEN) 5695792b7feSMike Smith goto out; 5701ac4b82bSMike Smith 5715792b7feSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 5725792b7feSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 5735792b7feSMike Smith mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk); 5745792b7feSMike Smith if (mlxd->mlxd_flags & MLXD_OPEN) { /* drive is mounted, abort detach */ 5755792b7feSMike Smith device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n"); 5765792b7feSMike Smith goto out; 5775792b7feSMike Smith } 5785792b7feSMike Smith } 5795792b7feSMike Smith } 5801ac4b82bSMike Smith if ((error = mlx_shutdown(dev))) 5815792b7feSMike Smith goto out; 5821ac4b82bSMike Smith 5831ac4b82bSMike Smith mlx_free(sc); 5841ac4b82bSMike Smith 5855792b7feSMike Smith error = 0; 5865792b7feSMike Smith out: 5875792b7feSMike Smith splx(s); 5885792b7feSMike Smith return(error); 5891ac4b82bSMike Smith } 5901ac4b82bSMike Smith 5911ac4b82bSMike Smith /******************************************************************************** 5921ac4b82bSMike Smith * Bring the controller down to a dormant state and detach all child devices. 5931ac4b82bSMike Smith * 5941ac4b82bSMike Smith * This function is called before detach, system shutdown, or before performing 5951ac4b82bSMike Smith * an operation which may add or delete system disks. (Call mlx_startup to 5961ac4b82bSMike Smith * resume normal operation.) 5971ac4b82bSMike Smith * 5988177437dSPoul-Henning Kamp * Note that we can assume that the bioq on the controller is empty, as we won't 5991ac4b82bSMike Smith * allow shutdown if any device is open. 6001ac4b82bSMike Smith */ 6011ac4b82bSMike Smith int 6021ac4b82bSMike Smith mlx_shutdown(device_t dev) 6031ac4b82bSMike Smith { 6041ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 6051ac4b82bSMike Smith int i, s, error; 6061ac4b82bSMike Smith 607da8bb3a3SMike Smith debug_called(1); 6081ac4b82bSMike Smith 6091ac4b82bSMike Smith s = splbio(); 6101ac4b82bSMike Smith error = 0; 6111ac4b82bSMike Smith 6121ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_SHUTDOWN; 6135792b7feSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 6141ac4b82bSMike Smith 6151ac4b82bSMike Smith /* flush controller */ 6161ac4b82bSMike Smith device_printf(sc->mlx_dev, "flushing cache..."); 6171ac4b82bSMike Smith if (mlx_flush(sc)) { 6181ac4b82bSMike Smith printf("failed\n"); 6191ac4b82bSMike Smith } else { 6201ac4b82bSMike Smith printf("done\n"); 6211ac4b82bSMike Smith } 6221ac4b82bSMike Smith 6231ac4b82bSMike Smith /* delete all our child devices */ 6241ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 6251ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 6261ac4b82bSMike Smith if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0) 6271ac4b82bSMike Smith goto out; 6281ac4b82bSMike Smith sc->mlx_sysdrive[i].ms_disk = 0; 6291ac4b82bSMike Smith } 6301ac4b82bSMike Smith } 6311ac4b82bSMike Smith 6321ac4b82bSMike Smith out: 6331ac4b82bSMike Smith splx(s); 6341ac4b82bSMike Smith return(error); 6351ac4b82bSMike Smith } 6361ac4b82bSMike Smith 6371ac4b82bSMike Smith /******************************************************************************** 6381ac4b82bSMike Smith * Bring the controller to a quiescent state, ready for system suspend. 6391ac4b82bSMike Smith */ 6401ac4b82bSMike Smith int 6411ac4b82bSMike Smith mlx_suspend(device_t dev) 6421ac4b82bSMike Smith { 6431ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 6441ac4b82bSMike Smith int s; 6451ac4b82bSMike Smith 646da8bb3a3SMike Smith debug_called(1); 6471ac4b82bSMike Smith 6481ac4b82bSMike Smith s = splbio(); 6491ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_SUSPEND; 6501ac4b82bSMike Smith 6511ac4b82bSMike Smith /* flush controller */ 6521ac4b82bSMike Smith device_printf(sc->mlx_dev, "flushing cache..."); 6531ac4b82bSMike Smith printf("%s\n", mlx_flush(sc) ? "failed" : "done"); 6541ac4b82bSMike Smith 6551ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); 6561ac4b82bSMike Smith splx(s); 6571ac4b82bSMike Smith 6581ac4b82bSMike Smith return(0); 6591ac4b82bSMike Smith } 6601ac4b82bSMike Smith 6611ac4b82bSMike Smith /******************************************************************************** 6621ac4b82bSMike Smith * Bring the controller back to a state ready for operation. 6631ac4b82bSMike Smith */ 6641ac4b82bSMike Smith int 6651ac4b82bSMike Smith mlx_resume(device_t dev) 6661ac4b82bSMike Smith { 6671ac4b82bSMike Smith struct mlx_softc *sc = device_get_softc(dev); 6681ac4b82bSMike Smith 669da8bb3a3SMike Smith debug_called(1); 6701ac4b82bSMike Smith 6711ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_SUSPEND; 6721ac4b82bSMike Smith sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); 6731ac4b82bSMike Smith 6741ac4b82bSMike Smith return(0); 6751ac4b82bSMike Smith } 6761ac4b82bSMike Smith 6771ac4b82bSMike Smith /******************************************************************************* 6781ac4b82bSMike Smith * Take an interrupt, or be poked by other code to look for interrupt-worthy 6791ac4b82bSMike Smith * status. 6801ac4b82bSMike Smith */ 6811ac4b82bSMike Smith void 6821ac4b82bSMike Smith mlx_intr(void *arg) 6831ac4b82bSMike Smith { 6841ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)arg; 6851ac4b82bSMike Smith 686da8bb3a3SMike Smith debug_called(1); 6871ac4b82bSMike Smith 6885792b7feSMike Smith /* collect finished commands, queue anything waiting */ 6895792b7feSMike Smith mlx_done(sc); 6901ac4b82bSMike Smith }; 6911ac4b82bSMike Smith 6921ac4b82bSMike Smith /******************************************************************************* 6931ac4b82bSMike Smith * Receive a buf structure from a child device and queue it on a particular 6941ac4b82bSMike Smith * disk resource, then poke the disk resource to start as much work as it can. 6951ac4b82bSMike Smith */ 6961ac4b82bSMike Smith int 69715fd5d22SMike Smith mlx_submit_buf(struct mlx_softc *sc, mlx_bio *bp) 6981ac4b82bSMike Smith { 6994b006d7bSMike Smith int s; 7004b006d7bSMike Smith 701da8bb3a3SMike Smith debug_called(1); 7021ac4b82bSMike Smith 7034b006d7bSMike Smith s = splbio(); 70415fd5d22SMike Smith MLX_BIO_QINSERT(sc->mlx_bioq, bp); 7051ac4b82bSMike Smith sc->mlx_waitbufs++; 7064b006d7bSMike Smith splx(s); 7071ac4b82bSMike Smith mlx_startio(sc); 7081ac4b82bSMike Smith return(0); 7091ac4b82bSMike Smith } 7101ac4b82bSMike Smith 7111ac4b82bSMike Smith /******************************************************************************** 7121ac4b82bSMike Smith * Accept an open operation on the control device. 7131ac4b82bSMike Smith */ 7141ac4b82bSMike Smith int 715b40ce416SJulian Elischer mlx_open(dev_t dev, int flags, int fmt, struct thread *td) 7161ac4b82bSMike Smith { 7171ac4b82bSMike Smith int unit = minor(dev); 7181ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 7191ac4b82bSMike Smith 7201ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_OPEN; 7211ac4b82bSMike Smith return(0); 7221ac4b82bSMike Smith } 7231ac4b82bSMike Smith 7241ac4b82bSMike Smith /******************************************************************************** 7251ac4b82bSMike Smith * Accept the last close on the control device. 7261ac4b82bSMike Smith */ 7271ac4b82bSMike Smith int 728b40ce416SJulian Elischer mlx_close(dev_t dev, int flags, int fmt, struct thread *td) 7291ac4b82bSMike Smith { 7301ac4b82bSMike Smith int unit = minor(dev); 7311ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 7321ac4b82bSMike Smith 7331ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_OPEN; 7341ac4b82bSMike Smith return (0); 7351ac4b82bSMike Smith } 7361ac4b82bSMike Smith 7371ac4b82bSMike Smith /******************************************************************************** 7381ac4b82bSMike Smith * Handle controller-specific control operations. 7391ac4b82bSMike Smith */ 7401ac4b82bSMike Smith int 741b40ce416SJulian Elischer mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td) 7421ac4b82bSMike Smith { 7431ac4b82bSMike Smith int unit = minor(dev); 7441ac4b82bSMike Smith struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit); 745421f2f7dSMike Smith struct mlx_rebuild_request *rb = (struct mlx_rebuild_request *)addr; 746421f2f7dSMike Smith struct mlx_rebuild_status *rs = (struct mlx_rebuild_status *)addr; 7471ac4b82bSMike Smith int *arg = (int *)addr; 7481ac4b82bSMike Smith struct mlx_pause *mp; 7491ac4b82bSMike Smith struct mlx_sysdrive *dr; 7501ac4b82bSMike Smith struct mlxd_softc *mlxd; 7511ac4b82bSMike Smith int i, error; 7521ac4b82bSMike Smith 7531ac4b82bSMike Smith switch(cmd) { 7541ac4b82bSMike Smith /* 7551ac4b82bSMike Smith * Enumerate connected system drives; returns the first system drive's 7561ac4b82bSMike Smith * unit number if *arg is -1, or the next unit after *arg if it's 7571ac4b82bSMike Smith * a valid unit on this controller. 7581ac4b82bSMike Smith */ 7591ac4b82bSMike Smith case MLX_NEXT_CHILD: 7601ac4b82bSMike Smith /* search system drives */ 7611ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 7621ac4b82bSMike Smith /* is this one attached? */ 7631ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 7641ac4b82bSMike Smith /* looking for the next one we come across? */ 7651ac4b82bSMike Smith if (*arg == -1) { 766421f2f7dSMike Smith *arg = device_get_unit(sc->mlx_sysdrive[0].ms_disk); 7671ac4b82bSMike Smith return(0); 7681ac4b82bSMike Smith } 7691ac4b82bSMike Smith /* we want the one after this one */ 7701ac4b82bSMike Smith if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) 7711ac4b82bSMike Smith *arg = -1; 7721ac4b82bSMike Smith } 7731ac4b82bSMike Smith } 7741ac4b82bSMike Smith return(ENOENT); 7751ac4b82bSMike Smith 7761ac4b82bSMike Smith /* 7771ac4b82bSMike Smith * Scan the controller to see whether new drives have appeared. 7781ac4b82bSMike Smith */ 7791ac4b82bSMike Smith case MLX_RESCAN_DRIVES: 7801ac4b82bSMike Smith mlx_startup(sc); 7811ac4b82bSMike Smith return(0); 7821ac4b82bSMike Smith 7831ac4b82bSMike Smith /* 7841ac4b82bSMike Smith * Disconnect from the specified drive; it may be about to go 7851ac4b82bSMike Smith * away. 7861ac4b82bSMike Smith */ 7871ac4b82bSMike Smith case MLX_DETACH_DRIVE: /* detach one drive */ 7881ac4b82bSMike Smith 7891ac4b82bSMike Smith if (((dr = mlx_findunit(sc, *arg)) == NULL) || 7901ac4b82bSMike Smith ((mlxd = device_get_softc(dr->ms_disk)) == NULL)) 7911ac4b82bSMike Smith return(ENOENT); 7921ac4b82bSMike Smith 7931ac4b82bSMike Smith device_printf(dr->ms_disk, "detaching..."); 7941ac4b82bSMike Smith error = 0; 7951ac4b82bSMike Smith if (mlxd->mlxd_flags & MLXD_OPEN) { 7961ac4b82bSMike Smith error = EBUSY; 7971ac4b82bSMike Smith goto detach_out; 7981ac4b82bSMike Smith } 7991ac4b82bSMike Smith 8001ac4b82bSMike Smith /* flush controller */ 8011ac4b82bSMike Smith if (mlx_flush(sc)) { 8021ac4b82bSMike Smith error = EBUSY; 8031ac4b82bSMike Smith goto detach_out; 8041ac4b82bSMike Smith } 8051ac4b82bSMike Smith 8061ac4b82bSMike Smith /* nuke drive */ 8071ac4b82bSMike Smith if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0) 8081ac4b82bSMike Smith goto detach_out; 8091ac4b82bSMike Smith dr->ms_disk = 0; 8101ac4b82bSMike Smith 8111ac4b82bSMike Smith detach_out: 8121ac4b82bSMike Smith if (error) { 8131ac4b82bSMike Smith printf("failed\n"); 8141ac4b82bSMike Smith } else { 8151ac4b82bSMike Smith printf("done\n"); 8161ac4b82bSMike Smith } 8171ac4b82bSMike Smith return(error); 8181ac4b82bSMike Smith 8191ac4b82bSMike Smith /* 8201ac4b82bSMike Smith * Pause one or more SCSI channels for a period of time, to assist 8211ac4b82bSMike Smith * in the process of hot-swapping devices. 8221ac4b82bSMike Smith * 8231ac4b82bSMike Smith * Note that at least the 3.51 firmware on the DAC960PL doesn't seem 8241ac4b82bSMike Smith * to do this right. 8251ac4b82bSMike Smith */ 8261ac4b82bSMike Smith case MLX_PAUSE_CHANNEL: /* schedule a channel pause */ 8271ac4b82bSMike Smith /* Does this command work on this firmware? */ 8281ac4b82bSMike Smith if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS)) 8291ac4b82bSMike Smith return(EOPNOTSUPP); 8301ac4b82bSMike Smith 8311ac4b82bSMike Smith mp = (struct mlx_pause *)addr; 8321ac4b82bSMike Smith if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) { 8331ac4b82bSMike Smith /* cancel a pending pause operation */ 8341ac4b82bSMike Smith sc->mlx_pause.mp_which = 0; 8351ac4b82bSMike Smith } else { 8361ac4b82bSMike Smith /* fix for legal channels */ 8379eee27f1SMike Smith mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1); 8381ac4b82bSMike Smith /* check time values */ 8391ac4b82bSMike Smith if ((mp->mp_when < 0) || (mp->mp_when > 3600)) 8401ac4b82bSMike Smith return(EINVAL); 8411ac4b82bSMike Smith if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30))) 8421ac4b82bSMike Smith return(EINVAL); 8431ac4b82bSMike Smith 8441ac4b82bSMike Smith /* check for a pause currently running */ 8451ac4b82bSMike Smith if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) 8461ac4b82bSMike Smith return(EBUSY); 8471ac4b82bSMike Smith 8481ac4b82bSMike Smith /* looks ok, go with it */ 8491ac4b82bSMike Smith sc->mlx_pause.mp_which = mp->mp_which; 8501ac4b82bSMike Smith sc->mlx_pause.mp_when = time_second + mp->mp_when; 8511ac4b82bSMike Smith sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong; 8521ac4b82bSMike Smith } 8531ac4b82bSMike Smith return(0); 8541ac4b82bSMike Smith 8551ac4b82bSMike Smith /* 8561ac4b82bSMike Smith * Accept a command passthrough-style. 8571ac4b82bSMike Smith */ 8581ac4b82bSMike Smith case MLX_COMMAND: 8591ac4b82bSMike Smith return(mlx_user_command(sc, (struct mlx_usercommand *)addr)); 8601ac4b82bSMike Smith 861421f2f7dSMike Smith /* 862421f2f7dSMike Smith * Start a rebuild on a given SCSI disk 863421f2f7dSMike Smith */ 864421f2f7dSMike Smith case MLX_REBUILDASYNC: 865421f2f7dSMike Smith if (sc->mlx_background != 0) { 866421f2f7dSMike Smith rb->rr_status = 0x0106; 867421f2f7dSMike Smith return(EBUSY); 868421f2f7dSMike Smith } 869421f2f7dSMike Smith rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target); 870421f2f7dSMike Smith switch (rb->rr_status) { 871421f2f7dSMike Smith case 0: 872421f2f7dSMike Smith error = 0; 873421f2f7dSMike Smith break; 874421f2f7dSMike Smith case 0x10000: 875421f2f7dSMike Smith error = ENOMEM; /* couldn't set up the command */ 876421f2f7dSMike Smith break; 877421f2f7dSMike Smith case 0x0002: 878421f2f7dSMike Smith error = EBUSY; 879421f2f7dSMike Smith break; 880421f2f7dSMike Smith case 0x0104: 881421f2f7dSMike Smith error = EIO; 882421f2f7dSMike Smith break; 883421f2f7dSMike Smith case 0x0105: 884421f2f7dSMike Smith error = ERANGE; 885421f2f7dSMike Smith break; 886421f2f7dSMike Smith case 0x0106: 887421f2f7dSMike Smith error = EBUSY; 888421f2f7dSMike Smith break; 889421f2f7dSMike Smith default: 890421f2f7dSMike Smith error = EINVAL; 891421f2f7dSMike Smith break; 892421f2f7dSMike Smith } 893421f2f7dSMike Smith if (error == 0) 894421f2f7dSMike Smith sc->mlx_background = MLX_BACKGROUND_REBUILD; 895421f2f7dSMike Smith return(error); 896421f2f7dSMike Smith 897421f2f7dSMike Smith /* 898421f2f7dSMike Smith * Get the status of the current rebuild or consistency check. 899421f2f7dSMike Smith */ 900421f2f7dSMike Smith case MLX_REBUILDSTAT: 901421f2f7dSMike Smith *rs = sc->mlx_rebuildstat; 902421f2f7dSMike Smith return(0); 903421f2f7dSMike Smith 904421f2f7dSMike Smith /* 905421f2f7dSMike Smith * Return the per-controller system drive number matching the 906421f2f7dSMike Smith * disk device number in (arg), if it happens to belong to us. 907421f2f7dSMike Smith */ 908421f2f7dSMike Smith case MLX_GET_SYSDRIVE: 909421f2f7dSMike Smith error = ENOENT; 910421f2f7dSMike Smith mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg); 911421f2f7dSMike Smith if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) && 912421f2f7dSMike Smith (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) { 913421f2f7dSMike Smith error = 0; 914421f2f7dSMike Smith *arg = mlxd->mlxd_drive - sc->mlx_sysdrive; 915421f2f7dSMike Smith } 916421f2f7dSMike Smith return(error); 917421f2f7dSMike Smith 9181ac4b82bSMike Smith default: 9191ac4b82bSMike Smith return(ENOTTY); 9201ac4b82bSMike Smith } 9211ac4b82bSMike Smith } 9221ac4b82bSMike Smith 9231ac4b82bSMike Smith /******************************************************************************** 9241ac4b82bSMike Smith * Handle operations requested by a System Drive connected to this controller. 9251ac4b82bSMike Smith */ 9261ac4b82bSMike Smith int 9271ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd, 928b40ce416SJulian Elischer caddr_t addr, int32_t flag, struct thread *td) 9291ac4b82bSMike Smith { 9301ac4b82bSMike Smith int *arg = (int *)addr; 931421f2f7dSMike Smith int error, result; 9321ac4b82bSMike Smith 9331ac4b82bSMike Smith switch(cmd) { 9341ac4b82bSMike Smith /* 9351ac4b82bSMike Smith * Return the current status of this drive. 9361ac4b82bSMike Smith */ 9371ac4b82bSMike Smith case MLXD_STATUS: 9381ac4b82bSMike Smith *arg = drive->ms_state; 9391ac4b82bSMike Smith return(0); 9401ac4b82bSMike Smith 9411ac4b82bSMike Smith /* 942421f2f7dSMike Smith * Start a background consistency check on this drive. 9431ac4b82bSMike Smith */ 944421f2f7dSMike Smith case MLXD_CHECKASYNC: /* start a background consistency check */ 945421f2f7dSMike Smith if (sc->mlx_background != 0) { 946421f2f7dSMike Smith *arg = 0x0106; 9471ac4b82bSMike Smith return(EBUSY); 948421f2f7dSMike Smith } 949421f2f7dSMike Smith result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]); 950421f2f7dSMike Smith switch (result) { 9511ac4b82bSMike Smith case 0: 9521ac4b82bSMike Smith error = 0; 9531ac4b82bSMike Smith break; 9541ac4b82bSMike Smith case 0x10000: 9551ac4b82bSMike Smith error = ENOMEM; /* couldn't set up the command */ 9561ac4b82bSMike Smith break; 9571ac4b82bSMike Smith case 0x0002: 9581ac4b82bSMike Smith error = EIO; 9591ac4b82bSMike Smith break; 9601ac4b82bSMike Smith case 0x0105: 9611ac4b82bSMike Smith error = ERANGE; 9621ac4b82bSMike Smith break; 963421f2f7dSMike Smith case 0x0106: 964421f2f7dSMike Smith error = EBUSY; 965421f2f7dSMike Smith break; 9661ac4b82bSMike Smith default: 9671ac4b82bSMike Smith error = EINVAL; 9681ac4b82bSMike Smith break; 9691ac4b82bSMike Smith } 970421f2f7dSMike Smith if (error == 0) 971421f2f7dSMike Smith sc->mlx_background = MLX_BACKGROUND_CHECK; 972421f2f7dSMike Smith *arg = result; 9731ac4b82bSMike Smith return(error); 9741ac4b82bSMike Smith 9751ac4b82bSMike Smith } 9761ac4b82bSMike Smith return(ENOIOCTL); 9771ac4b82bSMike Smith } 9781ac4b82bSMike Smith 9791ac4b82bSMike Smith 9801ac4b82bSMike Smith /******************************************************************************** 9811ac4b82bSMike Smith ******************************************************************************** 9821ac4b82bSMike Smith Status Monitoring 9831ac4b82bSMike Smith ******************************************************************************** 9841ac4b82bSMike Smith ********************************************************************************/ 9851ac4b82bSMike Smith 9861ac4b82bSMike Smith /******************************************************************************** 9871ac4b82bSMike Smith * Fire off commands to periodically check the status of connected drives. 9881ac4b82bSMike Smith */ 9891ac4b82bSMike Smith static void 9901ac4b82bSMike Smith mlx_periodic(void *data) 9911ac4b82bSMike Smith { 9921ac4b82bSMike Smith struct mlx_softc *sc = (struct mlx_softc *)data; 9931ac4b82bSMike Smith 994da8bb3a3SMike Smith debug_called(1); 9951ac4b82bSMike Smith 9961ac4b82bSMike Smith /* 9971ac4b82bSMike Smith * Run a bus pause? 9981ac4b82bSMike Smith */ 9991ac4b82bSMike Smith if ((sc->mlx_pause.mp_which != 0) && 10001ac4b82bSMike Smith (sc->mlx_pause.mp_when > 0) && 10011ac4b82bSMike Smith (time_second >= sc->mlx_pause.mp_when)){ 10021ac4b82bSMike Smith 10031ac4b82bSMike Smith mlx_pause_action(sc); /* pause is running */ 10041ac4b82bSMike Smith sc->mlx_pause.mp_when = 0; 10051ac4b82bSMike Smith sysbeep(500, hz); 10061ac4b82bSMike Smith 10071ac4b82bSMike Smith /* 10081ac4b82bSMike Smith * Bus pause still running? 10091ac4b82bSMike Smith */ 10101ac4b82bSMike Smith } else if ((sc->mlx_pause.mp_which != 0) && 10111ac4b82bSMike Smith (sc->mlx_pause.mp_when == 0)) { 10121ac4b82bSMike Smith 10131ac4b82bSMike Smith /* time to stop bus pause? */ 10141ac4b82bSMike Smith if (time_second >= sc->mlx_pause.mp_howlong) { 10151ac4b82bSMike Smith mlx_pause_action(sc); 10161ac4b82bSMike Smith sc->mlx_pause.mp_which = 0; /* pause is complete */ 10171ac4b82bSMike Smith sysbeep(500, hz); 10181ac4b82bSMike Smith } else { 10191ac4b82bSMike Smith sysbeep((time_second % 5) * 100 + 500, hz/8); 10201ac4b82bSMike Smith } 10211ac4b82bSMike Smith 10221ac4b82bSMike Smith /* 10231ac4b82bSMike Smith * Run normal periodic activities? 10241ac4b82bSMike Smith */ 10255792b7feSMike Smith } else if (time_second > (sc->mlx_lastpoll + 10)) { 10261ac4b82bSMike Smith sc->mlx_lastpoll = time_second; 10271ac4b82bSMike Smith 10281ac4b82bSMike Smith /* 10291ac4b82bSMike Smith * Check controller status. 10305792b7feSMike Smith * 10315792b7feSMike Smith * XXX Note that this may not actually launch a command in situations of high load. 10321ac4b82bSMike Smith */ 1033da8bb3a3SMike Smith mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY, 1034da8bb3a3SMike Smith imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry); 10351ac4b82bSMike Smith 10361ac4b82bSMike Smith /* 10371ac4b82bSMike Smith * Check system drive status. 10381ac4b82bSMike Smith * 10391ac4b82bSMike Smith * XXX This might be better left to event-driven detection, eg. I/O to an offline 10401ac4b82bSMike Smith * drive will detect it's offline, rebuilds etc. should detect the drive is back 10411ac4b82bSMike Smith * online. 10421ac4b82bSMike Smith */ 10431ac4b82bSMike Smith mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES, 10441ac4b82bSMike Smith mlx_periodic_enquiry); 10451ac4b82bSMike Smith 10461ac4b82bSMike Smith } 10471ac4b82bSMike Smith 1048421f2f7dSMike Smith /* get drive rebuild/check status */ 1049421f2f7dSMike Smith /* XXX should check sc->mlx_background if this is only valid while in progress */ 1050421f2f7dSMike Smith mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild); 1051421f2f7dSMike Smith 10525792b7feSMike Smith /* deal with possibly-missed interrupts and timed-out commands */ 10535792b7feSMike Smith mlx_done(sc); 10541ac4b82bSMike Smith 10551ac4b82bSMike Smith /* reschedule another poll next second or so */ 10561ac4b82bSMike Smith sc->mlx_timeout = timeout(mlx_periodic, sc, hz); 10571ac4b82bSMike Smith } 10581ac4b82bSMike Smith 10591ac4b82bSMike Smith /******************************************************************************** 10601ac4b82bSMike Smith * Handle the result of an ENQUIRY command instigated by periodic status polling. 10611ac4b82bSMike Smith */ 10621ac4b82bSMike Smith static void 10631ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc) 10641ac4b82bSMike Smith { 10651ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 10661ac4b82bSMike Smith 1067da8bb3a3SMike Smith debug_called(1); 10681ac4b82bSMike Smith 10691ac4b82bSMike Smith /* Command completed OK? */ 10701ac4b82bSMike Smith if (mc->mc_status != 0) { 1071da8bb3a3SMike Smith device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc)); 10721ac4b82bSMike Smith goto out; 10731ac4b82bSMike Smith } 10741ac4b82bSMike Smith 10751ac4b82bSMike Smith /* respond to command */ 10761ac4b82bSMike Smith switch(mc->mc_mailbox[0]) { 10771ac4b82bSMike Smith /* 1078da8bb3a3SMike Smith * This is currently a bit fruitless, as we don't know how to extract the eventlog 1079da8bb3a3SMike Smith * pointer yet. 1080da8bb3a3SMike Smith */ 1081da8bb3a3SMike Smith case MLX_CMD_ENQUIRY_OLD: 1082da8bb3a3SMike Smith { 1083da8bb3a3SMike Smith struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data; 1084da8bb3a3SMike Smith struct mlx_enquiry_old *meo = (struct mlx_enquiry_old *)mc->mc_data; 1085da8bb3a3SMike Smith int i; 1086da8bb3a3SMike Smith 1087da8bb3a3SMike Smith /* convert data in-place to new format */ 1088da8bb3a3SMike Smith for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) { 1089da8bb3a3SMike Smith me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan; 1090da8bb3a3SMike Smith me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ; 1091da8bb3a3SMike Smith } 1092da8bb3a3SMike Smith me->me_misc_flags = 0; 1093da8bb3a3SMike Smith me->me_rebuild_count = meo->me_rebuild_count; 1094da8bb3a3SMike Smith me->me_dead_count = meo->me_dead_count; 1095da8bb3a3SMike Smith me->me_critical_sd_count = meo->me_critical_sd_count; 1096da8bb3a3SMike Smith me->me_event_log_seq_num = 0; 1097da8bb3a3SMike Smith me->me_offline_sd_count = meo->me_offline_sd_count; 1098da8bb3a3SMike Smith me->me_max_commands = meo->me_max_commands; 1099da8bb3a3SMike Smith me->me_rebuild_flag = meo->me_rebuild_flag; 1100da8bb3a3SMike Smith me->me_fwmajor = meo->me_fwmajor; 1101da8bb3a3SMike Smith me->me_fwminor = meo->me_fwminor; 1102da8bb3a3SMike Smith me->me_status_flags = meo->me_status_flags; 1103da8bb3a3SMike Smith me->me_flash_age = meo->me_flash_age; 1104da8bb3a3SMike Smith for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) { 1105da8bb3a3SMike Smith if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) { 1106da8bb3a3SMike Smith me->me_drvsize[i] = 0; /* drive beyond supported range */ 1107da8bb3a3SMike Smith } else { 1108da8bb3a3SMike Smith me->me_drvsize[i] = meo->me_drvsize[i]; 1109da8bb3a3SMike Smith } 1110da8bb3a3SMike Smith } 1111da8bb3a3SMike Smith me->me_num_sys_drvs = meo->me_num_sys_drvs; 1112da8bb3a3SMike Smith } 1113da8bb3a3SMike Smith /* FALLTHROUGH */ 1114da8bb3a3SMike Smith 1115da8bb3a3SMike Smith /* 11161ac4b82bSMike Smith * Generic controller status update. We could do more with this than just 11171ac4b82bSMike Smith * checking the event log. 11181ac4b82bSMike Smith */ 11191ac4b82bSMike Smith case MLX_CMD_ENQUIRY: 11201ac4b82bSMike Smith { 11211ac4b82bSMike Smith struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data; 11221ac4b82bSMike Smith 1123421f2f7dSMike Smith if (sc->mlx_currevent == -1) { 11249eee27f1SMike Smith /* initialise our view of the event log */ 11259eee27f1SMike Smith sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num; 11265d278f5cSMike Smith } else if ((me->me_event_log_seq_num != sc->mlx_lastevent) && !(sc->mlx_flags & MLX_EVENTLOG_BUSY)) { 11271ac4b82bSMike Smith /* record where current events are up to */ 11281ac4b82bSMike Smith sc->mlx_currevent = me->me_event_log_seq_num; 1129da8bb3a3SMike Smith debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent); 11301ac4b82bSMike Smith 11315d278f5cSMike Smith /* mark the event log as busy */ 11325d278f5cSMike Smith atomic_set_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY); 11335d278f5cSMike Smith 11349eee27f1SMike Smith /* drain new eventlog entries */ 11351ac4b82bSMike Smith mlx_periodic_eventlog_poll(sc); 11361ac4b82bSMike Smith } 11371ac4b82bSMike Smith break; 11381ac4b82bSMike Smith } 11391ac4b82bSMike Smith case MLX_CMD_ENQSYSDRIVE: 11401ac4b82bSMike Smith { 11411ac4b82bSMike Smith struct mlx_enq_sys_drive *mes = (struct mlx_enq_sys_drive *)mc->mc_data; 11421ac4b82bSMike Smith struct mlx_sysdrive *dr; 11431ac4b82bSMike Smith int i; 11441ac4b82bSMike Smith 11451ac4b82bSMike Smith for (i = 0, dr = &sc->mlx_sysdrive[0]; 11461ac4b82bSMike Smith (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); 11471ac4b82bSMike Smith i++) { 11481ac4b82bSMike Smith 11491ac4b82bSMike Smith /* has state been changed by controller? */ 11501ac4b82bSMike Smith if (dr->ms_state != mes[i].sd_state) { 11511ac4b82bSMike Smith switch(mes[i].sd_state) { 11521ac4b82bSMike Smith case MLX_SYSD_OFFLINE: 11531ac4b82bSMike Smith device_printf(dr->ms_disk, "drive offline\n"); 11541ac4b82bSMike Smith break; 11551ac4b82bSMike Smith case MLX_SYSD_ONLINE: 11561ac4b82bSMike Smith device_printf(dr->ms_disk, "drive online\n"); 11571ac4b82bSMike Smith break; 11581ac4b82bSMike Smith case MLX_SYSD_CRITICAL: 11591ac4b82bSMike Smith device_printf(dr->ms_disk, "drive critical\n"); 11601ac4b82bSMike Smith break; 11611ac4b82bSMike Smith } 11621ac4b82bSMike Smith /* save new state */ 11631ac4b82bSMike Smith dr->ms_state = mes[i].sd_state; 11641ac4b82bSMike Smith } 11651ac4b82bSMike Smith } 11661ac4b82bSMike Smith break; 11671ac4b82bSMike Smith } 11681ac4b82bSMike Smith default: 11696e551fb6SDavid E. O'Brien device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __func__, mc->mc_mailbox[0]); 11701ac4b82bSMike Smith break; 11711ac4b82bSMike Smith } 11721ac4b82bSMike Smith 11731ac4b82bSMike Smith out: 11741ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 11751ac4b82bSMike Smith mlx_releasecmd(mc); 11761ac4b82bSMike Smith } 11771ac4b82bSMike Smith 11781ac4b82bSMike Smith /******************************************************************************** 11791ac4b82bSMike Smith * Instigate a poll for one event log message on (sc). 11801ac4b82bSMike Smith * We only poll for one message at a time, to keep our command usage down. 11811ac4b82bSMike Smith */ 11821ac4b82bSMike Smith static void 11831ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc) 11841ac4b82bSMike Smith { 11851ac4b82bSMike Smith struct mlx_command *mc; 11861ac4b82bSMike Smith void *result = NULL; 11871ac4b82bSMike Smith int error; 11881ac4b82bSMike Smith 1189da8bb3a3SMike Smith debug_called(1); 11901ac4b82bSMike Smith 11911ac4b82bSMike Smith /* get ourselves a command buffer */ 11921ac4b82bSMike Smith error = 1; 11931ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 11941ac4b82bSMike Smith goto out; 11951ac4b82bSMike Smith /* allocate the response structure */ 119633c8cb18SMike Smith if ((result = malloc(/*sizeof(struct mlx_eventlog_entry)*/1024, M_DEVBUF, M_NOWAIT)) == NULL) 11971ac4b82bSMike Smith goto out; 11981ac4b82bSMike Smith /* get a command slot */ 11991ac4b82bSMike Smith if (mlx_getslot(mc)) 12001ac4b82bSMike Smith goto out; 12011ac4b82bSMike Smith 12021ac4b82bSMike Smith /* map the command so the controller can see it */ 12031ac4b82bSMike Smith mc->mc_data = result; 120433c8cb18SMike Smith mc->mc_length = /*sizeof(struct mlx_eventlog_entry)*/1024; 12051ac4b82bSMike Smith mlx_mapcmd(mc); 12061ac4b82bSMike Smith 12071ac4b82bSMike Smith /* build the command to get one entry */ 12081ac4b82bSMike Smith mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0); 12091ac4b82bSMike Smith mc->mc_complete = mlx_periodic_eventlog_respond; 12101ac4b82bSMike Smith mc->mc_private = mc; 12111ac4b82bSMike Smith 12121ac4b82bSMike Smith /* start the command */ 12131ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 12141ac4b82bSMike Smith goto out; 12151ac4b82bSMike Smith 12161ac4b82bSMike Smith error = 0; /* success */ 12171ac4b82bSMike Smith out: 121833c8cb18SMike Smith if (error != 0) { 12191ac4b82bSMike Smith if (mc != NULL) 12201ac4b82bSMike Smith mlx_releasecmd(mc); 122133c8cb18SMike Smith if (result != NULL) 12221ac4b82bSMike Smith free(result, M_DEVBUF); 12231ac4b82bSMike Smith } 122433c8cb18SMike Smith } 12251ac4b82bSMike Smith 12261ac4b82bSMike Smith /******************************************************************************** 12271ac4b82bSMike Smith * Handle the result of polling for a log message, generate diagnostic output. 12281ac4b82bSMike Smith * If this wasn't the last message waiting for us, we'll go collect another. 12291ac4b82bSMike Smith */ 12301ac4b82bSMike Smith static char *mlx_sense_messages[] = { 12311ac4b82bSMike Smith "because write recovery failed", 12321ac4b82bSMike Smith "because of SCSI bus reset failure", 12331ac4b82bSMike Smith "because of double check condition", 12341ac4b82bSMike Smith "because it was removed", 12351ac4b82bSMike Smith "because of gross error on SCSI chip", 12361ac4b82bSMike Smith "because of bad tag returned from drive", 12371ac4b82bSMike Smith "because of timeout on SCSI command", 12381ac4b82bSMike Smith "because of reset SCSI command issued from system", 12391ac4b82bSMike Smith "because busy or parity error count exceeded limit", 12401ac4b82bSMike Smith "because of 'kill drive' command from system", 12411ac4b82bSMike Smith "because of selection timeout", 12421ac4b82bSMike Smith "due to SCSI phase sequence error", 12431ac4b82bSMike Smith "due to unknown status" 12441ac4b82bSMike Smith }; 12451ac4b82bSMike Smith 12461ac4b82bSMike Smith static void 12471ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc) 12481ac4b82bSMike Smith { 12491ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 12501ac4b82bSMike Smith struct mlx_eventlog_entry *el = (struct mlx_eventlog_entry *)mc->mc_data; 12511ac4b82bSMike Smith char *reason; 12521ac4b82bSMike Smith 1253da8bb3a3SMike Smith debug_called(1); 12541ac4b82bSMike Smith 12555792b7feSMike Smith sc->mlx_lastevent++; /* next message... */ 12561ac4b82bSMike Smith if (mc->mc_status == 0) { 12571ac4b82bSMike Smith 12581ac4b82bSMike Smith /* handle event log message */ 12591ac4b82bSMike Smith switch(el->el_type) { 12601ac4b82bSMike Smith /* 12611ac4b82bSMike Smith * This is the only sort of message we understand at the moment. 12621ac4b82bSMike Smith * The tests here are probably incomplete. 12631ac4b82bSMike Smith */ 12641ac4b82bSMike Smith case MLX_LOGMSG_SENSE: /* sense data */ 12651ac4b82bSMike Smith /* Mylex vendor-specific message indicating a drive was killed? */ 12661ac4b82bSMike Smith if ((el->el_sensekey == 9) && 12671ac4b82bSMike Smith (el->el_asc == 0x80)) { 12681ac4b82bSMike Smith if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) { 12691ac4b82bSMike Smith reason = mlx_sense_messages[el->el_asq]; 12701ac4b82bSMike Smith } else { 12711ac4b82bSMike Smith reason = "for unknown reason"; 12721ac4b82bSMike Smith } 12731ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n", 12741ac4b82bSMike Smith el->el_channel, el->el_target, reason); 12751ac4b82bSMike Smith } 12761ac4b82bSMike Smith /* SCSI drive was reset? */ 12771ac4b82bSMike Smith if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) { 12781ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d reset\n", 12791ac4b82bSMike Smith el->el_channel, el->el_target); 12801ac4b82bSMike Smith } 12811ac4b82bSMike Smith /* SCSI drive error? */ 12821ac4b82bSMike Smith if (!((el->el_sensekey == 0) || 12831ac4b82bSMike Smith ((el->el_sensekey == 2) && 12841ac4b82bSMike Smith (el->el_asc == 0x04) && 12851ac4b82bSMike Smith ((el->el_asq == 0x01) || 12861ac4b82bSMike Smith (el->el_asq == 0x02))))) { 12871ac4b82bSMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n", 12881ac4b82bSMike Smith el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq); 12891ac4b82bSMike Smith device_printf(sc->mlx_dev, " info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":"); 12901ac4b82bSMike Smith } 12911ac4b82bSMike Smith break; 12921ac4b82bSMike Smith 12931ac4b82bSMike Smith default: 12941ac4b82bSMike Smith device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type); 12951ac4b82bSMike Smith break; 12961ac4b82bSMike Smith } 12971ac4b82bSMike Smith } else { 12981ac4b82bSMike Smith device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc)); 12995d278f5cSMike Smith /* give up on all the outstanding messages, as we may have come unsynched */ 13005d278f5cSMike Smith sc->mlx_lastevent = sc->mlx_currevent; 13011ac4b82bSMike Smith } 13021ac4b82bSMike Smith 13031ac4b82bSMike Smith /* dispose of command and data */ 13041ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 13051ac4b82bSMike Smith mlx_releasecmd(mc); 13061ac4b82bSMike Smith 13071ac4b82bSMike Smith /* is there another message to obtain? */ 13085d278f5cSMike Smith if (sc->mlx_lastevent != sc->mlx_currevent) { 13091ac4b82bSMike Smith mlx_periodic_eventlog_poll(sc); 13105d278f5cSMike Smith } else { 13115d278f5cSMike Smith /* clear log-busy status */ 13125d278f5cSMike Smith atomic_clear_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY); 13135d278f5cSMike Smith } 13141ac4b82bSMike Smith } 13151ac4b82bSMike Smith 13161ac4b82bSMike Smith /******************************************************************************** 1317421f2f7dSMike Smith * Handle check/rebuild operations in progress. 13181ac4b82bSMike Smith */ 13191ac4b82bSMike Smith static void 13201ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc) 13211ac4b82bSMike Smith { 13221ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 1323421f2f7dSMike Smith struct mlx_rebuild_status *mr = (struct mlx_rebuild_status *)mc->mc_data; 13241ac4b82bSMike Smith 13251ac4b82bSMike Smith switch(mc->mc_status) { 1326421f2f7dSMike Smith case 0: /* operation running, update stats */ 1327421f2f7dSMike Smith sc->mlx_rebuildstat = *mr; 1328421f2f7dSMike Smith 1329421f2f7dSMike Smith /* spontaneous rebuild/check? */ 1330421f2f7dSMike Smith if (sc->mlx_background == 0) { 1331421f2f7dSMike Smith sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS; 1332421f2f7dSMike Smith device_printf(sc->mlx_dev, "background check/rebuild operation started\n"); 1333421f2f7dSMike Smith } 13341ac4b82bSMike Smith break; 13351ac4b82bSMike Smith 1336421f2f7dSMike Smith case 0x0105: /* nothing running, finalise stats and report */ 1337421f2f7dSMike Smith switch(sc->mlx_background) { 1338421f2f7dSMike Smith case MLX_BACKGROUND_CHECK: 1339421f2f7dSMike Smith device_printf(sc->mlx_dev, "consistency check completed\n"); /* XXX print drive? */ 1340421f2f7dSMike Smith break; 1341421f2f7dSMike Smith case MLX_BACKGROUND_REBUILD: 1342421f2f7dSMike Smith device_printf(sc->mlx_dev, "drive rebuild completed\n"); /* XXX print channel/target? */ 1343421f2f7dSMike Smith break; 1344421f2f7dSMike Smith case MLX_BACKGROUND_SPONTANEOUS: 1345421f2f7dSMike Smith default: 1346421f2f7dSMike Smith /* if we have previously been non-idle, report the transition */ 1347421f2f7dSMike Smith if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) { 1348421f2f7dSMike Smith device_printf(sc->mlx_dev, "background check/rebuild operation completed\n"); 13491ac4b82bSMike Smith } 1350421f2f7dSMike Smith } 1351421f2f7dSMike Smith sc->mlx_background = 0; 1352421f2f7dSMike Smith sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE; 13531ac4b82bSMike Smith break; 13541ac4b82bSMike Smith } 13551ac4b82bSMike Smith free(mc->mc_data, M_DEVBUF); 13561ac4b82bSMike Smith mlx_releasecmd(mc); 13571ac4b82bSMike Smith } 13581ac4b82bSMike Smith 13591ac4b82bSMike Smith /******************************************************************************** 13601ac4b82bSMike Smith ******************************************************************************** 13611ac4b82bSMike Smith Channel Pause 13621ac4b82bSMike Smith ******************************************************************************** 13631ac4b82bSMike Smith ********************************************************************************/ 13641ac4b82bSMike Smith 13651ac4b82bSMike Smith /******************************************************************************** 13661ac4b82bSMike Smith * It's time to perform a channel pause action for (sc), either start or stop 13671ac4b82bSMike Smith * the pause. 13681ac4b82bSMike Smith */ 13691ac4b82bSMike Smith static void 13701ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc) 13711ac4b82bSMike Smith { 13721ac4b82bSMike Smith struct mlx_command *mc; 13731ac4b82bSMike Smith int failsafe, i, command; 13741ac4b82bSMike Smith 13751ac4b82bSMike Smith /* What are we doing here? */ 13761ac4b82bSMike Smith if (sc->mlx_pause.mp_when == 0) { 13771ac4b82bSMike Smith command = MLX_CMD_STARTCHANNEL; 13781ac4b82bSMike Smith failsafe = 0; 13791ac4b82bSMike Smith 13801ac4b82bSMike Smith } else { 13811ac4b82bSMike Smith command = MLX_CMD_STOPCHANNEL; 13821ac4b82bSMike Smith 13831ac4b82bSMike Smith /* 13841ac4b82bSMike Smith * Channels will always start again after the failsafe period, 13851ac4b82bSMike Smith * which is specified in multiples of 30 seconds. 13861ac4b82bSMike Smith * This constrains us to a maximum pause of 450 seconds. 13871ac4b82bSMike Smith */ 13881ac4b82bSMike Smith failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30; 13891ac4b82bSMike Smith if (failsafe > 0xf) { 13901ac4b82bSMike Smith failsafe = 0xf; 13911ac4b82bSMike Smith sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5; 13921ac4b82bSMike Smith } 13931ac4b82bSMike Smith } 13941ac4b82bSMike Smith 13951ac4b82bSMike Smith /* build commands for every channel requested */ 13969eee27f1SMike Smith for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) { 13971ac4b82bSMike Smith if ((1 << i) & sc->mlx_pause.mp_which) { 13981ac4b82bSMike Smith 13991ac4b82bSMike Smith /* get ourselves a command buffer */ 14001ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 14011ac4b82bSMike Smith goto fail; 14021ac4b82bSMike Smith /* get a command slot */ 14031ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_PRIORITY; 14041ac4b82bSMike Smith if (mlx_getslot(mc)) 14051ac4b82bSMike Smith goto fail; 14061ac4b82bSMike Smith 14071ac4b82bSMike Smith /* build the command */ 14081ac4b82bSMike Smith mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0); 14091ac4b82bSMike Smith mc->mc_complete = mlx_pause_done; 14101ac4b82bSMike Smith mc->mc_private = sc; /* XXX not needed */ 14111ac4b82bSMike Smith if (mlx_start(mc)) 14121ac4b82bSMike Smith goto fail; 14131ac4b82bSMike Smith /* command submitted OK */ 14141ac4b82bSMike Smith return; 14151ac4b82bSMike Smith 14161ac4b82bSMike Smith fail: 14171ac4b82bSMike Smith device_printf(sc->mlx_dev, "%s failed for channel %d\n", 14181ac4b82bSMike Smith command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i); 14191ac4b82bSMike Smith if (mc != NULL) 14201ac4b82bSMike Smith mlx_releasecmd(mc); 14211ac4b82bSMike Smith } 14221ac4b82bSMike Smith } 14231ac4b82bSMike Smith } 14241ac4b82bSMike Smith 14251ac4b82bSMike Smith static void 14261ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc) 14271ac4b82bSMike Smith { 14281ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 14291ac4b82bSMike Smith int command = mc->mc_mailbox[0]; 14301ac4b82bSMike Smith int channel = mc->mc_mailbox[2] & 0xf; 14311ac4b82bSMike Smith 14321ac4b82bSMike Smith if (mc->mc_status != 0) { 14331ac4b82bSMike Smith device_printf(sc->mlx_dev, "%s command failed - %s\n", 14341ac4b82bSMike Smith command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc)); 14351ac4b82bSMike Smith } else if (command == MLX_CMD_STOPCHANNEL) { 14361ac4b82bSMike Smith device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n", 143772c10febSPeter Wemm channel, (long)(sc->mlx_pause.mp_howlong - time_second)); 14381ac4b82bSMike Smith } else { 14391ac4b82bSMike Smith device_printf(sc->mlx_dev, "channel %d resuming\n", channel); 14401ac4b82bSMike Smith } 14411ac4b82bSMike Smith mlx_releasecmd(mc); 14421ac4b82bSMike Smith } 14431ac4b82bSMike Smith 14441ac4b82bSMike Smith /******************************************************************************** 14451ac4b82bSMike Smith ******************************************************************************** 14461ac4b82bSMike Smith Command Submission 14471ac4b82bSMike Smith ******************************************************************************** 14481ac4b82bSMike Smith ********************************************************************************/ 14491ac4b82bSMike Smith 14501ac4b82bSMike Smith /******************************************************************************** 14511ac4b82bSMike Smith * Perform an Enquiry command using a type-3 command buffer and a return a single 14521ac4b82bSMike Smith * linear result buffer. If the completion function is specified, it will 14531ac4b82bSMike Smith * be called with the completed command (and the result response will not be 14541ac4b82bSMike Smith * valid until that point). Otherwise, the command will either be busy-waited 14551ac4b82bSMike Smith * for (interrupts not enabled), or slept for. 14561ac4b82bSMike Smith */ 14571ac4b82bSMike Smith static void * 14581ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc)) 14591ac4b82bSMike Smith { 14601ac4b82bSMike Smith struct mlx_command *mc; 14611ac4b82bSMike Smith void *result; 14621ac4b82bSMike Smith int error; 14631ac4b82bSMike Smith 1464da8bb3a3SMike Smith debug_called(1); 14651ac4b82bSMike Smith 14661ac4b82bSMike Smith /* get ourselves a command buffer */ 14671ac4b82bSMike Smith error = 1; 14681ac4b82bSMike Smith result = NULL; 14691ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 14701ac4b82bSMike Smith goto out; 14711ac4b82bSMike Smith /* allocate the response structure */ 14721ac4b82bSMike Smith if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL) 14731ac4b82bSMike Smith goto out; 14741ac4b82bSMike Smith /* get a command slot */ 14751ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT; 14761ac4b82bSMike Smith if (mlx_getslot(mc)) 14771ac4b82bSMike Smith goto out; 14781ac4b82bSMike Smith 14791ac4b82bSMike Smith /* map the command so the controller can see it */ 14801ac4b82bSMike Smith mc->mc_data = result; 14811ac4b82bSMike Smith mc->mc_length = bufsize; 14821ac4b82bSMike Smith mlx_mapcmd(mc); 14831ac4b82bSMike Smith 14841ac4b82bSMike Smith /* build an enquiry command */ 14851ac4b82bSMike Smith mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0); 14861ac4b82bSMike Smith 14871ac4b82bSMike Smith /* do we want a completion callback? */ 14881ac4b82bSMike Smith if (complete != NULL) { 14891ac4b82bSMike Smith mc->mc_complete = complete; 14901ac4b82bSMike Smith mc->mc_private = mc; 14911ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 14921ac4b82bSMike Smith goto out; 14931ac4b82bSMike Smith } else { 14941ac4b82bSMike Smith /* run the command in either polled or wait mode */ 14951ac4b82bSMike Smith if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc)) 14961ac4b82bSMike Smith goto out; 14971ac4b82bSMike Smith 14981ac4b82bSMike Smith /* command completed OK? */ 14991ac4b82bSMike Smith if (mc->mc_status != 0) { 15001ac4b82bSMike Smith device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", mlx_diagnose_command(mc)); 15011ac4b82bSMike Smith goto out; 15021ac4b82bSMike Smith } 15031ac4b82bSMike Smith } 15041ac4b82bSMike Smith error = 0; /* success */ 15051ac4b82bSMike Smith out: 15061ac4b82bSMike Smith /* we got a command, but nobody else will free it */ 15071ac4b82bSMike Smith if ((complete == NULL) && (mc != NULL)) 15081ac4b82bSMike Smith mlx_releasecmd(mc); 150933c8cb18SMike Smith /* we got an error, and we allocated a result */ 15101ac4b82bSMike Smith if ((error != 0) && (result != NULL)) { 15111ac4b82bSMike Smith free(result, M_DEVBUF); 15121ac4b82bSMike Smith result = NULL; 15131ac4b82bSMike Smith } 15141ac4b82bSMike Smith return(result); 15151ac4b82bSMike Smith } 15161ac4b82bSMike Smith 15171ac4b82bSMike Smith 15181ac4b82bSMike Smith /******************************************************************************** 15191ac4b82bSMike Smith * Perform a Flush command on the nominated controller. 15201ac4b82bSMike Smith * 15211ac4b82bSMike Smith * May be called with interrupts enabled or disabled; will not return until 15221ac4b82bSMike Smith * the flush operation completes or fails. 15231ac4b82bSMike Smith */ 15241ac4b82bSMike Smith static int 15251ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc) 15261ac4b82bSMike Smith { 15271ac4b82bSMike Smith struct mlx_command *mc; 15281ac4b82bSMike Smith int error; 15291ac4b82bSMike Smith 1530da8bb3a3SMike Smith debug_called(1); 15311ac4b82bSMike Smith 15321ac4b82bSMike Smith /* get ourselves a command buffer */ 15331ac4b82bSMike Smith error = 1; 15341ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 15351ac4b82bSMike Smith goto out; 15361ac4b82bSMike Smith /* get a command slot */ 15371ac4b82bSMike Smith if (mlx_getslot(mc)) 15381ac4b82bSMike Smith goto out; 15391ac4b82bSMike Smith 15401ac4b82bSMike Smith /* build a flush command */ 15411ac4b82bSMike Smith mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0); 15421ac4b82bSMike Smith 15435792b7feSMike Smith /* can't assume that interrupts are going to work here, so play it safe */ 15445792b7feSMike Smith if (mlx_poll_command(mc)) 15451ac4b82bSMike Smith goto out; 15461ac4b82bSMike Smith 15471ac4b82bSMike Smith /* command completed OK? */ 15481ac4b82bSMike Smith if (mc->mc_status != 0) { 15491ac4b82bSMike Smith device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc)); 15501ac4b82bSMike Smith goto out; 15511ac4b82bSMike Smith } 15521ac4b82bSMike Smith 15531ac4b82bSMike Smith error = 0; /* success */ 15541ac4b82bSMike Smith out: 15551ac4b82bSMike Smith if (mc != NULL) 15561ac4b82bSMike Smith mlx_releasecmd(mc); 15571ac4b82bSMike Smith return(error); 15581ac4b82bSMike Smith } 15591ac4b82bSMike Smith 15601ac4b82bSMike Smith /******************************************************************************** 1561421f2f7dSMike Smith * Start a background consistency check on (drive). 1562421f2f7dSMike Smith * 1563421f2f7dSMike Smith * May be called with interrupts enabled or disabled; will return as soon as the 1564421f2f7dSMike Smith * operation has started or been refused. 1565421f2f7dSMike Smith */ 1566421f2f7dSMike Smith static int 1567421f2f7dSMike Smith mlx_check(struct mlx_softc *sc, int drive) 1568421f2f7dSMike Smith { 1569421f2f7dSMike Smith struct mlx_command *mc; 1570421f2f7dSMike Smith int error; 1571421f2f7dSMike Smith 1572421f2f7dSMike Smith debug_called(1); 1573421f2f7dSMike Smith 1574421f2f7dSMike Smith /* get ourselves a command buffer */ 1575421f2f7dSMike Smith error = 0x10000; 1576421f2f7dSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 1577421f2f7dSMike Smith goto out; 1578421f2f7dSMike Smith /* get a command slot */ 1579421f2f7dSMike Smith if (mlx_getslot(mc)) 1580421f2f7dSMike Smith goto out; 1581421f2f7dSMike Smith 1582421f2f7dSMike Smith /* build a checkasync command, set the "fix it" flag */ 1583421f2f7dSMike Smith mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0); 1584421f2f7dSMike Smith 1585421f2f7dSMike Smith /* start the command and wait for it to be returned */ 1586421f2f7dSMike Smith if (mlx_wait_command(mc)) 1587421f2f7dSMike Smith goto out; 1588421f2f7dSMike Smith 1589421f2f7dSMike Smith /* command completed OK? */ 1590421f2f7dSMike Smith if (mc->mc_status != 0) { 1591421f2f7dSMike Smith device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc)); 1592421f2f7dSMike Smith } else { 1593421f2f7dSMike Smith device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started"); 1594421f2f7dSMike Smith } 1595421f2f7dSMike Smith error = mc->mc_status; 1596421f2f7dSMike Smith 1597421f2f7dSMike Smith out: 1598421f2f7dSMike Smith if (mc != NULL) 1599421f2f7dSMike Smith mlx_releasecmd(mc); 1600421f2f7dSMike Smith return(error); 1601421f2f7dSMike Smith } 1602421f2f7dSMike Smith 1603421f2f7dSMike Smith /******************************************************************************** 1604421f2f7dSMike Smith * Start a background rebuild of the physical drive at (channel),(target). 16051ac4b82bSMike Smith * 16061ac4b82bSMike Smith * May be called with interrupts enabled or disabled; will return as soon as the 16071ac4b82bSMike Smith * operation has started or been refused. 16081ac4b82bSMike Smith */ 16091ac4b82bSMike Smith static int 16101ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target) 16111ac4b82bSMike Smith { 16121ac4b82bSMike Smith struct mlx_command *mc; 16131ac4b82bSMike Smith int error; 16141ac4b82bSMike Smith 1615da8bb3a3SMike Smith debug_called(1); 16161ac4b82bSMike Smith 16171ac4b82bSMike Smith /* get ourselves a command buffer */ 16181ac4b82bSMike Smith error = 0x10000; 16191ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 16201ac4b82bSMike Smith goto out; 16211ac4b82bSMike Smith /* get a command slot */ 16221ac4b82bSMike Smith if (mlx_getslot(mc)) 16231ac4b82bSMike Smith goto out; 16241ac4b82bSMike Smith 1625421f2f7dSMike Smith /* build a checkasync command, set the "fix it" flag */ 16261ac4b82bSMike Smith mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0); 16271ac4b82bSMike Smith 1628421f2f7dSMike Smith /* start the command and wait for it to be returned */ 1629421f2f7dSMike Smith if (mlx_wait_command(mc)) 16301ac4b82bSMike Smith goto out; 16311ac4b82bSMike Smith 16321ac4b82bSMike Smith /* command completed OK? */ 16331ac4b82bSMike Smith if (mc->mc_status != 0) { 16341ac4b82bSMike Smith device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc)); 16351ac4b82bSMike Smith } else { 1636421f2f7dSMike Smith device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target); 16371ac4b82bSMike Smith } 16381ac4b82bSMike Smith error = mc->mc_status; 16391ac4b82bSMike Smith 16401ac4b82bSMike Smith out: 16411ac4b82bSMike Smith if (mc != NULL) 16421ac4b82bSMike Smith mlx_releasecmd(mc); 16431ac4b82bSMike Smith return(error); 16441ac4b82bSMike Smith } 16451ac4b82bSMike Smith 16461ac4b82bSMike Smith /******************************************************************************** 16471ac4b82bSMike Smith * Run the command (mc) and return when it completes. 16481ac4b82bSMike Smith * 16491ac4b82bSMike Smith * Interrupts need to be enabled; returns nonzero on error. 16501ac4b82bSMike Smith */ 16511ac4b82bSMike Smith static int 16521ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc) 16531ac4b82bSMike Smith { 16541ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 16551ac4b82bSMike Smith int error, count; 16561ac4b82bSMike Smith 1657da8bb3a3SMike Smith debug_called(1); 16581ac4b82bSMike Smith 16591ac4b82bSMike Smith mc->mc_complete = NULL; 16601ac4b82bSMike Smith mc->mc_private = mc; /* wake us when you're done */ 16611ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 16621ac4b82bSMike Smith return(error); 16631ac4b82bSMike Smith 16641ac4b82bSMike Smith count = 0; 16651ac4b82bSMike Smith /* XXX better timeout? */ 16661ac4b82bSMike Smith while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) { 16671ac4b82bSMike Smith tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz); 16681ac4b82bSMike Smith } 16691ac4b82bSMike Smith 16701ac4b82bSMike Smith if (mc->mc_status != 0) { 1671da8bb3a3SMike Smith device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc)); 16721ac4b82bSMike Smith return(EIO); 16731ac4b82bSMike Smith } 16741ac4b82bSMike Smith return(0); 16751ac4b82bSMike Smith } 16761ac4b82bSMike Smith 16771ac4b82bSMike Smith 16781ac4b82bSMike Smith /******************************************************************************** 16791ac4b82bSMike Smith * Start the command (mc) and busy-wait for it to complete. 16801ac4b82bSMike Smith * 1681da8bb3a3SMike Smith * Should only be used when interrupts can't be relied upon. Returns 0 on 16821ac4b82bSMike Smith * success, nonzero on error. 16831ac4b82bSMike Smith * Successfully completed commands are dequeued. 16841ac4b82bSMike Smith */ 16851ac4b82bSMike Smith static int 16861ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc) 16871ac4b82bSMike Smith { 16881ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 16891ac4b82bSMike Smith int error, count, s; 16901ac4b82bSMike Smith 1691da8bb3a3SMike Smith debug_called(1); 16921ac4b82bSMike Smith 16931ac4b82bSMike Smith mc->mc_complete = NULL; 16941ac4b82bSMike Smith mc->mc_private = NULL; /* we will poll for it */ 16951ac4b82bSMike Smith if ((error = mlx_start(mc)) != 0) 16961ac4b82bSMike Smith return(error); 16971ac4b82bSMike Smith 16981ac4b82bSMike Smith count = 0; 16991ac4b82bSMike Smith do { 17001ac4b82bSMike Smith /* poll for completion */ 17011ac4b82bSMike Smith mlx_done(mc->mc_sc); 1702da8bb3a3SMike Smith 1703da8bb3a3SMike Smith } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000)); 17041ac4b82bSMike Smith if (mc->mc_status != MLX_STATUS_BUSY) { 17051ac4b82bSMike Smith s = splbio(); 17064b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 17071ac4b82bSMike Smith splx(s); 17081ac4b82bSMike Smith return(0); 17091ac4b82bSMike Smith } 1710421f2f7dSMike Smith device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc)); 17111ac4b82bSMike Smith return(EIO); 17121ac4b82bSMike Smith } 17131ac4b82bSMike Smith 17141ac4b82bSMike Smith /******************************************************************************** 17151ac4b82bSMike Smith * Pull as much work off the softc's work queue as possible and give it to the 17161ac4b82bSMike Smith * controller. Leave a couple of slots free for emergencies. 17171ac4b82bSMike Smith * 17181ac4b82bSMike Smith * Must be called at splbio or in an equivalent fashion that prevents 17198177437dSPoul-Henning Kamp * reentry or activity on the bioq. 17201ac4b82bSMike Smith */ 17211ac4b82bSMike Smith static void 17221ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc) 17231ac4b82bSMike Smith { 17241ac4b82bSMike Smith struct mlx_command *mc; 17251ac4b82bSMike Smith struct mlxd_softc *mlxd; 172615fd5d22SMike Smith mlx_bio *bp; 17271ac4b82bSMike Smith int blkcount; 17281ac4b82bSMike Smith int driveno; 17291ac4b82bSMike Smith int cmd; 17304b006d7bSMike Smith int s; 17311ac4b82bSMike Smith 17325792b7feSMike Smith /* avoid reentrancy */ 17335792b7feSMike Smith if (mlx_lock_tas(sc, MLX_LOCK_STARTING)) 17345792b7feSMike Smith return; 17355792b7feSMike Smith 17361ac4b82bSMike Smith /* spin until something prevents us from doing any work */ 17374b006d7bSMike Smith s = splbio(); 17381ac4b82bSMike Smith for (;;) { 17391ac4b82bSMike Smith 17401ac4b82bSMike Smith /* see if there's work to be done */ 174115fd5d22SMike Smith if ((bp = MLX_BIO_QFIRST(sc->mlx_bioq)) == NULL) 17421ac4b82bSMike Smith break; 17431ac4b82bSMike Smith /* get a command */ 17441ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 17451ac4b82bSMike Smith break; 17461ac4b82bSMike Smith /* get a slot for the command */ 17471ac4b82bSMike Smith if (mlx_getslot(mc) != 0) { 17481ac4b82bSMike Smith mlx_releasecmd(mc); 17491ac4b82bSMike Smith break; 17501ac4b82bSMike Smith } 17511ac4b82bSMike Smith /* get the buf containing our work */ 175215fd5d22SMike Smith MLX_BIO_QREMOVE(sc->mlx_bioq, bp); 17531ac4b82bSMike Smith sc->mlx_waitbufs--; 17544b006d7bSMike Smith splx(s); 17551ac4b82bSMike Smith 17561ac4b82bSMike Smith /* connect the buf to the command */ 17571ac4b82bSMike Smith mc->mc_complete = mlx_completeio; 17581ac4b82bSMike Smith mc->mc_private = bp; 175915fd5d22SMike Smith mc->mc_data = MLX_BIO_DATA(bp); 176015fd5d22SMike Smith mc->mc_length = MLX_BIO_LENGTH(bp); 176115fd5d22SMike Smith if (MLX_BIO_IS_READ(bp)) { 17621ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_DATAIN; 1763da8bb3a3SMike Smith cmd = MLX_CMD_READSG; 17641ac4b82bSMike Smith } else { 17651ac4b82bSMike Smith mc->mc_flags |= MLX_CMD_DATAOUT; 1766da8bb3a3SMike Smith cmd = MLX_CMD_WRITESG; 17671ac4b82bSMike Smith } 17681ac4b82bSMike Smith 17691ac4b82bSMike Smith /* map the command so the controller can work with it */ 17701ac4b82bSMike Smith mlx_mapcmd(mc); 17711ac4b82bSMike Smith 17721ac4b82bSMike Smith /* build a suitable I/O command (assumes 512-byte rounded transfers) */ 177315fd5d22SMike Smith mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp); 17745792b7feSMike Smith driveno = mlxd->mlxd_drive - sc->mlx_sysdrive; 177515fd5d22SMike Smith blkcount = (MLX_BIO_LENGTH(bp) + MLX_BLKSIZE - 1) / MLX_BLKSIZE; 17761ac4b82bSMike Smith 177715fd5d22SMike Smith if ((MLX_BIO_LBA(bp) + blkcount) > sc->mlx_sysdrive[driveno].ms_size) 177807e929daSBruce Evans device_printf(sc->mlx_dev, 177907e929daSBruce Evans "I/O beyond end of unit (%lld,%d > %lu)\n", 178007e929daSBruce Evans (long long)MLX_BIO_LBA(bp), blkcount, 178107e929daSBruce Evans (u_long)sc->mlx_sysdrive[driveno].ms_size); 17821ac4b82bSMike Smith 17831ac4b82bSMike Smith /* 17841ac4b82bSMike Smith * Build the I/O command. Note that the SG list type bits are set to zero, 17851ac4b82bSMike Smith * denoting the format of SG list that we are using. 17861ac4b82bSMike Smith */ 1787da8bb3a3SMike Smith if (sc->mlx_iftype == MLX_IFTYPE_2) { 1788da8bb3a3SMike Smith mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD : MLX_CMD_READSG_OLD, 1789da8bb3a3SMike Smith blkcount & 0xff, /* xfer length low byte */ 179015fd5d22SMike Smith MLX_BIO_LBA(bp), /* physical block number */ 1791da8bb3a3SMike Smith driveno, /* target drive number */ 1792da8bb3a3SMike Smith mc->mc_sgphys, /* location of SG list */ 1793da8bb3a3SMike Smith mc->mc_nsgent & 0x3f); /* size of SG list (top 3 bits clear) */ 1794da8bb3a3SMike Smith } else { 17951ac4b82bSMike Smith mlx_make_type5(mc, cmd, 17961ac4b82bSMike Smith blkcount & 0xff, /* xfer length low byte */ 1797f01f2af6SMike Smith (driveno << 3) | ((blkcount >> 8) & 0x07), /* target and length high 3 bits */ 179815fd5d22SMike Smith MLX_BIO_LBA(bp), /* physical block number */ 17991ac4b82bSMike Smith mc->mc_sgphys, /* location of SG list */ 1800da8bb3a3SMike Smith mc->mc_nsgent & 0x3f); /* size of SG list (top 3 bits clear) */ 1801da8bb3a3SMike Smith } 18021ac4b82bSMike Smith 18031ac4b82bSMike Smith /* try to give command to controller */ 18041ac4b82bSMike Smith if (mlx_start(mc) != 0) { 18051ac4b82bSMike Smith /* fail the command */ 18061ac4b82bSMike Smith mc->mc_status = MLX_STATUS_WEDGED; 18071ac4b82bSMike Smith mlx_completeio(mc); 18081ac4b82bSMike Smith } 18094b006d7bSMike Smith s = splbio(); 18101ac4b82bSMike Smith } 18114b006d7bSMike Smith splx(s); 18125792b7feSMike Smith mlx_lock_clr(sc, MLX_LOCK_STARTING); 18131ac4b82bSMike Smith } 18141ac4b82bSMike Smith 18151ac4b82bSMike Smith /******************************************************************************** 18161ac4b82bSMike Smith * Handle completion of an I/O command. 18171ac4b82bSMike Smith */ 18181ac4b82bSMike Smith static void 18191ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc) 18201ac4b82bSMike Smith { 18211ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 182215fd5d22SMike Smith mlx_bio *bp = (mlx_bio *)mc->mc_private; 182315fd5d22SMike Smith struct mlxd_softc *mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp); 18241ac4b82bSMike Smith 18251ac4b82bSMike Smith if (mc->mc_status != MLX_STATUS_OK) { /* could be more verbose here? */ 182615fd5d22SMike Smith MLX_BIO_SET_ERROR(bp, EIO); 18271ac4b82bSMike Smith 18281ac4b82bSMike Smith switch(mc->mc_status) { 18291ac4b82bSMike Smith case MLX_STATUS_RDWROFFLINE: /* system drive has gone offline */ 18301ac4b82bSMike Smith device_printf(mlxd->mlxd_dev, "drive offline\n"); 1831f6b84b08SMike Smith /* should signal this with a return code */ 18321ac4b82bSMike Smith mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE; 18331ac4b82bSMike Smith break; 18341ac4b82bSMike Smith 18351ac4b82bSMike Smith default: /* other I/O error */ 18361ac4b82bSMike Smith device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc)); 18371ac4b82bSMike Smith #if 0 1838cd4ace0cSMike Smith device_printf(sc->mlx_dev, " b_bcount %ld blkcount %ld b_pblkno %d\n", 183915fd5d22SMike Smith MLX_BIO_LENGTH(bp), MLX_BIO_LENGTH(bp) / MLX_BLKSIZE, MLX_BIO_LBA(bp)); 18401ac4b82bSMike Smith device_printf(sc->mlx_dev, " %13D\n", mc->mc_mailbox, " "); 18411ac4b82bSMike Smith #endif 18421ac4b82bSMike Smith break; 18431ac4b82bSMike Smith } 18441ac4b82bSMike Smith } 18451ac4b82bSMike Smith mlx_releasecmd(mc); 18461ac4b82bSMike Smith mlxd_intr(bp); 18471ac4b82bSMike Smith } 18481ac4b82bSMike Smith 18491ac4b82bSMike Smith /******************************************************************************** 18501ac4b82bSMike Smith * Take a command from user-space and try to run it. 1851da8bb3a3SMike Smith * 1852da8bb3a3SMike Smith * XXX Note that this can't perform very much in the way of error checking, and 1853da8bb3a3SMike Smith * as such, applications _must_ be considered trustworthy. 1854da8bb3a3SMike Smith * XXX Commands using S/G for data are not supported. 18551ac4b82bSMike Smith */ 18561ac4b82bSMike Smith static int 18571ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu) 18581ac4b82bSMike Smith { 18591ac4b82bSMike Smith struct mlx_command *mc; 1860da8bb3a3SMike Smith struct mlx_dcdb *dcdb; 18611ac4b82bSMike Smith void *kbuf; 18621ac4b82bSMike Smith int error; 18631ac4b82bSMike Smith 1864da8bb3a3SMike Smith debug_called(0); 1865da8bb3a3SMike Smith 18661ac4b82bSMike Smith kbuf = NULL; 18671ac4b82bSMike Smith mc = NULL; 1868da8bb3a3SMike Smith dcdb = NULL; 18691ac4b82bSMike Smith error = ENOMEM; 1870da8bb3a3SMike Smith 1871da8bb3a3SMike Smith /* get ourselves a command and copy in from user space */ 18721ac4b82bSMike Smith if ((mc = mlx_alloccmd(sc)) == NULL) 18731ac4b82bSMike Smith goto out; 18741ac4b82bSMike Smith bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox)); 1875da8bb3a3SMike Smith debug(0, "got command buffer"); 1876da8bb3a3SMike Smith 1877da8bb3a3SMike Smith /* if we need a buffer for data transfer, allocate one and copy in its initial contents */ 1878da8bb3a3SMike Smith if (mu->mu_datasize > 0) { 1879bf61e266SKris Kennaway if (mu->mu_datasize > MAXPHYS) 1880bf61e266SKris Kennaway return (EINVAL); 1881a163d034SWarner Losh if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) || 1882da8bb3a3SMike Smith (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))) 18831ac4b82bSMike Smith goto out; 1884da8bb3a3SMike Smith debug(0, "got kernel buffer"); 1885da8bb3a3SMike Smith } 18861ac4b82bSMike Smith 18871ac4b82bSMike Smith /* get a command slot */ 18881ac4b82bSMike Smith if (mlx_getslot(mc)) 18891ac4b82bSMike Smith goto out; 1890da8bb3a3SMike Smith debug(0, "got a slot"); 18911ac4b82bSMike Smith 18921ac4b82bSMike Smith /* map the command so the controller can see it */ 18931ac4b82bSMike Smith mc->mc_data = kbuf; 18941ac4b82bSMike Smith mc->mc_length = mu->mu_datasize; 18951ac4b82bSMike Smith mlx_mapcmd(mc); 1896da8bb3a3SMike Smith debug(0, "mapped"); 18971ac4b82bSMike Smith 1898da8bb3a3SMike Smith /* 1899da8bb3a3SMike Smith * If this is a passthrough SCSI command, the DCDB is packed at the 1900da8bb3a3SMike Smith * beginning of the data area. Fix up the DCDB to point to the correct physical 1901da8bb3a3SMike Smith * address and override any bufptr supplied by the caller since we know 1902da8bb3a3SMike Smith * what it's meant to be. 1903da8bb3a3SMike Smith */ 1904da8bb3a3SMike Smith if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) { 1905da8bb3a3SMike Smith dcdb = (struct mlx_dcdb *)kbuf; 1906da8bb3a3SMike Smith dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb); 1907da8bb3a3SMike Smith mu->mu_bufptr = 8; 19081ac4b82bSMike Smith } 19091ac4b82bSMike Smith 1910da8bb3a3SMike Smith /* 1911da8bb3a3SMike Smith * If there's a data buffer, fix up the command's buffer pointer. 1912da8bb3a3SMike Smith */ 1913da8bb3a3SMike Smith if (mu->mu_datasize > 0) { 1914da8bb3a3SMike Smith 1915da8bb3a3SMike Smith /* range check the pointer to physical buffer address */ 1916da8bb3a3SMike Smith if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) - sizeof(u_int32_t)))) { 1917da8bb3a3SMike Smith error = EINVAL; 1918da8bb3a3SMike Smith goto out; 1919da8bb3a3SMike Smith } 1920da8bb3a3SMike Smith mc->mc_mailbox[mu->mu_bufptr ] = mc->mc_dataphys & 0xff; 1921da8bb3a3SMike Smith mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8) & 0xff; 1922da8bb3a3SMike Smith mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff; 1923da8bb3a3SMike Smith mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff; 1924da8bb3a3SMike Smith } 1925da8bb3a3SMike Smith debug(0, "command fixup"); 1926da8bb3a3SMike Smith 19271ac4b82bSMike Smith /* submit the command and wait */ 19281ac4b82bSMike Smith if ((error = mlx_wait_command(mc)) != 0) 19291ac4b82bSMike Smith goto out; 19301ac4b82bSMike Smith 19311ac4b82bSMike Smith /* copy out status and data */ 19321ac4b82bSMike Smith mu->mu_status = mc->mc_status; 19331ac4b82bSMike Smith if ((mu->mu_datasize > 0) && ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize)))) 19341ac4b82bSMike Smith goto out; 19351ac4b82bSMike Smith error = 0; 19361ac4b82bSMike Smith 19371ac4b82bSMike Smith out: 19381ac4b82bSMike Smith mlx_releasecmd(mc); 19391ac4b82bSMike Smith if (kbuf != NULL) 19401ac4b82bSMike Smith free(kbuf, M_DEVBUF); 19411ac4b82bSMike Smith return(error); 19421ac4b82bSMike Smith } 19431ac4b82bSMike Smith 19441ac4b82bSMike Smith /******************************************************************************** 19451ac4b82bSMike Smith ******************************************************************************** 19461ac4b82bSMike Smith Command I/O to Controller 19471ac4b82bSMike Smith ******************************************************************************** 19481ac4b82bSMike Smith ********************************************************************************/ 19491ac4b82bSMike Smith 19501ac4b82bSMike Smith /******************************************************************************** 19511ac4b82bSMike Smith * Find a free command slot for (mc). 19521ac4b82bSMike Smith * 19531ac4b82bSMike Smith * Don't hand out a slot to a normal-priority command unless there are at least 19541ac4b82bSMike Smith * 4 slots free for priority commands. 19551ac4b82bSMike Smith */ 19561ac4b82bSMike Smith static int 19571ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc) 19581ac4b82bSMike Smith { 19591ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 1960baff09dbSMike Smith int s, slot, limit; 19611ac4b82bSMike Smith 1962da8bb3a3SMike Smith debug_called(1); 19631ac4b82bSMike Smith 1964baff09dbSMike Smith /* 1965baff09dbSMike Smith * Enforce slot-usage limit, if we have the required information. 1966baff09dbSMike Smith */ 1967baff09dbSMike Smith if (sc->mlx_enq2 != NULL) { 1968baff09dbSMike Smith limit = sc->mlx_enq2->me_max_commands; 1969baff09dbSMike Smith } else { 1970baff09dbSMike Smith limit = 2; 1971baff09dbSMike Smith } 1972baff09dbSMike Smith if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ? limit : limit - 4)) 19731ac4b82bSMike Smith return(EBUSY); 19741ac4b82bSMike Smith 19751ac4b82bSMike Smith /* 19761ac4b82bSMike Smith * Allocate an outstanding command slot 19771ac4b82bSMike Smith * 19781ac4b82bSMike Smith * XXX linear search is slow 19791ac4b82bSMike Smith */ 19801ac4b82bSMike Smith s = splbio(); 1981baff09dbSMike Smith for (slot = 0; slot < limit; slot++) { 1982da8bb3a3SMike Smith debug(2, "try slot %d", slot); 19831ac4b82bSMike Smith if (sc->mlx_busycmd[slot] == NULL) 19841ac4b82bSMike Smith break; 19851ac4b82bSMike Smith } 1986baff09dbSMike Smith if (slot < limit) { 19871ac4b82bSMike Smith sc->mlx_busycmd[slot] = mc; 19881ac4b82bSMike Smith sc->mlx_busycmds++; 19891ac4b82bSMike Smith } 19901ac4b82bSMike Smith splx(s); 19911ac4b82bSMike Smith 19921ac4b82bSMike Smith /* out of slots? */ 1993baff09dbSMike Smith if (slot >= limit) 19941ac4b82bSMike Smith return(EBUSY); 19951ac4b82bSMike Smith 1996da8bb3a3SMike Smith debug(2, "got slot %d", slot); 19971ac4b82bSMike Smith mc->mc_slot = slot; 19981ac4b82bSMike Smith return(0); 19991ac4b82bSMike Smith } 20001ac4b82bSMike Smith 20011ac4b82bSMike Smith /******************************************************************************** 20021ac4b82bSMike Smith * Map/unmap (mc)'s data in the controller's addressable space. 20031ac4b82bSMike Smith */ 20041ac4b82bSMike Smith static void 20051ac4b82bSMike Smith mlx_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 20061ac4b82bSMike Smith { 20071ac4b82bSMike Smith struct mlx_command *mc = (struct mlx_command *)arg; 20081ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 20091ac4b82bSMike Smith struct mlx_sgentry *sg; 20101ac4b82bSMike Smith int i; 20111ac4b82bSMike Smith 2012da8bb3a3SMike Smith debug_called(1); 20131ac4b82bSMike Smith 2014baff09dbSMike Smith /* XXX should be unnecessary */ 2015baff09dbSMike Smith if (sc->mlx_enq2 && (nsegments > sc->mlx_enq2->me_max_sg)) 2016baff09dbSMike Smith panic("MLX: too many s/g segments (%d, max %d)", nsegments, sc->mlx_enq2->me_max_sg); 2017baff09dbSMike Smith 20181ac4b82bSMike Smith /* get base address of s/g table */ 2019baff09dbSMike Smith sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG); 20201ac4b82bSMike Smith 20211ac4b82bSMike Smith /* save s/g table information in command */ 20221ac4b82bSMike Smith mc->mc_nsgent = nsegments; 2023baff09dbSMike Smith mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry)); 20241ac4b82bSMike Smith mc->mc_dataphys = segs[0].ds_addr; 20251ac4b82bSMike Smith 20261ac4b82bSMike Smith /* populate s/g table */ 20271ac4b82bSMike Smith for (i = 0; i < nsegments; i++, sg++) { 20281ac4b82bSMike Smith sg->sg_addr = segs[i].ds_addr; 20291ac4b82bSMike Smith sg->sg_count = segs[i].ds_len; 20301ac4b82bSMike Smith } 20311ac4b82bSMike Smith } 20321ac4b82bSMike Smith 20331ac4b82bSMike Smith static void 20341ac4b82bSMike Smith mlx_mapcmd(struct mlx_command *mc) 20351ac4b82bSMike Smith { 20361ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 20371ac4b82bSMike Smith 2038da8bb3a3SMike Smith debug_called(1); 20391ac4b82bSMike Smith 20401ac4b82bSMike Smith /* if the command involves data at all */ 20411ac4b82bSMike Smith if (mc->mc_data != NULL) { 20421ac4b82bSMike Smith 20431ac4b82bSMike Smith /* map the data buffer into bus space and build the s/g list */ 20441ac4b82bSMike Smith bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length, 20451ac4b82bSMike Smith mlx_setup_dmamap, mc, 0); 20461ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAIN) 20471ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREREAD); 20481ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAOUT) 20491ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREWRITE); 20501ac4b82bSMike Smith } 20511ac4b82bSMike Smith } 20521ac4b82bSMike Smith 20531ac4b82bSMike Smith static void 20541ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc) 20551ac4b82bSMike Smith { 20561ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 20571ac4b82bSMike Smith 2058da8bb3a3SMike Smith debug_called(1); 20591ac4b82bSMike Smith 20601ac4b82bSMike Smith /* if the command involved data at all */ 20611ac4b82bSMike Smith if (mc->mc_data != NULL) { 20621ac4b82bSMike Smith 20631ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAIN) 20641ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD); 20651ac4b82bSMike Smith if (mc->mc_flags & MLX_CMD_DATAOUT) 20661ac4b82bSMike Smith bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE); 20671ac4b82bSMike Smith 20681ac4b82bSMike Smith bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap); 20691ac4b82bSMike Smith } 20701ac4b82bSMike Smith } 20711ac4b82bSMike Smith 20721ac4b82bSMike Smith /******************************************************************************** 20735792b7feSMike Smith * Try to deliver (mc) to the controller. 20741ac4b82bSMike Smith * 20751ac4b82bSMike Smith * Can be called at any interrupt level, with or without interrupts enabled. 20761ac4b82bSMike Smith */ 20771ac4b82bSMike Smith static int 20781ac4b82bSMike Smith mlx_start(struct mlx_command *mc) 20791ac4b82bSMike Smith { 20801ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 20815792b7feSMike Smith int i, s, done; 20821ac4b82bSMike Smith 2083da8bb3a3SMike Smith debug_called(1); 20841ac4b82bSMike Smith 20851ac4b82bSMike Smith /* save the slot number as ident so we can handle this command when complete */ 20861ac4b82bSMike Smith mc->mc_mailbox[0x1] = mc->mc_slot; 20871ac4b82bSMike Smith 20884b006d7bSMike Smith /* mark the command as currently being processed */ 20891ac4b82bSMike Smith mc->mc_status = MLX_STATUS_BUSY; 20901ac4b82bSMike Smith 20915792b7feSMike Smith /* set a default 60-second timeout XXX tunable? XXX not currently used */ 20925792b7feSMike Smith mc->mc_timeout = time_second + 60; 20931ac4b82bSMike Smith 20941ac4b82bSMike Smith /* spin waiting for the mailbox */ 20951ac4b82bSMike Smith for (i = 100000, done = 0; (i > 0) && !done; i--) { 20961ac4b82bSMike Smith s = splbio(); 20974b006d7bSMike Smith if (sc->mlx_tryqueue(sc, mc)) { 20984b006d7bSMike Smith done = 1; 20994b006d7bSMike Smith /* move command to work queue */ 21004b006d7bSMike Smith TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link); 21014b006d7bSMike Smith } 21025792b7feSMike Smith splx(s); /* drop spl to allow completion interrupts */ 21031ac4b82bSMike Smith } 21041ac4b82bSMike Smith 21051ac4b82bSMike Smith /* command is enqueued */ 21061ac4b82bSMike Smith if (done) 21071ac4b82bSMike Smith return(0); 21081ac4b82bSMike Smith 21091ac4b82bSMike Smith /* 21101ac4b82bSMike Smith * We couldn't get the controller to take the command. Revoke the slot 21111ac4b82bSMike Smith * that the command was given and return it with a bad status. 21121ac4b82bSMike Smith */ 21131ac4b82bSMike Smith sc->mlx_busycmd[mc->mc_slot] = NULL; 21141ac4b82bSMike Smith device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n"); 21151ac4b82bSMike Smith mc->mc_status = MLX_STATUS_WEDGED; 21165792b7feSMike Smith mlx_complete(sc); 21171ac4b82bSMike Smith return(EIO); 21181ac4b82bSMike Smith } 21191ac4b82bSMike Smith 21201ac4b82bSMike Smith /******************************************************************************** 21215792b7feSMike Smith * Poll the controller (sc) for completed commands. 21225792b7feSMike Smith * Update command status and free slots for reuse. If any slots were freed, 21235792b7feSMike Smith * new commands may be posted. 21241ac4b82bSMike Smith * 21255792b7feSMike Smith * Returns nonzero if one or more commands were completed. 21261ac4b82bSMike Smith */ 21271ac4b82bSMike Smith static int 21281ac4b82bSMike Smith mlx_done(struct mlx_softc *sc) 21291ac4b82bSMike Smith { 21301ac4b82bSMike Smith struct mlx_command *mc; 21315792b7feSMike Smith int s, result; 21321ac4b82bSMike Smith u_int8_t slot; 21331ac4b82bSMike Smith u_int16_t status; 21341ac4b82bSMike Smith 2135da8bb3a3SMike Smith debug_called(2); 21361ac4b82bSMike Smith 21375792b7feSMike Smith result = 0; 21381ac4b82bSMike Smith 21395792b7feSMike Smith /* loop collecting completed commands */ 21404b006d7bSMike Smith s = splbio(); 21415792b7feSMike Smith for (;;) { 21425792b7feSMike Smith /* poll for a completed command's identifier and status */ 21431ac4b82bSMike Smith if (sc->mlx_findcomplete(sc, &slot, &status)) { 21445792b7feSMike Smith result = 1; 21451ac4b82bSMike Smith mc = sc->mlx_busycmd[slot]; /* find command */ 21461ac4b82bSMike Smith if (mc != NULL) { /* paranoia */ 21471ac4b82bSMike Smith if (mc->mc_status == MLX_STATUS_BUSY) { 21481ac4b82bSMike Smith mc->mc_status = status; /* save status */ 21491ac4b82bSMike Smith 21501ac4b82bSMike Smith /* free slot for reuse */ 21511ac4b82bSMike Smith sc->mlx_busycmd[slot] = NULL; 21521ac4b82bSMike Smith sc->mlx_busycmds--; 21531ac4b82bSMike Smith } else { 21541ac4b82bSMike Smith device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot); 21551ac4b82bSMike Smith } 21561ac4b82bSMike Smith } else { 21571ac4b82bSMike Smith device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot); 21581ac4b82bSMike Smith } 21595792b7feSMike Smith } else { 21605792b7feSMike Smith break; 21611ac4b82bSMike Smith } 21625792b7feSMike Smith } 2163baff09dbSMike Smith splx(s); 21641ac4b82bSMike Smith 21655792b7feSMike Smith /* if we've completed any commands, try posting some more */ 21665792b7feSMike Smith if (result) 21675792b7feSMike Smith mlx_startio(sc); 21685792b7feSMike Smith 21695792b7feSMike Smith /* handle completion and timeouts */ 21705792b7feSMike Smith mlx_complete(sc); 21715792b7feSMike Smith 21725792b7feSMike Smith return(result); 21731ac4b82bSMike Smith } 21741ac4b82bSMike Smith 21751ac4b82bSMike Smith /******************************************************************************** 21765792b7feSMike Smith * Perform post-completion processing for commands on (sc). 21771ac4b82bSMike Smith */ 21781ac4b82bSMike Smith static void 21791ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc) 21801ac4b82bSMike Smith { 21811ac4b82bSMike Smith struct mlx_command *mc, *nc; 21821ac4b82bSMike Smith int s, count; 21831ac4b82bSMike Smith 2184da8bb3a3SMike Smith debug_called(2); 21851ac4b82bSMike Smith 21865792b7feSMike Smith /* avoid reentrancy XXX might want to signal and request a restart */ 21875792b7feSMike Smith if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING)) 21885792b7feSMike Smith return; 21895792b7feSMike Smith 21901ac4b82bSMike Smith s = splbio(); 21911ac4b82bSMike Smith count = 0; 21921ac4b82bSMike Smith 21935792b7feSMike Smith /* scan the list of busy/done commands */ 21944b006d7bSMike Smith mc = TAILQ_FIRST(&sc->mlx_work); 21951ac4b82bSMike Smith while (mc != NULL) { 21961ac4b82bSMike Smith nc = TAILQ_NEXT(mc, mc_link); 21971ac4b82bSMike Smith 21985792b7feSMike Smith /* Command has been completed in some fashion */ 21994b006d7bSMike Smith if (mc->mc_status != MLX_STATUS_BUSY) { 22004b006d7bSMike Smith 22015792b7feSMike Smith /* unmap the command's data buffer */ 22025792b7feSMike Smith mlx_unmapcmd(mc); 22031ac4b82bSMike Smith /* 22041ac4b82bSMike Smith * Does the command have a completion handler? 22051ac4b82bSMike Smith */ 22061ac4b82bSMike Smith if (mc->mc_complete != NULL) { 22071ac4b82bSMike Smith /* remove from list and give to handler */ 22084b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 22091ac4b82bSMike Smith mc->mc_complete(mc); 22101ac4b82bSMike Smith 22111ac4b82bSMike Smith /* 22121ac4b82bSMike Smith * Is there a sleeper waiting on this command? 22131ac4b82bSMike Smith */ 22141ac4b82bSMike Smith } else if (mc->mc_private != NULL) { /* sleeping caller wants to know about it */ 22151ac4b82bSMike Smith 22161ac4b82bSMike Smith /* remove from list and wake up sleeper */ 22174b006d7bSMike Smith TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); 22181ac4b82bSMike Smith wakeup_one(mc->mc_private); 22191ac4b82bSMike Smith 22201ac4b82bSMike Smith /* 22211ac4b82bSMike Smith * Leave the command for a caller that's polling for it. 22221ac4b82bSMike Smith */ 22231ac4b82bSMike Smith } else { 22241ac4b82bSMike Smith } 22254b006d7bSMike Smith } 22261ac4b82bSMike Smith mc = nc; 22271ac4b82bSMike Smith } 22281ac4b82bSMike Smith splx(s); 22291ac4b82bSMike Smith 22305792b7feSMike Smith mlx_lock_clr(sc, MLX_LOCK_COMPLETING); 22311ac4b82bSMike Smith } 22321ac4b82bSMike Smith 22331ac4b82bSMike Smith /******************************************************************************** 22341ac4b82bSMike Smith ******************************************************************************** 22351ac4b82bSMike Smith Command Buffer Management 22361ac4b82bSMike Smith ******************************************************************************** 22371ac4b82bSMike Smith ********************************************************************************/ 22381ac4b82bSMike Smith 22391ac4b82bSMike Smith /******************************************************************************** 22401ac4b82bSMike Smith * Get a new command buffer. 22411ac4b82bSMike Smith * 22421ac4b82bSMike Smith * This may return NULL in low-memory cases. 22431ac4b82bSMike Smith * 22441ac4b82bSMike Smith * Note that using malloc() is expensive (the command buffer is << 1 page) but 22451ac4b82bSMike Smith * necessary if we are to be a loadable module before the zone allocator is fixed. 22461ac4b82bSMike Smith * 22471ac4b82bSMike Smith * If possible, we recycle a command buffer that's been used before. 22481ac4b82bSMike Smith * 22491ac4b82bSMike Smith * XXX Note that command buffers are not cleaned out - it is the caller's 22501ac4b82bSMike Smith * responsibility to ensure that all required fields are filled in before 22511ac4b82bSMike Smith * using a buffer. 22521ac4b82bSMike Smith */ 22531ac4b82bSMike Smith static struct mlx_command * 22541ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc) 22551ac4b82bSMike Smith { 22561ac4b82bSMike Smith struct mlx_command *mc; 22571ac4b82bSMike Smith int error; 22581ac4b82bSMike Smith int s; 22591ac4b82bSMike Smith 2260da8bb3a3SMike Smith debug_called(1); 22611ac4b82bSMike Smith 22621ac4b82bSMike Smith s = splbio(); 22631ac4b82bSMike Smith if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) 22641ac4b82bSMike Smith TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); 22651ac4b82bSMike Smith splx(s); 22661ac4b82bSMike Smith 22671ac4b82bSMike Smith /* allocate a new command buffer? */ 22681ac4b82bSMike Smith if (mc == NULL) { 2269ca89ee27SDavid Malone mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT | M_ZERO); 22701ac4b82bSMike Smith if (mc != NULL) { 22711ac4b82bSMike Smith mc->mc_sc = sc; 22721ac4b82bSMike Smith error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap); 22731ac4b82bSMike Smith if (error) { 22741ac4b82bSMike Smith free(mc, M_DEVBUF); 22751ac4b82bSMike Smith return(NULL); 22761ac4b82bSMike Smith } 22771ac4b82bSMike Smith } 22781ac4b82bSMike Smith } 22791ac4b82bSMike Smith return(mc); 22801ac4b82bSMike Smith } 22811ac4b82bSMike Smith 22821ac4b82bSMike Smith /******************************************************************************** 22831ac4b82bSMike Smith * Release a command buffer for recycling. 22841ac4b82bSMike Smith * 22851ac4b82bSMike Smith * XXX It might be a good idea to limit the number of commands we save for reuse 22861ac4b82bSMike Smith * if it's shown that this list bloats out massively. 22871ac4b82bSMike Smith */ 22881ac4b82bSMike Smith static void 22891ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc) 22901ac4b82bSMike Smith { 22911ac4b82bSMike Smith int s; 22921ac4b82bSMike Smith 2293da8bb3a3SMike Smith debug_called(1); 22941ac4b82bSMike Smith 22951ac4b82bSMike Smith s = splbio(); 22961ac4b82bSMike Smith TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link); 22971ac4b82bSMike Smith splx(s); 22981ac4b82bSMike Smith } 22991ac4b82bSMike Smith 23001ac4b82bSMike Smith /******************************************************************************** 23011ac4b82bSMike Smith * Permanently discard a command buffer. 23021ac4b82bSMike Smith */ 23031ac4b82bSMike Smith static void 23041ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc) 23051ac4b82bSMike Smith { 23061ac4b82bSMike Smith struct mlx_softc *sc = mc->mc_sc; 23071ac4b82bSMike Smith 2308da8bb3a3SMike Smith debug_called(1); 23091ac4b82bSMike Smith bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap); 23101ac4b82bSMike Smith free(mc, M_DEVBUF); 23111ac4b82bSMike Smith } 23121ac4b82bSMike Smith 23131ac4b82bSMike Smith 23141ac4b82bSMike Smith /******************************************************************************** 23151ac4b82bSMike Smith ******************************************************************************** 23161ac4b82bSMike Smith Type 3 interface accessor methods 23171ac4b82bSMike Smith ******************************************************************************** 23181ac4b82bSMike Smith ********************************************************************************/ 23191ac4b82bSMike Smith 23201ac4b82bSMike Smith /******************************************************************************** 23211ac4b82bSMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 23221ac4b82bSMike Smith * (the controller is not ready to take a command). 23231ac4b82bSMike Smith * 23241ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 23251ac4b82bSMike Smith */ 23261ac4b82bSMike Smith static int 23271ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 23281ac4b82bSMike Smith { 23291ac4b82bSMike Smith int i; 23301ac4b82bSMike Smith 2331da8bb3a3SMike Smith debug_called(2); 23321ac4b82bSMike Smith 23331ac4b82bSMike Smith /* ready for our command? */ 23341ac4b82bSMike Smith if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) { 23351ac4b82bSMike Smith /* copy mailbox data to window */ 23361ac4b82bSMike Smith for (i = 0; i < 13; i++) 23371ac4b82bSMike Smith MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 23381ac4b82bSMike Smith 23391ac4b82bSMike Smith /* post command */ 2340f6b84b08SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL); 23411ac4b82bSMike Smith return(1); 23421ac4b82bSMike Smith } 23431ac4b82bSMike Smith return(0); 23441ac4b82bSMike Smith } 23451ac4b82bSMike Smith 23461ac4b82bSMike Smith /******************************************************************************** 23471ac4b82bSMike Smith * See if a command has been completed, if so acknowledge its completion 23481ac4b82bSMike Smith * and recover the slot number and status code. 23491ac4b82bSMike Smith * 23501ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 23511ac4b82bSMike Smith */ 23521ac4b82bSMike Smith static int 23531ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 23541ac4b82bSMike Smith { 23551ac4b82bSMike Smith 2356da8bb3a3SMike Smith debug_called(2); 23571ac4b82bSMike Smith 23581ac4b82bSMike Smith /* status available? */ 23591ac4b82bSMike Smith if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) { 23601ac4b82bSMike Smith *slot = MLX_V3_GET_STATUS_IDENT(sc); /* get command identifier */ 23611ac4b82bSMike Smith *status = MLX_V3_GET_STATUS(sc); /* get status */ 23621ac4b82bSMike Smith 23631ac4b82bSMike Smith /* acknowledge completion */ 2364f6b84b08SMike Smith MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL); 2365f6b84b08SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK); 23661ac4b82bSMike Smith return(1); 23671ac4b82bSMike Smith } 23681ac4b82bSMike Smith return(0); 23691ac4b82bSMike Smith } 23701ac4b82bSMike Smith 23711ac4b82bSMike Smith /******************************************************************************** 23721ac4b82bSMike Smith * Enable/disable interrupts as requested. (No acknowledge required) 23731ac4b82bSMike Smith * 23741ac4b82bSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 23751ac4b82bSMike Smith */ 23761ac4b82bSMike Smith static void 23771ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action) 23781ac4b82bSMike Smith { 2379da8bb3a3SMike Smith debug_called(1); 23801ac4b82bSMike Smith 23811ac4b82bSMike Smith switch(action) { 23821ac4b82bSMike Smith case MLX_INTACTION_DISABLE: 23831ac4b82bSMike Smith MLX_V3_PUT_IER(sc, 0); 23841ac4b82bSMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 23851ac4b82bSMike Smith break; 23861ac4b82bSMike Smith case MLX_INTACTION_ENABLE: 23871ac4b82bSMike Smith MLX_V3_PUT_IER(sc, 1); 23881ac4b82bSMike Smith sc->mlx_state |= MLX_STATE_INTEN; 23891ac4b82bSMike Smith break; 23901ac4b82bSMike Smith } 23911ac4b82bSMike Smith } 23921ac4b82bSMike Smith 2393da8bb3a3SMike Smith /******************************************************************************** 2394da8bb3a3SMike Smith * Poll for firmware error codes during controller initialisation. 2395da8bb3a3SMike Smith * Returns 0 if initialisation is complete, 1 if still in progress but no 2396da8bb3a3SMike Smith * error has been fetched, 2 if an error has been retrieved. 2397da8bb3a3SMike Smith */ 2398da8bb3a3SMike Smith static int 2399da8bb3a3SMike Smith mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 2400da8bb3a3SMike Smith { 2401da8bb3a3SMike Smith u_int8_t fwerror; 2402da8bb3a3SMike Smith static int initted = 0; 2403da8bb3a3SMike Smith 2404da8bb3a3SMike Smith debug_called(2); 2405da8bb3a3SMike Smith 2406da8bb3a3SMike Smith /* first time around, clear any hardware completion status */ 2407da8bb3a3SMike Smith if (!initted) { 2408da8bb3a3SMike Smith MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK); 2409da8bb3a3SMike Smith DELAY(1000); 2410da8bb3a3SMike Smith initted = 1; 2411da8bb3a3SMike Smith } 2412da8bb3a3SMike Smith 2413da8bb3a3SMike Smith /* init in progress? */ 2414da8bb3a3SMike Smith if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY)) 2415da8bb3a3SMike Smith return(0); 2416da8bb3a3SMike Smith 2417da8bb3a3SMike Smith /* test error value */ 2418da8bb3a3SMike Smith fwerror = MLX_V3_GET_FWERROR(sc); 2419da8bb3a3SMike Smith if (!(fwerror & MLX_V3_FWERROR_PEND)) 2420da8bb3a3SMike Smith return(1); 2421da8bb3a3SMike Smith 2422da8bb3a3SMike Smith /* mask status pending bit, fetch status */ 2423da8bb3a3SMike Smith *error = fwerror & ~MLX_V3_FWERROR_PEND; 2424da8bb3a3SMike Smith *param1 = MLX_V3_GET_FWERROR_PARAM1(sc); 2425da8bb3a3SMike Smith *param2 = MLX_V3_GET_FWERROR_PARAM2(sc); 2426da8bb3a3SMike Smith 2427da8bb3a3SMike Smith /* acknowledge */ 2428da8bb3a3SMike Smith MLX_V3_PUT_FWERROR(sc, 0); 2429da8bb3a3SMike Smith 2430da8bb3a3SMike Smith return(2); 2431da8bb3a3SMike Smith } 24321ac4b82bSMike Smith 24331ac4b82bSMike Smith /******************************************************************************** 24341ac4b82bSMike Smith ******************************************************************************** 2435f6b84b08SMike Smith Type 4 interface accessor methods 2436f6b84b08SMike Smith ******************************************************************************** 2437f6b84b08SMike Smith ********************************************************************************/ 2438f6b84b08SMike Smith 2439f6b84b08SMike Smith /******************************************************************************** 2440f6b84b08SMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 2441f6b84b08SMike Smith * (the controller is not ready to take a command). 2442f6b84b08SMike Smith * 2443f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2444f6b84b08SMike Smith */ 2445f6b84b08SMike Smith static int 2446f6b84b08SMike Smith mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 2447f6b84b08SMike Smith { 2448f6b84b08SMike Smith int i; 2449f6b84b08SMike Smith 2450da8bb3a3SMike Smith debug_called(2); 2451f6b84b08SMike Smith 2452f6b84b08SMike Smith /* ready for our command? */ 2453f6b84b08SMike Smith if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) { 2454f6b84b08SMike Smith /* copy mailbox data to window */ 2455f6b84b08SMike Smith for (i = 0; i < 13; i++) 2456f6b84b08SMike Smith MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 2457f6b84b08SMike Smith 2458da8bb3a3SMike Smith /* memory-mapped controller, so issue a write barrier to ensure the mailbox is filled */ 2459da8bb3a3SMike Smith bus_space_barrier(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH, 2460da8bb3a3SMike Smith BUS_SPACE_BARRIER_WRITE); 2461da8bb3a3SMike Smith 2462f6b84b08SMike Smith /* post command */ 2463f6b84b08SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD); 2464f6b84b08SMike Smith return(1); 2465f6b84b08SMike Smith } 2466f6b84b08SMike Smith return(0); 2467f6b84b08SMike Smith } 2468f6b84b08SMike Smith 2469f6b84b08SMike Smith /******************************************************************************** 2470f6b84b08SMike Smith * See if a command has been completed, if so acknowledge its completion 2471f6b84b08SMike Smith * and recover the slot number and status code. 2472f6b84b08SMike Smith * 2473f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2474f6b84b08SMike Smith */ 2475f6b84b08SMike Smith static int 2476f6b84b08SMike Smith mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 2477f6b84b08SMike Smith { 2478f6b84b08SMike Smith 2479da8bb3a3SMike Smith debug_called(2); 2480f6b84b08SMike Smith 2481f6b84b08SMike Smith /* status available? */ 2482f6b84b08SMike Smith if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) { 2483f6b84b08SMike Smith *slot = MLX_V4_GET_STATUS_IDENT(sc); /* get command identifier */ 2484f6b84b08SMike Smith *status = MLX_V4_GET_STATUS(sc); /* get status */ 2485f6b84b08SMike Smith 2486f6b84b08SMike Smith /* acknowledge completion */ 2487f6b84b08SMike Smith MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK); 2488f6b84b08SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK); 2489f6b84b08SMike Smith return(1); 2490f6b84b08SMike Smith } 2491f6b84b08SMike Smith return(0); 2492f6b84b08SMike Smith } 2493f6b84b08SMike Smith 2494f6b84b08SMike Smith /******************************************************************************** 2495f6b84b08SMike Smith * Enable/disable interrupts as requested. 2496f6b84b08SMike Smith * 2497f6b84b08SMike Smith * Must be called at splbio or in a fashion that prevents reentry. 2498f6b84b08SMike Smith */ 2499f6b84b08SMike Smith static void 2500f6b84b08SMike Smith mlx_v4_intaction(struct mlx_softc *sc, int action) 2501f6b84b08SMike Smith { 2502da8bb3a3SMike Smith debug_called(1); 2503f6b84b08SMike Smith 2504f6b84b08SMike Smith switch(action) { 2505f6b84b08SMike Smith case MLX_INTACTION_DISABLE: 2506f6b84b08SMike Smith MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT); 2507f6b84b08SMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 2508f6b84b08SMike Smith break; 2509f6b84b08SMike Smith case MLX_INTACTION_ENABLE: 2510f6b84b08SMike Smith MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT); 2511f6b84b08SMike Smith sc->mlx_state |= MLX_STATE_INTEN; 2512f6b84b08SMike Smith break; 2513f6b84b08SMike Smith } 2514f6b84b08SMike Smith } 2515f6b84b08SMike Smith 2516da8bb3a3SMike Smith /******************************************************************************** 2517da8bb3a3SMike Smith * Poll for firmware error codes during controller initialisation. 2518da8bb3a3SMike Smith * Returns 0 if initialisation is complete, 1 if still in progress but no 2519da8bb3a3SMike Smith * error has been fetched, 2 if an error has been retrieved. 2520da8bb3a3SMike Smith */ 2521da8bb3a3SMike Smith static int 2522da8bb3a3SMike Smith mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 2523da8bb3a3SMike Smith { 2524da8bb3a3SMike Smith u_int8_t fwerror; 2525da8bb3a3SMike Smith static int initted = 0; 2526da8bb3a3SMike Smith 2527da8bb3a3SMike Smith debug_called(2); 2528da8bb3a3SMike Smith 2529da8bb3a3SMike Smith /* first time around, clear any hardware completion status */ 2530da8bb3a3SMike Smith if (!initted) { 2531da8bb3a3SMike Smith MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK); 2532da8bb3a3SMike Smith DELAY(1000); 2533da8bb3a3SMike Smith initted = 1; 2534da8bb3a3SMike Smith } 2535da8bb3a3SMike Smith 2536da8bb3a3SMike Smith /* init in progress? */ 2537da8bb3a3SMike Smith if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY)) 2538da8bb3a3SMike Smith return(0); 2539da8bb3a3SMike Smith 2540da8bb3a3SMike Smith /* test error value */ 2541da8bb3a3SMike Smith fwerror = MLX_V4_GET_FWERROR(sc); 2542da8bb3a3SMike Smith if (!(fwerror & MLX_V4_FWERROR_PEND)) 2543da8bb3a3SMike Smith return(1); 2544da8bb3a3SMike Smith 2545da8bb3a3SMike Smith /* mask status pending bit, fetch status */ 2546da8bb3a3SMike Smith *error = fwerror & ~MLX_V4_FWERROR_PEND; 2547da8bb3a3SMike Smith *param1 = MLX_V4_GET_FWERROR_PARAM1(sc); 2548da8bb3a3SMike Smith *param2 = MLX_V4_GET_FWERROR_PARAM2(sc); 2549da8bb3a3SMike Smith 2550da8bb3a3SMike Smith /* acknowledge */ 2551da8bb3a3SMike Smith MLX_V4_PUT_FWERROR(sc, 0); 2552da8bb3a3SMike Smith 2553da8bb3a3SMike Smith return(2); 2554da8bb3a3SMike Smith } 2555f6b84b08SMike Smith 2556f6b84b08SMike Smith /******************************************************************************** 2557f6b84b08SMike Smith ******************************************************************************** 25585792b7feSMike Smith Type 5 interface accessor methods 25595792b7feSMike Smith ******************************************************************************** 25605792b7feSMike Smith ********************************************************************************/ 25615792b7feSMike Smith 25625792b7feSMike Smith /******************************************************************************** 25635792b7feSMike Smith * Try to give (mc) to the controller. Returns 1 if successful, 0 on failure 25645792b7feSMike Smith * (the controller is not ready to take a command). 25655792b7feSMike Smith * 25665792b7feSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 25675792b7feSMike Smith */ 25685792b7feSMike Smith static int 25695792b7feSMike Smith mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc) 25705792b7feSMike Smith { 25715792b7feSMike Smith int i; 25725792b7feSMike Smith 2573da8bb3a3SMike Smith debug_called(2); 25745792b7feSMike Smith 25755792b7feSMike Smith /* ready for our command? */ 25765792b7feSMike Smith if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) { 25775792b7feSMike Smith /* copy mailbox data to window */ 25785792b7feSMike Smith for (i = 0; i < 13; i++) 25795792b7feSMike Smith MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]); 25805792b7feSMike Smith 25815792b7feSMike Smith /* post command */ 25825792b7feSMike Smith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD); 25835792b7feSMike Smith return(1); 25845792b7feSMike Smith } 25855792b7feSMike Smith return(0); 25865792b7feSMike Smith } 25875792b7feSMike Smith 25885792b7feSMike Smith /******************************************************************************** 25895792b7feSMike Smith * See if a command has been completed, if so acknowledge its completion 25905792b7feSMike Smith * and recover the slot number and status code. 25915792b7feSMike Smith * 25925792b7feSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 25935792b7feSMike Smith */ 25945792b7feSMike Smith static int 25955792b7feSMike Smith mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status) 25965792b7feSMike Smith { 25975792b7feSMike Smith 2598da8bb3a3SMike Smith debug_called(2); 25995792b7feSMike Smith 26005792b7feSMike Smith /* status available? */ 26015792b7feSMike Smith if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) { 26025792b7feSMike Smith *slot = MLX_V5_GET_STATUS_IDENT(sc); /* get command identifier */ 26035792b7feSMike Smith *status = MLX_V5_GET_STATUS(sc); /* get status */ 26045792b7feSMike Smith 26055792b7feSMike Smith /* acknowledge completion */ 26065792b7feSMike Smith MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK); 26075792b7feSMike Smith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK); 26085792b7feSMike Smith return(1); 26095792b7feSMike Smith } 26105792b7feSMike Smith return(0); 26115792b7feSMike Smith } 26125792b7feSMike Smith 26135792b7feSMike Smith /******************************************************************************** 26145792b7feSMike Smith * Enable/disable interrupts as requested. 26155792b7feSMike Smith * 26165792b7feSMike Smith * Must be called at splbio or in a fashion that prevents reentry. 26175792b7feSMike Smith */ 26185792b7feSMike Smith static void 26195792b7feSMike Smith mlx_v5_intaction(struct mlx_softc *sc, int action) 26205792b7feSMike Smith { 2621da8bb3a3SMike Smith debug_called(1); 26225792b7feSMike Smith 26235792b7feSMike Smith switch(action) { 26245792b7feSMike Smith case MLX_INTACTION_DISABLE: 2625da8bb3a3SMike Smith MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT); 26265792b7feSMike Smith sc->mlx_state &= ~MLX_STATE_INTEN; 26275792b7feSMike Smith break; 26285792b7feSMike Smith case MLX_INTACTION_ENABLE: 2629da8bb3a3SMike Smith MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT); 26305792b7feSMike Smith sc->mlx_state |= MLX_STATE_INTEN; 26315792b7feSMike Smith break; 26325792b7feSMike Smith } 26335792b7feSMike Smith } 26345792b7feSMike Smith 2635da8bb3a3SMike Smith /******************************************************************************** 2636da8bb3a3SMike Smith * Poll for firmware error codes during controller initialisation. 2637da8bb3a3SMike Smith * Returns 0 if initialisation is complete, 1 if still in progress but no 2638da8bb3a3SMike Smith * error has been fetched, 2 if an error has been retrieved. 2639da8bb3a3SMike Smith */ 2640da8bb3a3SMike Smith static int 2641da8bb3a3SMike Smith mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2) 2642da8bb3a3SMike Smith { 2643da8bb3a3SMike Smith u_int8_t fwerror; 2644da8bb3a3SMike Smith static int initted = 0; 2645da8bb3a3SMike Smith 2646da8bb3a3SMike Smith debug_called(2); 2647da8bb3a3SMike Smith 2648da8bb3a3SMike Smith /* first time around, clear any hardware completion status */ 2649da8bb3a3SMike Smith if (!initted) { 2650da8bb3a3SMike Smith MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK); 2651da8bb3a3SMike Smith DELAY(1000); 2652da8bb3a3SMike Smith initted = 1; 2653da8bb3a3SMike Smith } 2654da8bb3a3SMike Smith 2655da8bb3a3SMike Smith /* init in progress? */ 2656da8bb3a3SMike Smith if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE) 2657da8bb3a3SMike Smith return(0); 2658da8bb3a3SMike Smith 2659da8bb3a3SMike Smith /* test for error value */ 2660da8bb3a3SMike Smith fwerror = MLX_V5_GET_FWERROR(sc); 2661da8bb3a3SMike Smith if (!(fwerror & MLX_V5_FWERROR_PEND)) 2662da8bb3a3SMike Smith return(1); 2663da8bb3a3SMike Smith 2664da8bb3a3SMike Smith /* mask status pending bit, fetch status */ 2665da8bb3a3SMike Smith *error = fwerror & ~MLX_V5_FWERROR_PEND; 2666da8bb3a3SMike Smith *param1 = MLX_V5_GET_FWERROR_PARAM1(sc); 2667da8bb3a3SMike Smith *param2 = MLX_V5_GET_FWERROR_PARAM2(sc); 2668da8bb3a3SMike Smith 2669da8bb3a3SMike Smith /* acknowledge */ 2670da8bb3a3SMike Smith MLX_V5_PUT_FWERROR(sc, 0xff); 2671da8bb3a3SMike Smith 2672da8bb3a3SMike Smith return(2); 2673da8bb3a3SMike Smith } 26745792b7feSMike Smith 26755792b7feSMike Smith /******************************************************************************** 26765792b7feSMike Smith ******************************************************************************** 26771ac4b82bSMike Smith Debugging 26781ac4b82bSMike Smith ******************************************************************************** 26791ac4b82bSMike Smith ********************************************************************************/ 26801ac4b82bSMike Smith 26811ac4b82bSMike Smith /******************************************************************************** 26821ac4b82bSMike Smith * Return a status message describing (mc) 26831ac4b82bSMike Smith */ 26841ac4b82bSMike Smith static char *mlx_status_messages[] = { 26851ac4b82bSMike Smith "normal completion", /* 00 */ 26861ac4b82bSMike Smith "irrecoverable data error", /* 01 */ 26871ac4b82bSMike Smith "drive does not exist, or is offline", /* 02 */ 26881ac4b82bSMike Smith "attempt to write beyond end of drive", /* 03 */ 26891ac4b82bSMike Smith "bad data encountered", /* 04 */ 26901ac4b82bSMike Smith "invalid log entry request", /* 05 */ 26911ac4b82bSMike Smith "attempt to rebuild online drive", /* 06 */ 26921ac4b82bSMike Smith "new disk failed during rebuild", /* 07 */ 26931ac4b82bSMike Smith "invalid channel/target", /* 08 */ 26941ac4b82bSMike Smith "rebuild/check already in progress", /* 09 */ 26951ac4b82bSMike Smith "one or more disks are dead", /* 10 */ 26961ac4b82bSMike Smith "invalid or non-redundant drive", /* 11 */ 26971ac4b82bSMike Smith "channel is busy", /* 12 */ 26981ac4b82bSMike Smith "channel is not stopped", /* 13 */ 2699da8bb3a3SMike Smith "rebuild successfully terminated", /* 14 */ 2700da8bb3a3SMike Smith "unsupported command", /* 15 */ 2701da8bb3a3SMike Smith "check condition received", /* 16 */ 2702da8bb3a3SMike Smith "device is busy", /* 17 */ 2703da8bb3a3SMike Smith "selection or command timeout", /* 18 */ 2704da8bb3a3SMike Smith "command terminated abnormally", /* 19 */ 2705da8bb3a3SMike Smith "" 27061ac4b82bSMike Smith }; 27071ac4b82bSMike Smith 27081ac4b82bSMike Smith static struct 27091ac4b82bSMike Smith { 27101ac4b82bSMike Smith int command; 27111ac4b82bSMike Smith u_int16_t status; 27121ac4b82bSMike Smith int msg; 27131ac4b82bSMike Smith } mlx_messages[] = { 2714da8bb3a3SMike Smith {MLX_CMD_READSG, 0x0001, 1}, 2715da8bb3a3SMike Smith {MLX_CMD_READSG, 0x0002, 1}, 2716da8bb3a3SMike Smith {MLX_CMD_READSG, 0x0105, 3}, 2717da8bb3a3SMike Smith {MLX_CMD_READSG, 0x010c, 4}, 2718da8bb3a3SMike Smith {MLX_CMD_WRITESG, 0x0001, 1}, 2719da8bb3a3SMike Smith {MLX_CMD_WRITESG, 0x0002, 1}, 2720da8bb3a3SMike Smith {MLX_CMD_WRITESG, 0x0105, 3}, 2721da8bb3a3SMike Smith {MLX_CMD_READSG_OLD, 0x0001, 1}, 2722da8bb3a3SMike Smith {MLX_CMD_READSG_OLD, 0x0002, 1}, 2723da8bb3a3SMike Smith {MLX_CMD_READSG_OLD, 0x0105, 3}, 2724da8bb3a3SMike Smith {MLX_CMD_WRITESG_OLD, 0x0001, 1}, 2725da8bb3a3SMike Smith {MLX_CMD_WRITESG_OLD, 0x0002, 1}, 2726da8bb3a3SMike Smith {MLX_CMD_WRITESG_OLD, 0x0105, 3}, 27271ac4b82bSMike Smith {MLX_CMD_LOGOP, 0x0105, 5}, 27281ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0002, 6}, 27291ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0004, 7}, 27301ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0105, 8}, 27311ac4b82bSMike Smith {MLX_CMD_REBUILDASYNC, 0x0106, 9}, 2732da8bb3a3SMike Smith {MLX_CMD_REBUILDASYNC, 0x0107, 14}, 27331ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0002, 10}, 27341ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0105, 11}, 27351ac4b82bSMike Smith {MLX_CMD_CHECKASYNC, 0x0106, 9}, 27361ac4b82bSMike Smith {MLX_CMD_STOPCHANNEL, 0x0106, 12}, 27371ac4b82bSMike Smith {MLX_CMD_STOPCHANNEL, 0x0105, 8}, 27381ac4b82bSMike Smith {MLX_CMD_STARTCHANNEL, 0x0005, 13}, 27391ac4b82bSMike Smith {MLX_CMD_STARTCHANNEL, 0x0105, 8}, 2740da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x0002, 16}, 2741da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x0008, 17}, 2742da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x000e, 18}, 2743da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x000f, 19}, 2744da8bb3a3SMike Smith {MLX_CMD_DIRECT_CDB, 0x0105, 8}, 2745da8bb3a3SMike Smith 2746da8bb3a3SMike Smith {0, 0x0104, 14}, 27471ac4b82bSMike Smith {-1, 0, 0} 27481ac4b82bSMike Smith }; 27491ac4b82bSMike Smith 27501ac4b82bSMike Smith static char * 27511ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc) 27521ac4b82bSMike Smith { 27531ac4b82bSMike Smith static char unkmsg[80]; 27541ac4b82bSMike Smith int i; 27551ac4b82bSMike Smith 27561ac4b82bSMike Smith /* look up message in table */ 27571ac4b82bSMike Smith for (i = 0; mlx_messages[i].command != -1; i++) 2758da8bb3a3SMike Smith if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) && 2759466454bdSMike Smith (mc->mc_status == mlx_messages[i].status)) 27601ac4b82bSMike Smith return(mlx_status_messages[mlx_messages[i].msg]); 27611ac4b82bSMike Smith 27621ac4b82bSMike Smith sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]); 27631ac4b82bSMike Smith return(unkmsg); 27641ac4b82bSMike Smith } 27651ac4b82bSMike Smith 27661ac4b82bSMike Smith /******************************************************************************* 2767da8bb3a3SMike Smith * Print a string describing the controller (sc) 27681ac4b82bSMike Smith */ 27695792b7feSMike Smith static struct 27705792b7feSMike Smith { 27715792b7feSMike Smith int hwid; 27725792b7feSMike Smith char *name; 27735792b7feSMike Smith } mlx_controller_names[] = { 27745792b7feSMike Smith {0x01, "960P/PD"}, 27755792b7feSMike Smith {0x02, "960PL"}, 27765792b7feSMike Smith {0x10, "960PG"}, 27775792b7feSMike Smith {0x11, "960PJ"}, 27789eee27f1SMike Smith {0x12, "960PR"}, 27799eee27f1SMike Smith {0x13, "960PT"}, 27809eee27f1SMike Smith {0x14, "960PTL0"}, 27819eee27f1SMike Smith {0x15, "960PRL"}, 27829eee27f1SMike Smith {0x16, "960PTL1"}, 27839eee27f1SMike Smith {0x20, "1164PVX"}, 27845792b7feSMike Smith {-1, NULL} 27855792b7feSMike Smith }; 27865792b7feSMike Smith 27879eee27f1SMike Smith static void 27889eee27f1SMike Smith mlx_describe_controller(struct mlx_softc *sc) 27891ac4b82bSMike Smith { 27901ac4b82bSMike Smith static char buf[80]; 27915792b7feSMike Smith char *model; 27929eee27f1SMike Smith int i; 27931ac4b82bSMike Smith 27945792b7feSMike Smith for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) { 27959eee27f1SMike Smith if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) { 27965792b7feSMike Smith model = mlx_controller_names[i].name; 27971ac4b82bSMike Smith break; 27981ac4b82bSMike Smith } 27995792b7feSMike Smith } 28005792b7feSMike Smith if (model == NULL) { 28019eee27f1SMike Smith sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff); 28025792b7feSMike Smith model = buf; 28035792b7feSMike Smith } 2804da8bb3a3SMike Smith device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n", 28059eee27f1SMike Smith model, 28069eee27f1SMike Smith sc->mlx_enq2->me_actual_channels, 28079eee27f1SMike Smith sc->mlx_enq2->me_actual_channels > 1 ? "s" : "", 28089eee27f1SMike Smith sc->mlx_enq2->me_firmware_id & 0xff, 28099eee27f1SMike Smith (sc->mlx_enq2->me_firmware_id >> 8) & 0xff, 28109eee27f1SMike Smith (sc->mlx_enq2->me_firmware_id >> 24) & 0xff, 2811b9256fe3SMike Smith (sc->mlx_enq2->me_firmware_id >> 16) & 0xff, 28129eee27f1SMike Smith sc->mlx_enq2->me_mem_size / (1024 * 1024)); 28139eee27f1SMike Smith 28149eee27f1SMike Smith if (bootverbose) { 28159eee27f1SMike Smith device_printf(sc->mlx_dev, " Hardware ID 0x%08x\n", sc->mlx_enq2->me_hardware_id); 28169eee27f1SMike Smith device_printf(sc->mlx_dev, " Firmware ID 0x%08x\n", sc->mlx_enq2->me_firmware_id); 28179eee27f1SMike Smith device_printf(sc->mlx_dev, " Configured/Actual channels %d/%d\n", sc->mlx_enq2->me_configured_channels, 28189eee27f1SMike Smith sc->mlx_enq2->me_actual_channels); 28199eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Targets %d\n", sc->mlx_enq2->me_max_targets); 28209eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Tags %d\n", sc->mlx_enq2->me_max_tags); 28219eee27f1SMike Smith device_printf(sc->mlx_dev, " Max System Drives %d\n", sc->mlx_enq2->me_max_sys_drives); 28229eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Arms %d\n", sc->mlx_enq2->me_max_arms); 28239eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Spans %d\n", sc->mlx_enq2->me_max_spans); 28249eee27f1SMike Smith device_printf(sc->mlx_dev, " DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size, 28259eee27f1SMike Smith sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size); 28269eee27f1SMike Smith device_printf(sc->mlx_dev, " DRAM type %d\n", sc->mlx_enq2->me_mem_type); 28279eee27f1SMike Smith device_printf(sc->mlx_dev, " Clock Speed %dns\n", sc->mlx_enq2->me_clock_speed); 28289eee27f1SMike Smith device_printf(sc->mlx_dev, " Hardware Speed %dns\n", sc->mlx_enq2->me_hardware_speed); 28299eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Commands %d\n", sc->mlx_enq2->me_max_commands); 28309eee27f1SMike Smith device_printf(sc->mlx_dev, " Max SG Entries %d\n", sc->mlx_enq2->me_max_sg); 28319eee27f1SMike Smith device_printf(sc->mlx_dev, " Max DP %d\n", sc->mlx_enq2->me_max_dp); 28329eee27f1SMike Smith device_printf(sc->mlx_dev, " Max IOD %d\n", sc->mlx_enq2->me_max_iod); 28339eee27f1SMike Smith device_printf(sc->mlx_dev, " Max Comb %d\n", sc->mlx_enq2->me_max_comb); 28349eee27f1SMike Smith device_printf(sc->mlx_dev, " Latency %ds\n", sc->mlx_enq2->me_latency); 28359eee27f1SMike Smith device_printf(sc->mlx_dev, " SCSI Timeout %ds\n", sc->mlx_enq2->me_scsi_timeout); 28369eee27f1SMike Smith device_printf(sc->mlx_dev, " Min Free Lines %d\n", sc->mlx_enq2->me_min_freelines); 28379eee27f1SMike Smith device_printf(sc->mlx_dev, " Rate Constant %d\n", sc->mlx_enq2->me_rate_const); 28389eee27f1SMike Smith device_printf(sc->mlx_dev, " MAXBLK %d\n", sc->mlx_enq2->me_maxblk); 28399eee27f1SMike Smith device_printf(sc->mlx_dev, " Blocking Factor %d sectors\n", sc->mlx_enq2->me_blocking_factor); 28409eee27f1SMike Smith device_printf(sc->mlx_dev, " Cache Line Size %d blocks\n", sc->mlx_enq2->me_cacheline); 28419eee27f1SMike Smith device_printf(sc->mlx_dev, " SCSI Capability %s%dMHz, %d bit\n", 28429eee27f1SMike Smith sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "", 28439eee27f1SMike Smith (1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10, 28449eee27f1SMike Smith 8 << (sc->mlx_enq2->me_scsi_cap & 0x3)); 28459eee27f1SMike Smith device_printf(sc->mlx_dev, " Firmware Build Number %d\n", sc->mlx_enq2->me_firmware_build); 28469eee27f1SMike Smith device_printf(sc->mlx_dev, " Fault Management Type %d\n", sc->mlx_enq2->me_fault_mgmt_type); 28479eee27f1SMike Smith device_printf(sc->mlx_dev, " Features %b\n", sc->mlx_enq2->me_firmware_features, 28489eee27f1SMike Smith "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n"); 28499eee27f1SMike Smith 28509eee27f1SMike Smith } 28511ac4b82bSMike Smith } 28521ac4b82bSMike Smith 2853da8bb3a3SMike Smith /******************************************************************************* 2854da8bb3a3SMike Smith * Emit a string describing the firmware handshake status code, and return a flag 2855da8bb3a3SMike Smith * indicating whether the code represents a fatal error. 2856da8bb3a3SMike Smith * 2857da8bb3a3SMike Smith * Error code interpretations are from the Linux driver, and don't directly match 2858da8bb3a3SMike Smith * the messages printed by Mylex's BIOS. This may change if documentation on the 2859da8bb3a3SMike Smith * codes is forthcoming. 2860da8bb3a3SMike Smith */ 2861da8bb3a3SMike Smith static int 2862da8bb3a3SMike Smith mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2) 2863da8bb3a3SMike Smith { 2864da8bb3a3SMike Smith switch(error) { 2865da8bb3a3SMike Smith case 0x00: 2866da8bb3a3SMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1); 2867da8bb3a3SMike Smith break; 2868da8bb3a3SMike Smith case 0x08: 2869da8bb3a3SMike Smith /* we could be neater about this and give some indication when we receive more of them */ 2870da8bb3a3SMike Smith if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) { 2871da8bb3a3SMike Smith device_printf(sc->mlx_dev, "spinning up drives...\n"); 2872da8bb3a3SMike Smith sc->mlx_flags |= MLX_SPINUP_REPORTED; 2873da8bb3a3SMike Smith } 2874da8bb3a3SMike Smith break; 2875da8bb3a3SMike Smith case 0x30: 2876da8bb3a3SMike Smith device_printf(sc->mlx_dev, "configuration checksum error\n"); 2877da8bb3a3SMike Smith break; 2878da8bb3a3SMike Smith case 0x60: 2879da8bb3a3SMike Smith device_printf(sc->mlx_dev, "mirror race recovery failed\n"); 2880da8bb3a3SMike Smith break; 2881da8bb3a3SMike Smith case 0x70: 2882da8bb3a3SMike Smith device_printf(sc->mlx_dev, "mirror race recovery in progress\n"); 2883da8bb3a3SMike Smith break; 2884da8bb3a3SMike Smith case 0x90: 2885da8bb3a3SMike Smith device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1); 2886da8bb3a3SMike Smith break; 2887da8bb3a3SMike Smith case 0xa0: 2888da8bb3a3SMike Smith device_printf(sc->mlx_dev, "logical drive installation aborted\n"); 2889da8bb3a3SMike Smith break; 2890da8bb3a3SMike Smith case 0xb0: 2891da8bb3a3SMike Smith device_printf(sc->mlx_dev, "mirror race on a critical system drive\n"); 2892da8bb3a3SMike Smith break; 2893da8bb3a3SMike Smith case 0xd0: 2894da8bb3a3SMike Smith device_printf(sc->mlx_dev, "new controller configuration found\n"); 2895da8bb3a3SMike Smith break; 2896da8bb3a3SMike Smith case 0xf0: 2897da8bb3a3SMike Smith device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n"); 2898da8bb3a3SMike Smith return(1); 2899da8bb3a3SMike Smith default: 2900da8bb3a3SMike Smith device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2); 2901da8bb3a3SMike Smith break; 2902da8bb3a3SMike Smith } 2903da8bb3a3SMike Smith return(0); 2904da8bb3a3SMike Smith } 2905da8bb3a3SMike Smith 29061ac4b82bSMike Smith /******************************************************************************** 29071ac4b82bSMike Smith ******************************************************************************** 29081ac4b82bSMike Smith Utility Functions 29091ac4b82bSMike Smith ******************************************************************************** 29101ac4b82bSMike Smith ********************************************************************************/ 29111ac4b82bSMike Smith 29121ac4b82bSMike Smith /******************************************************************************** 29131ac4b82bSMike Smith * Find the disk whose unit number is (unit) on this controller 29141ac4b82bSMike Smith */ 29151ac4b82bSMike Smith static struct mlx_sysdrive * 29161ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit) 29171ac4b82bSMike Smith { 29181ac4b82bSMike Smith int i; 29191ac4b82bSMike Smith 29201ac4b82bSMike Smith /* search system drives */ 29211ac4b82bSMike Smith for (i = 0; i < MLX_MAXDRIVES; i++) { 29221ac4b82bSMike Smith /* is this one attached? */ 29231ac4b82bSMike Smith if (sc->mlx_sysdrive[i].ms_disk != 0) { 29241ac4b82bSMike Smith /* is this the one? */ 29251ac4b82bSMike Smith if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk)) 29261ac4b82bSMike Smith return(&sc->mlx_sysdrive[i]); 29271ac4b82bSMike Smith } 29281ac4b82bSMike Smith } 29291ac4b82bSMike Smith return(NULL); 29301ac4b82bSMike Smith } 2931